| |
| """ZabaanAI-v2 SFT Data Curation Pipeline |
| |
| Downloads instruction datasets from HF Hub and converts to ChatML messages format |
| for Qwen2.5-7B-Instruct SFT training. |
| |
| Supported formats -> ChatML conversion: |
| - Alpaca (instruction/input/output) -> messages |
| - Custom (text fields) -> messages |
| |
| Run: python scripts/01_curate_sft_data.py |
| """ |
| import json |
| import argparse |
| from pathlib import Path |
| from datasets import load_dataset |
|
|
| DATASETS = { |
| "urdu_instruct": { |
| "source": "large-traversaal/urdu-instruct", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "ur", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| "sindhi_intelligence": { |
| "source": "aakashMeghwar01/Sindhi-Intelligence-Core-SFT", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "sd", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| "pashto_alpaca": { |
| "source": "saillab/alpaca_pashto_taco", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "ps", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| "urdu_news_gen": { |
| "source": "AhmadMustafa/Urdu-Instruct-News-Article-Generation", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "ur", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| "urdu_news_class": { |
| "source": "AhmadMustafa/Urdu-Instruct-News-Category-Classification", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "ur", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| "urdu_news_headline": { |
| "source": "AhmadMustafa/Urdu-Instruct-News-Headline-Generation", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "ur", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| "urdu_alpaca": { |
| "source": "ravithejads/alpaca_urdu_cleaned_instruction", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "ur", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| "punjabi_alpaca": { |
| "source": "japneets/Alpaca_instruction_fine_tune_Punjabi", |
| "split": "train", |
| "format": "alpaca", |
| "lang": "pa", |
| "instruction_col": "instruction", |
| "input_col": "input", |
| "output_col": "output", |
| }, |
| } |
|
|
|
|
| def alpaca_to_messages(example, inst_col, inp_col, out_col, lang): |
| """Convert Alpaca format to ChatML messages format for SFT.""" |
| instruction = str(example.get(inst_col, "")).strip() |
| inp = str(example.get(inp_col, "")).strip() |
| output = str(example.get(out_col, "")).strip() |
|
|
| if not instruction or not output: |
| return None |
|
|
| |
| if inp and inp.lower() not in ["none", "nil", ""]: |
| user_content = f"{instruction}\n\n{inp}" |
| else: |
| user_content = instruction |
|
|
| |
| messages = [ |
| {"role": "system", "content": f"You are ZabaanAI, a helpful AI assistant fluent in {lang} and other Pakistan languages. Respond accurately and respectfully."}, |
| {"role": "user", "content": user_content}, |
| {"role": "assistant", "content": output}, |
| ] |
|
|
| return {"messages": messages, "language": lang} |
|
|
|
|
| def process_dataset(name, config, output_dir, max_samples=None): |
| """Download and format a dataset.""" |
| print(f"\nLoading: {name} ({config[\"source\"]})") |
| try: |
| ds = load_dataset( |
| config["source"], |
| split=config["split"], |
| trust_remote_code=True, |
| ) |
| if max_samples: |
| ds = ds.select(range(min(max_samples, len(ds)))) |
| print(f" Loaded {len(ds):,} examples") |
| except Exception as e: |
| print(f" Error: {e}") |
| return 0 |
| |
| formatted = [] |
| for example in ds: |
| if config["format"] == "alpaca": |
| result = alpaca_to_messages( |
| example, |
| config["instruction_col"], |
| config["input_col"], |
| config["output_col"], |
| config["lang"], |
| ) |
| else: |
| continue |
| |
| if result: |
| formatted.append(result) |
| |
| # Save |
| output_file = output_dir / f"{name}.jsonl" |
| with open(output_file, "w", encoding="utf-8") as f: |
| for item in formatted: |
| f.write(json.dumps(item, ensure_ascii=False) + "\n") |
| |
| print(f" Saved {len(formatted):,} examples to {output_file.name}") |
| return len(formatted) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--output_dir", default="data/formatted") |
| parser.add_argument("--max_samples", type=int, default=None) |
| args = parser.parse_args() |
| |
| output_dir = Path(args.output_dir) |
| output_dir.mkdir(parents=True, exist_ok=True) |
| |
| print("=" * 60) |
| print(" ZabaanAI-v2 SFT Data Curation") |
| print("=" * 60) |
| |
| total = 0 |
| for name, config in DATASETS.items(): |
| count = process_dataset(name, config, output_dir, args.max_samples) |
| total += count |
| |
| print(f"\nTotal formatted examples: {total:,}") |
| print("=" * 60) |
| |
| |
| if __name__ == "__main__": |
| main() |
| |