Skip to content

Commit 4b2c920

Browse files
jakemannixclaude
andcommitted
feat(memory): eval_real --n-seeds (mean±sd) per the stats protocol
Baselines + generative now run over N held-out draws and report mean±sd; scale/persistence runs once (already worst-case over many facts). Committed before the chained milestone-3 run reaches the eval so it picks this up via git pull. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c818aaa commit 4b2c920

1 file changed

Lines changed: 34 additions & 10 deletions

File tree

‎scripts/memory/eval_real.py‎

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ def mean(xs):
4040
return sum(xs) / max(len(xs), 1)
4141

4242

43+
def agg(dicts, keys):
44+
"""mean ± sd of each key across per-seed result dicts (stats protocol)."""
45+
out = {}
46+
for k in keys:
47+
vals = [d[k] for d in dicts]
48+
m = mean(vals)
49+
sd = (sum((v - m) ** 2 for v in vals) / max(len(vals) - 1, 1)) ** 0.5
50+
out[k] = {"mean": m, "sd": sd}
51+
return out
52+
53+
4354
def fact_bank(gen: RealCorpusGenerator, n: int):
4455
"""n facts with DISTINCT entities (so 'ingest all, query each' has no duplicates)."""
4556
seen, facts = set(), []
@@ -146,6 +157,7 @@ def main():
146157
ap.add_argument("--skill", required=True)
147158
ap.add_argument("--squad", action="store_true")
148159
ap.add_argument("--baseline-facts", type=int, default=64)
160+
ap.add_argument("--n-seeds", type=int, default=3, help="repeat baselines/generative over N held-out draws -> mean±sd")
149161
ap.add_argument("--scale", default="8,16,32,64,128")
150162
ap.add_argument("--paraphrase", action="store_true", help="query with questions (content-addressable)")
151163
ap.add_argument("--seed", type=int, default=1234)
@@ -161,22 +173,34 @@ def main():
161173
skill = MemorySkill.load(args.skill, device=args.device)
162174

163175
records = load_squad_records() if args.squad else None
164-
gen = RealCorpusGenerator(records=records, split="eval", seed=args.seed)
165176
checkpoints = [int(x) for x in args.scale.split(",")]
166-
bank = fact_bank(gen, max(args.baseline_facts, max(checkpoints)))
167-
print(f"held-out bank: {len(bank)} distinct-entity real facts, paraphrase={args.paraphrase}")
168177

178+
# baselines + generative over N seeds (different held-out draws) -> mean ± sd
179+
base_runs, gen_runs = [], []
180+
for s in range(args.n_seeds):
181+
gs = RealCorpusGenerator(records=records, split="eval", seed=args.seed + s)
182+
bank_s = fact_bank(gs, args.baseline_facts)
183+
base_runs.append(run_baselines(model, tok, skill, bank_s, args.device, args.paraphrase))
184+
gen_runs.append(run_generative(model, tok, skill, bank_s, args.device, args.paraphrase))
185+
bkeys = ["floor_lift_nats", "memory_lift_nats", "in_context_lift_nats", "retrieval_at1", "retrieval_recall_top5"]
186+
187+
# scale + persistence once on the seed-0 bank (already worst-case over many facts)
188+
scale_bank = fact_bank(RealCorpusGenerator(records=records, split="eval", seed=args.seed), max(checkpoints))
169189
report = {
170-
"n_held_out": len(bank),
190+
"n_held_out_entities": len({f.entity for f in scale_bank}),
171191
"paraphrase": args.paraphrase,
172-
"baselines": run_baselines(model, tok, skill, bank[: args.baseline_facts], args.device, args.paraphrase),
173-
"generative": run_generative(model, tok, skill, bank[: args.baseline_facts], args.device, args.paraphrase),
174-
"scale": run_scale(model, tok, skill, bank, checkpoints, args.device, args.paraphrase, NEUTRAL),
192+
"n_seeds": args.n_seeds,
193+
"baselines": agg(base_runs, bkeys),
194+
"generative": agg(gen_runs, ["generative_exact_match"]),
195+
"scale": run_scale(model, tok, skill, scale_bank, checkpoints, args.device, args.paraphrase, NEUTRAL),
175196
}
176197
b = report["baselines"]
177-
print(f"\nbaselines (n={b['n']}): floor {b['floor_lift_nats']:.2f} | memory {b['memory_lift_nats']:.2f} | "
178-
f"in-context {b['in_context_lift_nats']:.2f} | retrieval@1 {b['retrieval_at1']:.2f}")
179-
print(f"generative EM: {report['generative']['generative_exact_match']:.2f}")
198+
print(f"\nbaselines (n_seeds={args.n_seeds}, mean±sd): "
199+
f"floor {b['floor_lift_nats']['mean']:.2f}±{b['floor_lift_nats']['sd']:.2f} | "
200+
f"memory {b['memory_lift_nats']['mean']:.2f}±{b['memory_lift_nats']['sd']:.2f} | "
201+
f"in-context {b['in_context_lift_nats']['mean']:.2f} | retrieval@1 {b['retrieval_at1']['mean']:.2f}")
202+
print(f"generative EM: {report['generative']['generative_exact_match']['mean']:.2f}"
203+
f"±{report['generative']['generative_exact_match']['sd']:.2f}")
180204
Path(args.out).parent.mkdir(parents=True, exist_ok=True)
181205
Path(args.out).write_text(json.dumps(report, indent=2))
182206
print(f"wrote {args.out}")

0 commit comments

Comments
 (0)