Files
Tokenizer_Swap/dataset_building/build_heldout_public_mcq_benchmark.py
2026-06-18 10:10:57 +00:00

378 lines
10 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import json
import os
import random
from pathlib import Path
from datasets import get_dataset_config_names, load_dataset
MMLU_DATASET = "cais/mmlu"
GPQA_DATASET = "Idavidrein/gpqa"
CEVAL_DATASET = "ceval/ceval-exam"
CMMLU_DATASET = "haonan-li/cmmlu"
MMLU_CONFIGS = [
"abstract_algebra",
"anatomy",
"astronomy",
"business_ethics",
"clinical_knowledge",
"college_biology",
"college_chemistry",
"college_computer_science",
"college_mathematics",
"college_physics",
"computer_security",
"conceptual_physics",
"econometrics",
"electrical_engineering",
"elementary_mathematics",
"formal_logic",
"global_facts",
"high_school_biology",
"high_school_chemistry",
"high_school_computer_science",
"high_school_government_and_politics",
"high_school_macroeconomics",
"high_school_mathematics",
"high_school_physics",
"high_school_statistics",
"international_law",
"jurisprudence",
"machine_learning",
"management",
"marketing",
"medical_genetics",
"moral_disputes",
"nutrition",
"philosophy",
"professional_law",
"professional_medicine",
"professional_psychology",
"public_relations",
"security_studies",
"sociology",
"us_foreign_policy",
"world_religions",
]
CEVAL_CONFIGS = [
"computer_network",
"operating_system",
"computer_architecture",
"college_programming",
"college_physics",
"college_chemistry",
"advanced_mathematics",
"probability_and_statistics",
"discrete_mathematics",
"electrical_engineer",
"metrology_engineer",
"high_school_mathematics",
"high_school_physics",
"high_school_chemistry",
"high_school_biology",
"legal_professional",
"business_administration",
"marxism",
"mao_zedong_thought",
"education_science",
"teacher_qualification",
"modern_chinese_history",
"chinese_language_and_literature",
"logic",
]
CMMLU_CONFIGS = [
"agronomy",
"anatomy",
"ancient_chinese",
"arts",
"astronomy",
"business_ethics",
"chinese_civil_service_exam",
"chinese_driving_rule",
"chinese_food_culture",
"chinese_foreign_policy",
"chinese_history",
"college_actuarial_science",
"college_education",
"college_engineering_hydrology",
"college_law",
"college_mathematics",
"college_medical_statistics",
"college_medicine",
"computer_science",
"conceptual_physics",
"econometrics",
"education",
"electrical_engineering",
"elementary_chinese",
"elementary_commonsense",
"elementary_information_and_technology",
"elementary_mathematics",
"ethnology",
"food_science",
"genetics",
"global_facts",
"high_school_biology",
"high_school_chemistry",
"high_school_geography",
"high_school_mathematics",
"high_school_physics",
"human_sexuality",
"international_law",
"journalism",
"jurisprudence",
"legal_and_moral_basis",
"logical",
"machine_learning",
"management",
"marketing",
"marxist_theory",
"modern_chinese",
"nutrition",
"philosophy",
"professional_accounting",
"professional_law",
"professional_medicine",
"professional_psychology",
"public_relations",
"security_study",
"sociology",
"sports_science",
"traditional_chinese_medicine",
"virology",
"world_history",
"world_religions",
]
def norm(text):
return " ".join(str(text or "").replace("\x00", " ").split()).strip()
def stable_sample(rng, rows, n):
if len(rows) <= n:
return list(rows)
return rng.sample(rows, n)
def add_item(items, prefix, idx, source, subset, question, choices, answer_idx, metadata=None):
question = norm(question)
choices = [norm(x) for x in choices]
if not question or len(choices) < 2:
return
if answer_idx < 0 or answer_idx >= len(choices):
return
answer_text = choices[answer_idx]
if not answer_text:
return
mcq_prompt = (
"Answer the following multiple-choice question. Choose the single best option.\n\n"
f"Question:\n{question}\n\nAnswer:"
)
ppl_text = f"Question: {question}\nCorrect answer: {answer_text}"
items.append(
{
"id": f"{prefix}_{idx:05d}",
"category": prefix,
"source": source,
"subset": subset,
"ppl_text": ppl_text,
"mcq_prompt": mcq_prompt,
"choices": choices,
"answer_idx": int(answer_idx),
"answer_text": answer_text,
"metadata": metadata or {},
}
)
def build_mmlu(rng, quota):
configs = list(MMLU_CONFIGS)
rng.shuffle(configs)
rows = []
per_config = max(20, quota // 24)
for cfg in configs:
print(f"[info] load mmlu {cfg}", flush=True)
try:
ds = load_dataset(MMLU_DATASET, cfg, split="test")
except Exception as exc:
print(f"[warn] skip MMLU {cfg}: {type(exc).__name__}: {str(exc)[:160]}")
continue
sampled = stable_sample(rng, list(ds), min(per_config, len(ds)))
for r in sampled:
rows.append((cfg, r))
if len(rows) >= quota * 2:
break
rng.shuffle(rows)
items = []
for i, (cfg, r) in enumerate(rows[:quota]):
add_item(
items,
"mmlu",
i,
MMLU_DATASET,
cfg,
r.get("question"),
r.get("choices", []),
r.get("answer"),
{"subject": r.get("subject", cfg)},
)
return items
def build_gpqa(rng, quota, config="gpqa_main"):
ds = load_dataset(GPQA_DATASET, config, split="train", token=True)
rows = stable_sample(rng, list(ds), min(quota, len(ds)))
items = []
for i, r in enumerate(rows):
choices = [
r.get("Correct Answer"),
r.get("Incorrect Answer 1"),
r.get("Incorrect Answer 2"),
r.get("Incorrect Answer 3"),
]
order = list(range(4))
rng.shuffle(order)
shuffled = [choices[j] for j in order]
answer_idx = order.index(0)
add_item(
items,
"gpqa",
i,
GPQA_DATASET,
config,
r.get("Question"),
shuffled,
answer_idx,
{
"high_level_domain": r.get("High-level domain"),
"subdomain": r.get("Subdomain"),
"record_id": r.get("Record ID"),
},
)
return items
def letter_answer_idx(ans):
if ans is None:
return -1
s = str(ans).strip()
if s in {"0", "1", "2", "3"}:
return int(s)
if s:
ch = s[0].upper()
if ch in "ABCD":
return ord(ch) - ord("A")
return -1
def build_abcd_dataset(rng, dataset_name, prefix, quota):
if prefix == "ceval":
configs = list(CEVAL_CONFIGS)
elif prefix == "cmmlu":
configs = list(CMMLU_CONFIGS)
else:
configs = get_dataset_config_names(dataset_name)
rng.shuffle(configs)
rows = []
per_config = max(24, quota // 18)
for cfg in configs:
print(f"[info] load {prefix} {cfg}", flush=True)
split = "test"
try:
ds = load_dataset(dataset_name, cfg, split=split)
except Exception:
try:
split = "val"
ds = load_dataset(dataset_name, cfg, split=split)
except Exception as exc:
print(f"[warn] skip {prefix} {cfg}: {type(exc).__name__}: {str(exc)[:160]}")
continue
sampled = stable_sample(rng, list(ds), min(per_config, len(ds)))
for r in sampled:
rows.append((cfg, split, r))
if len(rows) >= quota * 2:
break
rng.shuffle(rows)
items = []
for i, (cfg, split, r) in enumerate(rows[:quota]):
choices = [r.get("A"), r.get("B"), r.get("C"), r.get("D")]
add_item(
items,
prefix,
i,
dataset_name,
cfg,
r.get("question"),
choices,
letter_answer_idx(r.get("answer")),
{"split": split},
)
return items
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out-dir", required=True)
ap.add_argument("--seed", type=int, default=20260607)
ap.add_argument("--mmlu", type=int, default=700)
ap.add_argument("--gpqa", type=int, default=400)
ap.add_argument("--ceval", type=int, default=450)
ap.add_argument("--cmmlu", type=int, default=450)
args = ap.parse_args()
rng = random.Random(args.seed)
out_dir = Path(args.out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
print("[info] HF_ENDPOINT", os.environ.get("HF_ENDPOINT", ""))
builders = [
("mmlu", lambda: build_mmlu(rng, args.mmlu)),
("gpqa", lambda: build_gpqa(rng, args.gpqa)),
("ceval", lambda: build_abcd_dataset(rng, CEVAL_DATASET, "ceval", args.ceval)),
("cmmlu", lambda: build_abcd_dataset(rng, CMMLU_DATASET, "cmmlu", args.cmmlu)),
]
all_items = []
errors = {}
for name, fn in builders:
if getattr(args, name) <= 0:
print(f"[info] skip {name}: quota=0")
continue
try:
items = fn()
print(f"[info] built {name}: {len(items)}")
all_items.extend(items)
except Exception as exc:
errors[name] = f"{type(exc).__name__}: {str(exc)[:500]}"
print(f"[error] {name}: {errors[name]}")
rng.shuffle(all_items)
bench_path = out_dir / "heldout_public_mcq_2k.jsonl"
with bench_path.open("w", encoding="utf-8") as f:
for item in all_items:
f.write(json.dumps(item, ensure_ascii=False) + "\n")
counts = {}
for item in all_items:
counts[item["category"]] = counts.get(item["category"], 0) + 1
stats = {
"seed": args.seed,
"requested": {"mmlu": args.mmlu, "gpqa": args.gpqa, "ceval": args.ceval, "cmmlu": args.cmmlu},
"total_items": len(all_items),
"counts": counts,
"errors": errors,
"output": str(bench_path),
}
with (out_dir / "heldout_public_mcq_2k_stats.json").open("w", encoding="utf-8") as f:
json.dump(stats, f, ensure_ascii=False, indent=2)
print(json.dumps(stats, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()