GOVINDFROM's picture
Automated push from Zaratan
dbbc6f9 verified
Raw History Blame Contribute Delete
3.76 kB
"""Did the baseline fail to PARALLELISE, or fail to LEARN?
At 20 digits the baseline scores 0.0% at one pass and 0.0% at sixteen. It is
tempting to read that as a very large parallel-decoding gap, but the standard
pass grid stops at sixteen while a 20-digit answer occupies 21 slots - so that
number was never a fully sequential decode, and the two explanations are not
distinguished by it:
(a) the baseline learned the task and merely cannot commit it in parallel
-> it should succeed once decoding is fully sequential;
(b) the baseline never learned the task at all
-> it fails at every budget, and the claim is about sample efficiency
rather than about parallelism.
These are different papers. This script decodes every run at a budget equal to
the number of answer positions, which is genuinely one token per pass, and
reports accuracy at that budget next to the one-pass number.
"""
import argparse, glob, json, os, sys
import numpy as np
import torch
sys.path.insert(0, os.path.dirname(__file__))
from data import build_addition
from model import Transformer
import diffusion as dfn
from train_add import exact_match
p = argparse.ArgumentParser()
p.add_argument("--runs", default="runs")
p.add_argument("--pattern", default="g13-*")
p.add_argument("--n_eval", type=int, default=200)
p.add_argument("--eval_bs", type=int, default=50)
p.add_argument("--out", default="figures/seq_check.json")
a = p.parse_args()
dev = "cuda" if torch.cuda.is_available() else "cpu"
rows = []
for run_dir in sorted(glob.glob(os.path.join(a.runs, a.pattern))):
rf = os.path.join(run_dir, "result.json")
mf = os.path.join(run_dir, "model.pt")
if not (os.path.exists(rf) and os.path.exists(mf)):
continue
r = json.load(open(rf))
cfg = r["args"]
d, SL = cfg["digits"], cfg["seq_len"]
X, A, tok = build_addition(a.n_eval, d, SL, seed=91_000, exact=True)
X, A = torch.from_numpy(X), torch.from_numpy(A)
n_slots = int(A[0].sum())
m = Transformer(len(tok), cfg["d"], cfg["layers"], cfg["heads"], cfg["pe"],
causal=False, max_len=SL + 8).to(dev)
m.load_state_dict(torch.load(mf, map_location=dev))
m.eval()
accs = {}
for steps in (1, n_slots):
vals = []
for i in range(0, len(X), a.eval_bs):
xb, ab = X[i:i + a.eval_bs], A[i:i + a.eval_bs]
out = dfn.cond_decode(m, xb, ab, tok, steps, dev)
vals.append(exact_match(out.cpu(), xb, ab, tok))
accs[steps] = float(np.mean(vals))
rows.append({"run": os.path.basename(run_dir), "digits": d,
"mode": cfg["mode"], "seed": cfg["seed"], "slots": n_slots,
"acc_1pass": accs[1], "acc_sequential": accs[n_slots]})
print(f"{rows[-1]['run']:<22} {d:>3}d {cfg['mode']:<6} "
f"1-pass {accs[1]*100:6.2f} fully-sequential ({n_slots} passes) "
f"{accs[n_slots]*100:6.2f}", flush=True)
print("\n=== does the baseline learn the task at ANY budget? ===")
agg = {}
for r in rows:
agg.setdefault((r["digits"], r["mode"]), []).append(r)
print(f"{'digits':>7} {'mode':<7}{'1 pass':>9}{'sequential':>12} verdict")
for k in sorted(agg):
v = agg[k]
one = np.mean([x["acc_1pass"] for x in v]) * 100
seq = np.mean([x["acc_sequential"] for x in v]) * 100
if seq < 5:
verdict = "never learned the task (sample efficiency, not parallelism)"
elif one < seq - 5:
verdict = "learned it, cannot parallelise (a true parallelism gap)"
else:
verdict = "learned it and parallelises"
print(f"{k[0]:>7} {k[1]:<7}{one:8.1f} {seq:11.1f} {verdict}")
os.makedirs(os.path.dirname(a.out) or ".", exist_ok=True)
json.dump(rows, open(a.out, "w"), indent=2)
print(f"\nwrote {a.out}")