Buckets:
Driver-WM-kp — port of the Driver-WM reproduction to keypoint part tokens (arch attribution experiment (2))
Owner-approved rule (docs/decisions.md 2026-09-14, "AIDE 架构归因公平实验" (2)): take the Driver-WM reproduction architecture (dual stream + gated causal injection + ST-GCN decoder), replace the INTERNAL-stream input (frozen Qwen3-VL in-cabin features) by the keypoint part tokens of K (cwm3d_kp, same tokenizer), keep the external-context stream, and train it with the shared keypoint recipe (residual over the last observed pose, zero-initialised output = zero-velocity at init, identical augmentation / selection), 3 seeds. Pre-specified reading: per-seed paired Δ(Driver-WM-kp − K) on All-MPJPE with CI containing 0 and |Δ| < 1 px on every seed → the lead belongs to the keypoint formulation; K significantly better (CI excl. 0 on ≥ 2/3 seeds) → supports an architecture advantage under the keypoint formulation; Driver-WM-kp better → reported as is.
Code: aide/models/driverwm_kp.py (model), aide/train/train_kp.py (--model driverwm_kp: additive dispatch branch; the exterior features are loaded and passed exactly through the existing --visual path), tests aide/tests/test_arch_attribution.py. Runs aide/results/runs/driverwm_kp_s{42,123,456}_v2; table aide/results/tables/arch_attribution.md §2 (aide/eval/report_arch_attribution.py).
1. What was kept from the reproduction (aide/models/driverwm_repro.py, protocol.md C10–C16)
| component | reproduction | Driver-WM-kp |
|---|---|---|
| external-context stream | mean over the 3 exterior views (front / left / right) of the pooled Qwen3-VL-2B features (2048-d, frozen, results/feats/feats_qwen.npz[pooled]) + learned view embedding + step embedding → one causal pre-norm TransformerEncoderLayer(2048, 4 heads, d_ff 4096) (pre_ext) |
identical (same classes / shapes; view_emb, pos_ext, pre_ext) |
| external transition | residual MLP f_ext: 2048 → 4096 → 2048, e_{t+1} = e_t + f_ext(e_t) |
identical |
| context module | Perceiver-style: mean-pooled internal history → LN + Linear → 4 latent queries in a 256-d bottleneck; keys / values = LN + Linear(256) of the external prefix ≤ t; MultiheadAttention(256, 4 heads); output Linear → added to the last internal state (m_t) | identical structure; the query / output projections are D_int-wide (1024) instead of 2048 because the internal latent is 1024-d (PerceiverCtxKP) |
| gated causal injection | g_t = sigmoid(MLP(e_t)) (LN → Linear(2048, 2048) → GELU → Linear(2048, D)), z_{t+1} = (1 − g_t) ⊙ (z_t + f_int(z_t)) + g_t ⊙ m_t; override clamps g (Table-3 interventions) |
identical (last Linear outputs D_int = 1024) |
| rollout | 5 closed-loop steps, both histories grow by the predicted latent, prefix-only context (causal) | identical |
| skeleton decoder | Linear(D → 136 × 32) → 2 × STGCNBlock (spatial graph conv over the normalised HALPE-136 adjacency protocol.EDGES_ALL + self loops, temporal conv k = 3 over the 5 rolled-out steps, residual, LayerNorm) → Linear(32 → 2) |
identical blocks (STGCNBlock, normalized_adjacency imported unchanged); read-out changed, see §2 |
| leak guard | forward accepts exactly T_OBS frames | identical (obs and vis both asserted to T_OBS frames; test_arch_attribution.py checks that the future keypoints / features cannot reach the output) |
2. What was replaced
| component | reproduction | Driver-WM-kp | why |
|---|---|---|---|
| internal-stream input | frozen 2048-d Qwen3-VL feature of the in-cabin view per observed frame (+ view / step embedding) → causal TransformerEncoderLayer(2048, 4, 4096) |
heads.PartTokenizer(d = 256) — K's tokenizer, unchanged: 4 part tokens per observed frame (body 26 joints, face 68, left hand 21, right hand 21; per joint (Δx, Δy)/200 to the LAST observed pose, x/W, y/H, confidence; shared hand Linear + side embedding; part + step embeddings), concatenated to ONE 1024-d latent per frame → causal pre-norm TransformerEncoderLayer(1024, 4 heads, d_ff 4096) (pre_int) |
the rule: same tokenizer as K. Driver-WM's latent world model is "one latent per step" (z_t), so the 4 part tokens are concatenated (D_int = 4 × 256) rather than kept as separate tokens; the causal pre-encoder, f_int, ctx and gate then run in 1024-d. The in-cabin Qwen feature is not read at all (test: perturbing it leaves the output bit-identical). |
| internal transition | f_int: 2048 → 4096 → 2048 |
f_int: 1024 → 4096 → 1024 (same hidden width) |
follows D_int |
| decoder read-out | sigmoid → absolute normalised coordinates, pixels = (x·W, y·H) (Eq. 8; no residual anchor, C10) | pred = last observed xy + 200 · Linear(32 → 2)(·), Linear ZERO-initialised → the untrained model is exactly zero-velocity |
the shared recipe (residual over the last observed pose, zero-init output) — the same parametrisation as cwm3d_kp / SiMLPe-kp / MotionBERT-kp / ST-GCN-kp |
| internal positional / view embedding | learned view embedding + step embedding added to the 2048-d feature | none on top of the tokenizer: the PartTokenizer already carries part + step embeddings; a view embedding is meaningless for a single-view keypoint stream | avoid a duplicated step embedding |
3. What was dropped (loss and heads)
| item | reproduction (C13) | Driver-WM-kp | why |
|---|---|---|---|
| L_lat^vel (Eq. 14) | latent-velocity regression; target = the same in-cabin pre-encoder run causally over the 10-frame in-cabin FEATURE sequence [observed; GT future], detached | dropped | its target requires the frozen in-cabin features of the future frames, which the keypoint internal stream does not have; re-running the keypoint tokenizer over the 10-frame block would change the token semantics ((Δx, Δy) relative to the last frame of the sequence) and is not part of the shared recipe |
| L_aux (4 CE heads DBR / DER / TCR / VCR) | on the last rolled-out internal / external states | dropped (no semantic heads) | the comparison target K = cwm3d_kp is geometry-only; the shared keypoint recipe has no semantic loss |
| L_skel (Eq. 23, conf-masked squared error in normalised coordinates) + L_phys = L_bone + L_smooth (Eq. 25–26), λ_lat = λ_phys = 0.1 | replaced by the shared recipe's loss: the model returns a plain tensor and train_kp --loss euc applies the mean Euclidean pixel error (== the metric), exactly as for MotionBERT-kp / ST-GCN-kp (hardening D; cwm3d_kp additionally has its Laplace-NLL / bone / jerk terms, which need its dict output) |
same loss as the other shared-recipe baselines | |
| training (C14) | AdamW 5e-5, wd 1e-5, cosine without warm-up, bs 32, 100 epochs, no early stop, no augmentation, no EMA | shared recipe verbatim (run_stage_bc.RECIPE): AdamW 3e-4, wd 0.05, 10 warm-up epochs + cosine, bs 32, ≤ 200 epochs, EMA 0.999, early stop patience 40 on val All-MPJPE (EMA weights), sub-window augmentation (--aug-window 1), keypoint noise 2(1 − conf) px, joint dropout 0.1, translate ± 20 px, scale ± 5 %, no flip; exterior features of a training sub-window frame = feature of the nearest protocol frame (as for cwm3d --visual, S2); no modality dropout (the reproduction has none) |
the rule: identical augmentation / selection |
Selection is val-only; the official test split is evaluated once per run by the existing harness (paired Δ vs zero-velocity / ZV-shrink / seed-matched SiMLPe-kp, cwm3d_kp and MotionBERT-kp as refs; the table's paired bootstraps are recomputed from the pred_test.npz files by the report script).
4. Parameter count (measured, python aide/models/driverwm_kp.py)
| module | Driver-WM reproduction (123,655,254) | Driver-WM-kp (85,721,410) |
|---|---|---|
tokenizer (tok, = cwm3d_kp's PartTokenizer) |
— | 150,784 |
pre_int (internal causal pre-encoder) |
33,576,960 (2048-d) | 12,596,224 (1024-d) |
pre_ext (external causal pre-encoder) |
33,576,960 | 33,576,960 |
f_int |
16,787,456 | 8,395,776 |
f_ext |
16,787,456 | 16,787,456 |
ctx (Perceiver context) |
5,517,824 | 3,417,600 |
gate |
8,396,800 | 6,298,624 |
dec (ST-GCN skeleton decoder) |
8,925,762 | 4,469,314 |
| semantic heads | 57,364 | — |
view_emb / pos |
8,192 / 20,480 | 8,192 / 20,480 |
For reference K = cwm3d_kp has 5,771,449 parameters (same 150,784-parameter tokenizer), MotionBERT-kp 813,088, ST-GCN-kp 530,080. Driver-WM-kp is ≈ 15 × K; the comparison is deliberately not parameter-matched (the rule fixes the architecture, not the size).
5. Guards (aide/tests/test_arch_attribution.py, all passing)
- exact zero-velocity at initialisation; 10-frame inputs rejected;
- the output is bit-identical when the FUTURE keypoints / features are changed (only the observed block is handed in), changes when the observed exterior features change (the external stream is live), and is bit-identical when the in-cabin Qwen feature changes (it is not read);
- the internal stream uses
heads.PartTokenizer(K's tokenizer) with D_int = 4 × 256.
6. Deviations from the pre-specified rule
None in the architecture or recipe. The two judgement calls that the rule left open are (i) concatenating the 4 part tokens into one per-step latent (Driver-WM's single-latent transition) and (ii) dropping L_lat / L_aux / L_skel-L_phys in favour of the shared recipe's plain Euclidean loss (the rule says "shared recipe"); both are recorded above and in the model docstring.
7. Result (from aide/results/tables/arch_attribution.md §2, generated 20260914T182913Z)
Test All-MPJPE 50.40 / 50.57 / 50.97 (seeds 42 / 123 / 456; best epoch 61 / 62 / 74, early stop patience 40; 6.1 / 5.9 / 5.2 GPU-min). Paired Δ (Driver-WM-kp − K) All = +1.63 [+0.99, +2.31] / +1.91 [+1.43, +2.40] / +2.51 [+1.89, +3.14] (K better on 3/3 seeds, CI excl. 0); PCK@0.05 −0.79 ± 0.20 (WORSE); HM / HM h=5 INCONCLUSIVE. Pre-specified reading obtained: (b) "支持架构在关键点表述下的优势". Against MotionBERT-kp: Δ All +2.27 ± 0.20 (WORSE).
Xet Storage Details
- Size:
- 10.1 kB
- Xet hash:
- 9cf120ab4046cfc20a1fe5e2022fffc58907cd252762f1c636cd9d2e2d8b28a4
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.