Buckets:
| #!/usr/bin/env python3 | |
| """tools/vlm_handson/evaluate_vlm_handson.py — metrics + acceptance verdict of the VLM hands-on candidates on the owner-labelled validation | |
| frames (pre-registration docs/decisions.md 2026-09-16T23:26Z). NEW files only. | |
| Per candidate x input variant: frame accuracy at P >= 0.5, AUROC, per-class recall (on / off), per regime (engaged / manual), per-item | |
| agreement (majority of the item's frames correct), and for each clicked-return item the detected off->on time — the first ON frame after the | |
| LAST OFF run, (i) plain threshold 0.5 and (ii) hysteresis (off -> on at P >= 0.6, on -> off at P <= 0.4; initial state from P >= 0.5) — vs the | |
| owner's click (|delta| <= 0.5 s counts). Acceptance (pre-registered): accuracy >= 0.90 AND AUROC >= 0.95 AND >= 3 of the 4 clicked returns | |
| within +-0.5 s (hysteresis detector is the primary one; the plain one is reported). Candidates are judged in the FIXED order | |
| qwen3vl2b -> qwen25vl3b -> qwen3vl4b; the first that passes (on either clean variant) is 'accepted'; every scored candidate is reported. | |
| Writes results/tables/vlm_handson_validation.md and results/vlm_handson/metrics.json. | |
| python tools/vlm_handson/evaluate_vlm_handson.py | |
| """ | |
| from __future__ import annotations | |
| import json, sys, time | |
| from pathlib import Path | |
| import numpy as np | |
| CWM3 = Path(__file__).resolve().parents[2] | |
| OUT = CWM3 / "results/vlm_handson" | |
| TABLE = CWM3 / "results/tables/vlm_handson_validation.md" | |
| ORDER = ["qwen3vl2b", "qwen25vl3b", "qwen3vl4b"] | |
| NAMES = {"qwen3vl2b": "Qwen/Qwen3-VL-2B-Instruct", "qwen25vl3b": "Qwen/Qwen2.5-VL-3B-Instruct", "qwen3vl4b": "Qwen/Qwen3-VL-4B-Instruct"} | |
| CLEAN_VARIANTS = ("crop", "full896") | |
| ACC_MIN, AUROC_MIN, CLICK_MIN, CLICK_TOL = 0.90, 0.95, 3, 0.5 | |
| JOB_FRAMES = 190_000 # engaged 25 h + manual 28 h at 1 Hz | |
| def auroc(y, p): | |
| from sklearn.metrics import roc_auc_score | |
| return float(roc_auc_score(y, p)) if len(set(y.tolist())) == 2 else float("nan") | |
| def detect_return(ts, P, hyst: bool): | |
| """-> (t_detected | None, reason, state string). State per frame; detected = first ON frame after the LAST OFF run.""" | |
| if hyst: | |
| st = []; s = int(P[0] >= 0.5) | |
| for p in P: | |
| if s == 1 and p <= 0.4: | |
| s = 0 | |
| elif s == 0 and p >= 0.6: | |
| s = 1 | |
| st.append(s) | |
| st = np.array(st) | |
| else: | |
| st = (P >= 0.5).astype(int) | |
| if st.min() == 1: | |
| return None, "never_off", "".join(map(str, st)) | |
| if st.max() == 0: | |
| return None, "never_on", "".join(map(str, st)) | |
| last_off = int(np.where(st == 0)[0].max()) | |
| if last_off == len(st) - 1: | |
| return None, "ends_off", "".join(map(str, st)) | |
| return float(ts[last_off + 1]), "ok", "".join(map(str, st)) | |
| def evaluate(val, sc, variant): | |
| d = sc["variants"][variant] | |
| P = np.array(d["p_yes"]); fr = sc["frames"] | |
| y = np.array([f["label"] for f in fr]); reg = np.array([f["regime"] for f in fr]); item = np.array([f["item"] for f in fr]) | |
| pred = (P >= 0.5).astype(int) | |
| m = {"n": int(len(y)), "acc": float((pred == y).mean()), "auroc": auroc(y, P), "recall_on": float((pred[y == 1] == 1).mean()), "recall_off": float((pred[y == 0] == 0).mean()), | |
| "p_mean_on": float(P[y == 1].mean()), "p_mean_off": float(P[y == 0].mean())} | |
| for r in ("engaged", "manual"): | |
| k = reg == r | |
| m[f"{r}_n"] = int(k.sum()); m[f"{r}_acc"] = float((pred[k] == y[k]).mean()); m[f"{r}_auroc"] = auroc(y[k], P[k]) | |
| # best-threshold diagnostic (NOT the pre-registered operating point) | |
| ths = np.unique(P); accs = [((P >= t).astype(int) == y).mean() for t in ths] | |
| j = int(np.argmax(accs)); m["diag_best_thr"] = float(ths[j]); m["diag_best_acc"] = float(accs[j]) | |
| items = {it["id"]: it for it in val["items"]} | |
| agree = []; per_item = [] | |
| for iid in items: | |
| k = item == iid | |
| frac = float((pred[k] == y[k]).mean()); agree.append(frac > 0.5) | |
| per_item.append({"id": iid, "owner": items[iid]["owner_label"], "regime": items[iid]["regime"], "frac_correct": round(frac, 3), "p_mean": round(float(P[k].mean()), 3)}) | |
| m["item_agree"] = int(sum(agree)); m["item_n"] = len(agree); m["per_item"] = per_item | |
| clicks = [] | |
| for it in val["items"]: | |
| if it["click_dt"] is None: | |
| continue | |
| k = item == it["id"]; ts = np.array([f["t"] for f in fr])[k]; Pk = P[k]; o = np.argsort(ts); ts, Pk = ts[o], Pk[o] | |
| t_click = it["t_event"] + it["click_dt"] | |
| rec = {"id": it["id"], "regime": it["regime"], "t_click": round(t_click, 2), "click_dt": it["click_dt"], "p_trace": [round(float(v), 2) for v in Pk]} | |
| for tag, hy in (("plain", False), ("hyst", True)): | |
| td, why, st = detect_return(ts, Pk, hy) | |
| rec[tag] = {"t_det": None if td is None else round(td, 2), "delta_s": None if td is None else round(td - t_click, 2), "reason": why, "states": st, | |
| "within": bool(td is not None and abs(td - t_click) <= CLICK_TOL + 1e-9)} | |
| clicks.append(rec) | |
| m["clicks"] = clicks; m["clicks_within_hyst"] = int(sum(c["hyst"]["within"] for c in clicks)); m["clicks_within_plain"] = int(sum(c["plain"]["within"] for c in clicks)); m["clicks_n"] = len(clicks) | |
| m["pass"] = bool(m["acc"] >= ACC_MIN and m["auroc"] >= AUROC_MIN and m["clicks_within_hyst"] >= CLICK_MIN) | |
| m["timing"] = {k: d[k] for k in ("wall_s", "model_s", "preproc_s", "frames_per_s_wall", "frames_per_s_model", "peak_vram_alloc_gb", "peak_vram_reserved_gb", "seq_len_max", "image_tokens_max", "top1_in_yes_no", "n_full_fallback") if k in d} | |
| m["timing"]["yes_no_mass_median"] = float(np.median(d["yes_no_mass"])) | |
| return m | |
| def overlay_effect(sc): | |
| if "overlay" not in sc["variants"] or "full896" not in sc["variants"]: | |
| return None | |
| a = np.array(sc["variants"]["full896"]["p_yes"]); b = np.array(sc["variants"]["overlay"]["p_yes"]) | |
| return {"mean_abs_dP": float(np.abs(a - b).mean()), "flip_rate_at_0.5": float(((a >= 0.5) != (b >= 0.5)).mean()), "mean_P_clean": float(a.mean()), "mean_P_overlay": float(b.mean()), | |
| "corr": float(np.corrcoef(a, b)[0, 1])} | |
| def f(x, nd=3): | |
| return "—" if x is None or (isinstance(x, float) and np.isnan(x)) else (f"{x:.{nd}f}" if isinstance(x, float) else str(x)) | |
| def main() -> int: | |
| val = json.load(open(OUT / "validation_frames.json")) | |
| scored = {c: json.load(open(OUT / f"scores_{c}.json")) for c in ORDER if (OUT / f"scores_{c}.json").exists()} | |
| metrics = {"built_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "acceptance": {"acc_min": ACC_MIN, "auroc_min": AUROC_MIN, "clicks_min": CLICK_MIN, "click_tol_s": CLICK_TOL, "primary_detector": "hysteresis 0.4/0.6"}, | |
| "validation_summary": val["summary"], "candidates": {}, "accepted": None, "not_evaluated": []} | |
| for c in ORDER: | |
| if c not in scored: | |
| metrics["not_evaluated"].append(c); continue | |
| sc = scored[c] | |
| metrics["candidates"][c] = {"model_id": sc["meta"]["model_id"], "n_params_M": sc["meta"]["n_params_M"], "batch": sc["meta"]["batch"], "load_s": sc["load_s"], "vram_after_load_gb": sc["vram_after_load_gb"], "gate": sc["gate"], | |
| "yes_tokens": sc["meta"]["yes_tokens"], "no_tokens": sc["meta"]["no_tokens"], "variants": {v: evaluate(val, sc, v) for v in sc["variants"]}, "overlay_effect": overlay_effect(sc)} | |
| metrics["candidates"][c]["pass_any_clean"] = any(metrics["candidates"][c]["variants"][v]["pass"] for v in CLEAN_VARIANTS if v in sc["variants"]) | |
| metrics["candidates"][c]["verdict"] = "PASS" if metrics["candidates"][c]["pass_any_clean"] else "FAIL" | |
| if metrics["accepted"] is None and metrics["candidates"][c]["pass_any_clean"]: | |
| best = max((v for v in CLEAN_VARIANTS if v in sc["variants"] and metrics["candidates"][c]["variants"][v]["pass"]), key=lambda v: metrics["candidates"][c]["variants"][v]["auroc"]) | |
| metrics["accepted"] = {"cand": c, "variant": best} | |
| (OUT / "metrics.json").write_text(json.dumps(metrics, indent=1)) | |
| # ------------------------------------------------------------------ table | |
| S = val["summary"]; L = [] | |
| L.append("# VLM hands-on gold — validation against the owner's frame-level clicks\n") | |
| L.append(f"Built {metrics['built_utc']} by `tools/vlm_handson/evaluate_vlm_handson.py` from `results/vlm_handson/scores_<cand>.json` (scorer `tools/vlm_handson/score_vlm_handson.py`) " | |
| f"and `results/vlm_handson/validation_frames.json` (`tools/vlm_handson/build_validation_frames.py`). Pre-registration: docs/decisions.md 2026-09-16T23:26Z " | |
| "\"vision-based hands-on gold\". Evaluation-only artefact; no frozen file or result was modified; frames never left the machine (local HF models).\n") | |
| L.append("## Validation set\n") | |
| L.append(f"* {S['n_items']} owner-labelled items → **{S['n_frames']} frames** ({S['n_pos']} hands-on = 1, {S['n_neg']} hands-off = 0); pass-4 items {S['by_source']['pass4']['items']} × 21 frames " | |
| f"(dt −1.0 … +1.0 s), v3-page items {S['by_source']['v3']['items']} × 31 frames (dt −2.0 … +1.0 s); regimes: engaged {S['by_regime']['engaged']['items']} items / {S['by_regime']['engaged']['frames']} frames, " | |
| f"manual {S['by_regime']['manual']['items']} items / {S['by_regime']['manual']['frames']} frames. Owner labels: never_on {S['by_owner_label'].get('never_on', 0)}, always_on {S['by_owner_label'].get('always_on', 0)}, " | |
| f"clicked returns (ret_dt) {S['by_owner_label'].get('ret_dt', 0)} ({', '.join(c['id'] + ' ' + c['regime'] + ' click ' + str(c['click_dt']) + ' s' for c in S['clicked_returns'])}). " | |
| f"Skipped: {len(val['skipped_items'])} items (5 pass-4 'unsure', 90 unlabelled V3 items).") | |
| L.append("* Label rules: never_on → all frames 0; always_on → all frames 1; ret_dt=c → 0 for dt < c, 1 for dt ≥ c. Labels source `results/review_tool/labels_latest.json`; items " | |
| "`items_relabel_hr_engaged_r4.json` (ids `<orig>__r4`) and `items_relabel_v3.json` (ids `V3_*`).") | |
| L.append(f"* Frames RE-EXTRACTED CLEAN from the local videos with the review tool's own convention (plain cv2 seek at frame_idx = round(20 t)); each clean frame was checked against the owner's overlay jpg: " | |
| f"mean |diff| outside the box outline / text band median {S['overlay_check_mad']['median']}, p95 {S['overlay_check_mad']['p95']}, max {S['overlay_check_mad']['max']} (JPEG-noise level → identical frames). " | |
| f"Driver box defined on {S['n_frames'] - S['n_box_undefined']}/{S['n_frames']} frames. Driver side = route-median box centre (right of the image on 40/41 items; B108__r4 has a known-bad box, cx = 0.28).") | |
| L.append("* Inputs: **crop** = causal driver box expanded ×1.6 horizontally toward the wheel side (+0.5 w outward, +0.1 w inward) and ×1.3 vertically (+0.25 h down, +0.05 h up), long side ≤ 896 px; " | |
| "**full896** = whole frame resized to 896 px wide; **overlay** = the owner's 640-px review jpg with the red box and text (fallback check only). Prompt exactly: " | |
| "\"Look at the driver. Is at least one of the driver's hands touching the steering wheel? Answer with yes or no.\" P(yes) = softmax over the first-token logits of the single-token " | |
| "case variants of yes vs no (summed). bf16, sdpa, batch 8, left padding, ≤ 4 CPU threads; GPU shared with the running MotionBERT trainer.\n") | |
| L.append(f"## Acceptance (pre-registered)\n\nframe accuracy at P ≥ 0.5 **≥ {ACC_MIN}** AND AUROC **≥ {AUROC_MIN}** AND **≥ {CLICK_MIN} of the 4** clicked returns detected as off→on within ±{CLICK_TOL} s " | |
| "(detector: first ON frame after the last OFF run; hysteresis 0.4/0.6 primary, plain 0.5 also shown). Candidates in fixed order; the first passing on a clean variant is used; all evaluated are reported.\n") | |
| L.append("## Summary\n") | |
| L.append("| candidate | variant | n | acc@0.5 | AUROC | recall on | recall off | engaged acc / AUROC (n) | manual acc / AUROC (n) | items agree | clicks ±0.5 s hyst / plain | verdict |") | |
| L.append("|---|---|---|---|---|---|---|---|---|---|---|---|") | |
| for c, C in metrics["candidates"].items(): | |
| for v, m in C["variants"].items(): | |
| verdict = ("**PASS**" if m["pass"] else "fail") if v in CLEAN_VARIANTS else ("pass" if m["pass"] else "fail") + " (overlay check)" | |
| L.append(f"| {NAMES[c]} | {v} | {m['n']} | {f(m['acc'])} | {f(m['auroc'])} | {f(m['recall_on'])} | {f(m['recall_off'])} | {f(m['engaged_acc'])} / {f(m['engaged_auroc'])} ({m['engaged_n']}) | " | |
| f"{f(m['manual_acc'])} / {f(m['manual_auroc'])} ({m['manual_n']}) | {m['item_agree']}/{m['item_n']} | {m['clicks_within_hyst']}/{m['clicks_n']} / {m['clicks_within_plain']}/{m['clicks_n']} | {verdict} |") | |
| for c in metrics["not_evaluated"]: | |
| L.append(f"| {NAMES[c]} | — | — | — | — | — | — | — | — | — | — | not evaluated (an earlier candidate passed) |" if metrics["accepted"] else | |
| f"| {NAMES[c]} | — | — | — | — | — | — | — | — | — | — | not evaluated |") | |
| acc = metrics["accepted"] | |
| L.append("") | |
| L.append(f"**Accepted candidate: {NAMES[acc['cand']] + ' (' + acc['variant'] + ')' if acc else 'NONE — every evaluated candidate fails the pre-registered acceptance'}.**\n") | |
| for c, C in metrics["candidates"].items(): | |
| L.append(f"## {NAMES[c]} — verdict {C['verdict']}\n") | |
| L.append(f"* {C['n_params_M']} M params; load {C['load_s']} s; VRAM after load {C['vram_after_load_gb']} GB; gate at start: free VRAM {C['gate']['free_vram_gb']} GB, MemAvailable {C['gate']['mem_available_gb']} GB. " | |
| f"yes tokens {C['yes_tokens']}; no tokens {C['no_tokens']}.") | |
| L.append("\n| variant | wall s | frames/s wall | frames/s model-only | preproc s | peak VRAM alloc / reserved GB | seq len max | image tokens max (batch) | top-1 ∈ {yes,no} | yes+no mass median | full-frame fallbacks | diag best thr → acc |") | |
| L.append("|---|---|---|---|---|---|---|---|---|---|---|---|") | |
| for v, m in C["variants"].items(): | |
| t = m["timing"] | |
| L.append(f"| {v} | {t['wall_s']} | {t['frames_per_s_wall']} | {t['frames_per_s_model']} | {t['preproc_s']} | {t['peak_vram_alloc_gb']} / {t['peak_vram_reserved_gb']} | {t['seq_len_max']} | {t['image_tokens_max']} | " | |
| f"{f(t['top1_in_yes_no'])} | {f(t['yes_no_mass_median'])} | {t.get('n_full_fallback', 0)} | {f(m['diag_best_thr'])} → {f(m['diag_best_acc'])} |") | |
| L.append("\nMean P(yes) on hands-on vs hands-off frames: " + "; ".join(f"{v}: {f(m['p_mean_on'])} vs {f(m['p_mean_off'])}" for v, m in C["variants"].items()) + ".") | |
| oe = C["overlay_effect"] | |
| if oe: | |
| L.append(f"\nOverlay check (owner's jpg vs clean full frame, same frames): mean |ΔP| {f(oe['mean_abs_dP'])}, decision flips at 0.5 {f(oe['flip_rate_at_0.5'])}, mean P clean {f(oe['mean_P_clean'])} vs overlay {f(oe['mean_P_overlay'])}, corr {f(oe['corr'])}. " | |
| "(The overlay jpg is 640 px wide with a red box and a text band; it was not used for the verdict.)") | |
| L.append("\n### Clicked returns (owner's click = first frame with a hand on the wheel)\n") | |
| L.append("| variant | item | regime | click dt | t_click | hyst: t_det / Δ s / reason | within ±0.5 | plain: t_det / Δ s / reason | within ±0.5 | P(yes) trace (dt −2.0 … +1.0, 0.1 s) |") | |
| L.append("|---|---|---|---|---|---|---|---|---|---|") | |
| for v, m in C["variants"].items(): | |
| for ck in m["clicks"]: | |
| h, p = ck["hyst"], ck["plain"] | |
| L.append(f"| {v} | {ck['id']} | {ck['regime']} | {ck['click_dt']:+.1f} | {ck['t_click']} | {f(h['t_det'], 2)} / {f(h['delta_s'], 2)} / {h['reason']} | {'yes' if h['within'] else 'no'} | " | |
| f"{f(p['t_det'], 2)} / {f(p['delta_s'], 2)} / {p['reason']} | {'yes' if p['within'] else 'no'} | {' '.join(f'{x:.2f}' for x in ck['p_trace'])} |") | |
| L.append("\n### Per item (fraction of frames correct at 0.5; mean P(yes))\n") | |
| vs = list(C["variants"]) | |
| L.append("| item | regime | owner label | " + " | ".join(f"{v} frac / mean P" for v in vs) + " |") | |
| L.append("|---|---|---|" + "---|" * len(vs)) | |
| rows = {r["id"]: {} for r in C["variants"][vs[0]]["per_item"]} | |
| for v in vs: | |
| for r in C["variants"][v]["per_item"]: | |
| rows[r["id"]][v] = r | |
| for iid, rr in rows.items(): | |
| r0 = rr[vs[0]] | |
| L.append(f"| {iid} | {r0['regime']} | {r0['owner']} | " + " | ".join(f"{rr[v]['frac_correct']:.2f} / {rr[v]['p_mean']:.2f}" for v in vs) + " |") | |
| L.append("") | |
| # throughput for the labelling job | |
| L.append("## Throughput estimate for the test-split labelling job (≈ 190 k frames at 1 Hz: engaged 25 h + manual 28 h)\n") | |
| L.append("| candidate | variant | batch | frames/s wall (2B/3B: GPU shared with the ~20 GB MotionBERT trainer; 4B: idle GPU) | frames/s model-only | hours for 190 k frames (wall) | hours (model-only) |") | |
| L.append("|---|---|---|---|---|---|---|") | |
| for c, C in metrics["candidates"].items(): | |
| for v, m in C["variants"].items(): | |
| if v == "overlay": | |
| continue | |
| t = m["timing"] | |
| L.append(f"| {NAMES[c]} | {v} | {C['batch']} | {t['frames_per_s_wall']} | {t['frames_per_s_model']} | {JOB_FRAMES / t['frames_per_s_wall'] / 3600:.1f} | {JOB_FRAMES / t['frames_per_s_model'] / 3600:.1f} |") | |
| L.append("\n## Notes / failure analysis\n") | |
| L.append("* Qwen3-VL-2B answers 'yes' almost regardless of the image: median P(yes) on hands-OFF frames 0.95 (crop) / 0.88 (full896); 22/25 never_on items have mean P ≥ 0.5 (crop), 9/25 for the 3B. The never_on item " | |
| "scored highest (B103__r4, TOYOTA_PRIUS, night IR, P = 0.99) shows the driver holding a phone with both hands directly over the wheel rim — hands hovering at the wheel, not touching: the " | |
| "distinction the keypoint-geometry labels v2–v4b also failed on (decisions 23:26Z). Its AUROC (0.81 / 0.76) shows some ranking signal but far below 0.95, and no threshold reaches 0.90 accuracy " | |
| "(best-threshold diagnostic 0.81 at P ≥ 0.996).") | |
| L.append("* Qwen2.5-VL-3B is better calibrated (mean P on / off 0.68 / 0.44, crop) but separates weakly: acc 0.67, AUROC 0.80 (crop); 0.65 / 0.73 (full896). With hysteresis it places 3/4 clicked " | |
| "returns within ±0.5 s on the crop (V3_000 −0.1 s, V3_004 −0.3 s, V3_009 +0.3 s; V3_001 fires 2.1 s early), 0/4 on the full frame — the click criterion alone would pass on the crop, the " | |
| "accuracy / AUROC criteria fail by a wide margin.") | |
| L.append("* The route KIA_NIRO_EV__driver_47__00000138 (items B108__r4 never_on, B110__r4 always_on) has a wrong causal driver box (passenger head-rest, cx = 0.28; the frozen pose cache is " | |
| "used as given), so its crop never contains the wheel — B110 is the lowest-scored always_on item for every model (2B 0.06, 3B 0.46). This affects 42 of 981 frames and does not change any verdict.") | |
| L.append("* P(yes) is computed from bf16 logits, so the probabilities are quantised (a few hundred distinct levels over 981 frames); ties slightly depress AUROC but cannot explain the gap to 0.95.") | |
| L.append("* The overlay jpg (red box + text) shifts the 2B scores little (mean |ΔP| 0.09, 4 % decision flips) and the 3B scores substantially (mean |ΔP| 0.15, 29 % flips, lower AUROC 0.66 vs 0.73) — " | |
| "the clean re-extraction was the right input; all verdicts use clean frames only.") | |
| L.append("* Qwen3-VL-4B (best of the three: AUROC 0.85 on both clean variants, acc 0.70 crop / 0.64 full896, best-threshold diagnostic 0.80–0.81) still says 'yes' throughout the two clicked " | |
| "returns V3_000 and V3_001 (never off in the 2 s before the owner's click) and passes only 1/4 (crop) or 2/4 (full896) clicks; 10/25 never_on items keep mean P ≥ 0.5 on the crop. " | |
| "It was run at batch 4 (not 8) so that its 8.9 GB of bf16 weights plus activations stay inside the ≤ 10 GB coexistence budget (peak alloc 9.8 GB); by the time its gate opened the " | |
| "MotionBERT run had finished, so its throughput was measured on an otherwise idle GPU, whereas the 2B and 3B shared the GPU with the ~20 GB trainer." if "qwen3vl4b" in metrics["candidates"] | |
| else "* Qwen3-VL-4B: see the summary row for whether it was evaluated.") | |
| L.append("* Conclusion: at this camera view (wide-angle cabin camera, wheel seen edge-on from above, hands often resting at or hovering over the rim) a 2–4 B local VLM asked the yes/no question " | |
| "per frame does not resolve 'touching' from 'resting just off the wheel' either — the same mechanism that defeated the keypoint-geometry labels v3/v4/v4b. The pre-registered fallback applies: " | |
| "the owner decides between a manual gold set and dropping the engaged hand cells.") | |
| L.append("\nThe wall figure includes JPEG decoding + PIL resizing in the main thread on clean frames already on disk; the labelling job must additionally decode video " | |
| "(index-exact `baselines_dms/video_io.py` decode, ~1 frame per second of video at 1 Hz, i.e. a full decode pass over 53 h of video is the floor) — plan for the wall figure " | |
| "or worse, and for the 5 Hz densification around state changes on top. Measured while the MotionBERT trainer (~20 GB) shared the GPU; an idle GPU is faster.\n") | |
| TABLE.parent.mkdir(parents=True, exist_ok=True) | |
| TABLE.write_text("\n".join(L)) | |
| print("\n".join(L[:40])) | |
| print(f"TABLE {TABLE}\nMETRICS {OUT / 'metrics.json'}\nACCEPTED {acc}") | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |
Xet Storage Details
- Size:
- 22 kB
- Xet hash:
- 5ababd740ca9a79fc377f0ff887f0a829a6019ba62061147212956a1dbde2fb9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.