AsianPlayer/TriDrive / code /data /build_behaviour_targets.py
AsianPlayer's picture
download
raw
8.52 kB
"""cwm3/data/build_behaviour_targets.py — DRIVER-BEHAVIOUR event chains B[N,25,3] {hand_return, hand_leave, head_turn_onset}
on the s12 anchor grid, sourced from the CAUSAL kinematics where they exist (no pose work is redone here).
Sources, in priority order per row (recorded in B_source):
3 causal_routes results/targets_kin_causal_routes/kin_causal_{regime}_{split}.npz (route-level causal pose, all rows —
used only when the full set exists; the 5-route test build is ignored)
1 causal_clip results/targets_kin_causal/kin_causal_{regime}_{split}.npz (clip-window causal pose, train routes only)
2 old_noncausal cwm2/results/targets_kin/kin_{regime}_{split}.npz (route-median ROI, whole-route medians incl.
the future — DISCLOSED fallback; the old events agree with the causal ones only at F1 ~ 0.3 on hand events,
qc.json) ; 0 none
The event definitions are those of the source files (hands_on = min_hand_rest_dist < 0.10; head_turned = hysteresis
0.1941/0.1026 on |head_turn_dev|; onsets = inactive->active of the three chains {hands_on, 1-hands_on, head_turned}).
Outputs results/targets_behaviour/behaviour_{regime}_{split}.npz (row-aligned with s12; uid, t asserted):
B[N,25,3] u8, Bvalid[N,25,3] bool, B0[N,3] f32 (chain state at t; NaN if unknown), Bstate[N,25,2] f32 {hands_on, head_turned},
Bstate0[N,2] f32, B_first[N,3] int16 (first onset step given inactive & valid at t; -1 none; -2 anchor active / invalid),
B_source[N] u8, Bc_only[N,25,3] u8 / Bc_valid[N,25,3] bool (causal-only copy: False/0 where no causal source),
events, states, uid, t, dongle
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import time
from pathlib import Path
os.environ.setdefault("OMP_NUM_THREADS", "8")
import numpy as np # noqa: E402
ROOT = Path(__file__).resolve().parents[1]
EXPLORE = ROOT.parent
sys.path.insert(0, str(ROOT / "common"))
from runmeta import write_run_json # noqa: E402
S12 = EXPLORE / "s12_wm" / "results"
KIN_CLIP = ROOT / "results" / "targets_kin_causal"
KIN_ROUTES = ROOT / "results" / "targets_kin_causal_routes"
KIN_OLD = EXPLORE / "cwm2" / "results" / "targets_kin"
OUT = ROOT / "results" / "targets_behaviour"
EVENTS = ["hand_return", "hand_leave", "head_turn_onset"]
STATES = ["hands_on", "head_turned"]
SRC = {"none": 0, "causal_clip": 1, "old_noncausal": 2, "causal_routes": 3}
def _first(B0, B, Bv):
"""B0: chain inactive-at-anchor flag (1 = inactive & known); onsets B[N,25] where valid."""
onset = (B > 0) & Bv
first = np.where(onset.any(1), onset.argmax(1), -1).astype(np.int16)
first[~B0] = -2
return first
def chain_inactive0(E0):
"""E0 = state of each event's chain at the anchor (kin convention: inactive = < 0.5; NaN unknown)."""
return np.isfinite(E0) & (E0 < 0.5)
def build_split(regime, split, use_routes):
z = np.load(S12 / f"targets_{regime}_{split}.npz", allow_pickle=True)
uid, t, dongle, M = z["uid"], z["t"].astype(np.float64), z["dongle"], z["M"]
N = len(t)
B = np.zeros((N, 25, 3), np.uint8)
Bv = np.zeros((N, 25, 3), bool)
B0 = np.full((N, 3), np.nan, np.float32)
Bs = np.full((N, 25, 2), np.nan, np.float32)
Bs0 = np.full((N, 2), np.nan, np.float32)
src = np.zeros(N, np.uint8)
cov = {}
def take(rows, E, EM, E0, S, S0, code):
B[rows] = E[rows]
Bv[rows] = EM[rows]
B0[rows] = E0[rows]
Bs[rows] = S[rows]
Bs0[rows] = S0[rows]
src[rows] = code
# 3 route-level causal (only a complete set)
pr = KIN_ROUTES / f"kin_causal_{regime}_{split}.npz"
if use_routes and pr.exists():
k = np.load(pr, allow_pickle=True)
assert np.array_equal(k["uid"], uid) and np.allclose(k["t"], t)
rows = np.flatnonzero(k["row_ok"] if "row_ok" in k else k["EM_c"].any((1, 2)))
take(rows, k["E_c"], k["EM_c"], k["E0_c"], k["S_c"], k["S0_c"], SRC["causal_routes"])
cov["causal_routes"] = int(len(rows))
# 1 clip-window causal
pc = KIN_CLIP / f"kin_causal_{regime}_{split}.npz"
if pc.exists():
k = np.load(pc, allow_pickle=True)
assert np.array_equal(k["uid"], uid) and np.allclose(k["t"], t)
rows = np.flatnonzero(k["row_ok"] & (src == 0))
take(rows, k["E_c"], k["EM_c"], k["E0_c"], k["S_c"], k["S0_c"], SRC["causal_clip"])
cov["causal_clip"] = int(len(rows))
Bc, Bcv = B.copy(), Bv.copy() # causal-only copy before the fallback
# 2 old non-causal fallback
po = KIN_OLD / f"kin_{regime}_{split}.npz"
if po.exists():
k = np.load(po, allow_pickle=True)
assert np.array_equal(k["uid"], uid) and np.allclose(k["t"], t)
EM = k["EM"] & M[:, :, None]
has = EM.any((1, 2)) | np.isfinite(k["E0"]).any(1)
rows = np.flatnonzero(has & (src == 0))
take(rows, k["E"], EM, k["E0"], k["S"], k["S0"], SRC["old_noncausal"])
cov["old_noncausal"] = int(len(rows))
cov["none"] = int((src == 0).sum())
inact0 = chain_inactive0(B0)
B_first = np.stack([_first(inact0[:, e], B[:, :, e], Bv[:, :, e]) for e in range(3)], 1)
d = dict(B=B, Bvalid=Bv, B0=B0, Bstate=Bs, Bstate0=Bs0, B_first=B_first, B_source=src, Bc_only=Bc, Bc_valid=Bcv,
events=np.array(EVENTS), states=np.array(STATES), uid=uid, t=t, dongle=dongle)
stats = dict(N=N, coverage=cov, coverage_frac={k: v / N for k, v in cov.items()})
for e, ev in enumerate(EVENTS):
for code, name in ((1, "causal_clip"), (2, "old_noncausal"), (3, "causal_routes")):
r = src == code
if not r.any():
continue
sel3 = r & (B_first[:, e] != -2) & Bv[:, :15, e].all(1)
y3 = (B_first[sel3, e] >= 0) & (B_first[sel3, e] < 15)
stats[f"{ev}__{name}"] = dict(valid_frac_steps=float(Bv[r, :, e].mean()), onset_rate_steps=float(B[r][:, :, e][Bv[r][:, :, e]].mean()) if Bv[r][:, :, e].any() else float("nan"),
n_inactive_anchors_3s=int(sel3.sum()), onset_anchors_3s=int(y3.sum()),
base_rate_3s=float(y3.mean()) if sel3.any() else float("nan"))
return d, stats
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--regimes", default="manual,engaged")
ap.add_argument("--splits", default="train,val,test")
ap.add_argument("--force", action="store_true")
ap.add_argument("--use-routes", action="store_true", help="use results/targets_kin_causal_routes when the full set exists")
a = ap.parse_args()
OUT.mkdir(parents=True, exist_ok=True)
t_start = time.time()
summary, files, inputs = {}, [], []
for regime in a.regimes.split(","):
for split in a.splits.split(","):
d, st = build_split(regime, split, a.use_routes)
p = OUT / f"behaviour_{regime}_{split}.npz"
if p.exists() and not a.force:
raise FileExistsError(f"{p} exists (append-only; --force to overwrite)")
np.savez_compressed(p, **d)
files.append(str(p))
summary[f"{regime}_{split}"] = st
print(f"[{regime}/{split}] N={st['N']} coverage={st['coverage']}", flush=True)
for k, v in st.items():
if "__" in k:
print(f" {k:32s} base_rate_3s={v['base_rate_3s']:.5f} ({v['onset_anchors_3s']}/{v['n_inactive_anchors_3s']}) valid={v['valid_frac_steps']:.3f}", flush=True)
for src_dir in (KIN_CLIP, KIN_OLD, KIN_ROUTES if a.use_routes else None):
if src_dir is None:
continue
f = src_dir / (f"kin_causal_{regime}_{split}.npz" if src_dir != KIN_OLD else f"kin_{regime}_{split}.npz")
if f.exists():
inputs.append(f)
inputs.append(S12 / f"targets_{regime}_{split}.npz")
stamp = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
(OUT / f"build_stats_{stamp}.json").write_text(json.dumps(summary, indent=1))
(OUT / "build_stats_latest.json").write_text(json.dumps(summary, indent=1))
write_run_json(OUT, "build_behaviour_targets", [Path(__file__)], sorted(set(inputs)), 0, t_start,
extra=dict(files_written=files, events=EVENTS, states=STATES, source_codes=SRC, use_routes=a.use_routes, dms_fields_used=[]))
print("BEHAVIOUR_DONE", f"{time.time() - t_start:.0f}s", flush=True)
if __name__ == "__main__":
main()

Xet Storage Details

Size:
8.52 kB
·
Xet hash:
1a25a581006d45cdfb2b8a1d0b784a5055a1f958458c92b3079b2c9ba9ee0742

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