AsianPlayer/TriDrive / code /weak /face_vs_dms_diag.py
AsianPlayer's picture
download
raw
7.94 kB
"""cwm3/weak/face_vs_dms_diag.py — DIAGNOSTIC — never a feature.
Spearman correlation of OUR landmark-derived face channels (results/targets_face_causal_routes_v2, data/build_face_causal.py: lm_yaw, lm_yaw_dev,
lm_pitch_dev, lm_roll_dev, ear_conf_asym, eye_open, blink_rate_4s, gaze_off, gaze_off_dwell_s) with the DERIVED openpilot DMS teacher channels of
results/targets_dms_teacher/dms_{regime}_{train,val}.npz (weak/dms_teacher_targets.py: face_yaw, face_pitch, eye_open, blink, distracted_soft,
gaze_off_soft) on TRAIN and VAL anchors only (the teacher pack holds no test rows — asserted). This file is registered in tests/test_no_dms_inputs.py
DMS_READERS as a reader of DMS-derived quantities; models/ train/ eval/ data/ never import it, nothing it writes is read back by any model or head, and
no DMS quantity is joined to a test row. Purpose: a sanity check that the landmark yaw / eye channels measure what the DMS measures (a correlation of
the right sign and magnitude), reported in results/tables/face_vs_dms_diag.md.
python weak/face_vs_dms_diag.py [--max-rows 200000]
"""
from __future__ import annotations
import argparse
import json
import sys
import time
from pathlib import Path
import numpy as np
import pandas as pd
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "common"))
from runmeta import write_run_json # noqa: E402
FACE = ROOT / "results" / "targets_face_causal_routes_v2"
DMS = ROOT / "results" / "targets_dms_teacher"
TABLE = ROOT / "results" / "tables" / "face_vs_dms_diag.md"
SPLITS_ALLOWED = ("train", "val")
PAIRS = [("lm_yaw", "face_yaw"), ("lm_yaw_dev", "face_yaw"), ("lm_pitch_dev", "face_pitch"), ("eye_open", "eye_open"), ("blink_rate_4s", "blink"),
("gaze_off", "gaze_off_soft"), ("gaze_off_dwell_s", "gaze_off_soft"), ("gaze_off", "distracted_soft"), ("ear_conf_asym", "face_yaw"), ("lm_roll_dev", "face_yaw")]
def spearman(x, y):
from scipy.stats import spearmanr
m = np.isfinite(x) & np.isfinite(y)
if m.sum() < 100:
return np.nan, int(m.sum())
return float(spearmanr(x[m], y[m]).statistic), int(m.sum())
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--max-rows", type=int, default=200000, help="seeded uniform subsample of anchors per (regime, split) with both packs valid")
ap.add_argument("--seed", type=int, default=0)
a = ap.parse_args()
t0 = time.time()
rng = np.random.default_rng(a.seed)
rows, auc_rows = [], []
for regime in ("manual", "engaged"):
for split in SPLITS_ALLOWED:
pd_ = DMS / f"dms_{regime}_{split}.npz"
pf = FACE / f"facehist_causal_{regime}_{split}.npz"
if not pd_.exists() or not pf.exists():
print(f"skip {regime}/{split}: missing pack")
continue
with np.load(pd_, allow_pickle=True) as zd, np.load(pf, allow_pickle=True) as zf:
assert np.array_equal(zd["uid"], zf["uid"]) and np.allclose(zd["t"], zf["t"]), "row alignment"
dch = [str(c) for c in zd["channels"]]
fch = [str(c) for c in zf["channels"]]
T0, T0v, mv = zd["T0"].astype(np.float32), zd["T0valid"].astype(bool), zd["Moving0"].astype(bool)
F0, F0v = zf["F0_c"].astype(np.float32), zf["F0valid"].astype(bool)
both = T0v & F0v & mv
idx = np.flatnonzero(both)
if len(idx) > a.max_rows:
idx = np.sort(rng.choice(idx, a.max_rows, replace=False))
print(f"{regime}/{split}: anchors {len(T0)}, DMS valid {T0v.mean():.3f}, face valid {F0v.mean():.3f}, both & moving {both.mean():.3f}, used {len(idx)}", flush=True)
for fc, dc in PAIRS:
r, n = spearman(F0[idx, fch.index(fc)].astype(np.float64), T0[idx, dch.index(dc)].astype(np.float64))
rows.append(dict(regime=regime, split=split, face_channel=fc, dms_channel=dc, spearman_r=r, n=n))
# binary DMS soft flags: AUROC of each landmark channel for distracted_soft > 0.5 / gaze_off_soft > 0.5 (descriptive)
from sklearn.metrics import roc_auc_score
for dc in ("distracted_soft", "gaze_off_soft"):
yb = (T0[idx, dch.index(dc)] > 0.5).astype(int)
if yb.min() == yb.max():
continue
for fc in fch:
x = F0[idx, fch.index(fc)].astype(np.float64)
m = np.isfinite(x)
if m.sum() < 100 or yb[m].min() == yb[m].max():
continue
auc_rows.append(dict(regime=regime, split=split, dms_flag=dc, face_channel=fc, auroc=float(roc_auc_score(yb[m], x[m])), n=int(m.sum()), pos_rate=float(yb[m].mean())))
R, A = pd.DataFrame(rows), pd.DataFrame(auc_rows)
stamp = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
out = ROOT / "results" / "face_vs_dms_diag"
out.mkdir(parents=True, exist_ok=True)
R.to_csv(out / f"spearman_{stamp}.csv", index=False)
A.to_csv(out / f"flag_auroc_{stamp}.csv", index=False)
L = [f"## DIAGNOSTIC — never a feature: landmark face channels vs the derived openpilot DMS teacher channels (weak/face_vs_dms_diag.py, run {stamp})", "",
"TRAIN / VAL anchors only (moving, both packs valid; seeded subsample ≤ {:,} per cell); Spearman r between `facehist_causal_routes_v2` F0 channels and `targets_dms_teacher` T0 channels; "
"AUROC of each landmark channel for the DMS soft flags (> 0.5). Nothing here enters any model or head; the DMS pack holds no test rows.".format(a.max_rows), "",
"| face channel (ours) | DMS channel | " + " | ".join(f"{r}/{s} r (n)" for r in ("manual", "engaged") for s in SPLITS_ALLOWED) + " |", "|---|---|" + "---|" * 4]
for fc, dc in PAIRS:
cells = []
for r in ("manual", "engaged"):
for s in SPLITS_ALLOWED:
x = R[(R.regime == r) & (R.split == s) & (R.face_channel == fc) & (R.dms_channel == dc)]
cells.append(f"{x.spearman_r.iloc[0]:+.3f} ({int(x.n.iloc[0])})" if len(x) and np.isfinite(x.spearman_r.iloc[0]) else "—")
L.append(f"| {fc} | {dc} | " + " | ".join(cells) + " |")
if len(A):
L += ["", "AUROC of the landmark channels for the DMS soft flags (train + val pooled per regime; descriptive):", "",
"| DMS flag | face channel | manual AUROC (n, pos rate) | engaged AUROC (n, pos rate) |", "|---|---|---|---|"]
for dc in ("distracted_soft", "gaze_off_soft"):
for fc in R.face_channel.unique():
cells = []
for r in ("manual", "engaged"):
x = A[(A.regime == r) & (A.dms_flag == dc) & (A.face_channel == fc)]
cells.append(f"{np.average(x.auroc, weights=x.n):.3f} ({int(x.n.sum())}, {np.average(x.pos_rate, weights=x.n):.3f})" if len(x) else "—")
if any(c != "—" for c in cells):
L.append(f"| {dc} | {fc} | " + " | ".join(cells) + " |")
L.append("")
md = "\n".join(L) + "\n"
if not TABLE.exists():
TABLE.write_text("# DIAGNOSTIC — never a feature: our face-landmark channels vs the derived openpilot DMS channels (train/val anchors only)\n\n")
with open(TABLE, "a") as f:
f.write(md)
print(md)
write_run_json(out, "face_vs_dms_diag", [Path(__file__)], [DMS / f"dms_{r}_{s}.npz" for r in ("manual", "engaged") for s in SPLITS_ALLOWED if (DMS / f"dms_{r}_{s}.npz").exists()],
seed=a.seed, t_start=t0, input_listing=False, extra={"splits": list(SPLITS_ALLOWED), "dms_fields_used": ["targets_dms_teacher T0 channels (derived; train/val only)"],
"purpose": "DIAGNOSTIC — never a feature", "pairs": PAIRS})
print(f"DONE in {time.time() - t0:.0f}s")
if __name__ == "__main__":
main()

Xet Storage Details

Size:
7.94 kB
·
Xet hash:
a933c50545e547b5dd620571c409087dd8926b71c47b96c482c57d452b794d30

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.