Upload run_benchmarks.py with huggingface_hub
Browse files- run_benchmarks.py +127 -0
run_benchmarks.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
"""Standalone downstream-benchmark runner for XScript checkpoints on any GPU.
|
| 3 |
+
|
| 4 |
+
Isambard-AI is walled off by a CPU-minutes quota, so we export the trained
|
| 5 |
+
checkpoints to a (private) HF repo and evaluate them elsewhere. This script
|
| 6 |
+
pulls the checkpoints + tokenizers + bundled `xscript` source from that repo,
|
| 7 |
+
lays them out exactly as `xscript.paths` expects, and runs the lm-eval-harness
|
| 8 |
+
benchmarks (Global-MMLU / Belebele / XNLI) via the same `eval-bench` harness
|
| 9 |
+
used on-cluster -- so scores are directly comparable.
|
| 10 |
+
|
| 11 |
+
Quick validation pass over everything (recommended first):
|
| 12 |
+
pip install -r requirements.txt
|
| 13 |
+
# install a torch build matching your CUDA first, e.g.:
|
| 14 |
+
# pip install torch --index-url https://download.pytorch.org/whl/cu121
|
| 15 |
+
export HF_TOKEN=hf_... # needed while the repo is private
|
| 16 |
+
python run_benchmarks.py --repo jvonrad/xscript-eval --limit 200
|
| 17 |
+
|
| 18 |
+
Full suite (all examples):
|
| 19 |
+
python run_benchmarks.py --repo jvonrad/xscript-eval
|
| 20 |
+
|
| 21 |
+
Results land in ./xscript_bench/results/bench/<run>_final.json (one per run),
|
| 22 |
+
plus a combined summary.json. Send those JSONs back for analysis.
|
| 23 |
+
"""
|
| 24 |
+
import argparse
|
| 25 |
+
import json
|
| 26 |
+
import os
|
| 27 |
+
import sys
|
| 28 |
+
from pathlib import Path
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def main() -> None:
|
| 32 |
+
ap = argparse.ArgumentParser(description=__doc__,
|
| 33 |
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
| 34 |
+
ap.add_argument("--repo", required=True, help="HF repo id holding the export")
|
| 35 |
+
ap.add_argument("--repo-type", default="model", choices=["model", "dataset"])
|
| 36 |
+
ap.add_argument("--workdir", default="./xscript_bench")
|
| 37 |
+
ap.add_argument("--limit", type=float, default=None,
|
| 38 |
+
help="examples per task (omit for full suite)")
|
| 39 |
+
ap.add_argument("--runs", nargs="*", default=None,
|
| 40 |
+
help="subset of friendly model names (default: all in models.json)")
|
| 41 |
+
ap.add_argument("--tasks", nargs="*", default=None,
|
| 42 |
+
help="override task list (default: the run's training languages)")
|
| 43 |
+
ap.add_argument("--num-fewshot", type=int, default=0)
|
| 44 |
+
ap.add_argument("--batch-size", type=int, default=8)
|
| 45 |
+
ap.add_argument("--keep-checkpoints", action="store_true",
|
| 46 |
+
help="keep each 4GB checkpoint after eval (default: delete to save disk)")
|
| 47 |
+
args = ap.parse_args()
|
| 48 |
+
|
| 49 |
+
from huggingface_hub import hf_hub_download, list_repo_files
|
| 50 |
+
|
| 51 |
+
work = Path(args.workdir).resolve()
|
| 52 |
+
scratch = work / "xscript"
|
| 53 |
+
(scratch / "runs").mkdir(parents=True, exist_ok=True)
|
| 54 |
+
(scratch / "tokenizers").mkdir(parents=True, exist_ok=True)
|
| 55 |
+
os.environ["XSCRIPT_SCRATCH"] = str(scratch)
|
| 56 |
+
os.environ["XSCRIPT_RESULTS"] = str(work / "results")
|
| 57 |
+
|
| 58 |
+
dl = dict(repo_id=args.repo, repo_type=args.repo_type, local_dir=str(scratch.parent / "_repo"))
|
| 59 |
+
repo_files = list_repo_files(args.repo, repo_type=args.repo_type)
|
| 60 |
+
|
| 61 |
+
# 1) bundled xscript source -> importable
|
| 62 |
+
src_root = scratch.parent / "_repo"
|
| 63 |
+
for f in repo_files:
|
| 64 |
+
if f.startswith("src/xscript/"):
|
| 65 |
+
hf_hub_download(filename=f, **dl)
|
| 66 |
+
sys.path.insert(0, str(src_root / "src"))
|
| 67 |
+
|
| 68 |
+
# 2) tokenizers (small)
|
| 69 |
+
for f in repo_files:
|
| 70 |
+
if f.startswith("tokenizers/"):
|
| 71 |
+
local = hf_hub_download(filename=f, **dl)
|
| 72 |
+
dest = scratch / f
|
| 73 |
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
| 74 |
+
if not dest.exists():
|
| 75 |
+
dest.symlink_to(local)
|
| 76 |
+
|
| 77 |
+
# 3) model manifest (friendly name -> real tokenizer)
|
| 78 |
+
models = json.loads(Path(hf_hub_download(filename="models.json", **dl)).read_text())
|
| 79 |
+
runs = args.runs or sorted(models)
|
| 80 |
+
missing = [r for r in runs if r not in models]
|
| 81 |
+
if missing:
|
| 82 |
+
sys.exit(f"models not in repo: {missing}\navailable: {sorted(models)}")
|
| 83 |
+
print(f"[bench] {len(runs)} model(s) to evaluate: {runs}")
|
| 84 |
+
|
| 85 |
+
import torch
|
| 86 |
+
from xscript.eval import bench
|
| 87 |
+
if not torch.cuda.is_available():
|
| 88 |
+
print("[bench] WARNING: no CUDA device -- this will be very slow on CPU.")
|
| 89 |
+
|
| 90 |
+
summary = {}
|
| 91 |
+
for i, run in enumerate(runs, 1):
|
| 92 |
+
tok = models[run]["tok"]
|
| 93 |
+
print(f"\n===== [{i}/{len(runs)}] {run} (tok={tok}, limit={args.limit}) =====")
|
| 94 |
+
ckpt_rel = f"runs/{run}/checkpoints/final.pt"
|
| 95 |
+
local_ckpt = hf_hub_download(filename=ckpt_rel, **dl)
|
| 96 |
+
dest = scratch / ckpt_rel
|
| 97 |
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
| 98 |
+
if not dest.exists():
|
| 99 |
+
dest.symlink_to(local_ckpt)
|
| 100 |
+
try:
|
| 101 |
+
scores = bench.run(run, tok, tag="final", tasks=args.tasks,
|
| 102 |
+
num_fewshot=args.num_fewshot, limit=args.limit,
|
| 103 |
+
log_wandb=False, batch_size=args.batch_size)
|
| 104 |
+
summary[run] = scores
|
| 105 |
+
except Exception as exc:
|
| 106 |
+
print(f"[bench] {run} FAILED: {type(exc).__name__}: {exc}")
|
| 107 |
+
summary[run] = {"error": f"{type(exc).__name__}: {exc}"}
|
| 108 |
+
finally:
|
| 109 |
+
if not args.keep_checkpoints:
|
| 110 |
+
# free the ~4GB blob (both the symlink target in HF cache and our link)
|
| 111 |
+
try:
|
| 112 |
+
real = Path(local_ckpt).resolve()
|
| 113 |
+
dest.unlink(missing_ok=True)
|
| 114 |
+
real.unlink(missing_ok=True)
|
| 115 |
+
except OSError as exc:
|
| 116 |
+
print(f"[bench] cleanup warning for {run}: {exc}")
|
| 117 |
+
|
| 118 |
+
out = work / "results" / "summary.json"
|
| 119 |
+
out.parent.mkdir(parents=True, exist_ok=True)
|
| 120 |
+
out.write_text(json.dumps({"limit": args.limit, "num_fewshot": args.num_fewshot,
|
| 121 |
+
"scores": summary}, indent=2))
|
| 122 |
+
print(f"\n[bench] wrote {out}")
|
| 123 |
+
print(f"[bench] per-run JSON in {work / 'results' / 'bench'}")
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
if __name__ == "__main__":
|
| 127 |
+
main()
|