Video-Only Training

OpenWAM trains causal video prediction through the same public composition boundary as its video/action policies:

ExperimentConfig -> VariantPipeline -> VisualTower -> PolicyVariant -> ActionDecoder

causal_video_prediction owns an explicit video sequence program. The shared VisualTower executes the video transformer, and video_only_decoder computes masked latent-flow supervision. No training or evaluation path calls a concrete backbone directly.

This guide covers both the installed package and optional source-checkout utilities. Training uses the package-owned CLI. The Wan conversion commands and decoded side-by-side rollout command are source-checkout integrations under scripts/; they are not included in the wheel or source distribution.

Prepare An Initialization (Source Checkout)

Wan2.2 is published in two transformer layouts:

Source Converter
Raw upstream keys such as blocks.*.self_attn.* scripts/convert_wan22_to_lingbot_init.py
Hugging Face Diffusers keys already aligned with LingBot scripts/convert_wan22_diffusers_to_lingbot_init.py

For Wan-AI/Wan2.2-TI2V-5B-Diffusers, pin a source revision and use a LingBot-format transformer as the target schema:

uv run python scripts/convert_wan22_diffusers_to_lingbot_init.py \
  --wan-diffusers-root /models/Wan2.2-TI2V-5B-Diffusers/transformer \
  --lingbot-template-root /models/lingbot-va-base/transformer \
  --output-root /models/wan22-diffusers-lingbot-init

Both converters publish into a new output directory only after every file has been written successfully. They reject existing destinations, so stale shards cannot survive a conversion. The Diffusers converter also validates the template against its config.json model schema on the meta device and checks every shard/index entry and tensor shape. Matching video tensors come from Wan; the flattened patch MLP is derived from Wan's Conv3d patch embedding; any remaining supported action tensors come from the structural template. Video training does not optimize or export those action tensors.

Data Contract

The canonical LIBERO preset is configs/experiments/causal_video_prediction_libero_latent_local.yaml. Its adapter discovers one or more local LeRobot repositories below data.local_root, reads pre-encoded per-camera latents, and emits:

  • one canonical latent canvas with validated, non-overlapping view placements;
  • identical frame counts, channels, and dtypes across assembled views;
  • identical, integral, nonempty, nondecreasing frame IDs when present;
  • one exact integral latent stride shared by every view placement;
  • text tensors matching the selected task-prompt or blank-text conditioning mode;
  • an observed prefix followed by a supervised future suffix;
  • explicit latent-frame counts in sample metadata.

The five maintained buckets are 1+3, 2+6, 3+9, 4+12, and 5+15 observed/future latent frames. These are latent counts, not raw RGB frame counts. Evaluation selects through the same adapter, so nonzero sample starts retain the episode's causal VAE history and exact camera assembly. Frame-ID count is intentionally independent of latent count because IDs may describe raw encoder inputs; repeated IDs remain valid for boundary padding. Generic policy fields such as training.chunk_size and training.window_size do not define this objective and are rejected from causal video configs. Serialized view layouts use the strict open_wam.canonical_view_layout.v1 schema; consumers should reject missing or unknown versions instead of guessing camera placement semantics.

Sequence Programs

Video-only configs select one program explicitly; there is no checkpoint- or dataset-based inference:

Program Data and model sequence Supervision
prefix_suffix One noisy video stream from a causal prefix/suffix bucket Future suffix only
chunked_conditioned_video Native [V_noisy, V_condition] sequence using VTA chunk/window attention Every valid target frame after one external condition frame

prefix_suffix is the existing objective and remains selected by causal_video_prediction_libero_latent_local.yaml.

causal_video_prediction_libero_chunked_conditioned.yaml is the video marginal of the maintained VTA recipe. It samples full trajectory segments with replacement, randomizes chunk size from 1 through 4 and attention window from 4 through 64, prepends the separately encoded frame at offset -1, and applies independent frame-wise video noise. The condition copy is augmented with probability 0.5; the external frame stays clean in both copies. Task-text dropout remains independent at probability 0.1. The maintained preset enables per-block activation recomputation, matching the M5 recipe and keeping the full-segment objective viable under single-device execution.

Both chunked causal-video and VTA training consume the dataset-provided latent loss range. Fixed-shape materialization may retain a zero-order-held tail, but frames outside that valid range have zero video loss. The maintained full-trajectory LIBERO recipe stores each shorter trajectory compactly, so this mask hardening does not alter its existing checkpoint objective.

The packed runtime contains only the two video copies. It does not construct a zero-width or placeholder action sequence, invoke the action embedder, or project an action output. Its attention profile applies the same VTA visibility predicate to a native video-only token layout. Video outputs therefore match the VTA video branch when action loss is disabled and history visibility is video_only; gradients from an enabled action objective are outside this marginal by definition.

Text Conditioning

Video-only training exposes one policy-level choice:

policy_variant:
  text_conditioning_mode: task_prompt  # task_prompt | disabled

task_prompt is the canonical default. It requires one non-empty instruction and one finite, nonzero [B, tokens, dim] positive embedding per sample. The maintained LIBERO preset applies blank-text conditioning to 0.1 of training samples and uses video guidance 5.0 at inference, matching the maintained action-enabled LIBERO policy recipe. Classifier-free training replaces the selected positive embeddings with the standard blank-text embedding; its probability must be in [0, 1). Any positive dropout probability requires a blank-text embedding from the dataset adapter, normally configured through data.empty_text_embedding_path; zeros are not a valid substitute.

disabled is unconditional video training. The shared frontend ignores task strings and positive embeddings, then uses the standard blank-text embedding as both text branches in training and inference when one is available. It requires training.text_condition_dropout_prob: 0.0 and inference.guidance_scale: 1.0. Real pretraining should provide the encoded blank prompt through data.empty_text_embedding_path (or the repository-local empty_emb.pt) or a configured frontend text encoder. When neither is available, the shared core uses its fixed zero no-context tensor; this keeps asset-free synthetic smoke configs runnable. Existing latent datasets do not need to be rewritten; their positive text payload is simply ignored in this mode.

Train

Configure paths in configs/local_paths.yaml, then launch the canonical preset:

CUDA_VISIBLE_DEVICES=0,1,2,3 \
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
TOKENIZERS_PARALLELISM=false \
uv run --extra train python -m torch.distributed.run \
  --nproc_per_node=4 \
  --master_port=29614 \
  -m open_wam.cli.train \
  --config-name causal_video_prediction_libero_latent_local \
  --devices 4 \
  --save-root /runs/wan22_video_only \
  --set data.local_root=/datasets/libero_subsets \
  --set data.empty_text_embedding_path=/datasets/empty_emb.pt \
  --set backbone.pretrained_model_name_or_path=/models/wan22-diffusers-lingbot-init

A source checkout also provides scripts/run_causal_video_prediction_posttrain_libero.sh as a convenience launcher for the same package training entrypoint. It is not a separate training implementation or an installed API.

Select the VTA-marginal program through the same launcher, without a second training script:

uv run python scripts/augment_lerobot_latents_with_single_frame_condition.py \
  --data-root /datasets/libero_subsets \
  --reference-assets-root /models/lingbot-va-base \
  --source-frame-offset -1 \
  --sanity-check

The augmentation is idempotent unless --overwrite is supplied. The latent adapter rejects payloads whose recorded source offset does not match the program config.

CONFIG_NAME=causal_video_prediction_libero_chunked_conditioned \
  scripts/run_causal_video_prediction_posttrain_libero.sh \
  --save-root /runs/libero_chunked_conditioned_video

The preset trains only visual_tower.shared_video_backbone and freezes the action runtime and runtime adapters. Full training state remains the default, so optimizer, scheduler, strategy/scaler, and counters support stateful continuation. Process and dataloader RNG streams are not checkpointed, so a restarted run is not bitwise identical.

Each checkpoint also contains a scoped transformer/ export. Its runtime_backbone_manifest.json records the semantic component set and exact tensor inventory. Loading recomputes that inventory from the active visual component topology and rejects tensors outside the declared components. A later policy loader therefore initializes omitted action components from its model defaults without relying on a remembered consumer override. Complete policy exports continue to declare visual_tower.runtime_backbone and retain their existing load behavior.

Evaluate (Source Checkout)

Generate an exact adapter-selected target and open-loop prediction:

uv run python scripts/generate_video_only_rollout.py \
  --config causal_video_prediction_libero_latent_local \
  --checkpoint /runs/wan22_video_only/checkpoints/checkpoint_step_3000 \
  --reference-assets-root /models/lingbot-va-base \
  --data-root /datasets/libero_subsets \
  --empty-text-embedding /datasets/empty_emb.pt \
  --split val \
  --sample-index 0 \
  --seed 1234 \
  --output-dir outputs/video_only_rollout

For prefix_suffix, --num-chunks 1 evaluates the adapter-produced target. Larger values append equal-width open-loop predictions by re-feeding the complete generated latent history; only the first chunk has a dataset target. Multi-view latents are decoded independently and reassembled from the canonical layout contract. The required --checkpoint selects transformer weights even when the source experiment config disabled initialization-time reference loading. --config remains the sole runtime and data contract after explicit CLI overrides. Adjacent checkpoint metadata is not merged implicitly. To reproduce a checkpoint's recorded behavior, pass its resolved_config.yaml explicitly with --config. Detached transformer exports therefore follow the same rule and do not require an adjacent config. When passing a checkpoint step, that step must contain a usable local transformer/ export. A state file by itself is not sufficient for this decoded-rollout utility. backbone.transformer_subdir normally names a component beneath pretrained_model_name_or_path; absolute values remain valid for historical or deliberately authored configs. Prefer runtime_backbone_artifact_path for new detached-artifact configs.

The canonical preset uses guidance 5.0 with task-prompt dropout 0.1. Guidance above 1.0 is rejected unless the resolved training config records dropout strictly between 0 and 1 and runtime positive and negative text embeddings are finite and shape-matched. Positive text dropout requires trainer.batch_adapter=latents, where dropout operates on an explicit encoded text context. The executor retains the positive source tensor for validation while supplying the selected positive or blank context through the same visual-stack input used by every policy. View batches are rejected instead of silently training without the requested unconditional branch. Guidance is intentionally unavailable in disabled mode because its two text branches are identical.

For chunked_conditioned_video, the same command reads the external condition latent from the uniform-segment adapter and evaluates up to num_chunks * inference.frame_chunk_size target frames autoregressively. Each completed video chunk is committed to both video-history streams before the next chunk is denoised. This preserves the VTA rollout contract without introducing action tokens or action caches. prefix_suffix retains its existing iterative open-loop behavior.

Outputs are first written to a temporary sibling and then atomically published. The output identity includes the resolved config, checkpoint/reference file inventory, sample metadata, latent/text tensor digests, and inference controls. It uses standard-cost artifact provenance: files larger than 1 MiB contribute path, size, and modification time rather than a content digest. Source and environment provenance are recorded in summary.json, but are not part of the output-directory suffix. Therefore, that suffix is deterministic bookkeeping, not a content-addressed model or result ID. Use a new output root when code or weights may have changed in place, and record independent full artifact digests for publication results. Output directories are create-only and are published atomically after all files are complete.

Extend The Workflow

New datasets should implement the uniform latent data contract and produce the same canonical layout metadata; camera-name parsing stays in the data adapter. New video objectives should remain a PolicyVariant plus compatible ActionDecoder, while shared transformer execution stays in VisualTower. Use topology component groups for trainability and exports rather than matching parameter-name prefixes.

The permanent strict-gradient and multi-chunk gates are documented in Testing.