Download code/scripts/summarize_refine_sweep.py from nottygian/Leplanner: direct link, hf CLI and curl.
- Browser
- Download file 3.65 kB
-
https://huggingface.co/nottygian/Leplanner/resolve/main/code/scripts/summarize_refine_sweep.py
- Command line
-
hf download hf://nottygian/Leplanner/code/scripts/summarize_refine_sweep.py
-
curl -L -o summarize_refine_sweep.py https://huggingface.co/nottygian/Leplanner/resolve/main/code/scripts/summarize_refine_sweep.py
3.65 kB
| """Summarize the refinement-count sweep, with paired tests. | |
| Success rates at n=50 have a binomial standard error near 7 points, so an | |
| 8-point gap between two K values is about one SE and means nothing on its own. | |
| Every row in a sweep shares its held-out start/goal pairs at a fixed seed, so | |
| the comparisons are paired and McNemar's exact test applies -- which is the | |
| standard ``run_report.md`` already holds the rest of the project to. | |
| Reports success vs K per receding horizon, pooled across seeds, and tests each | |
| K against the best-performing K. | |
| """ | |
| import argparse | |
| import json | |
| from collections import defaultdict | |
| from math import comb | |
| from pathlib import Path | |
| def mcnemar_exact(a, b): | |
| """Two-sided exact McNemar on paired boolean outcome lists. | |
| Returns ``(delta_points, n_discordant, p)``. ``delta`` is ``b`` minus ``a`` | |
| in percentage points. | |
| """ | |
| assert len(a) == len(b), 'unpaired inputs' | |
| b_only = sum(1 for x, y in zip(a, b) if not x and y) | |
| a_only = sum(1 for x, y in zip(a, b) if x and not y) | |
| n = b_only + a_only | |
| delta = 100.0 * (b_only - a_only) / len(a) | |
| if n == 0: | |
| return delta, 0, 1.0 | |
| k = min(b_only, a_only) | |
| tail = sum(comb(n, i) for i in range(k + 1)) / (2 ** n) | |
| return delta, n, min(1.0, 2 * tail) | |
| def main(): | |
| p = argparse.ArgumentParser() | |
| p.add_argument('--results', default='data/runs/eval_refine_sweep/results.jsonl') | |
| args = p.parse_args() | |
| rows = [json.loads(line) for line in Path(args.results).read_text().splitlines()] | |
| rows = [r for r in rows if r.get('refinements') is not None] | |
| # (rh, K) -> {seed: episode_successes} | |
| grouped = defaultdict(dict) | |
| for r in rows: | |
| grouped[(r['receding_horizon'], r['refinements'])][r['seed']] = ( | |
| r['episode_successes'] | |
| ) | |
| horizons = sorted({rh for rh, _ in grouped}) | |
| for rh in horizons: | |
| ks = sorted({k for h, k in grouped if h == rh}) | |
| seeds = sorted(set.intersection( | |
| *[set(grouped[(rh, k)]) for k in ks] | |
| )) if ks else [] | |
| if not seeds: | |
| continue | |
| def pooled(k): | |
| out = [] | |
| for s in seeds: | |
| out.extend(grouped[(rh, k)][s]) | |
| return out | |
| series = {k: pooled(k) for k in ks} | |
| rates = {k: 100 * sum(v) / len(v) for k, v in series.items()} | |
| best = max(rates, key=rates.get) | |
| n = len(series[best]) | |
| print(f'\nreceding_horizon={rh} seeds={seeds} n={n} episodes pooled') | |
| print(f'{"K":>4}{"success%":>10}{"per-seed":>16}' | |
| f'{"vs best (K=%d)" % best:>28}') | |
| for k in ks: | |
| per_seed = ' '.join( | |
| f'{100 * sum(grouped[(rh, k)][s]) / len(grouped[(rh, k)][s]):.0f}' | |
| for s in seeds | |
| ) | |
| if k == best: | |
| verdict = '(best)' | |
| else: | |
| delta, nd, pv = mcnemar_exact(series[k], series[best]) | |
| verdict = f'{delta:+.1f}pt n_d={nd} p={pv:.3f}' | |
| print(f'{k:>4}{rates[k]:>10.1f}{per_seed:>16}{verdict:>28}') | |
| # the question the sweep exists to answer | |
| tail = [rates[k] for k in ks if k >= best] | |
| monotone = all( | |
| rates[ks[i]] <= rates[ks[i + 1]] + 4 for i in range(len(ks) - 1) | |
| ) | |
| print(f' peak at K={best}; ' | |
| f'{"non-decreasing in K (within 4pt noise)" if monotone else "NOT monotone in K"}' | |
| f'; drop from peak to K={ks[-1]}: {rates[best] - rates[ks[-1]]:+.1f}pt') | |
| if __name__ == '__main__': | |
| main() | |