skyuu72's picture
kibble: 1000-fact pack, loader, figures, write-up and all the code
0985f91 verified
Raw History Blame Contribute Delete
5.91 kB
# one runner for every method, so they all face the same protocol.
# usage: python methods/run.py --exp E05 --method baselines:MEMIT --proto scale,seq [--hp layers=[2,3,4,5,6] lam=15000]
# protos: tune (100 tune facts, light eval) | scale (1/10/100/800 eval facts) | seq (10 batches of 80)
# | heldout (final report only: 200 held-out facts, then all 1000 at once)
import argparse, ast, importlib, threading, sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from common import *
from harness import Judge, brief
from values import req
def avg(ms):
keys = {k for m in ms for k in m}
return {k: sum(m[k] for m in ms if k in m) / sum(1 for m in ms if k in m) for k in keys
if all(isinstance(m.get(k, 0), (int, float)) for m in ms)}
def watchdog(minutes, path, payload):
def boom():
payload["status"] = f"KILLED: over {minutes} min, too costly"; jdump(payload, path)
print(payload["status"], flush=True); os._exit(3)
t = threading.Timer(minutes * 60, boom); t.daemon = True; t.start()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--exp", required=True); ap.add_argument("--method", required=True)
ap.add_argument("--proto", default="scale,seq"); ap.add_argument("--hp", nargs="*", default=[])
ap.add_argument("--seed", type=int, default=0); ap.add_argument("--tag", default="")
ap.add_argument("--n_tune", type=int, default=100); ap.add_argument("--judge_bs", type=int, default=16); ap.add_argument("--minutes", type=float, default=45)
a = ap.parse_args()
hp = {}
for kv in a.hp:
k, v = kv.split("=", 1)
try: hp[k] = ast.literal_eval(v)
except Exception: hp[k] = v
torch.manual_seed(a.seed); import random; random.seed(a.seed)
mod, cls = a.method.split(":")
name = f"{a.exp}_{cls}{('_' + a.tag) if a.tag else ''}"; path = os.path.join(RES, name + ".json")
out = jload(path) if os.path.exists(path) else {}
out.update(exp=a.exp, method=cls, hp=hp, seed=a.seed, status="running")
watchdog(a.minutes, path, out)
meta = jload(os.path.join(ROOT, "data", "meta.json")); model, tok = load_model(meta["model"])
J = Judge(tok, bs=a.judge_bs); M = getattr(importlib.import_module(mod), cls)(model, tok, **hp)
protos = a.proto.split(",")
ev, tune = J.facts("eval"), J.facts("tune")[:a.n_tune]
if a.seed: random.Random(a.seed).shuffle(ev) # fresh seed = fresh fact order, so different batches and different N-subsets
used = (tune if "tune" in protos else []) + (ev if ("scale" in protos or "seq" in protos) else [])
if "heldout" in protos: used = used + ev + J.facts("heldout")
used = list({f["id"]: f for f in used}.values())
J.prepare(model, used)
t0 = time.time(); M.precompute([req(f) for f in used]); out["precompute_sec"] = round(time.time() - t0, 1)
def one(facts, full):
M.reset(); t = time.time()
M.edit([req(f) for f in facts]); es = time.time() - t
m = J.evaluate(model, facts, full=full, **M.eval_kwargs()); m["edit_sec"] = round(es, 2); return m
def save():
out["peak_vram_mb"] = peak_mb(); out["method_stats"] = getattr(M, "stats", {}); jdump(out, path)
try:
if "tune" in protos:
out["tune"] = one(tune, False); print("tune ", brief(out["tune"]), flush=True); save()
if "scale" in protos:
out.setdefault("scale", {})
groups = {1: [[f] for f in ev[:10]], 10: [ev[0:10], ev[10:20], ev[20:30]], 100: [ev[:100]], 800: [ev[:800]]}
for N, gs in groups.items():
if str(N) in out["scale"]: continue
try:
ms = [one(g, i == 0) for i, g in enumerate(gs)]
out["scale"][str(N)] = avg(ms); print(f"scale {N:4d}", brief(out["scale"][str(N)]), flush=True)
except Skip as e:
out["scale"][str(N)] = dict(skipped=str(e)); print(f"scale {N} skipped: {e}", flush=True)
save()
if "seq" in protos and "seq" not in out and hp.get("mode") == "all":
out["seq"] = dict(skipped="800 facts do not fit in the context window"); save()
if "seq" in protos and "seq" not in out:
M.reset(); B = [ev[i * 80:(i + 1) * 80] for i in range(10)]; curve = []; t = time.time()
for i, b in enumerate(B):
M.edit([req(f) for f in b])
m = J.evaluate(model, B[0], full=False, **M.eval_kwargs())
curve.append({k: m[k] for k in ("efficacy_em", "para_em", "hop_em", "loc_kl", "loc_qa_em", "nbr_real_em")})
print(f"seq after batch {i + 1}: batch1", brief(m), flush=True)
es = time.time() - t
final = J.evaluate(model, ev[:800], full=True, **M.eval_kwargs()); final["edit_sec"] = round(es, 1)
per_batch = [J.evaluate(model, b, full=False, **M.eval_kwargs())["efficacy_em"] for b in B]
out["seq"] = dict(final=final, batch1_curve=curve, final_eff_per_batch=per_batch)
print("seq final", brief(final), "| eff per batch", [round(x, 2) for x in per_batch], flush=True); save()
if "heldout" in protos:
held = J.facts("heldout")
out["heldout_200"] = one(held, True); print("heldout ", brief(out["heldout_200"]), flush=True); save()
try:
out["all_1000"] = one(ev + held, True); print("all 1000 ", brief(out["all_1000"]), flush=True)
except Skip as e:
out["all_1000"] = dict(skipped=str(e))
save()
out["status"] = "ok"
except Exception as e:
import traceback; traceback.print_exc()
out["status"] = "FAILED: " + repr(e)[:300]
out["total_sec"] = round(time.time() - t0, 1); save(); print(f"[saved] results/{name}.json status={out['status']} peak={out['peak_vram_mb']}MB")
if __name__ == "__main__":
main()