Merge pull request #309 from bokiko/fix/bench-fetch-robust
bench: survive transient hub errors — retry with backoff, per-task isolation, atomic writes (#304)
This commit is contained in:
@@ -661,6 +661,16 @@ def cmd_bench(a):
|
|||||||
print(f" {C.dim}downloading missing datasets: {', '.join(missing)}{C.r}")
|
print(f" {C.dim}downloading missing datasets: {', '.join(missing)}{C.r}")
|
||||||
subprocess.call([py, os.path.join(TOOLS,"fetch_benchmarks.py"),
|
subprocess.call([py, os.path.join(TOOLS,"fetch_benchmarks.py"),
|
||||||
"--out", a.data, "--tasks", ",".join(missing), "--limit", str(max(a.limit,200))])
|
"--out", a.data, "--tasks", ",".join(missing), "--limit", str(max(a.limit,200))])
|
||||||
|
# il fetch riprova da solo (#304), ma se l'hub resta giu' il bench gira sui task
|
||||||
|
# disponibili invece di passare a eval file inesistenti.
|
||||||
|
# EN: the fetch retries on its own (#304), but if the hub stays down the bench
|
||||||
|
# runs on the available tasks instead of handing eval nonexistent files.
|
||||||
|
still=[t for t in tasks.split(",") if not os.path.exists(os.path.join(a.data,f"{t}.jsonl"))]
|
||||||
|
if still:
|
||||||
|
tasks=",".join(t for t in tasks.split(",") if t not in still)
|
||||||
|
print(f" {C.yel}skipping (download failed, rerun later): {', '.join(still)}{C.r}")
|
||||||
|
if not tasks:
|
||||||
|
print(f" {C.yel}no datasets available — nothing to bench{C.r}"); sys.exit(1)
|
||||||
cmd=[py, os.path.join(TOOLS,"eval_glm.py"), "--glm", GLM, "--snap",a.model,
|
cmd=[py, os.path.join(TOOLS,"eval_glm.py"), "--glm", GLM, "--snap",a.model,
|
||||||
"--tasks", tasks, "--limit", str(a.limit), "--data", a.data]
|
"--tasks", tasks, "--limit", str(a.limit), "--data", a.data]
|
||||||
if a.ram: cmd+=["--ram",str(a.ram)]
|
if a.ram: cmd+=["--ram",str(a.ram)]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ USO:
|
|||||||
Poi:
|
Poi:
|
||||||
python3 tools/eval_glm.py --snap /home/vincenzo/glm52_i4 --data ./bench --tasks mmlu --limit 40 --ram 15
|
python3 tools/eval_glm.py --snap /home/vincenzo/glm52_i4 --data ./bench --tasks mmlu --limit 40 --ram 15
|
||||||
"""
|
"""
|
||||||
import os, json, argparse, random
|
import os, sys, json, time, argparse, random
|
||||||
|
|
||||||
def f_hellaswag(d):
|
def f_hellaswag(d):
|
||||||
ctx = (d["activity_label"] + ": " + d["ctx_a"] + " " + d["ctx_b"].capitalize()).strip()
|
ctx = (d["activity_label"] + ": " + d["ctx_a"] + " " + d["ctx_b"].capitalize()).strip()
|
||||||
@@ -38,19 +38,44 @@ TASKS = { # task: (path, config, split, formatter)
|
|||||||
"openbookqa": ("allenai/openbookqa", "main", "validation", f_openbookqa),
|
"openbookqa": ("allenai/openbookqa", "main", "validation", f_openbookqa),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def load_retry(path, cfg, split, tries=5):
|
||||||
|
"""L'hub restituisce 5xx/timeout transitori (#304): riprova con backoff invece di
|
||||||
|
morire al primo HEAD fallito. hf_hub riprende i download parziali dalla cache,
|
||||||
|
quindi il retry riparte da dove si era fermato, non da zero.
|
||||||
|
EN: the hub throws transient 5xx/timeouts (#304): retry with backoff instead of
|
||||||
|
dying on the first failed HEAD. hf_hub resumes partial downloads from its cache,
|
||||||
|
so a retry continues where it stopped rather than starting over."""
|
||||||
|
from datasets import load_dataset
|
||||||
|
for k in range(tries):
|
||||||
|
try:
|
||||||
|
return load_dataset(path, cfg, split=split)
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
if k == tries - 1: raise
|
||||||
|
wait = 2 ** (k + 1)
|
||||||
|
print(f" {path}: {type(e).__name__}: {e} — retry {k+1}/{tries-1} in {wait}s")
|
||||||
|
time.sleep(wait)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
ap.add_argument("--out", default="./bench")
|
ap.add_argument("--out", default="./bench")
|
||||||
ap.add_argument("--tasks", default="hellaswag,arc_challenge,mmlu")
|
ap.add_argument("--tasks", default="hellaswag,arc_challenge,mmlu")
|
||||||
ap.add_argument("--limit", type=int, default=300)
|
ap.add_argument("--limit", type=int, default=300)
|
||||||
ap.add_argument("--seed", type=int, default=1234)
|
ap.add_argument("--seed", type=int, default=1234)
|
||||||
|
ap.add_argument("--tries", type=int, default=5)
|
||||||
a = ap.parse_args()
|
a = ap.parse_args()
|
||||||
from datasets import load_dataset
|
|
||||||
os.makedirs(a.out, exist_ok=True)
|
os.makedirs(a.out, exist_ok=True)
|
||||||
|
failed = []
|
||||||
for t in [x.strip() for x in a.tasks.split(",") if x.strip()]:
|
for t in [x.strip() for x in a.tasks.split(",") if x.strip()]:
|
||||||
if t not in TASKS: print("unknown task:", t); continue
|
if t not in TASKS: print("unknown task:", t); failed.append(t); continue
|
||||||
path, cfg, split, fn = TASKS[t]
|
path, cfg, split, fn = TASKS[t]
|
||||||
ds = load_dataset(path, cfg, split=split)
|
try:
|
||||||
|
ds = load_retry(path, cfg, split, tries=max(a.tries, 1))
|
||||||
|
except Exception as e:
|
||||||
|
# un task fallito non deve uccidere gli altri / one failed task must not kill the rest
|
||||||
|
print(f"{t}: FAILED after {a.tries} tries ({type(e).__name__}: {e}) — skipping")
|
||||||
|
failed.append(t); continue
|
||||||
idx = list(range(len(ds))); random.Random(a.seed).shuffle(idx)
|
idx = list(range(len(ds))); random.Random(a.seed).shuffle(idx)
|
||||||
rows, n = [], 0
|
rows, n = [], 0
|
||||||
for i in idx:
|
for i in idx:
|
||||||
@@ -61,9 +86,18 @@ def main():
|
|||||||
except Exception: continue
|
except Exception: continue
|
||||||
if n >= a.limit: break
|
if n >= a.limit: break
|
||||||
outp = os.path.join(a.out, t + ".jsonl")
|
outp = os.path.join(a.out, t + ".jsonl")
|
||||||
with open(outp, "w") as f:
|
# scrittura atomica: coli controlla solo l'ESISTENZA del file, quindi un jsonl
|
||||||
|
# troncato da un run interrotto bloccherebbe il re-download per sempre.
|
||||||
|
# EN: atomic write: coli only checks the file EXISTS, so a truncated jsonl from
|
||||||
|
# an interrupted run would block re-download forever.
|
||||||
|
tmp = outp + ".part"
|
||||||
|
with open(tmp, "w") as f:
|
||||||
for r in rows: f.write(json.dumps(r) + "\n")
|
for r in rows: f.write(json.dumps(r) + "\n")
|
||||||
|
os.replace(tmp, outp)
|
||||||
print(f"{t}: {len(rows)} -> {outp}")
|
print(f"{t}: {len(rows)} -> {outp}")
|
||||||
|
if failed:
|
||||||
|
print(f"incomplete: {', '.join(failed)} — rerun when the hub recovers (cached progress is kept)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user