| |
| """ |
| Kannada SFT Data Preparation Script |
| ==================================== |
| Prepares instruction-tuning datasets for Kannada SFT. |
| |
| Datasets used: |
| 1. Cognitive-Lab/Kannada_Bilingual_Instruct (instruction, output, translation) |
| 2. ai4bharat/indic-align (multiple configs: Dolly, WikiHow, etc.) |
| 3. Cognitive-Lab/hh_dpo_kannada_translated (converted to SFT format from chosen responses) |
| |
| Output: ./data/sft_kannada/ (parquet shards in conversational messages format) |
| |
| The SFTTrainer expects either: |
| - {"text": "..."} for language modeling |
| - {"messages": [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}]} |
| - {"prompt": "...", "completion": "..."} |
| |
| We produce the conversational messages format. |
| |
| Usage: |
| python prepare_sft_data.py [--output_dir ./data/sft_kannada] |
| """ |
|
|
| import argparse |
| import os |
| import sys |
| import logging |
| import json |
|
|
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") |
| log = logging.getLogger(__name__) |
|
|
|
|
| |
| |
| |
|
|
| def load_kannada_bilingual_instruct(): |
| """ |
| Cognitive-Lab/Kannada_Bilingual_Instruct |
| Columns: instruction, output, translation, text |
| Each row has Kannada instruction + Kannada output + English translation. |
| """ |
| from datasets import load_dataset |
| log.info("Loading Cognitive-Lab/Kannada_Bilingual_Instruct...") |
| try: |
| ds = load_dataset("Cognitive-Lab/Kannada_Bilingual_Instruct", split="train", streaming=True) |
| count = 0 |
| for row in ds: |
| instruction = row.get("instruction", "").strip() |
| output = row.get("output", "").strip() |
| if not instruction or not output: |
| continue |
| yield { |
| "messages": [ |
| {"role": "user", "content": instruction}, |
| {"role": "assistant", "content": output}, |
| ], |
| "source": "kannada_bilingual_instruct", |
| } |
| count += 1 |
| log.info(f" kannada_bilingual_instruct: {count:,} examples") |
| except Exception as e: |
| log.error(f" Failed: {e}") |
|
|
|
|
| def load_indic_align(): |
| """ |
| ai4bharat/indic-align — multi-config dataset with translated instruction data. |
| Configs include: dolly_translated, wikihow_translated, vicuna_translated, etc. |
| We try to load Kannada-specific configs. |
| """ |
| from datasets import load_dataset |
|
|
| |
| configs_to_try = [ |
| "kn", |
| ] |
|
|
| log.info("Loading ai4bharat/indic-align...") |
| for config in configs_to_try: |
| try: |
| ds = load_dataset("ai4bharat/indic-align", config, split="train", streaming=True) |
| count = 0 |
| for row in ds: |
| |
| instruction = row.get("instruction", "").strip() |
| inp = row.get("input", "").strip() |
| output = row.get("output", "").strip() |
|
|
| if inp: |
| instruction = f"{instruction}\n\n{inp}" |
|
|
| if not instruction or not output: |
| continue |
|
|
| yield { |
| "messages": [ |
| {"role": "user", "content": instruction}, |
| {"role": "assistant", "content": output}, |
| ], |
| "source": "indic_align", |
| } |
| count += 1 |
| log.info(f" indic_align ({config}): {count:,} examples") |
| except Exception as e: |
| log.warning(f" indic-align config '{config}' failed: {e}") |
|
|
| |
| try: |
| ds = load_dataset("ai4bharat/indic-align", split="train", streaming=True) |
| count = 0 |
| for row in ds: |
| lang = row.get("language", "").lower() |
| if "kan" not in lang and "kannada" not in lang: |
| continue |
| instruction = row.get("instruction", "").strip() |
| inp = row.get("input", "").strip() |
| output = row.get("output", "").strip() |
|
|
| if inp: |
| instruction = f"{instruction}\n\n{inp}" |
|
|
| if not instruction or not output: |
| continue |
|
|
| yield { |
| "messages": [ |
| {"role": "user", "content": instruction}, |
| {"role": "assistant", "content": output}, |
| ], |
| "source": "indic_align_filtered", |
| } |
| count += 1 |
| log.info(f" indic_align (filtered kn): {count:,} examples") |
| except Exception as e: |
| log.warning(f" indic-align multi-language filtering failed: {e}") |
|
|
|
|
| def load_hh_dpo_kannada_as_sft(): |
| """ |
| Cognitive-Lab/hh_dpo_kannada_translated |
| This is a DPO dataset (prompt, chosen, rejected) — we extract the chosen responses |
| as SFT data. |
| """ |
| from datasets import load_dataset |
| log.info("Loading Cognitive-Lab/hh_dpo_kannada_translated (as SFT)...") |
| try: |
| ds = load_dataset("Cognitive-Lab/hh_dpo_kannada_translated", split="train", streaming=True) |
| count = 0 |
| for row in ds: |
| |
| prompt = row.get("prompt", "").strip() |
| chosen = row.get("chosen", "").strip() |
| if not prompt or not chosen: |
| continue |
| yield { |
| "messages": [ |
| {"role": "user", "content": prompt}, |
| {"role": "assistant", "content": chosen}, |
| ], |
| "source": "hh_dpo_kannada_chosen", |
| } |
| count += 1 |
| log.info(f" hh_dpo_kannada_chosen: {count:,} examples") |
| except Exception as e: |
| log.error(f" Failed: {e}") |
|
|
|
|
| def load_kannada_instruct_dataset(): |
| """ |
| Cognitive-Lab/Kannada-Instruct-dataset (may be gated) |
| """ |
| from datasets import load_dataset |
| log.info("Loading Cognitive-Lab/Kannada-Instruct-dataset...") |
| try: |
| ds = load_dataset("Cognitive-Lab/Kannada-Instruct-dataset", split="train", streaming=True) |
| count = 0 |
| for row in ds: |
| instruction = row.get("instruction", row.get("input", "")).strip() |
| output = row.get("output", row.get("response", "")).strip() |
| if not instruction or not output: |
| continue |
| yield { |
| "messages": [ |
| {"role": "user", "content": instruction}, |
| {"role": "assistant", "content": output}, |
| ], |
| "source": "kannada_instruct", |
| } |
| count += 1 |
| log.info(f" kannada_instruct: {count:,} examples") |
| except Exception as e: |
| log.warning(f" Kannada-Instruct-dataset failed (may be gated): {e}") |
|
|
|
|
| def load_aya_kannada(): |
| """ |
| Cognitive-Lab/Aya_Kannada (may be gated) |
| Part of the Cohere Aya collection. |
| """ |
| from datasets import load_dataset |
| log.info("Loading Cognitive-Lab/Aya_Kannada...") |
| try: |
| ds = load_dataset("Cognitive-Lab/Aya_Kannada", "aya_dataset", split="train", streaming=True) |
| count = 0 |
| for row in ds: |
| |
| instruction = row.get("instruction", row.get("inputs", "")).strip() |
| output = row.get("output", row.get("targets", "")).strip() |
| if not instruction or not output: |
| continue |
| yield { |
| "messages": [ |
| {"role": "user", "content": instruction}, |
| {"role": "assistant", "content": output}, |
| ], |
| "source": "aya_kannada", |
| } |
| count += 1 |
| log.info(f" aya_kannada: {count:,} examples") |
| except Exception as e: |
| log.warning(f" Aya_Kannada failed (may be gated): {e}") |
|
|
|
|
| |
| |
| |
|
|
| def main(): |
| parser = argparse.ArgumentParser(description="Prepare Kannada SFT data") |
| parser.add_argument("--output_dir", default="./data/sft_kannada", |
| help="Output directory") |
| parser.add_argument("--shard_size", type=int, default=50000, |
| help="Examples per shard") |
| args = parser.parse_args() |
|
|
| os.makedirs(args.output_dir, exist_ok=True) |
|
|
| log.info("=" * 70) |
| log.info("Kannada SFT Data Preparation") |
| log.info("=" * 70) |
|
|
| all_loaders = [ |
| load_kannada_bilingual_instruct, |
| load_indic_align, |
| load_hh_dpo_kannada_as_sft, |
| load_kannada_instruct_dataset, |
| load_aya_kannada, |
| ] |
|
|
| import pyarrow as pa |
| import pyarrow.parquet as pq |
|
|
| shard_num = 0 |
| current_shard = [] |
| total = 0 |
|
|
| for loader in all_loaders: |
| for item in loader(): |
| current_shard.append(item) |
| total += 1 |
|
|
| if len(current_shard) >= args.shard_size: |
| out_path = os.path.join(args.output_dir, f"sft_shard_{shard_num:05d}.parquet") |
| table = pa.Table.from_pylist(current_shard) |
| pq.write_table(table, out_path) |
| log.info(f"Wrote {out_path}: {len(current_shard):,} examples") |
| shard_num += 1 |
| current_shard = [] |
|
|
| |
| if current_shard: |
| out_path = os.path.join(args.output_dir, f"sft_shard_{shard_num:05d}.parquet") |
| table = pa.Table.from_pylist(current_shard) |
| pq.write_table(table, out_path) |
| log.info(f"Wrote {out_path}: {len(current_shard):,} examples") |
|
|
| log.info("=" * 70) |
| log.info(f"DONE: {total:,} examples in {shard_num + 1} shards") |
| log.info(f"Output: {args.output_dir}") |
| log.info("=" * 70) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|