headless / tests /analyze_efficiency.py
logos42's picture
Upload folder using huggingface_hub
216004d verified
Raw History Blame Contribute Delete
6.17 kB
#!/usr/bin/env python3
"""ๅญฆไน ๆ•ˆ็އ้ฆ–ๆฌกๆต‹้‡็š„ๅˆ†ๆž โ€”โ€” ๅˆคๆฎๅœจ `docs/learning_efficiency_spec.md` ยง4ใ€‚
ๆ ธๅฟƒๅฏน็…ง: **ๅŒไธ€ไธช run ๅ†…้ƒจๅŒๆ—ถ่ฎญ็ปƒ `naive` ไธŽ `replay`** โ‡’ ๅคฉ็„ถ้…ๅฏน
(ๅŒ seedใ€ๅŒๅŸŸใ€ๅŒๆ•ฐๆฎ้กบๅบใ€ๅŒๆจกๅž‹ๅˆๅง‹ๅŒ–), ๅ”ฏไธ€็š„ๅทฎๅˆซๆ˜ฏ"่ฆไธ่ฆๅคไน ๆ—งๅŸŸ"ใ€‚
ไธปๅˆคๆฎ = `retention_gain[d] = mean(start_level[kโ‰ฅ2]) โˆ’ start_level[1]`
โ‡’ "้‡้‡ๅˆฐ่ฟ™ไธชๅŸŸๆ—ถ, ไฝ ไธ€ไธŠๆฅๅฐฑไผšๅคšๅฐ‘" ็›ธๅฏน็ฌฌไธ€ๆฌก็š„ๆ”นๅ–„ใ€‚
ๅˆคๆฎ(ไบ‹ๅ‰):
โ‘  ๅกซๅ……้—ธ้—จ: `_n_visits` ๅ…จ้ƒจ โ‰ฅ1 ไธ” `steps_to_abs` ้žๅ…จ็ฉบ, ๅฆๅˆ™ๅˆค"ๆŒ‡ๆ ‡ๆœช้‡‡้›†ๆˆๅŠŸ"
โ‘ก ่ฆ†็›–็އ: ๆŠฅ `_already_ratio` / `_none_ratio`
โ‘ข ไฟ็•™: `mean(replay โˆ’ naive) > 0` ไธ”่‡ชๅŠฉๆณ•ๅŒบ้—ดไธ‹็•Œ > 0 โ‡’ ๆœ‰ไฟ็•™; ๅฆๅˆ™็…งๅฎžๅ†™"ๆ— ไฟ็•™"
"""
from __future__ import annotations
import glob
import json
import random
import statistics as st
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
B = 20000
def load(pat="results/lmeff_s*"):
runs = {}
for d in sorted(glob.glob(str(ROOT / pat))):
f = Path(d) / "lm4_wave_results.json"
if not f.exists():
continue
j = json.load(open(f))
seed = int(Path(d).name.rsplit("_s", 1)[1])
runs[seed] = j
return runs
def rg_of(j, arm):
e = (j.get(arm) or {}).get("efficiency") or {}
return e
def boot_ci(x, n=B, seed=0):
if not x:
return float("nan"), float("nan")
rng = random.Random(seed)
m = []
for _ in range(n):
m.append(st.mean([x[rng.randrange(len(x))] for _ in range(len(x))]))
m.sort()
return m[int(.025 * len(m))], m[int(.975 * len(m))]
runs = load()
print("=" * 92)
print(f"ๅญฆไน ๆ•ˆ็އ้ฆ–ๆฌกๆต‹้‡ โ€”โ€” {len(runs)} ไธช run: {sorted(runs)}")
print("=" * 92)
if not runs:
sys.exit("โœ— ๆฒกๆœ‰็ป“ๆžœ็›ฎๅฝ•")
# โ”€โ”€ โ‘  ๅกซๅ……้—ธ้—จ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
print("\nโ‘  ๅกซๅ……้—ธ้—จ")
gate_ok = True
for seed, j in sorted(runs.items()):
for arm in ("naive", "replay"):
e = rg_of(j, arm)
nv = e.get("_n_visits") or {}
if not nv:
print(f" โœ— seed={seed} {arm}: _n_visits ็ฉบ โ‡’ ๆŒ‡ๆ ‡ๆœช้‡‡้›†")
gate_ok = False
continue
print(f" seed={seed} {arm:6s} n_visits={nv} already={e.get('_already_ratio'):.3f}"
f" none={e.get('_none_ratio'):.3f} reachable_max={e.get('reachable_max')}")
print(f" โ‡’ ๅกซๅ……้—ธ้—จ: {'PASS' if gate_ok else '**FAIL(ๆŒ‡ๆ ‡ๆœช้‡‡้›†ๆˆๅŠŸ)'}")
# โ”€โ”€ โ‘ก ไฟ็•™ๅขž็›Š: ้€ๅŸŸๅฏน็…ง โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
print("\n" + "=" * 92)
print("โ‘ก โ˜… ไฟ็•™ๅขž็›Š retention_gain โ€”โ€” naive vs replay(ๅŒ seed ๅŒๅŸŸ, ๅคฉ็„ถ้…ๅฏน)")
print("=" * 92)
pairs = [] # (seed, dom, naive, replay)
doms = set()
for seed, j in sorted(runs.items()):
en, er = rg_of(j, "naive"), rg_of(j, "replay")
rn, rr = en.get("retention_gain") or {}, er.get("retention_gain") or {}
for d in sorted(set(rn) | set(rr), key=lambda x: int(x[1:])):
a, b = rn.get(d), rr.get(d)
if a is None or b is None:
continue
doms.add(d)
pairs.append((seed, d, float(a), float(b)))
print(f"{'ๅŸŸ':<8}{'n seed':>7}{'naive ๅ‡ๅ€ผ':>12}{'replay ๅ‡ๅ€ผ':>13}{'ฮ”(replayโˆ’naive)':>18}")
per_dom = {}
for d in sorted(doms, key=lambda x: int(x[1:])):
ps = [(a, b) for _, dd, a, b in pairs if dd == d]
if not ps:
continue
na = st.mean([a for a, _ in ps]); rp = st.mean([b for _, b in ps])
per_dom[d] = (na, rp, len(ps))
print(f"{d:<8}{len(ps):>7}{na:>12.4f}{rp:>13.4f}{rp - na:>+18.4f}")
if pairs:
dn = [a for _, _, a, _ in pairs]
dr = [b for _, _, _, b in pairs]
dif = [b - a for _, _, a, b in pairs]
print(f"\n ๅ…จๅŸŸๆฑ‡ๆ€ป(n={len(pairs)} ๅฏน):")
print(f" naive ๅ‡ๅ€ผ {st.mean(dn):+.4f} (std {st.pstdev(dn):.4f})")
print(f" replay ๅ‡ๅ€ผ {st.mean(dr):+.4f} (std {st.pstdev(dr):.4f})")
lo, hi = boot_ci(dif)
n_pos = sum(1 for x in dif if x > 0)
n_neg = sum(1 for x in dif if x < 0)
print(f" ฮ”(replayโˆ’naive) = {st.mean(dif):+.4f} ่‡ชๅŠฉๆณ• 95% CI [{lo:+.4f}, {hi:+.4f}]")
print(f" replay ๆ›ดๅฅฝ็š„ๅŸŸ: {n_pos}/{len(dif)} naive ๆ›ดๅฅฝ: {n_neg} ๅนณ: {len(dif)-n_pos-n_neg}")
print(f" โ‡’ โ‘ข ๅˆคๆฎ: mean>0 ไธ” CI ไธ‹็•Œ>0 ? "
f"{'**ๆœ‰ไฟ็•™**' if (st.mean(dif) > 0 and lo > 0) else '**ๆœช่พพๅˆฐ(็…งๅฎžๅ†™)'}")
# โ”€โ”€ โ‘ข ่ตท็‚นๆฐดๅนณๆœฌ่บซ(ไธไพ่ต–้˜ˆๅ€ผ, ๆ›ด่ดด่ฟ‘"่ฎฐไฝไบ†ๅคšๅฐ‘") โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
print("\n" + "=" * 92)
print("โ‘ข ่ตท็‚นๆฐดๅนณ start_level(k=1 vs ไปฅๅŽ) โ€”โ€” ็ปๅฏนๅฃๅพ„")
print("=" * 92)
print(f"{'ๅŸŸ':<8}{'arm':<8}{'k=1 ่ตท็‚น':>10}{'kโ‰ฅ2 ๅ‡ๅ€ผ':>11}{'่ตท็‚นๆžๅทฎ':>10}")
for seed, j in sorted(runs.items()):
for arm in ("naive", "replay"):
sl = (rg_of(j, arm).get("start_level") or {})
for d in sorted(sl, key=lambda x: int(x[1:])):
v = sl[d]
ks = sorted(v, key=lambda x: int(x))
first, later = v[ks[0]], [v[k] for k in ks[1:]]
if later:
print(f"{d:<8}{arm:<8}{first:>10.3f}{st.mean(later):>11.3f}"
f"{max(later)-min(later):>10.3f} seed={seed}")
# โ”€โ”€ โ‘ฃ ๅ…ถๅฎƒๅฃๅพ„ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
print("\n" + "=" * 92)
print("โ‘ฃ ๅ…ถๅฎƒๅฃๅพ„(ๆ›ดๆ–ฐๆ•ˆ็އ / ่ฎฟ้—ฎๅ†…ๅขž็›Š)")
print("=" * 92)
for arm in ("naive", "replay"):
wg, sp = [], []
for seed, j in sorted(runs.items()):
e = rg_of(j, arm)
for d, v in (e.get("within_gain") or {}).items():
wg += list(v.values())
for d, v in (e.get("slope_early") or {}).items():
sp += list(v.values())
print(f" {arm:6s} within_gain ๅ‡ๅ€ผ {st.mean(wg):+.5f} (n={len(wg)})"
f" | slope_early ๅ‡ๅ€ผ {st.mean(sp):+.6f} (n={len(sp)})")