GOVINDFROM commited on
Commit
37fb52a
·
verified ·
1 Parent(s): 73d8d4f

Retract input-injection claim; correct batch-size pooling error

Browse files
Files changed (1) hide show
  1. src/eval_ckpt.py +108 -0
src/eval_ckpt.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Evaluate timed-out runs from their checkpoints.
2
+
3
+ The R-MDM comparison runs hit their wall-clock limit at step 80,000 of 110,000,
4
+ so no result.json was written. All three stopped at the SAME step, which keeps
5
+ the comparison internally valid - it is simply at 80k steps rather than 110k, and
6
+ that is stated wherever the numbers are used.
7
+
8
+ Configuration is reconstructed from the run name rather than a config file,
9
+ because the config is only written at the end of a successful run.
10
+ """
11
+ import argparse, glob, json, os, sys
12
+
13
+ import numpy as np
14
+ import torch
15
+
16
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
17
+ from model import RecurrentDenoiser
18
+ import diffusion as dfn
19
+ from sudoku import CELLS, SudokuTokenizer
20
+
21
+ p = argparse.ArgumentParser()
22
+ p.add_argument("--hard", default="data/sudoku_hard")
23
+ p.add_argument("--runs", default="runs")
24
+ p.add_argument("--pattern", default="rmdm-*")
25
+ p.add_argument("--eval_bs", type=int, default=200)
26
+ p.add_argument("--out", default="figures/rmdm_compare.json")
27
+ a = p.parse_args()
28
+
29
+ dev = "cuda" if torch.cuda.is_available() else "cpu"
30
+ tok = SudokuTokenizer()
31
+ P = np.load(os.path.join(a.hard, "test_puz.npy"))
32
+ S = np.load(os.path.join(a.hard, "test_sol.npy"))
33
+ Pt, St = torch.from_numpy(P), torch.from_numpy(S - 1)
34
+ blank = Pt == 0
35
+ print(f"test set: {len(P)} puzzles, clues {(P>0).sum(1).min()}-{(P>0).sum(1).max()} "
36
+ f"(mean {(P>0).sum(1).mean():.1f})\n", flush=True)
37
+
38
+
39
+ def cfg_from_name(n):
40
+ """All three runs share d=128, 1 layer, 4 heads, R=32 train / 64 infer.
41
+ They differ only in whether input injection is on."""
42
+ return {"d": 128, "layers": 1, "heads": 4, "pe": "ape", "R": 32, "R_infer": 64,
43
+ "step_embed": True, "inject": "faithful" not in n}
44
+
45
+
46
+ res = {}
47
+ for d in sorted(glob.glob(os.path.join(a.runs, a.pattern))):
48
+ ck = os.path.join(d, "ckpt.pt")
49
+ if not os.path.exists(ck):
50
+ continue
51
+ name = os.path.basename(d)
52
+ c = cfg_from_name(name)
53
+ st = torch.load(ck, map_location=dev)
54
+ m = RecurrentDenoiser(len(tok), c["d"], c["layers"], c["heads"], c["pe"],
55
+ max_len=CELLS + 8, recurrences=c["R_infer"],
56
+ inject=c["inject"], step_embed=c["step_embed"]).to(dev)
57
+ m.load_state_dict(st["model"])
58
+ m.eval()
59
+ print(f"--- {name} step {st['step']:,} injection={c['inject']} "
60
+ f"step_embed={c['step_embed']} params {m.n_params()/1e3:.0f}k", flush=True)
61
+ row = {"step": int(st["step"]), "inject": c["inject"],
62
+ "step_embed": c["step_embed"], "decode": {}}
63
+ print(f" {'passes':>8}{'board':>10}{'cell':>9}", flush=True)
64
+ for K in (1, 4, 8, 16, 32, 61):
65
+ preds = []
66
+ with torch.no_grad():
67
+ for i in range(0, len(Pt), a.eval_bs):
68
+ gold = St[i:i + a.eval_bs].to(dev)
69
+ bl = blank[i:i + a.eval_bs].to(dev)
70
+ x = torch.where(bl, torch.full_like(gold, tok.mask), gold)
71
+ y, _ = dfn.fixed_k_decode(m, x, tok, dev, K, fillable=bl)
72
+ preds.append(y.cpu().numpy() + 1)
73
+ pred = np.concatenate(preds)
74
+ board = float((pred == S).all(axis=1).mean())
75
+ cell = float((pred[P == 0] == S[P == 0]).mean())
76
+ row["decode"][str(K)] = {"board": board, "cell": cell}
77
+ print(f" {K:>8}{board*100:>9.2f}%{cell*100:>8.2f}%", flush=True)
78
+ row["best"] = max(v["board"] for v in row["decode"].values())
79
+ res[name] = row
80
+ del m
81
+ torch.cuda.empty_cache()
82
+ print(flush=True)
83
+
84
+ if res:
85
+ print("=" * 72)
86
+ print("HEAD-TO-HEAD: does input injection help once R-MDM's step embedding is present?")
87
+ print("=" * 72)
88
+ off = [v["best"] for v in res.values() if not v["inject"]]
89
+ on = [v["best"] for v in res.values() if v["inject"]]
90
+ print(f" R-MDM faithful (injection OFF) : {np.mean(off)*100:6.2f}% "
91
+ f"n={len(off)} {[round(x*100,2) for x in off]}")
92
+ if on:
93
+ print(f" + input injection : {np.mean(on)*100:6.2f}% "
94
+ f"n={len(on)} {[round(x*100,2) for x in on]}")
95
+ d = (np.mean(on) - np.mean(off)) * 100
96
+ print(f"\n input injection is worth {d:+.2f} points here")
97
+ if d <= 0.5:
98
+ print(" >>> Injection does NOT help once the step embedding is present.")
99
+ print(" >>> The earlier +5.60 was confounded with batch size and with the")
100
+ print(" >>> absence of R-MDM's per-loop conditioning. This must be reported.")
101
+ else:
102
+ print(" >>> Injection still helps with their conditioning present.")
103
+ print("\n All runs stopped at step 80,000 of 110,000 (wall-clock limit), so they")
104
+ print(" are matched to each other but undertrained relative to the headline runs.")
105
+
106
+ os.makedirs(os.path.dirname(a.out) or ".", exist_ok=True)
107
+ json.dump(res, open(a.out, "w"), indent=2)
108
+ print(f"\nwrote {a.out}")