VT-WM baseline — vtm-cosmos aligned data

Generated 2026-09-23 16:39 · training in progress (10 epochs/task)

What this is. A faithful VT-WM predictor (Visuo-Tactile World Models, arXiv:2602.06001) trained on exactly the same bytes as the Cosmos3 visuo-tactile model: Wan2.2 C=48 latents, 15 fps extraction, 1 exocentric + 2 wrist + 2 tactile streams, force-augmented sensor-frame actions, and the authors' own holdout split. Everything that is not the method is held fixed, so a difference between the two is a difference of method.

Read action_gain, not val_loss

action_gain = rollout L1 under a zero-motion control action ÷ rollout L1 under the true action. >1 means the model actually uses its action conditioning. ≈1 means the action pathway is dead — a failure latent val_loss cannot detect, and one we have previously observed on this project (zero-action rollouts beating true-action rollouts).

The control is identity_action (dp=0, Rot6D(I)), not a zero vector: zeros encode a degenerate rotation that never occurs in training. gain (tactile) is the column that matters — the global mean is dominated by the three view streams.

Current results

tasktrain/val epstrain windowsval_losslast eval ep rollout L1gaingain (visual)gain (tactile)
pushT56/455,3420.388190.20261.8121.8191.784
motherboard21/271,4850.465590.21031.8721.9201.591
rope53/877,8470.418090.25121.4651.5501.206
toy24/352,9000.400530.19631.2691.3131.058

Trend per epoch

Prediction accuracy plateaus while action_gain keeps climbing — later training buys controllability rather than raw accuracy, and the two metrics are measurably decoupled.

Trends

Rollouts

Decoded with the stock diffusers Wan2.2 VAE for viewing only. Scoring uses the Cosmos3 decoder instead — see the pixel-metric note below.

pushT · sample_000 · view_middle
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
pushT · sample_000 · wrist_left
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
pushT · sample_000 · tl
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
pushT · sample_001 · view_middle
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
pushT · sample_001 · wrist_left
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
pushT · sample_001 · tl
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
motherboard · sample_000 · view_middle
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
motherboard · sample_000 · wrist_left
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
motherboard · sample_000 · tl
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
motherboard · sample_001 · view_middle
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
motherboard · sample_001 · wrist_left
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
motherboard · sample_001 · tl
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
rope · sample_000 · view_middle
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
rope · sample_000 · wrist_left
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
rope · sample_000 · tl
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
rope · sample_001 · view_middle
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
rope · sample_001 · wrist_left
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)
rope · sample_001 · tl
left: ground truth  |  right: rollout (first 2 latent frames are teacher-forced context)

Data provenance

Episodes are filtered to those with visual + wrist + tactile latents AND actions — the wrist era. The published mixed_mb_latents tree also carries 0510/0511/0519 episodes with no wrist and no actions (the retired era), which are excluded.

taskour train windowsauthors' countdifference
pushT55,19955,3420.26 %
motherboard71,38671,4850.14 %
rope77,18877,8470.85 %

Three independent window counts reproduce the authors' to under 1 % (the gap is windows we drop for invalid action rows). This is the evidence that both models read the same data the same way.

toy is not on the same footing as the other three, in two specific ways.

1. Its latents were encoded here. vtm-cosmos-data ships mb, pushT and rope only, so the mixed_toy_* tree was built for this run: 96 latent files (34 GB) and 96 action files from the raw React recordings. Three gates were passed first, each by reproducing something already published rather than by looking reasonable — RGB encoding lands at 0.044 of the published mb cache's std (normalising or cropping differently gives 0.15 and 3.03), the geometry conversions pass the repo's own test_robot_candidates 6/6, and the resulting action statistics match the other tasks to three digits (view |mean| 270.8 against 264.9–273.3, sensor 0.2222 against 0.2222–0.2223).

2. Its tactile latents sit in a slightly different distribution. The offline pipeline that built the published tactile cache is not in the repo, so toy's tactile was encoded with the online refflow-LoRA encoder, which reproduces that cache to 0.380 of its std — the range the repo's own notes record for it (0.31–0.43; the base VAE is 0.86), not a mistake, but not identical either. action_gain is a ratio inside one representation and is unaffected; absolute tactile numbers for toy should not be put beside the other three.

toy's split is also ours — the authors never ran this task — so its val_loss is comparable to our other three runs and to nothing they published.

Fidelity decisions

settingvaluewhy
tactile_delta_to_reffalsedelta-to-ref is our contribution, not VT-WM's; predicting the raw latent is also what Cosmos3 flow-matches, so both are scored on the same target
action_lookahead1an AR predictor supervises out[t] against x[t+1], so it must see the action that generates x[t+1]; lookahead 0 is a bug, not a hyperparameter
action_compresschunk_concatVT-WM provides the full chunk of delta-states, not its mean (15 raw → 3 latent transitions × chunk 5)
backbonevtwm cross-attnper-block cross-attention to action tokens, paper Sec. 3.2 (+15.6 M params over the adaLN path)

Action conditioning — full val split

Windows drawn uniformly at random across every episode in the split (fixed seed). The control action is identity_action (dp=0, Rot6D of the identity), not a zero vector. CI is a paired bootstrap: both rollouts are evaluated on the same windows.

taskwindowsrollout L1L1 (control) action_gain95 % CIwindows with gain > 1
pushT5120.19970.35601.782[1.765, 1.800]100.0%
motherboard5120.24370.38671.587[1.567, 1.609]100.0%
rope5120.21240.36171.703[1.678, 1.728]100.0%
toypendingpendingpendingpendingpendingpending
This table supersedes the per-epoch numbers above. Those come from the in-training callback, which iterates val_dataloader() built with shuffle=False and stops after 4 batches of 8 — so they are measured on the first 32 windows of the split, all from one episode and overlapping each other. Good enough to watch a run converge, not an estimate of the split. The gap is not small, not constant, and not even signed the same way: pushT 1.812 → 1.782 (−2 %), mb 1.872 → 1.587 (−15 %), rope 1.465 → 1.703 (+16 %). The callback ranks mb > pushT > rope; the full split ranks pushT > rope > mb. Every task changes position. A comparison table built from the callback numbers would have been wrong about which task this baseline handles best, in a way no amount of extra training would fix.

The column that carries the most weight is the last one: the ratio of means can be moved by a few extreme windows, "every window degrades under the control action" cannot.

Pixel metrics — our decoder (within-baseline only)

LPIPS ↓ / SSIM ↑ / PSNR ↑, on predicted frames only (8 history frames skipped), 16 rollout samples per task, decoded with the stock diffusers AutoencoderKLWan.

taskview_middlewrist_leftwrist_righttactile Ltactile R
pushT0.111 / 0.864 / 27.20.203 / 0.773 / 20.70.169 / 0.839 / 23.50.051 / 0.966 / 39.70.055 / 0.971 / 40.3
motherboard0.173 / 0.800 / 25.50.402 / 0.515 / 17.90.399 / 0.554 / 18.90.031 / 0.975 / 42.50.026 / 0.979 / 44.1
rope0.116 / 0.868 / 27.80.233 / 0.735 / 21.90.274 / 0.795 / 23.60.029 / 0.975 / 42.30.042 / 0.975 / 42.3
toynot evaluated yetnot evaluated yetnot evaluated yetnot evaluated yetnot evaluated yet
Do not put these next to the Cosmos3 numbers. That side decodes with a custom wan22_vae.Wan2_2_VAE plus a tactile LoRA and an anchor-restoration step (vtm_cosmos/infer/decode.py); this repo has only the stock diffusers decoder. PSNR/LPIPS across two decoders look comparable and are not — worst of all on the tactile streams, which is exactly where their LoRA acts. The numbers above are valid for comparing streams and tasks within this baseline, nothing more.

The comparable route is live: scripts/mot_predict_windows.py exports predicted latents in the format vtm_cosmos/scripts/score_external_predictions.py reads, so that scorer decodes both sides with the same decoder and asserts the GT latents match. pushT is exported (4,439 val windows); mb and rope follow when they finish.

World consistency

AllTracker keypoints, discrete Fréchet, stream view_middle. Object permanence ↓: how far the predicted track of a moving keypoint strays from GT, normalized by GT path length. Causal compliance ↓: motion invented on static keypoints. persist = freeze the last context frame; gtcopy = the GT itself.

taskrefOP Fréchet (norm)GT path pxpred path px CC Fréchet (norm)hallucinated px
pushTmodel0.441114.1010.170.07351.24
pushTpersist0.852414.100.100.07850.16
pushTgtcopy0.000014.1014.100.00001.15
motherboardmodel0.459415.3010.730.11721.24
motherboardpersist0.834415.300.090.09700.09
motherboardgtcopy0.000015.3015.300.00001.37
ropemodel0.399318.1213.140.06531.35
ropepersist0.749718.120.090.06580.14
ropegtcopy0.000018.1218.120.00001.33
toy——————
Read causal compliance with care. The gtcopy row is the GT compared against itself, so its Fréchet is 0 by construction — but its "hallucinated path" is not 0. That residual is the tracker's own jitter on static keypoints, and it is the floor this metric can resolve. Where the model and persist land close to that floor, CC is not separating them.
Both sides train from scratch. That is faithful to VT-WM, whose predictor is also trained from scratch — but Cosmos3 starts from a pretrained Cosmos3-Nano with LoRA. Any win therefore has to be defended against "you simply have a pretrained video prior", which needs an ablation on the Cosmos3 side, not a change to this baseline.