Buckets:
| """cwm3/train/batch.py — compact raw arrays (data/cwm3_dataset.py) -> per-branch model inputs + targets, built ON THE GPU. | |
| The child process (after the fork under the GPU lock) moves the stage's compact arrays to the device once (GpuStore) and every | |
| batch is an index + standardisation on the device (no per-batch numpy work; the host raw dict is never standardised). | |
| Standardisation statistics are TRAIN-only (both regimes pooled, one model): xmu/xsd (wm3 rule over all 40 steps of X — the same | |
| statistics apply to the 8 s history, same signals), kmu/ksd (kin_predictability.feat_stats nan-aware rule over Kh), | |
| ymu/ysd = s12 target_stats.npz (manual train; the s12 / cwm2 convention for the MDN channels). | |
| Δt channels are real step offsets in seconds: X / Kh -> -(39-k)*0.1, X8 (8 s @5 Hz) -> -(39-k)*0.2, tokens -> t_bank - t_anchor. | |
| Targets follow the cwm2 grid_batch conventions (chain z_prev = anchor state then z[:-1]; masks AND the s12 censoring M and the | |
| validity of both z_prev and z). No openpilot DMS field is read. | |
| """ | |
| from __future__ import annotations | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| CWM3 = Path(__file__).resolve().parents[1] | |
| sys.path.insert(0, str(CWM3)) | |
| sys.path.insert(0, str(CWM3 / "data")) | |
| from models import heads as hd # noqa: E402 | |
| HIST, H, DK = 40, 25, 11 | |
| IDX_MIN_DIST, IDX_HEAD_CX = 10, 0 | |
| X_CLIP, K_CLIP = 8.0, 8.0 # K_CLIP 5 -> 8 (= X_CLIP) for NEW runs (coordinator 2026-09-11, K4 v2 stats arm: the ±5 clamp truncated 1.3 % of head_cx | |
| K_CLIP_LEGACY = 5.0 # under the correct v2 SDs). The base trainer writes the value it used into cfg["kclip"] (run.json); prep_U falls back to the | |
| # LEGACY 5 when a cfg has no `kclip` (every run before this date, joint / OOF / twin evaluation of those runs) -> old runs reproduce. | |
| KEYS = {"V": ["X", "Xpre", "x8_ok", "Y", "M", "phase0", "phase", "Resp", "RespValid", "Resp0", "Resp0valid", "mode", "slice_tag"], | |
| "U": ["X", "Kh", "K", "Kvalid", "S", "S0", "EM", "M", "mode", "slice_tag", "vc256", "vc256_ok", "crop256", "crop256_ok", | |
| "Sg", "Sg0", "EMg", "Fh", "Fhvalid", "kpf"], # kpf: LANE F1 forecaster channel (2026-09-17; absent -> skipped); Sg* / EMg: gaze chain targets, Fh*: face-landmark inputs (opt-in U variants 2026-09-12; GpuStore skips absent keys) | |
| "E": ["tok_idx", "t", "vf256", "vf256_ok", "D", "Dvalid", "D0", "D0valid", "M", "F", "Fok", "mode", "slice_tag", | |
| "regionpca256", "regionpca256_ok", "P", "Pvalid", "P0", "P0valid", | |
| "vfcan256", "vfcan256_ok", "can256", "can256_ok", "vfcan256s", "vfcan256s_ok"]} # regionpca256: side vector (pack v2); P*: road-pressure chains (E0 `*_P`); vfcan256 / can256: F5b CAN side streams (2026-09-16; GpuStore skips absent keys) | |
| # ----------------------------------------------------------------------------- statistics (train only) | |
| STATS_VERSION = 2 # 2 (2026-09-11): float64 accumulation. v1 accumulated 58 M float32 values with np.mean/np.std -> stored vEgo mean 11.90 vs 17.01 | |
| # true, kinematics SDs 2-3x off (docs/latent_quality.md §9). Models trained with v1 stats are internally consistent (same affine | |
| # map on train / val / test) but their standardised MAEs are not in SD units; every table before this date is "stats v1". | |
| def compute_stats_v1(tr, target_stats): | |
| """the PRE-fix rule verbatim (every run before 2026-09-11): float32 arrays reduced along axis 0 with np.mean / np.std (X) and the | |
| kin_predictability nan-aware np.nanmean / np.nanstd (Kh) — sequential float32 accumulation over ~58 M values (vEgo mean 11.90 vs 17.01 | |
| true, kinematics SDs 2–3x off; results/tables/stats_fix.md). Kept ONLY for the stats-version disentangling arm (`--stats-version 1`, | |
| coordinator 2026-09-11); never the default.""" | |
| st = {"ymu": np.asarray(target_stats["mu"], np.float32), "ysd": np.asarray(target_stats["sd"], np.float32)} | |
| if "X" in tr: | |
| X = tr["X"].reshape(-1, 5).astype(np.float32) | |
| st["xmu"], st["xsd"] = X.mean(0).astype(np.float32), (X.std(0) + 1e-6).astype(np.float32) | |
| if "Kh" in tr: | |
| Kh = tr["Kh"].reshape(-1, tr["Kh"].shape[-1]).astype(np.float32) # width = the pack's Kh (11; 14 for kin v2p, 2026-09-18) | |
| st["kmu"], st["ksd"] = np.nanmean(Kh, 0).astype(np.float32), (np.nanstd(Kh, 0) + 1e-6).astype(np.float32) | |
| st["stats_version"] = np.int64(1) | |
| if "Resp0" in tr: | |
| v = tr["Resp0valid"] | |
| st["resp0_base"] = np.float32(tr["Resp0"][v].mean() if v.any() else 0.3) | |
| if "D0" in tr: | |
| st["d0_base"] = np.array([tr["D0"][tr["D0valid"][:, j], j].mean() if tr["D0valid"][:, j].any() else 0.0 for j in range(6)], np.float32) | |
| if "P0" in tr: | |
| st["p0_base"] = np.array([tr["P0"][tr["P0valid"][:, j], j].mean() if tr["P0valid"][:, j].any() else 0.0 for j in range(6)], np.float32) | |
| return st | |
| def compute_stats(tr, target_stats, version=STATS_VERSION): | |
| if int(version) == 1: | |
| return compute_stats_v1(tr, target_stats) | |
| assert int(version) == 2, version | |
| st = {"ymu": np.asarray(target_stats["mu"], np.float32), "ysd": np.asarray(target_stats["sd"], np.float32)} | |
| if "X" in tr: | |
| X = tr["X"].reshape(-1, 5) | |
| mu = np.zeros(5, np.float64) | |
| var = np.zeros(5, np.float64) | |
| for i in range(0, len(X), 1_000_000): # chunked float64 accumulation (exact to ~1e-12 relative) | |
| xb = X[i:i + 1_000_000].astype(np.float64) | |
| mu += xb.sum(0) | |
| var += (xb ** 2).sum(0) | |
| mu /= len(X) | |
| var = var / len(X) - mu ** 2 | |
| st["xmu"], st["xsd"] = mu.astype(np.float32), (np.sqrt(np.maximum(var, 0)) + 1e-6).astype(np.float32) | |
| if "Kh" in tr: | |
| DKh = tr["Kh"].shape[-1] # 11, or 14 for kin v2p (2026-09-18: 3 keypoint proxies appended; identical rule per column) | |
| Kh = tr["Kh"].reshape(-1, DKh) | |
| sm = np.zeros(DKh, np.float64) | |
| sq = np.zeros(DKh, np.float64) | |
| cnt = np.zeros(DKh, np.float64) | |
| for i in range(0, len(Kh), 1_000_000): | |
| kb = Kh[i:i + 1_000_000].astype(np.float64) | |
| ok = np.isfinite(kb) | |
| sm += np.where(ok, kb, 0.0).sum(0) | |
| sq += np.where(ok, kb ** 2, 0.0).sum(0) | |
| cnt += ok.sum(0) | |
| mu = sm / np.maximum(cnt, 1) | |
| var = sq / np.maximum(cnt, 1) - mu ** 2 | |
| st["kmu"], st["ksd"] = mu.astype(np.float32), (np.sqrt(np.maximum(var, 0)) + 1e-6).astype(np.float32) | |
| if "Fh" in tr: # face-landmark inputs (u_face, 2026-09-12): the same nan-aware float64 rule as Kh | |
| DF = tr["Fh"].shape[-1] | |
| Fh = tr["Fh"].reshape(-1, DF) | |
| sm = np.zeros(DF, np.float64) | |
| sq = np.zeros(DF, np.float64) | |
| cnt = np.zeros(DF, np.float64) | |
| for i in range(0, len(Fh), 1_000_000): | |
| fb = Fh[i:i + 1_000_000].astype(np.float64) | |
| ok = np.isfinite(fb) | |
| sm += np.where(ok, fb, 0.0).sum(0) | |
| sq += np.where(ok, fb ** 2, 0.0).sum(0) | |
| cnt += ok.sum(0) | |
| mu = sm / np.maximum(cnt, 1) | |
| var = sq / np.maximum(cnt, 1) - mu ** 2 | |
| st["fmu"], st["fsd"] = mu.astype(np.float32), (np.sqrt(np.maximum(var, 0)) + 1e-6).astype(np.float32) | |
| st["stats_version"] = np.int64(STATS_VERSION) | |
| if "Resp0" in tr: | |
| v = tr["Resp0valid"] | |
| st["resp0_base"] = np.float32(tr["Resp0"][v].mean() if v.any() else 0.3) | |
| if "D0" in tr: | |
| st["d0_base"] = np.array([tr["D0"][tr["D0valid"][:, j], j].mean() if tr["D0valid"][:, j].any() else 0.0 for j in range(6)], np.float32) | |
| if "P0" in tr: # road-pressure anchor base rates (fill for invalid anchors), as d0_base | |
| st["p0_base"] = np.array([tr["P0"][tr["P0valid"][:, j], j].mean() if tr["P0valid"][:, j].any() else 0.0 for j in range(6)], np.float32) | |
| return st | |
| # ----------------------------------------------------------------------------- device store | |
| class GpuStore: | |
| """Compact arrays of a raw dict (subset of keys) as tensors on `device`; the token bank lives here too (E).""" | |
| def __init__(self, raw, keys, device, bank=None): | |
| self.device = device | |
| self.N = len(raw["t"]) | |
| self.t = {} | |
| for k in keys: | |
| if k in raw: | |
| a = raw[k] | |
| if a.dtype == np.float16: | |
| self.t[k] = torch.from_numpy(a).to(device) # f16 stays f16 (cast per batch) | |
| elif a.dtype == np.bool_: | |
| self.t[k] = torch.from_numpy(a).to(device) | |
| else: | |
| self.t[k] = torch.from_numpy(np.ascontiguousarray(a)).to(device) | |
| if bank is not None: | |
| for k in ("tok", "pooled256"): | |
| if k in bank: | |
| self.t["bank_" + k] = torch.from_numpy(bank[k]).to(device) | |
| self.t["bank_t"] = torch.from_numpy(bank["t"].astype(np.float32)).to(device) | |
| def has(self, k): | |
| return k in self.t | |
| def bytes(self): | |
| return int(sum(v.numel() * v.element_size() for v in self.t.values())) | |
| def _prev_valid(v0, v): | |
| """[B] valid at anchor, [B,T] valid per step -> validity of z_prev per step [B,T].""" | |
| return torch.cat([v0[:, None], v[:, :-1]], 1) | |
| # ----------------------------------------------------------------------------- V | |
| def prep_V(store, idx, st, cfg, need_targets=True): | |
| d = store.device | |
| X = store.t["X"][idx].float() # [B,40,5] raw | |
| mode = store.t["mode"][idx].long() | |
| xmu, xsd = torch.as_tensor(st["xmu"], device=d), torch.as_tensor(st["xsd"], device=d) | |
| if cfg.get("can_hist", "4s") == "8s": | |
| X8 = torch.cat([store.t["Xpre"][idx].float(), X], 1)[:, 1::2] # 80 @10 Hz -> 40 @5 Hz, ends at t | |
| dt = -(39 - torch.arange(40, device=d).float()) * 0.2 | |
| Xin = torch.clamp((X8 - xmu) / xsd, -X_CLIP, X_CLIP) | |
| else: | |
| dt = -(39 - torch.arange(40, device=d).float()) * 0.1 | |
| Xin = torch.clamp((X - xmu) / xsd, -X_CLIP, X_CLIP) | |
| b = {"X": torch.cat([Xin, dt[None, :, None].expand(len(idx), -1, -1)], -1), "mode": mode} | |
| tg = None | |
| if need_targets: | |
| Y = store.t["Y"][idx].float() | |
| M = store.t["M"][idx] | |
| ymu, ysd = torch.as_tensor(st["ymu"], device=d), torch.as_tensor(st["ysd"], device=d) | |
| Ystd = torch.nan_to_num((Y[..., [0, 3, 4]] - ymu) / ysd) | |
| gas, brake = (Y[..., 1] > 0.5).float(), (Y[..., 2] > 0.5).float() | |
| phase = store.t["phase"][idx].float() | |
| resp = store.t["Resp"][idx].float() | |
| gas0, brake0 = (X[:, -1, 1] > 0.5).float(), (X[:, -1, 2] > 0.5).float() | |
| phase0 = store.t["phase0"][idx].float() | |
| r0v = store.t["Resp0valid"][idx] | |
| resp0 = torch.where(r0v, store.t["Resp0"][idx].float(), torch.full_like(gas0, float(st["resp0_base"]))) | |
| zV = torch.stack([gas, brake, phase, resp], -1) | |
| z0 = torch.stack([gas0, brake0, phase0, resp0], -1) | |
| zV_prev = torch.cat([z0[:, None], zV[:, :-1]], 1) | |
| rv = store.t["RespValid"][idx] | |
| respm = rv & M & _prev_valid(r0v, rv) | |
| zVm = torch.stack([M, M, M, respm], -1) | |
| tg = {"Ystd": Ystd, "M": M, "zV": zV, "zV_prev": zV_prev, "zVm": zVm, "z0": z0} | |
| return b, tg | |
| # ----------------------------------------------------------------------------- U | |
| def std_kh(Kh, st, d, clip=K_CLIP_LEGACY): | |
| kmu, ksd = torch.as_tensor(st["kmu"], device=d), torch.as_tensor(st["ksd"], device=d) | |
| valid = torch.isfinite(Kh[..., IDX_MIN_DIST]) & torch.isfinite(Kh[..., IDX_HEAD_CX]) | |
| Z = torch.nan_to_num(torch.clamp((Kh - kmu) / ksd, -clip, clip)) | |
| return Z, valid | |
| def k_clip(cfg): | |
| """the kinematics clamp of a run: cfg["kclip"] when recorded (new runs: K_CLIP = 8), else the legacy 5 of every older run.""" | |
| return float(cfg.get("kclip", K_CLIP_LEGACY)) | |
| def prep_U(store, idx, st, cfg, need_targets=True): | |
| d = store.device | |
| clip = k_clip(cfg) | |
| Kh = store.t["Kh"][idx].float() # [B,40,11] NaN = invalid ([B,40,14] for kin v2p: + 3 keypoint proxies, 2026-09-18) | |
| X = store.t["X"][idx].float() | |
| xmu, xsd = torch.as_tensor(st["xmu"], device=d), torch.as_tensor(st["xsd"], device=d) | |
| Khs, kvalid = std_kh(Kh, st, d, clip) | |
| Xs = torch.clamp((X - xmu) / xsd, -X_CLIP, X_CLIP) | |
| if cfg.get("recipe", {}).get("u_nocan"): | |
| # CAN ablation (2026-09-14, recipe `u_nocan`, plan Part A §A.5 arm (a); decisions.md 2026-09-15T01:48:39Z): the 5 standardised CAN columns are EXACTLY zero on | |
| # every history step; the valid bit and Δt are kept and Kx stays [B,40,18], so the GRU (and its paired seed init) is the parent's. Default off -> the old path bit-for-bit. | |
| Xs = torch.zeros_like(Xs) | |
| dt = (-(39 - torch.arange(40, device=d).float()) * 0.1)[None, :, None].expand(len(idx), -1, -1) | |
| Kx = torch.cat([Khs, kvalid[..., None].float(), Xs, dt], -1) # [B,40,18] ([B,40,21] for kin v2p: n_kin 14 -> DriverBranch(n_kin=14)) | |
| if cfg.get("recipe", {}).get("u_face") and store.has("Fh"): | |
| # face-landmark inputs (2026-09-12, recipe `u_face`): F standardised channels (train fmu / fsd, same clamp, NaN -> 0 = mean) + the per-step valid bit, | |
| # appended AFTER the 18 baseline channels (the input LN keeps covering the first 12 kinematics channels only) -> [B,40,18+F+1] | |
| fmu, fsd = torch.as_tensor(st["fmu"], device=d), torch.as_tensor(st["fsd"], device=d) | |
| Fh = store.t["Fh"][idx].float() | |
| fvalid = store.t["Fhvalid"][idx] & torch.isfinite(Fh[..., 1]) # lm_yaw_dev finite = the landmark geometry of the step is defined | |
| Fs = torch.nan_to_num(torch.clamp((Fh - fmu) / fsd, -clip, clip)) * fvalid[..., None].float() | |
| Kx = torch.cat([Kx, Fs, fvalid[..., None].float()], -1) | |
| b = {"Kx": Kx, "mode": store.t["mode"][idx].long()} | |
| if store.has("kpf"): | |
| # LANE F1 (2026-09-17): the detached keypoint-forecaster channel [B,10] (results/runs/KPF_*/kpf_{regime}_{split}.npz feat) for models/kpf_inject.KpfInject | |
| b["kpf"] = store.t["kpf"][idx].float() | |
| cab = cfg.get("cabin", "none") | |
| if cab != "none": | |
| b["cab"] = store.t[cab][idx].float() | |
| b["cab_ok"] = store.t[cab + "_ok"][idx] | |
| tg = None | |
| if need_targets: | |
| M = store.t["M"][idx] | |
| kmu, ksd = torch.as_tensor(st["kmu"], device=d)[:DK], torch.as_tensor(st["ksd"], device=d)[:DK] # the 11 K TARGET columns (kin v2p stats carry 14 entries: 3 input-only proxies) | |
| K = store.t["K"][idx].float() | |
| Km = store.t["Kvalid"][idx][:, :, None] & torch.isfinite(K) & M[:, :, None] | |
| Kstd = torch.nan_to_num(torch.clamp((K - kmu) / ksd, -clip, clip)) * Km | |
| K0std = Khs[:, -1, :DK] * kvalid[:, -1:].float() # K_0 = last history step (0 = mean when invalid); the 11 K TARGET columns only (v2p carries 3 extra input columns) | |
| S = store.t["S"][idx].float() # [B,25,2] NaN censored | |
| S0 = store.t["S0"][idx].float() | |
| zU = torch.nan_to_num(S) | |
| zU_prev = torch.cat([torch.nan_to_num(S0)[:, None], zU[:, :-1]], 1) | |
| Sprev = torch.cat([S0[:, None], S[:, :-1]], 1) | |
| EM = store.t["EM"][idx] | |
| zUm = torch.stack([EM[:, :, 0], EM[:, :, 2]], -1) & M[:, :, None] & torch.isfinite(S) & torch.isfinite(Sprev) | |
| tg = {"Kstd": Kstd, "Km": Km, "K0std": K0std, "zU": zU, "zU_prev": zU_prev, "zUm": zUm, "z0": torch.nan_to_num(S0), | |
| "z0valid": torch.isfinite(S0)} | |
| if cfg.get("recipe", {}).get("u_gaze_chain") and store.has("Sg"): | |
| # gaze_off chain targets (2026-09-12): the kin convention for a third driver state — z_prev = anchor state then z[:-1]; mask = onset mask (both | |
| # states finite) AND the s12 M AND finite z / z_prev (train/batch conventions of zUm) | |
| Sg = store.t["Sg"][idx].float() # [B,25] NaN censored | |
| Sg0 = store.t["Sg0"][idx].float() | |
| zG = torch.nan_to_num(Sg) | |
| zG_prev = torch.cat([torch.nan_to_num(Sg0)[:, None], zG[:, :-1]], 1) | |
| Sgprev = torch.cat([Sg0[:, None], Sg[:, :-1]], 1) | |
| EMg = store.t["EMg"][idx] | |
| tg.update(zG=zG, zG_prev=zG_prev, zGm=EMg[:, :, 0] & M & torch.isfinite(Sg) & torch.isfinite(Sgprev), zG0=torch.nan_to_num(Sg0), zG0valid=torch.isfinite(Sg0)) | |
| return b, tg | |
| # ----------------------------------------------------------------------------- E | |
| def prep_E(store, idx, st, cfg, need_targets=True): | |
| d = store.device | |
| n_slots = int(cfg.get("n_slots", 16)) | |
| ti = store.t["tok_idx"][idx][:, -n_slots:].long() # [B,L] | |
| ok = ti >= 0 | |
| safe = torch.where(ok, ti, torch.zeros_like(ti)) | |
| tokens = cfg.get("tokens", "region") | |
| if tokens == "region": | |
| tok = store.t["bank_tok"][safe].float() # [B,L,4,64] | |
| elif tokens == "spat256": | |
| tok = store.t["bank_tok"][safe].float().reshape(len(idx), n_slots, 1, 256) # [B,L,1,256]: the 4 region PCA-64 tokens FLATTENED (spatial order kept) | |
| elif tokens == "none": | |
| # 2026-09-16 (WP4 F5b sensor-only control, E0 `pooled_P_canonly` = `tokens: none` + `side: can256`): NO vision tokens — every slot is marked missing | |
| # (tok_ok False -> the model sees only its learned `missing` embedding + slot / dt embeddings; the bank is never touched), so the E trunk output is | |
| # input-independent and ALL road information enters through the side vector. Reached only with `tokens: none`; every other value follows the old path bit-for-bit. | |
| tok = torch.zeros(len(idx), n_slots, 1, 256, device=d) | |
| ok = torch.zeros_like(ok) | |
| else: | |
| tok = store.t["bank_pooled256"][safe].float()[:, :, None, :] # [B,L,1,256] | |
| tok = tok * ok[:, :, None, None].float() | |
| t_anchor = store.t["t"][idx].float() | |
| nominal = -(n_slots - 1 - torch.arange(n_slots, device=d).float()) * 0.5 | |
| dt = torch.where(ok, store.t["bank_t"][safe] - t_anchor[:, None], nominal[None, :].expand(len(idx), -1)) | |
| side = cfg.get("side", "vf256") # per-anchor side vector: vf256 [256] (default) | regionpca256 [512] | |
| b = {"tok": tok, "tok_ok": ok, "tok_dt": dt, "vf": store.t[side][idx].float(), "vf_ok": store.t[side + "_ok"][idx], | |
| "mode": store.t["mode"][idx].long()} | |
| tg = None | |
| if need_targets: | |
| M = store.t["M"][idx] | |
| D, Dv = store.t["D"][idx].float(), store.t["Dvalid"][idx] | |
| D0, D0v = store.t["D0"][idx].float(), store.t["D0valid"][idx] | |
| d0b = torch.as_tensor(st["d0_base"], device=d) | |
| z0 = torch.where(D0v, D0, d0b[None, :].expand_as(D0)) | |
| zD_prev = torch.cat([z0[:, None], D[:, :-1]], 1) | |
| zDm = Dv & M[:, :, None] & torch.cat([D0v[:, None], Dv[:, :-1]], 1) | |
| tg = {"zD": D, "zD_prev": zD_prev, "zDm": zDm, "F": store.t["F"][idx].float(), "Fok": store.t["Fok"][idx], "z0": z0, "z0valid": D0v} | |
| if store.has("P") and "p0_base" in st: # road-pressure chains (E0 `*_P`): the joint trainer's prep_aux convention | |
| Pz, Pv = store.t["P"][idx].float(), store.t["Pvalid"][idx] | |
| P0v = store.t["P0valid"][idx] | |
| p0b = torch.as_tensor(st["p0_base"], device=d) | |
| zP0 = torch.where(P0v, store.t["P0"][idx].float(), p0b[None, :].expand_as(Pv[:, 0])) | |
| tg.update(zP=Pz, zP_prev=torch.cat([zP0[:, None], Pz[:, :-1]], 1), zPm=Pv & M[:, :, None] & torch.cat([P0v[:, None], Pv[:, :-1]], 1), | |
| zP0=zP0, zP0valid=P0v) | |
| return b, tg | |
| def loss_U(out, tg, lam): | |
| """heads.driver_loss (+ lambda G x the gaze_off chain NLL when the branch emits "G" and the batch carries the gaze targets — opt-in `u_gaze_chain`, | |
| 2026-09-12; its part chain_gaze_off enters the selection objective like the hands_on / head_turned chains). Old recipes: exactly driver_loss. | |
| "HG" (recipe `u_can_gate`, 2026-09-14, plan Part A §A.5 arm (b)): heads.driver_loss_gated — the same chain BCE on the gated hazards λ' = clamp(λ·2g).""" | |
| if "HG" in out: | |
| # CAN release gate: identical arithmetic to driver_loss and bitwise the same loss while a = b = 0 (2g == 1); the K term is unchanged | |
| total, parts = hd.driver_loss_gated(out["U"], tg, out["HG"], lam_K=lam.get("K", 0.5)) | |
| else: | |
| total, parts = hd.driver_loss(out["U"], tg, lam_K=lam.get("K", 0.5)) | |
| if "G" in out and tg is not None and "zG" in tg: | |
| lg, pg = hd.gaze_loss(out["G"], tg) | |
| parts.update(pg) | |
| total = total + float(lam.get("G", 1.0)) * lg | |
| return total, parts | |
| PREP = {"V": prep_V, "U": prep_U, "E": prep_E} | |
| LOSS = {"V": lambda out, tg, lam: hd.vehicle_loss(out["V"], tg, lam_hz=lam.get("hz", 0.5)), | |
| "U": loss_U, | |
| "E": lambda out, tg, lam: hd.env_loss(out["E"], out["F"], tg, lam_F=lam.get("F", 0.1), P=out.get("P"), lam_P=lam.get("P", 1.0))} | |
| def train_rows(raw, branch, cfg): | |
| """rows eligible for training / val objective: V can8s needs a full 8 s history (x8_ok).""" | |
| N = len(raw["t"]) | |
| if branch == "V" and cfg.get("can_hist", "4s") == "8s": | |
| return np.flatnonzero(raw["x8_ok"]) | |
| return np.arange(N) | |
Xet Storage Details
- Size:
- 21.5 kB
- Xet hash:
- c0513a2c41584ec9e8a2dd24001f2ef417884315dea247329ca9233b34aa107f
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.