team-sinbotics / training /sinmmlu /build_dev.py
JaniRaviRash-523's picture
Upload folder using huggingface_hub
7a2335a verified
Raw History Blame Contribute Delete
5.39 kB
"""Validate the public data and build the labelled dev files from public train (Easy only).
dev/archive/easy_full.jsonl all 1,851 public-train questions
dev/archive/easy_clean.jsonl minus the questions with a near-duplicate in the test set (full Jaccard >= 0.5)
dev/archive/inference_subset.jsonl 20 random questions per subject from easy_clean (14 x 20 = 280): screening set for
inference conditions (prompts, scoring, translation) before any training
dev/archive/fewshot_pool.jsonl 5 fixed examples per subject (not in the subset), the only source of few-shot examples
dev/archive/inference_rest.jsonl the rest of easy_clean: confirmation set for the screening winners
Every dev row carries `overlaps_test`. The uid lists are also written to configs/subsets/ (tracked; dev/ is not).
Usage: python -m sinmmlu.build_dev [--per-subject 20 --shots 5 --seed 42]
"""
import argparse
import collections
import random
from . import dedup
from .data import ROOT, load_test, load_train, subject_si, write_jsonl
SUBSET_DIR = ROOT / "configs" / "subsets"
FIELDS = ("uid", "q_no", "subject", "category", "level", "file", "question", "choices")
def check(rows, name, labelled):
bad = [r["uid"] for r in rows if len(r["choices"]) != 4
or (labelled and int(r.get("answer", 0)) not in (1, 2, 3, 4))]
keys = collections.Counter((r["level"], r["subject"], r["category"], r["q_no"]) for r in rows)
dups = [k for k, v in keys.items() if v > 1]
print(f"{name}: n={len(rows)} files={len({r['file'] for r in rows})} bad={len(bad)} duplicate_keys={len(dups)} "
f"levels={dict(collections.Counter(r['level'] for r in rows))}")
if labelled:
print(" answers:", dict(sorted(collections.Counter(int(r["answer"]) for r in rows).items())))
assert not bad and not dups, (bad[:5], dups[:5])
def slim(r):
out = {k: r[k] for k in FIELDS}
out.update(subject_si=subject_si(r), answer=int(r["answer"]), year=r.get("metadata", {}).get("year"))
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--per-subject", type=int, default=20)
ap.add_argument("--shots", type=int, default=5, help="few-shot examples kept per subject")
ap.add_argument("--seed", type=int, default=42)
a = ap.parse_args()
train, test = load_train(), load_test()
check(train, "public train", True)
check(test, "public test", False)
dup = dedup.overlapping_uids(train, test, remove_t=dedup.THRESHOLDS["public"][0])
rows = [dict(slim(r), overlaps_test=r["uid"] in dup) for r in train]
write_jsonl("dev/archive/easy_full.jsonl", rows)
clean = [r for r in rows if not r["overlaps_test"]]
write_jsonl("dev/archive/easy_clean.jsonl", clean)
print(f"dev/archive/easy_full.jsonl n={len(rows)}; dev/archive/easy_clean.jsonl n={len(clean)} ({len(dup)} overlap the test set)")
sub = inference_subset(clean, a.per_subject, a.seed)
chosen = {r["uid"] for r in sub}
pool = fewshot_pool([r for r in clean if r["uid"] not in chosen], a.shots, a.seed)
chosen |= {r["uid"] for r in pool}
rest = [r for r in clean if r["uid"] not in chosen]
write_jsonl("dev/archive/inference_subset.jsonl", sub)
write_jsonl("dev/archive/fewshot_pool.jsonl", pool)
write_jsonl("dev/archive/inference_rest.jsonl", rest)
SUBSET_DIR.mkdir(parents=True, exist_ok=True)
for name, rs in (("inference_subset", sub), ("fewshot_pool", pool), ("inference_rest", rest)):
(SUBSET_DIR / f"{name}_uids.txt").write_text("".join(r["uid"] + "\n" for r in rs), encoding="utf-8", newline="\n")
print(f"dev/archive/inference_subset.jsonl n={len(sub)} ({a.per_subject} per subject, seed {a.seed}); "
f"dev/archive/fewshot_pool.jsonl n={len(pool)} ({a.shots} per subject); dev/archive/inference_rest.jsonl n={len(rest)}; "
f"uid lists -> {SUBSET_DIR.relative_to(ROOT)}")
def inference_subset(rows, per_subject, seed):
"""`per_subject` random questions from every subject (= source file), same seed -> same questions."""
rng = random.Random(seed)
by_file = collections.defaultdict(list)
for r in sorted(rows, key=lambda r: (r["file"], r["q_no"])):
by_file[r["file"]].append(r)
out = []
for f in sorted(by_file):
assert len(by_file[f]) >= per_subject, f"{f} has only {len(by_file[f])} questions"
out += sorted(rng.sample(by_file[f], per_subject), key=lambda r: r["q_no"])
return out
def fewshot_pool(rows, shots, seed):
"""`shots` examples per subject; the first four have four different answers (where possible) so that a k-shot
prompt does not teach a position bias. Order inside a subject = the order used for k-shot (first k)."""
rng = random.Random(seed + 1)
by_file = collections.defaultdict(list)
for r in sorted(rows, key=lambda r: (r["file"], r["q_no"])):
by_file[r["file"]].append(r)
out = []
for f in sorted(by_file):
cands = by_file[f][:]
rng.shuffle(cands)
picked, seen = [], set()
for r in cands:
if r["answer"] not in seen:
picked.append(r)
seen.add(r["answer"])
picked = picked[:min(4, shots)]
picked += [r for r in cands if r not in picked][:shots - len(picked)]
out += picked
return out
if __name__ == "__main__":
main()