File size: 7,192 Bytes
f908c84 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | """
Multilingual data prep — Helena (multilingual_model)
Using facebook/belebele as the main dataset — it has exactly our 5 target
languages (Italian, Spanish, Chinese, Russian, Hindi) with 900 MC questions
each. Already in the right format (passage + question + 4 options + answer).
Also loading xcopa for Italian and Chinese as extra data (commonsense MC).
Adapted from trainingPhuc/data.py but much simpler since belebele is
already MC — no need to extract boxed answers from math solutions etc.
Output: sft_train.jsonl, sft_val.jsonl, grpo_train.jsonl
"""
import random
from pathlib import Path
from datasets import load_dataset, Dataset
from transformers import AutoTokenizer
# our 5 target languages + belebele language codes (from the dataset card)
LANGS = {
"italian": "ita_Latn",
"spanish": "spa_Latn",
"chinese": "zho_Hans",
"russian": "rus_Cyrl",
"hindi": "hin_Deva",
}
# system prompt — tells the model to output \boxed{letter}
# keeping it short and language-neutral so it works across all 5 languages
SYSTEM = (
"Answer the multiple choice question by reasoning step by step. "
"Write your final answer as \\boxed{A}, \\boxed{B}, \\boxed{C}, etc."
)
def load_belebele(langs=LANGS):
"""Load belebele for all 5 languages.
Each language has 900 test examples. The dataset has:
- flores_passage: reading passage in the target language
- question: question text in the target language
- mc_answer1-4: the 4 options
- correct_answer_num: 1-4 (we convert to A-D)
"""
examples = []
for lang_name, lang_code in langs.items():
try:
ds = load_dataset(
"facebook/belebele", lang_code,
split="test", trust_remote_code=True
)
except Exception as e:
print(f" WARNING: could not load belebele/{lang_code}: {e}")
continue
for row in ds:
answer_letter = "ABCD"[int(row["correct_answer_num"]) - 1]
# format: passage + question + labeled options
prompt = (
f"{row['flores_passage'].strip()}\n\n"
f"{row['question'].strip()}\n\n"
f"A) {row['mc_answer1']}\n"
f"B) {row['mc_answer2']}\n"
f"C) {row['mc_answer3']}\n"
f"D) {row['mc_answer4']}"
)
examples.append({
"prompt": prompt,
"answer": answer_letter,
"language": lang_name,
"source": "belebele",
})
n = sum(1 for e in examples if e["language"] == lang_name)
print(f" belebele/{lang_code} ({lang_name}): {n} examples")
return examples
def load_xcopa(langs={"italian": "it", "chinese": "zh"}):
"""Extra data from XCOPA for Italian and Chinese.
XCOPA is commonsense MC with 2 options (cause/effect).
~500 examples per language across train/val/test splits.
Not available for all 5 languages so we only use it where it exists.
"""
examples = []
for lang_name, lang_code in langs.items():
count = 0
for split in ["train", "validation", "test"]:
try:
ds = load_dataset("xcopa", lang_code, split=split)
except Exception:
continue
for row in ds:
# xcopa has "cause" or "effect" as the question type
q_suffix = (
"What was the cause of this?"
if row["question"] == "cause"
else "What happened as a result?"
)
prompt = (
f"{row['premise'].strip()} {q_suffix}\n\n"
f"A) {row['choice1']}\n"
f"B) {row['choice2']}"
)
answer = "AB"[int(row["label"])]
examples.append({
"prompt": prompt,
"answer": answer,
"language": lang_name,
"source": "xcopa",
})
count += 1
print(f" xcopa/{lang_code} ({lang_name}): {count} examples")
return examples
def format_for_sft(examples, tokenizer):
"""Format examples as SFT training rows using the chat template.
Assistant response is just \boxed{letter} — the model will generate
its own reasoning in the <think> block at inference time (thinking=ON).
"""
rows = []
for ex in examples:
messages = [
{"role": "system", "content": SYSTEM},
{"role": "user", "content": ex["prompt"]},
{"role": "assistant", "content": f"\\boxed{{{ex['answer']}}}"},
]
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=False
)
rows.append({
"text": text,
"prompt": ex["prompt"],
"answer": ex["answer"],
"language": ex["language"],
"source": ex["source"],
})
return Dataset.from_list(rows)
def format_for_grpo(examples):
"""Format for GRPOTrainer — prompt only, no assistant turn.
GRPO samples its own completions and scores them with the reward function.
Gold answer is kept as a column for the reward function to compare against.
"""
rows = []
for ex in examples:
rows.append({
"prompt": [
{"role": "system", "content": SYSTEM},
{"role": "user", "content": ex["prompt"]},
],
"answer": ex["answer"],
"language": ex["language"],
"source": ex["source"],
})
return Dataset.from_list(rows)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--model", default="/shared-ro/models/Qwen/Qwen3-1.7B")
parser.add_argument("--output-dir", default="/scratch/multilingual_data")
parser.add_argument("--no-xcopa", action="store_true",
help="skip loading xcopa (use only belebele)")
parser.add_argument("--val-frac", type=float, default=0.05)
parser.add_argument("--seed", type=int, default=42)
args = parser.parse_args()
tokenizer = AutoTokenizer.from_pretrained(args.model)
out = Path(args.output_dir)
out.mkdir(parents=True, exist_ok=True)
print("Loading belebele...")
examples = load_belebele()
if not args.no_xcopa:
print("Loading xcopa (Italian + Chinese)...")
examples += load_xcopa()
print(f"\nTotal: {len(examples)} examples")
# shuffle and split
random.seed(args.seed)
random.shuffle(examples)
n_val = int(len(examples) * args.val_frac)
val_ex = examples[:n_val]
train_ex = examples[n_val:]
print(f"Train: {len(train_ex)}, Val: {len(val_ex)}")
# save all three splits
format_for_sft(train_ex, tokenizer).to_json(out / "sft_train.jsonl")
format_for_sft(val_ex, tokenizer).to_json(out / "sft_val.jsonl")
format_for_grpo(train_ex).to_json(out / "grpo_train.jsonl")
print(f"\nDone. Saved to {out}/")
|