Buckets:
| """cwm3/eval/report_kp_residual_f1.py — results/tables/kp_residual_f1.md (+ .json): LANE F1 keypoint residual forecaster + distillation into the driver branch | |
| (R2 WP4 batch 2; pre-registration docs/decisions.md 2026-09-17T19:33:12Z). Composes eval/report_joint.py + eval/report_final_face.py blocks (load / render / | |
| verdict / margin table) over results/paired_compare.csv, the forecaster evaluation json (eval/kp_residual_eval.py) and, when given, the alert-timing run of | |
| eval/alert_timing_eval.py. Nothing is recomputed here; every number is a script output. | |
| python eval/report_kp_residual_f1.py [--seeds 0,1,2] [--forecaster results/tables/kp_residual_forecaster.json] [--timing-stamp <stamp>] [--out results/tables/kp_residual_f1.md] | |
| Blocks: §1 forecaster (MPJPE per horizon / motion class vs zero- and constant-velocity; mb vs cwm at matched params; longer history vs downsampled observation; | |
| future-state probabilities vs persistence); §2 distillation — (a-val) finalcv_s<k>_allminusecankpf_vs_allminusecan on the VALIDATION split = the ADOPTION decision, | |
| (a) finalc_* test confirmation, finalcm_* masked protocol, finalc_*_vs_k0 (B1 on the variant), finalP_* P cells; §3 alert timing (kpf vs ecan driver_only_t15 | |
| scores, Recall_{>= δ s early} at 10 / 20 per h, paired); §4 params + GPU minutes. | |
| PRE-REGISTERED READING: adoption candidate iff on VALIDATION no own onset cell (report_joint.OWN) is WORSE, no cell breaks the margins (onset −0.005 mean Δ, hazard NLL | |
| +0.010 on the mean and every seed's CI) AND >= 1 of the four paper driver cells (manual hand_leave / hand_return / head_turn_onset, engaged head_turn_onset) BEATS. | |
| Writes results/tables/kp_residual_f1_decision.json. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| import time | |
| from pathlib import Path | |
| import numpy as np | |
| import pandas as pd | |
| CWM3 = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(CWM3 / "eval")) | |
| import report_joint as RJ # noqa: E402 | |
| import report_final_face as RF # noqa: E402 | |
| A, B = "allminusecankpf", "allminusecan" | |
| PAPER_DRV = [("manual", "drv_onset|hand_leave|3s"), ("manual", "drv_onset|hand_return|3s"), ("manual", "drv_onset|head_turn_onset|3s"), ("engaged", "drv_onset|head_turn_onset|3s")] | |
| VEH4 = ["veh_onset|gas_on|3s", "veh_onset|brake_on|3s", "veh_onset|steer_phase_on|3s", "veh_onset|veh_any_on|3s"] | |
| # 2026-09-18 coverage fix: paired_compare_joint writes the vehicle hazard-NLL cells as hazard_nll|{gas_on,brake_on,steer_phase_on} (RF.NLL_VEH names them | |
| # gas/brake/steer_phase -> "no rows" in the shared block); the pre-registered +0.010 margin is checked on these cells and veh_any_on is added to the onset margin cells. | |
| NLL_VEH = ["hazard_nll|gas_on", "hazard_nll|brake_on", "hazard_nll|steer_phase_on"] | |
| MARGIN_ONSET_CELLS = RF.DRV + VEH4 | |
| def forecaster_section(path): | |
| if not path.exists(): | |
| return [f"### 1. Forecaster — no evaluation yet (`{path}` missing)", ""], {} | |
| F = json.loads(path.read_text()) | |
| L = [f"### 1. Forecaster (eval/kp_residual_eval.py {F['stamp']}, split {F['split']}, n_boot {F['n_boot']}; MPJPE = frame-normalised units × 1000; motion classes with θ = {F['theta']:.5f} = {F['theta_rule']})", ""] | |
| L += ["Params: " + ", ".join(f"`{k}` {v['total']:,} (backbone {v['backbone']:,})" for k, v in F["params"].items()), ""] | |
| HS = ["0.5s", "1s", "2s", "3s"] | |
| L += ["| regime | arm | " + " | ".join(f"model {h}" for h in HS) + " | " + " | ".join(f"ZV {h}" for h in HS) + " | " + " | ".join(f"CV {h}" for h in HS) + " |", "|---|---|" + "---|" * 12] | |
| arms = sorted({k.rsplit("_s", 1)[0] for k in F["arms"]}) | |
| for r in ("manual", "engaged"): | |
| for arm in arms: | |
| recs = [v for k, v in F["arms"].items() if k.startswith(f"{arm}_s") and k.endswith(f"_{r}")] | |
| if recs: | |
| cells = [] | |
| for n in ("model", "zv", "cv"): | |
| for h in HS: | |
| v = np.array([x[f"mpjpe_{n}"][h] for x in recs]); cells.append(f"{v.mean():.2f} ± {v.std(ddof=1) if len(v) > 1 else 0:.2f}") | |
| L.append(f"| {r} | {arm} | " + " | ".join(cells) + " |") | |
| L += ["", "Paired Δ MPJPE (A − B, lower better; verdict over seeds = kill_checks.beats on −Δ, BEATS = A lower):", "", | |
| "| pair | regime | " + " | ".join(f"Δ {h} (verdict)" for h in HS) + " |", "|---|---|" + "---|" * 4] | |
| pairs = sorted({k.split("|")[0] for k in F["pairs"] if k != "class_delta"}) | |
| summary = {} | |
| for tag in pairs: | |
| for r in ("manual", "engaged"): | |
| cells = [] | |
| for h in HS: | |
| v = F["pairs"].get(f"{tag}|{r}|{h}") | |
| if v: | |
| d = np.mean([x["delta"] for x in v["seeds"]]); cells.append(f"{d:+.2f} ({v['verdict']['verdict']})"); summary[f"{tag}|{r}|{h}"] = {"mean_delta": float(d), "verdict": v["verdict"]["verdict"]} | |
| else: | |
| cells.append("—") | |
| L.append(f"| {tag} | {r} | " + " | ".join(cells) + " |") | |
| L += ["", "MPJPE by motion class (3-seed mean; model / ZV):", "", "| regime | arm | class | share | " + " | ".join(HS) + " |", "|---|---|---|---|" + "---|" * 4] | |
| for r in ("manual", "engaged"): | |
| for arm in arms: | |
| recs = [v for k, v in F["arms"].items() if k.startswith(f"{arm}_s") and k.endswith(f"_{r}")] | |
| if not recs: | |
| continue | |
| for c in ("still", "onset", "sustained", "recovery"): | |
| L.append(f"| {r} | {arm} | {c} | {F['class_share'][r][c]:.3f} | " + " | ".join(f"{np.nanmean([x['mpjpe_model_by_class'][c][h] for x in recs]):.2f} / {np.nanmean([x['mpjpe_zv_by_class'][c][h] for x in recs]):.2f}" for h in HS) + " |") | |
| L += ["", "Future-state probabilities of `mb_full` (3-seed mean) vs persistence (S0):", "", "| regime | state@h | Brier model / persistence | AUROC model / persistence | n |", "|---|---|---|---|---|"] | |
| for r in ("manual", "engaged"): | |
| recs = [v for k, v in F["arms"].items() if k.startswith("mb_full_s") and k.endswith(f"_{r}")] | |
| for sh in (recs[0]["states"] if recs else []): | |
| g = lambda f: float(np.nanmean([x["states"][sh][f] for x in recs])) | |
| L.append(f"| {r} | {sh} | {g('brier_model'):.4f} / {g('brier_persistence'):.4f} | {g('auroc_model'):.4f} / {g('auroc_persistence'):.4f} | {recs[0]['states'][sh]['n']:,} |") | |
| L.append("") | |
| return L, {"params": F["params"], "pairs": summary, "runs": F["runs"], "theta": F["theta"], "class_share": F["class_share"]} | |
| def timing_section(stamp): | |
| if not stamp: | |
| return ["### 3. Alert timing — not run for this lane yet", ""], {} | |
| d = CWM3 / "results" / "alert_timing" / stamp | |
| if not (d / "paired.csv").exists(): | |
| return [f"### 3. Alert timing — run {stamp} has no paired.csv", ""], {} | |
| pr = pd.read_csv(d / "paired.csv"); me = pd.read_csv(d / "metrics.csv") | |
| pr = pr[(pr.a == "kpf_drv") & (pr.b == "ecan_drv") & (pr.thr_fit == "val")] | |
| L = [f"### 3. Alert timing (eval/alert_timing_eval.py {stamp}; scores = the joint layouts' `driver_only_t15` column: `kpf_drv` = I1_all_minus_road2drv_s<k>_final_ecan_kpf, `ecan_drv` = the frozen ECAN main; val-fitted thresholds; Δ = kpf − ecan on identical resamples)", "", | |
| "| family | budget /h | regime | δ | Recall kpf per seed | Recall ecan per seed | Δ per seed [CI] | mean Δ | CI≠0 |", "|---|---|---|---|---|---|---|---|---|"] | |
| out = {} | |
| for (fam, bud, reg, dl), g in pr.groupby(["family", "budget", "regime", "delta_s"]): | |
| if fam not in ("A", "B") or reg == "pooled" and False: | |
| continue | |
| g = g.sort_values("seed") | |
| ex = int(((g.ci_lo > 0) | (g.ci_hi < 0)).sum()) | |
| L.append(f"| {fam} | {bud:g} | {reg} | {dl:g} | " + " / ".join(f"{x:.3f}" for x in g.value_a) + " | " + " / ".join(f"{x:.3f}" for x in g.value_b) + " | " | |
| + " / ".join(f"{x.diff:+.3f} [{x.ci_lo:+.3f},{x.ci_hi:+.3f}]" for x in g.itertuples()) + f" | {g['diff'].mean():+.3f} | {ex}/{len(g)} |") | |
| out[f"{fam}|{bud:g}|{reg}|{dl:g}"] = {"deltas": g["diff"].tolist(), "ci": list(zip(g.ci_lo.tolist(), g.ci_hi.tolist())), "n_excl": ex, "n_seeds": int(len(g))} | |
| ach = me[(me.score.isin(["kpf_drv", "ecan_drv"])) & (me.thr_fit == "val") & (me.family == "A")] | |
| if len(ach): | |
| L += ["", "Achieved test load (family A, val-fitted; alerts/h, mean over seeds): " + "; ".join(f"{s} {reg} @{b:g}/h: {g.alerts_per_h_test.mean():.1f}" for (s, reg, b), g in ach.groupby(["score", "regime", "budget"]))] | |
| return L + [""], out | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--seeds", default="0,1,2") | |
| ap.add_argument("--forecaster", default=str(CWM3 / "results" / "tables" / "kp_residual_forecaster.json")) | |
| ap.add_argument("--timing-stamp", default="") | |
| ap.add_argument("--out", default=str(CWM3 / "results" / "tables" / "kp_residual_f1.md")) | |
| a = ap.parse_args() | |
| seeds = [int(s) for s in a.seeds.split(",")] | |
| stamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) | |
| L = [f"# LANE F1 — keypoint residual forecaster on BATON + distillation into the driver branch (pre-registration docs/decisions.md 2026-09-17T19:33:12Z; report {stamp})", "", | |
| "Default configuration = CockpitWM (ECAN): E base `E0_pooled_P_can_s<k>_v2`; frozen main `I1_all_minus_road2drv_s<k>_final_ecan`; variant `I1_all_minus_road2drv_s<k>_final_ecan_kpf` = the same " | |
| "command line + `--u-kpf results/runs/KPF_mb_full_s<k>` (seed-matched forecaster; detached 10-channel state / mode probabilities through a zero-init injector on h_u). Pairings: " | |
| "eval/run_joint_evals_f1_kpf.sh (pc_joint_locked, n_boot 1000, identical rows, v2 causal kin targets); verdict = kill_checks.beats; margins onset −0.005 / NLL +0.010. Every number is a script output; the manuscript is not edited here.", ""] | |
| dec = {"report": stamp, "seeds": seeds} | |
| fl, dec["forecaster"] = forecaster_section(Path(a.forecaster)) | |
| L += fl | |
| L += ["### 2. Distillation into the joint model", ""] | |
| dfs = [] | |
| # (a-val) adoption decision | |
| dfv, body, verd_v, cl_v = RF.block("finalcv", A, B, "B2", "(a-val) variant vs FROZEN ECAN main — VALIDATION split (adoption decision)", seeds) | |
| dfs.append(dfv); L += body + [""] | |
| if len(dfv): | |
| mt, checks_v = RF.margin_table(dfv, verd_v, "(a-val) driver / vehicle cells and hazard-NLL costs with the acceptable-degradation margins (VALIDATION)", MARGIN_ONSET_CELLS, RF.NLL_DRV + NLL_VEH) | |
| L += mt | |
| worse, broke, _, ht, insuff = RF.own_status(verd_v, checks_v) | |
| paper_beats = [f"{r}|{c}" for r, c in PAPER_DRV if verd_v.get((r, c, "pr_auc"), {}).get("verdict") == "BEATS"] | |
| paper_worse = [f"{r}|{c}" for r, c in PAPER_DRV if verd_v.get((r, c, "pr_auc"), {}).get("verdict") == "WORSE"] | |
| nll_broke = [k for k, c in checks_v.items() if k[2] == "nll" and not c["ok"]] | |
| adopt = (not worse) and (not broke) and (not nll_broke) and len(paper_beats) >= 1 and not insuff | |
| status = "INSUFFICIENT (< 3 seeds or missing cells)" if insuff else ("ADOPTION CANDIDATE (validation)" if adopt else "REJECTED (validation)") | |
| L += [f"**(a-val) pre-registered reading: {status}.** own onset WORSE: {RF.fmt(worse)}; own onset cells beyond −0.005: {RF.fmt(broke)}; NLL cells beyond +0.010: {RF.fmt(nll_broke)}; " | |
| f"paper driver cells BEATS: {paper_beats or 'none'}; paper driver cells WORSE: {paper_worse or 'none'}; head_turn verdicts {ht}.", ""] | |
| dec["validation"] = {"status": status, "own_worse": RF.fmt(worse), "own_beyond_margin": RF.fmt(broke), "nll_beyond_margin": RF.fmt(nll_broke), "paper_beats": paper_beats, "paper_worse": paper_worse, | |
| "cells": {f"{r}|{c}|{m}": v["verdict"] for (r, c, m), v in verd_v.items()}, "margin_checks": {f"{r}|{c}|{m}": c_ for (r, c, m), c_ in checks_v.items()}} | |
| else: | |
| dec["validation"] = {"status": "NO ROWS"} | |
| # (a) test confirmation | |
| dft, body, verd_t, cl_t = RF.block("finalc", A, B, "B2", "(a) variant vs FROZEN ECAN main — TEST split (confirmation)", seeds) | |
| dfs.append(dft); L += body + [""] | |
| if len(dft): | |
| mt, checks_t = RF.margin_table(dft, verd_t, "(a) driver / vehicle cells and hazard-NLL costs with the margins (TEST)", MARGIN_ONSET_CELLS, RF.NLL_DRV + NLL_VEH) | |
| L += mt | |
| worse_t, broke_t, _, ht_t, _ = RF.own_status(verd_t, checks_t) | |
| dec["test"] = {"own_worse": RF.fmt(worse_t), "own_beyond_margin": RF.fmt(broke_t), "head_turn": ht_t, "B2_style": cl_t[0], | |
| "cells": {f"{r}|{c}|{m}": v["verdict"] for (r, c, m), v in verd_t.items()}, "paper_cells": {f"{r}|{c}": verd_t.get((r, c, "pr_auc"), {}).get("verdict", "NO ROWS") for r, c in PAPER_DRV}, | |
| "veh4": {f"{r}|{c}": verd_t.get((r, c, "pr_auc"), {}).get("verdict", "NO ROWS") for r in ("manual", "engaged") for c in VEH4}, | |
| "hazard_nll": {f"{r}|{c}": verd_t.get((r, c, "nll"), {}).get("verdict", "NO ROWS") for r in ("manual", "engaged") for c in RF.NLL_DRV + NLL_VEH}} | |
| L += [f"**(a) TEST:** own onset WORSE: {RF.fmt(worse_t)}; beyond −0.005: {RF.fmt(broke_t)}; head_turn {ht_t}; B2-style claim (no own WORSE and ≥ 1 cross-leg BEATS): {cl_t[0]}.", ""] | |
| else: | |
| dec["test"] = {"status": "NO ROWS"} | |
| dfm, body, verd_m, cl_m = RF.block("finalcm", A, B, "B2", "masked protocol (human-verified driver-box mask) — TEST", seeds) | |
| dfs.append(dfm); L += body + [""] | |
| dec["masked"] = {f"{r}|{c}|{m}": v["verdict"] for (r, c, m), v in verd_m.items()} | |
| # B1 vs k0 on the variant | |
| dfk = RJ.load("finalc", A, "k0", seeds); dfs.append(dfk) | |
| if len(dfk): | |
| L += ["### B1 on the variant — driver onset PR-AUC@3s vs the k0 heuristic (TEST)", "", "| regime | event | model per seed | k0 per seed | Δ per seed [CI] | verdict |", "|---|---|---|---|---|---|"] | |
| vk = {} | |
| for reg in ("manual", "engaged"): | |
| for ev in RF.EV: | |
| cell = f"drv_onset|{ev}|3s" | |
| g = dfk[(dfk.regime == reg) & (dfk.cell == cell) & (dfk.metric == "pr_auc")].sort_values("seed") | |
| if len(g): | |
| res = RJ.verdict(g); vk[f"{reg}|{cell}"] = res["verdict"] | |
| L.append(f"| {reg} | {ev} | {RF.ms(g.value_a)} | {RF.ms(g.value_b)} | " + " / ".join(f"{float(x.delta):+.3f} [{float(x.delta_ci_lo):+.3f},{float(x.delta_ci_hi):+.3f}]" for x in g.itertuples()) + f" | {res['verdict']} |") | |
| L.append(""); dec["b1_vs_k0"] = vk | |
| else: | |
| L += ["### B1 on the variant — no rows yet (`finalc_s<k>_allminusecankpf_vs_k0`)", ""] | |
| dfp, body, verd_p, _ = RF.block("finalP", A, B, "B2x", "P cells (eval/pressure_eval.py) — TEST", seeds) | |
| dfs.append(dfp); L += body + [""] | |
| dec["P"] = {f"{r}|{c}|{m}": v["verdict"] for (r, c, m), v in verd_p.items()} | |
| tl, dec["timing"] = timing_section(a.timing_stamp) | |
| L += tl | |
| # params + GPU minutes | |
| L += ["### 4. Parameters and GPU minutes", ""] | |
| rows = [] | |
| for s in seeds: | |
| rd = CWM3 / "results" / "runs" / f"I1_all_minus_road2drv_s{s}_final_ecan_kpf" | |
| pj = rd / "params.json" | |
| rj = sorted(rd.glob("run_train_I1_*_2*.json")) | |
| if pj.exists(): | |
| P = json.loads(pj.read_text()) | |
| wall = json.loads(rj[-1].read_text()).get("wall_s") if rj else None | |
| hist = [json.loads(x) for x in (rd / "history.jsonl").read_text().splitlines()] if (rd / "history.jsonl").exists() else [] | |
| rows.append({"seed": s, "total": P.get("total"), "new_total": P.get("new_total"), "kpf": P.get("kpf"), "interaction_total": P.get("interaction_total"), "wall_s": wall, "epochs": len(hist), | |
| "best_val": json.loads((rd / "TRAIN_DONE").read_text()).get("best") if (rd / "TRAIN_DONE").exists() else None}) | |
| if rows: | |
| L += ["| joint run | registered params (total) | new modules | of which kpf injector | interaction | epochs | wall s |", "|---|---|---|---|---|---|---|"] | |
| for r in rows: | |
| L.append(f"| I1_all_minus_road2drv_s{r['seed']}_final_ecan_kpf | {r['total']:,} | {r['new_total']:,} | {r['kpf']:,} | {r['interaction_total']:,} | {r['epochs']} | {r['wall_s']:.0f} |" if r["wall_s"] else | |
| f"| I1_all_minus_road2drv_s{r['seed']}_final_ecan_kpf | {r['total']:,} | {r['new_total']:,} | {r['kpf']:,} | {r['interaction_total']:,} | {r['epochs']} | — |") | |
| fp = dec.get("forecaster", {}).get("params", {}) | |
| fr = dec.get("forecaster", {}).get("runs", {}) | |
| kpf_min = sum(v.get("gpu_wall_s") or 0 for v in fr.values()) / 60 | |
| joint_min = sum(r["wall_s"] or 0 for r in rows) / 60 | |
| L += ["", f"Forecaster params: " + ", ".join(f"`{k}` {v['total']:,}" for k, v in fp.items()) + f". Total trainable of the F1 configuration = joint registered params (above) + forecaster `mb_full` {fp.get('mb_full', {}).get('total', 0):,} " | |
| f"= {(rows[0]['total'] if rows else 0) + fp.get('mb_full', {}).get('total', 0):,} (budget ≤ 7 M). GPU minutes: forecasters {kpf_min:.1f} ({len(fr)} runs), joint {joint_min:.1f} ({len(rows)} runs), total {kpf_min + joint_min:.1f}.", ""] | |
| dec["params"] = {"joint_runs": rows, "forecaster": fp, "gpu_min": {"forecasters": kpf_min, "joint": joint_min}} | |
| prov = [d for d in dfs if len(d)] | |
| if prov: # 2026-09-18 rendering fix: RJ.provenance returns (lines, stale) — was extended as a tuple (TypeError on join); numbers unchanged | |
| prov_lines, stale = RJ.provenance(pd.concat(prov)) | |
| L += ["### Provenance (layout files of every A / B run in the rendered rows; STALE = rows older than the current layout)" + (f" — **VOID: stale rows for {stale}**" if stale else ""), ""] + prov_lines + [""] | |
| dec["stale"] = stale | |
| Path(a.out).write_text("\n".join(L) + "\n") | |
| Path(a.out).with_name("kp_residual_f1_decision.json").write_text(json.dumps(dec, indent=1, default=str)) | |
| print(f"written {a.out}; VERDICT validation: {dec.get('validation', {}).get('status')}") | |
| if __name__ == "__main__": | |
| main() | |
Xet Storage Details
- Size:
- 18.1 kB
- Xet hash:
- 0a0b1b1b0320ab14afcd0f6c3fe07e4756fdbb1d0a8c807d87a87a273545ef6d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.