SpecMem / harness /bootstrap_ci.py
inweriok's picture
Initial release: SpecMem harness (code only, credentials-free)
a484e22 verified
Raw
History Blame Contribute Delete
4.33 kB
"""Bootstrap/paired CIs + variance decomposition (external-review item 5).
Deterministically replays the phase-2 seed-0 stream against CACHED targets
(no model calls) to obtain per-instance paired (static, personal) accepts,
then reports:
- paired bootstrap 95% CI for the post-warmup MAT gap and % gap
- per-user, per-session, per-signature-task gap distributions
Writes results/phase2_bootstrap_ci.json.
"""
from __future__ import annotations
import json
import random
from collections import defaultdict
from pathlib import Path
from . import metrics
from .data import load_bfcl
from .memory import Embedder, PersonalMemory, StaticGlobal
from .run_accept import MODEL_PATH, _parse_target
from .simulate import build_users
ROOT = Path(__file__).resolve().parent.parent
RESULTS = ROOT / "results"
def main():
metrics.get_tokenizer(MODEL_PATH)
tasks = load_bfcl()
embedder = Embedder()
instances = build_users(tasks, n_users=40, tasks_per_user=15,
n_sessions=12, queries_per_session=6, seed=0)
instances.sort(key=lambda x: (x.session, x.user_id))
targets = json.loads((RESULTS / "phase2_targets_seed0.json").read_text())
static, personal = StaticGlobal(), PersonalMemory(capacity=48,
eviction="lru")
rows = [] # (user, session, sig, a_static, a_personal)
cur = -1
for ins in instances:
tgt = targets.get(ins.query)
if tgt is None:
continue
if ins.session != cur:
cur = ins.session
if cur == 1:
static.freeze()
a_s = metrics.score(static.draft(ins.query, ins.functions,
ins.user_id, embedder),
tgt)["accept_length"]
a_p = metrics.score(personal.draft(ins.query, ins.functions,
ins.user_id, embedder),
tgt)["accept_length"]
rows.append((ins.user_id, ins.session, ins.signature_id, a_s, a_p))
cname, cargs = _parse_target(tgt)
for a in (static, personal):
a.observe(ins.query, ins.functions, ins.user_id, cname, cargs,
embedder)
if ins.session == 0:
personal.seed_shared(ins.query, cname, cargs, embedder)
post = [r for r in rows if r[1] > 0]
n = len(post)
mat_s = sum(r[3] for r in post) / n
mat_p = sum(r[4] for r in post) / n
rng = random.Random(0)
B = 10_000
gaps, pct = [], []
for _ in range(B):
idx = [rng.randrange(n) for _ in range(n)]
s = sum(post[i][3] for i in idx) / n
p = sum(post[i][4] for i in idx) / n
gaps.append(p - s)
pct.append(100 * (p - s) / s)
gaps.sort(); pct.sort()
ci = lambda xs: (round(xs[int(0.025 * B)], 3), round(xs[int(0.975 * B)], 3))
def group_gaps(key):
g = defaultdict(lambda: [0.0, 0.0, 0])
for r in post:
k = key(r); g[k][0] += r[3]; g[k][1] += r[4]; g[k][2] += 1
vals = sorted((v[1] - v[0]) / v[2] for v in g.values())
m = len(vals)
return {"n_groups": m,
"mean_gap": round(sum(vals) / m, 3),
"min": round(vals[0], 3), "p25": round(vals[m // 4], 3),
"median": round(vals[m // 2], 3),
"p75": round(vals[3 * m // 4], 3), "max": round(vals[-1], 3),
"groups_with_negative_gap": sum(1 for v in vals if v < 0)}
out = {
"config": {"seed": 0, "targets": "phase2_targets_seed0.json (cached)",
"n_post_warmup_paired": n, "bootstrap_resamples": B},
"MAT": {"static": round(mat_s, 3), "personal": round(mat_p, 3),
"gap": round(mat_p - mat_s, 3),
"gap_pct": round(100 * (mat_p - mat_s) / mat_s, 2)},
"paired_bootstrap_95CI": {"gap_MAT": ci(gaps), "gap_pct": ci(pct)},
"per_user_gap": group_gaps(lambda r: r[0]),
"per_session_gap": group_gaps(lambda r: r[1]),
"per_task_gap": group_gaps(lambda r: r[2]),
}
(RESULTS / "phase2_bootstrap_ci.json").write_text(json.dumps(out,
indent=2))
print(json.dumps(out, indent=2))
if __name__ == "__main__":
main()