unparallelled commited on
Commit
e06e57f
·
verified ·
1 Parent(s): 9a82d8a

Upload source/files/compare_nll_vs_generative.py with huggingface_hub

Browse files
source/files/compare_nll_vs_generative.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ compare_nll_vs_generative.py -- put the NLL proxy and the generative benchmark
4
+ side by side and judge three PRE-REGISTERED predictions against numeric
5
+ thresholds fixed before the numbers were seen.
6
+
7
+ usage: compare_nll_vs_generative.py <longeval_out_dir> <nll_ref_dir> [> EVAL_COMPARISON.md]
8
+
9
+ Why thresholds up front: at 100 prompts per (model, depth) cell the 95% Wilson
10
+ half-width is ~+-10 points, so "arm C beats arm B" is only meaningful relative to
11
+ that resolution. Judging after the fact would be post-hoc.
12
+
13
+ The two metrics are NOT on a common scale -- one is nats of NLL margin, the other
14
+ a hit rate -- so only ORDERINGS are compared, never absolute values.
15
+ """
16
+
17
+ import csv
18
+ import math
19
+ import os
20
+ import sys
21
+
22
+ # NLL probe dir -> generative checkpoint key
23
+ NLL_MODEL = {
24
+ "probe_run7_yarn4": "base",
25
+ "probe_arm_a": "arm_a",
26
+ "probe_arm_b": "arm_b",
27
+ "probe_arm_c": "arm_c",
28
+ }
29
+ PFRAC_DEPTH = {"p0": 0.0, "p0.25": 0.25, "p0.5": 0.5, "p0.75": 0.75, "p1": 1.0}
30
+
31
+
32
+ def wilson(h, n):
33
+ if not n:
34
+ return float("nan")
35
+ z, p = 1.96, h / n
36
+ return 100.0 * z * math.sqrt(p * (1 - p) / n + z * z / (4 * n * n)) / (1 + z * z / n)
37
+
38
+
39
+ def load_nll(ref):
40
+ """{model: {depth: margin_nats}} margin = nll_null - nll (higher = better)."""
41
+ out = {}
42
+ for d, key in NLL_MODEL.items():
43
+ p = os.path.join(ref, "logs", d, "reach.csv")
44
+ if not os.path.exists(p):
45
+ continue
46
+ out[key] = {}
47
+ for r in csv.DictReader(open(p)):
48
+ dep = PFRAC_DEPTH.get(r["p_frac"])
49
+ if dep is None:
50
+ continue
51
+ out[key][dep] = float(r["nll_null"]) - float(r["nll"])
52
+ return out
53
+
54
+
55
+ def load_gen(out_dir):
56
+ """{model: {depth: (hits, n)}} from depth_summary.csv."""
57
+ p = os.path.join(out_dir, "depth_summary.csv")
58
+ if not os.path.exists(p):
59
+ sys.exit(f"missing {p} -- run run_longeval.sh first")
60
+ g = {}
61
+ for r in csv.DictReader(open(p)):
62
+ g.setdefault(r["model"], {})[float(r["depth"])] = (int(r["hits"]), int(r["n"]))
63
+ return g
64
+
65
+
66
+ def main():
67
+ if len(sys.argv) < 3:
68
+ sys.exit(__doc__)
69
+ out_dir, ref = sys.argv[1], sys.argv[2]
70
+ nll, gen = load_nll(ref), load_gen(out_dir)
71
+ models = [m for m in ("base", "arm_a", "arm_b", "arm_c") if m in gen]
72
+ depths = sorted({d for m in gen.values() for d in m})
73
+ pct = lambda hn: 100.0 * hn[0] / hn[1] if hn[1] else float("nan")
74
+ G = lambda m, d: pct(gen[m][d]) if m in gen and d in gen[m] else float("nan")
75
+
76
+ print("# NLL proxy vs generative benchmark\n")
77
+ print("Two different measurements: NLL margin in nats (teacher-forced, answer")
78
+ print("already in the input) vs hit rate on generated text. Only the ORDERINGS")
79
+ print("are comparable.\n")
80
+
81
+ print("## Generative accuracy %, pooled by depth across tasks (+-95% Wilson)\n")
82
+ print("| depth | " + " | ".join(models) + " |")
83
+ print("|---" * (len(models) + 1) + "|")
84
+ for d in depths:
85
+ cells = []
86
+ for m in models:
87
+ if m in gen and d in gen[m]:
88
+ h, n = gen[m][d]
89
+ cells.append(f"{pct((h,n)):.1f} ±{wilson(h,n):.1f} (n={n})")
90
+ else:
91
+ cells.append("-")
92
+ print(f"| {d:.2f} | " + " | ".join(cells) + " |")
93
+
94
+ print("\n## NLL margin (nats), same models and depths\n")
95
+ print("| depth | " + " | ".join(models) + " |")
96
+ print("|---" * (len(models) + 1) + "|")
97
+ for d in depths:
98
+ print(f"| {d:.2f} | " + " | ".join(
99
+ (f"{nll[m][d]:.2f}" if m in nll and d in nll[m] else "-") for m in models) + " |")
100
+
101
+ # ---- pre-registered predictions -------------------------------------------
102
+ print("\n## Pre-registered predictions\n")
103
+ verdicts = []
104
+
105
+ # 1. depth 0.0 ~ 0% everywhere; conditional on the positive control
106
+ d0 = {m: G(m, 0.0) for m in models}
107
+ worst = max((v for v in d0.values() if v == v), default=float("nan"))
108
+ ctrl = {m: G(m, 1.0) for m in models}
109
+ ctrl_ok = any(v > 0 for v in ctrl.values() if v == v)
110
+ v1 = ("CONFIRMED" if worst <= 5 else "REFUTED" if worst >= 15 else "INCONCLUSIVE")
111
+ if not ctrl_ok:
112
+ v1 = "UNINTERPRETABLE (positive control failed)"
113
+ verdicts.append(("1. depth 0.0 ~= 0% for every model", v1,
114
+ f"max over models = {worst:.1f}%; positive control (any model >0% at depth 1.0) = "
115
+ f"{'PASS' if ctrl_ok else 'FAIL'}"))
116
+
117
+ # 2. arm C >> arm B at depth 0.25
118
+ if "arm_c" in gen and "arm_b" in gen:
119
+ diff2 = G("arm_c", 0.25) - G("arm_b", 0.25)
120
+ v2 = ("CONFIRMED" if diff2 >= 20 else "REFUTED" if abs(diff2) <= 5 else "INCONCLUSIVE")
121
+ verdicts.append(("2. arm C >> arm B at depth 0.25", v2,
122
+ f"armC {G('arm_c',0.25):.1f}% - armB {G('arm_b',0.25):.1f}% = {diff2:+.1f}pp "
123
+ f"(NLL said 2.80 vs 0.53 nats, ~9.5 sigma)"))
124
+
125
+ # 3. arm C non-monotonicity: depth 0.25 > depth 0.5
126
+ if "arm_c" in gen:
127
+ diff3 = G("arm_c", 0.25) - G("arm_c", 0.5)
128
+ v3 = ("CONFIRMED" if diff3 >= 20 else "REFUTED" if abs(diff3) <= 5 else "INCONCLUSIVE")
129
+ verdicts.append(("3. arm C depth 0.25 > depth 0.5", v3,
130
+ f"{G('arm_c',0.25):.1f}% - {G('arm_c',0.5):.1f}% = {diff3:+.1f}pp "
131
+ f"(NLL said 1.0865 nats at 6.87 sigma)"))
132
+
133
+ print("| prediction | verdict | evidence |")
134
+ print("|---|---|---|")
135
+ for name, v, ev in verdicts:
136
+ print(f"| {name} | **{v}** | {ev} |")
137
+
138
+ # ---- ordering agreement ----------------------------------------------------
139
+ print("\n## Ordering agreement (the only comparable thing)\n")
140
+ for m in models:
141
+ if m not in nll:
142
+ continue
143
+ gd = [(d, G(m, d)) for d in depths if G(m, d) == G(m, d)]
144
+ nd = [(d, nll[m][d]) for d in depths if d in nll[m]]
145
+ go = [d for d, _ in sorted(gd, key=lambda x: -x[1])]
146
+ no = [d for d, _ in sorted(nd, key=lambda x: -x[1])]
147
+ same = "AGREE" if go == no else "DIFFER"
148
+ print(f"- **{m}**: generative best->worst {go}; NLL best->worst {no} -> {same}")
149
+ if all(v == 0.0 for _, v in gd):
150
+ print(f" (all-zero generative: ordering is undefined, not disagreeing)")
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()