Training And Inference¶
Use this guide to configure assets, train or resume a model, and run offline evaluation or simulator rollouts.
Configure Local Assets¶
Keep datasets, checkpoints, simulator checkouts, and output roots outside versioned experiment YAML:
cp configs/local_paths.sample.yaml configs/local_paths.yaml
$EDITOR configs/local_paths.yaml
Select another registry with OPEN_WAM_LOCAL_PATHS=/absolute/path/paths.yaml.
Only populate keys referenced by the selected config; unrelated placeholders
may remain unchanged.
Validate and inspect a config before allocating a GPU:
uv run openwam-validate-config configs/experiments/<experiment>.yaml
uv run openwam-inspect-config --cfg configs/experiments/<experiment>.yaml
Static validation targets authored YAML. Checkpoint-generated
resolved_config.yaml files contain fully serialized typed defaults and should
be loaded through checkpoint-aware runtime commands rather than passed to
openwam-validate-config.
Supported Configs¶
The generic runtime covers these representative maintained families:
| Architecture / program | Experiment config |
|---|---|
| Parallel stream six video/action programs | parallel_stream_libero_<program>.yaml |
| Dual expert six video/action programs | dual_expert_libero_<program>.yaml |
| GJD, either architecture | <architecture>_libero_generalist_joint_denoising.yaml |
| Dual expert conditional FDM/IDM | dual_expert_libero_conditional_dynamics.yaml |
| Video-only prefix/suffix | causal_video_prediction_libero_latent_local.yaml |
| Video-only VTA marginal | causal_video_prediction_libero_chunked_conditioned.yaml |
The complete Wan/LingBot initialization, latent-data, scoped-export, and rollout workflow is in Video-Only Training.
This table identifies semantic families, not resource tiers. Use the public
tiny lifecycle in the quickstart for a
CPU first run. Full-size configs declare their actual backbone and trainer
settings in YAML; for example, the 30-layer dual-expert LIBERO references are
FSDP workloads characterized with four 48 GB GPUs. The process launcher, not
trainer.devices, creates the distributed workers.
Config names describe the architecture and program directly. Existing copied
YAMLs and checkpoint-local resolved_config.yaml files are loaded as written;
new runs, reports, and automation should use the canonical names above.
Training¶
All architectures and programs enter the same package-owned training runtime:
uv run --extra train openwam-train \
--cfg configs/experiments/<experiment>.yaml \
--save-root runs/<run-name>
For supported DualExpert latent-data recipes, see
Variable-Length Training Batches for
opt-in length bucketing, padding, and sequence-isolated packing. Existing
experiments keep the strict batching default.
Use repeatable --set section.field=value arguments only for intentional run
overrides. Keep the resolved config with the checkpoint. External schedulers
may set process placement and retry policy, but should invoke this command
without embedding cluster paths in tracked configs.
Multi-GPU¶
Worker processes are created by the external launcher, which supplies the
runtime topology through WORLD_SIZE, RANK, and LOCAL_RANK. Neither config
nor the training CLI creates workers. Launch under a process launcher:
torchrun --standalone --nproc-per-node=4 \
-m open_wam.cli.train \
--cfg configs/experiments/<experiment>.yaml \
--save-root runs/<run-name> \
--expected-world-size 4
open_wam.cli.train is the canonical module and console-script entrypoint. Both
forms share one parser and delegate to the same training implementation.
Under Slurm, run one task per node and let torchrun fan out the ranks:
#SBATCH --nodes=2
#SBATCH --gres=gpu:4
MASTER=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n1)
srun --ntasks-per-node=1 torchrun \
--nnodes="$SLURM_NNODES" --nproc-per-node=4 \
--rdzv-backend=c10d --rdzv-endpoint="$MASTER:29500" \
--rdzv-id="$SLURM_JOB_ID" \
-m open_wam.cli.train \
--cfg configs/experiments/<experiment>.yaml \
--save-root runs/<run-name> \
--expected-world-size "$((SLURM_NNODES * 4))"
Do not use bare srun --ntasks-per-node=<gpus> without a task-local wrapper.
Slurm exports SLURM_PROCID, SLURM_LOCALID, and SLURM_NTASKS, none of which
this runtime reads; it reads only RANK, LOCAL_RANK, and WORLD_SIZE. Every
task would see WORLD_SIZE=1 and run as an independent single-process job. A
direct srun integration must map those variables inside each task, not in the
parent allocation shell. Prefer the torchrun recipe above.
OpenWAM never creates worker processes from config. --expected-world-size N
validates the topology supplied by the launcher and fails before model
construction when WORLD_SIZE != N. The legacy --devices N option remains a
compatibility alias: it is still recorded as trainer.devices, and explicit
CLI values now establish the same launch expectation instead of being silently
ignored. Use --expected-world-size in new automation.
strategy: fsdp shards parameters, gradients, and optimizer state across the
launched processes. A single process gets no sharding benefit regardless of how
many GPUs are visible, so single-device memory must fit the whole model plus its
optimizer state.
Effective batch does not follow the config across process counts¶
The optimizer sees train_batch_size x gradient_accumulation_steps x
WORLD_SIZE samples per step. Gradients are averaged across ranks, so adding
processes multiplies the effective batch instead of splitting the existing one.
The shipped LIBERO configs set train_batch_size: 1 and
gradient_accumulation_steps: 10, which is an effective batch of 10 on one
process. Launching the same config under --nproc-per-node=4 trains at 40, and
nothing in the config or the logs says so.
Divide the accumulation when you change the process count:
| Processes | gradient_accumulation_steps |
Effective batch |
|---|---|---|
| 1 | 10 | 10 |
| 2 | 5 | 10 |
| 5 | 2 | 10 |
torchrun --standalone --nproc-per-node=5 \
-m open_wam.cli.train \
--cfg configs/experiments/dual_expert_libero_video_then_action.yaml \
--save-root runs/<run-name> \
--expected-world-size 5 \
--set training.gradient_accumulation_steps=2
Load an external dataset or policy extension before config construction:
uv run --extra train openwam-train \
--extension acme_open_wam \
--cfg /path/to/acme_experiment.yaml
For the six standard video/action programs, policy_variant.program is the
only public program switch. A named config is preferred for recorded runs, but
a one-off ablation can use:
uv run --extra train openwam-train \
--cfg configs/experiments/dual_expert_libero_joint.yaml \
--set policy_variant.program=video_then_action
Changing program also restores the checkpoint-validated noise-clock default,
joint_timestep_coupling: independent. VTA, ATV, and decoupled execution have
only one noisy modality per stage, so another value is rejected instead of
being stored and silently ignored. Joint, noisy-condition, and GJD programs
may still set an explicit timestep-coupling ablation such as match_sigma.
Sequence contracts never choose or rewrite the noise clock.
LIBERO Training Examples¶
Both recipes below use the shipped data and sampling settings. The flags select the output directory, training duration, and checkpoint retention. The single-process examples use an effective batch size of 10.
Joint denoising:
torchrun --standalone --nproc-per-node=1 \
-m open_wam.cli.train \
--cfg configs/experiments/dual_expert_libero_joint.yaml \
--save-root runs/libero-joint \
--run-name libero-joint \
--expected-world-size 1 \
--num-steps 10000 \
--set trainer.save_interval=500 \
--set trainer.max_checkpoints_to_keep=3 \
--disable-wandb
Video-then-action, the same recipe with only the config swapped:
torchrun --standalone --nproc-per-node=1 \
-m open_wam.cli.train \
--cfg configs/experiments/dual_expert_libero_video_then_action.yaml \
--save-root runs/libero-vta \
--run-name libero-vta \
--expected-world-size 1 \
--num-steps 10000 \
--set trainer.save_interval=500 \
--set trainer.max_checkpoints_to_keep=3 \
--disable-wandb
Architecture selects execution, not the data recipe, so the two differ only in
--cfg. On more than one process add
--set training.gradient_accumulation_steps=$((10 / N)); see
Effective batch.
Scoring a checkpoint means running the LIBERO evaluator once per episode and
counting the outcome recorded in each result filename. Count the first result
for a given task and episode and never re-run a failure: re-rolling only the
failures converges a directory to all-True.
LIBERO Policy Planning Default¶
The six shipped video/action program configs use one shared full-trajectory
W64 training recipe for both parallel_stream and dual_expert. Architecture
selects model execution; it does not select the data recipe.
| Setting | Shipped value |
|---|---|
| Replay rows | include_all; no replay-status validation split |
| Segment sampler | uniform_segment, 1000/1000, full segments only |
| Draw order | replacement, with uniform task/demo/trajectory powers |
| Packed geometry | chunk upper bound 4, window upper bound 64, randomized |
| Sequence semantics | legacy_prefix_single_frame_perchunk_proprio |
| Optimization budget | 10,000 steps, no sample-loss reweighting |
With randomized geometry, each sample draws a chunk size from [1, 4] and a
window size from [4, 64]. The sequence contract supplies the single-frame
condition latent, video-only history visibility, per-chunk additive proprio,
and prefix alignment. Do not repeat those owned fields as individual YAML or
CLI overrides; the config loader rejects ambiguous combinations.
These are YAML defaults, not runtime invariants. The generic config, data, and
policy layers neither recognize this recipe by name nor reject another
structurally valid combination. Override individual fields with --set or
ship another experiment YAML for an ablation.
GJD real_joint uses this same planning recipe and sequence contract. GJD-only
fields such as source/mode routes, timestep coupling, mode-token use, and total
optimization budget remain method knobs. Conditional FDM and IDM rows do not
inherit long-horizon planning context: they keep their target-only
singleton-t0, text-dropped, one-video-boundary history contract.
replay_status_policy: include_all must remain paired with
val_replay_status_policy: null, require_replay_status: false, and
val_require_replay_status: false. Reusing the former failure-only validation
split would make that split empty because the failed rows are now in training.
Initialization And Full-State Resume¶
Use --runtime-backbone-path to initialize a new run from a detached
video-transformer export. backbone.transformer_subdir normally names a
relative component inside pretrained_model_name_or_path; absolute values
remain supported for authored and historical configs. The explicit
runtime_backbone_artifact_path takes precedence and is preferred for new
detached-artifact configs. Use --initialize-weights-from for a checkpoint file
or checkpoint_step_* directory when starting a fresh optimizer and step
counter. The former --checkpoint-root option is intentionally rejected
because historical commands used it for full-state resume; choose the operation
explicitly instead.
When a checkpoint is also intended to supply a standalone runtime backbone,
retain its checkpoint-local transformer/ export. Do not rely on a
machine-specific runtime_backbone_artifact_path recorded by an older run, or
reinterpret a relative transformer_subdir as a path beneath the checkpoint
directory. Relative component paths always resolve beneath
pretrained_model_name_or_path.
When only a detached transformer is available, pass it explicitly with
--runtime-backbone-path. Ordinary full-state continuation still uses the state
files described below.
uv run --extra train openwam-train \
--cfg configs/experiments/<experiment>.yaml \
--save-root runs/<run-name> \
--initialize-weights-from runs/<parent-run>/checkpoints/checkpoint_step_N
For stateful continuation, use --resume-from with a checkpoint directory or
full_training_state.pt. Resume never falls back to model_state.pt and never
promotes a differently named file. It restores model, optimizer, scheduler,
strategy/scaler, step state, and the next sampler epoch/batch cursor. Full-state
checkpoints are accepted only at optimizer boundaries because partially
accumulated gradients are not serialized. Full-state checkpointing and resume
also require a sized training dataloader; custom iterable adapters must
implement __len__ or use model-only checkpoints. Process and stochastic
dataset/worker RNG streams are not checkpointed, so a restarted run is not a
bitwise continuation. Both operations use the invocation config unchanged;
resolved_config.yaml is an audit record. An explicitly named
checkpoint_step_* directory is treated as an operator assertion; pass the run
root to select the latest completion-marked checkpoint under checkpoints/.
Full-state checkpoints that predate the explicit next_batch_index cursor are
not exact-resumable; use them with --initialize-weights-from instead.
Distributed runs use trainer.distributed_timeout_seconds: 1800 by default.
The timeout includes rank-0 reads and writes of full-state checkpoints before
other ranks enter the next collective. Increase it for slower shared storage;
lower it only when faster failure detection is more important than large-state
resume support.
Generalist Joint Denoising¶
Installed-package GJD training uses the generic openwam-train entry point.
Parallel Stream and Dual Expert are model architectures under the same GJD
paradigm. Source checkouts also provide
scripts/run_gjd_libero.sh as a convenience for expanding named ablations and
launching LIBERO rollouts. The script is not part of the wheel or source
distribution and does not own model semantics.
Choose the interface by experiment intent:
| Goal | Interface | Live simulator policy? |
|---|---|---|
| Compare pure FDM or IDM within a GJD study | GJD config with pure FDM/IDM overrides | No; use offline diagnostics |
| Train a run whose primary identity is FDM or IDM | forward_dynamics or inverse_dynamics |
No; use offline diagnostics |
| Produce online robot actions | VTA, ATV, joint, decoupled, or a noisy-condition program | Yes |
The first two choices execute the same conditional tensor contract. They differ only in experiment identity and tracking: a GJD run records an ablation, while the standalone config records a fixed program. Parallel Stream and Dual Expert consume the same route and sequence contracts; choose the corresponding architecture config to change model topology.
openwam-train \
--config-name dual_expert_libero_generalist_joint_denoising \
--save-root runs/dual-expert-gjd-mode-token \
--dataset-root /path/to/libero_10 \
--runtime-backbone-path /path/to/base/transformer \
--enable-wandb \
--wandb-project openwam-gjd \
--set policy_variant.generalist_mode_text_token=true
The canonical config is vanilla GJD. Its five routes aggregate to
joint/FDM/IDM = 0.6/0.2/0.2, split evenly between real and counterfactual
sources for each conditional mode. The mode token is disabled. Setting
generalist_mode_text_token=true produces the mode-token variant above.
Pure-joint uses an empty route list. Pure-FDM and pure-IDM use only the matching real-demo and
counterfactual routes; their weights are a relative source ratio. For example,
pure FDM with a 3:1 real-to-counterfactual ratio is:
FDM_ROUTES='[{"source":"real_demo","mode":"action_conditioned_video","weight":3},{"source":"counterfactual_dynamics","mode":"action_conditioned_video","weight":1}]'
openwam-train \
--config-name dual_expert_libero_generalist_joint_denoising \
--save-root runs/dual-expert-gjd-pure-fdm \
--set policy_variant.generalist_mode_text_token=false \
--set "data.dynamics_routing.routes=${FDM_ROUTES}"
Under replacement sampling, the example draws real and counterfactual FDM
samples with a 3:1 ratio in expectation. Weights need not sum to one. Either
endpoint may be zero, but not both. Every conditional route requires encoded
dynamics train and validation roots: real_demo selects the rollout-local
gt branch and counterfactual_dynamics selects perturbed branches. Configure
those roots in configs/local_paths.yaml or with explicit --set overrides.
A nonempty data.dynamics_routing.routes list selects the source router and
target-only adapter; no second enable flag exists. Routes decide whether a run
uses real demonstrations, counterfactual rows, or both. The route list is the
only source and objective sampling authority, while the policy program is the
execution authority. Active routes require sample_order_mode: replacement;
route weights are replacement probabilities, so an epoch-order setting is
rejected rather than silently ignored. Retired routing markers such as mixed_dynamics and
dynamics_routed are validated and discarded only when loading immutable
checkpoint configs with runtime compatibility enabled. Authored configs must
omit them. That checkpoint migration reconstructs the historical effective
contract rather than trusting serialized defaults: explicit route rows win;
otherwise mixed_dynamics activates the old source weights, while demo_only
ignores those dormant weights and maps any conditional mode probabilities to
real-demo routes only. Pure-joint demo_only remains route-free so its original
categorical RNG draw is preserved.
Resume through the same package entry point and repeat the experiment-defining overrides (the checkpoint-local resolved config remains the audit record):
openwam-train \
--config-name dual_expert_libero_generalist_joint_denoising \
--save-root runs/dual-expert-gjd-mode-token \
--resume-from runs/dual-expert-gjd-mode-token/checkpoints/checkpoint_step_N \
--set policy_variant.generalist_mode_text_token=true
Standalone Conditional FDM And IDM¶
Use a fixed program when the entire experiment is conditional dynamics rather than a GJD ablation. Both programs use the same model, attention, data projection, scheduler, loss masks, and gradients as the corresponding 100% GJD mode:
Data Prerequisites¶
| Training path | Required data |
|---|---|
| Standard VTA, ATV, joint, decoupled, or noisy-condition policy | The normal encoded demonstration root only; no counterfactual root is used |
Conditional FDM/IDM with the default 1:1 ratio |
Encoded dynamics train and validation roots containing both gt and perturbed rows; the normal planning dataset is not loaded |
Conditional FDM/IDM with counterfactual weight 0 |
Encoded dynamics train and validation roots containing gt rows; the normal planning dataset is not loaded |
| GJD with positive joint and conditional routes | The normal planning dataset plus encoded dynamics roots; only sources with positive routes are loaded |
Encoded dynamics roots are not arbitrary videos. They must follow the encoded
target-only t0-plus-future contract: an observed t0, aligned future video
and action data, valid loss-boundary metadata, and aligned proprio when it is
available. Canonical manifests declare
artifact_schema: open_wam.encoded_dynamics.v1, an explicit
reference_branch, and an artifact-relative raw_payload_root that locates
aligned action and proprio payloads. Absolute dataset_root, config, and
checkpoint paths are provenance only and are never runtime location fields.
Canonical loading is strict: migrate an unversioned or pre-relocation v1 root
once before training:
uv run python scripts/migrate_encoded_dynamics_artifact.py /path/to/encoded_root
If the artifact was copied from another machine and its legacy provenance path no longer exists, identify the copied raw payload directory explicitly:
uv run python scripts/migrate_encoded_dynamics_artifact.py \
/path/to/encoded_root \
--raw-root /path/to/raw_payload_root
The migration validates all training-indexed payloads and updates only
manifest.json; latent, action, and simulator-state payloads are unchanged.
It stores the raw root relative to the encoded root, so moving their common
directory preserves the artifact. The loader checks every indexed payload
required by the positive routes before model execution. Set the two encoded
roots through configs/local_paths.yaml or explicit --set overrides.
Perturbed rows are required only when a counterfactual_dynamics route has
positive weight. The rollout-local encoded root is required by every
conditional objective because even a real-only route consumes its gt rows.
The ordinary planning dataset is required only when a positive joint route
can consume it.
To produce LIBERO counterfactual roots from a source checkout, first install
the sim dependencies and configure the local LIBERO repository. The generator
selects only successful rows from the replay-status file described in
LIBERO Training Prerequisites.
The following creates a 10,000-row train split and a disjoint 1,000-row
validation split using the maintained branch and random-t0 defaults:
CF_ROOT=/path/to/counterfactual_dynamics
REPLAY_STATUS=/path/to/replay_status.jsonl
uv run --extra sim python scripts/build_libero_fdm_counterfactual_demo_dataset.py \
--replay-status-path "$REPLAY_STATUS" \
--output-dir "$CF_ROOT" --run-id train \
--target-transitions 10000
uv run --extra sim python scripts/build_libero_fdm_counterfactual_demo_dataset.py \
--replay-status-path "$REPLAY_STATUS" \
--output-dir "$CF_ROOT" --run-id val \
--target-transitions 1000 --episodes-per-task 5 \
--t0-samples-per-episode 2 \
--exclude-source-dataset-root "$CF_ROOT/train" --seed 1
Encode both raw roots with the same VAE-capable checkpoint and config used by training:
VAE_CHECKPOINT=/path/to/checkpoint_step_N
for SPLIT in train val; do
uv run --extra eval python scripts/encode_libero_fdm_counterfactual_dataset.py \
--dataset-root "$CF_ROOT/$SPLIT" \
--config configs/experiments/dual_expert_libero_conditional_dynamics.yaml \
--checkpoint "$VAE_CHECKPOINT" --device cuda:0 \
--skip-condition-latents
done
Set the train and validation registry keys to
$CF_ROOT/train/encoded_latents and $CF_ROOT/val/encoded_latents. Both tools
fail on existing outputs unless replacement is requested explicitly; use their
--help output for sharding and larger production runs.
# FDM defaults to a 1:1 real/counterfactual source ratio.
uv run --extra train openwam-train \
--cfg configs/experiments/dual_expert_libero_conditional_dynamics.yaml \
--save-root runs/dual-expert-forward-dynamics
# IDM changes both execution mode and data routes. The cross-config validator
# rejects changing only one side.
IDM_ROUTES='[{"source":"real_demo","mode":"video_conditioned_action","weight":1},{"source":"counterfactual_dynamics","mode":"video_conditioned_action","weight":1}]'
uv run --extra train openwam-train \
--cfg configs/experiments/dual_expert_libero_conditional_dynamics.yaml \
--set policy_variant.program=inverse_dynamics \
--set "data.dynamics_routing.routes=${IDM_ROUTES}" \
--save-root runs/dual-expert-inverse-dynamics
The Dual Expert file above is a convenience profile. Parallel Stream uses the same fixed program and routes on its own GJD topology profile. Replace the two GJD validation probes with one probe whose objective is inherited from the fixed program:
FDM_ROUTES='[{"source":"real_demo","mode":"action_conditioned_video","weight":1},{"source":"counterfactual_dynamics","mode":"action_conditioned_video","weight":1}]'
FIXED_VAL='[{"name":"conditional_dynamics_val","dataset_split":"val","source":"dataset","max_batches":16,"report_prefix":"val_conditional_dynamics"}]'
uv run --extra train openwam-train \
--cfg configs/experiments/parallel_stream_libero_generalist_joint_denoising.yaml \
--set policy_variant.program=forward_dynamics \
--set "data.dynamics_routing.routes=${FDM_ROUTES}" \
--set "validation.auxiliary_tasks=${FIXED_VAL}" \
--save-root runs/parallel-stream-forward-dynamics
Use the same three overrides with inverse_dynamics and
video_conditioned_action routes for Parallel Stream IDM. The architecture
swap changes model/action packing and checkpoint topology, not the conditional
data or objective contract.
For a non-default ratio, replace the route list. For example, FDM 3:1 uses:
FDM_ROUTES='[{"source":"real_demo","mode":"action_conditioned_video","weight":3},{"source":"counterfactual_dynamics","mode":"action_conditioned_video","weight":1}]'
uv run --extra train openwam-train \
--cfg configs/experiments/dual_expert_libero_conditional_dynamics.yaml \
--set "data.dynamics_routing.routes=${FDM_ROUTES}" \
--save-root runs/dual-expert-forward-dynamics-real3-cf1
For IDM, use video_conditioned_action in both routes. Fixed programs reject
routes for the other mode, preventing a valid-looking config from sampling an
objective the program cannot execute. In both programs the data layer selects
the encoded gt or perturbed source view with the same target-only layout:
V0 is an observed, unsupervised singleton t0 chunk; loss starts at V1; each
following chunk retains the sampled 1-4 frame geometry and can attend one most
recent clean video/proprio boundary frame. Task text and the learned GJD mode
token are absent. FDM masks action loss; IDM masks video loss.
Standalone conditional FDM/IDM inference is offline: FDM needs future clean actions and IDM needs future clean video. The LIBERO live simulator runner rejects these programs instead of silently substituting joint rollout. Use the offline FDM/IDM diagnostics below with explicit condition tensors.
For FDM metrics, each evaluation row must provide t0, clean future actions,
and target future video. For IDM metrics, it must provide t0, clean future
video, and target actions. Strict real and counterfactual metrics both use the
encoded dynamics rows described above; ordinary full-trajectory demo crops are
not equivalent to a rollout-local causal-VAE reset at t0.
Offline Evaluation¶
Use eval configs for dataset-level metrics:
uv run --extra eval openwam-eval \
--cfg configs/evals/<evaluation>.yaml \
--device cuda:0 \
--max-batches 16 \
--output-json outputs/evaluation.json
Use openwam-sanity when the goal is to verify loading, one train/eval path,
and rollout-style tensor flow without making a benchmark claim:
uv run --extra train openwam-sanity \
--cfg configs/examples/<example>.yaml \
--device cpu \
--max-batches 1 \
--rollout-steps 1
Sanity reports intentionally inspect exactly one batch. Use openwam-eval
for aggregated multi-batch metrics.
FDM/IDM ablations and simulator counterfactual renders are checkout-only research diagnostics, not installed library APIs. Their stable commands require an explicit checkpoint and local data inputs:
uv run --extra eval python scripts/run_joint_denoising_fdm_ablation.py \
--cfg /path/to/checkpoint_step_N/resolved_config.yaml \
--checkpoint /path/to/checkpoint_step_N \
--dataset-root /path/to/libero_10
uv run --extra sim python scripts/run_joint_denoising_fdm_counterfactual.py \
--checkpoint /path/to/checkpoint_step_N \
--replay-status-path /path/to/replay_status.jsonl \
--episode-indices 0,1,2 \
--dataset-root /path/to/libero_10
When --mode is omitted, a forward_dynamics checkpoint runs only
forced_action_joint_fdm, and an inverse_dynamics checkpoint runs only
video_conditioned_action. An explicitly incompatible mode fails before data
or model loading. A non-fixed GJD config retains the research behavior of
running every diagnostic mode; use repeated --mode arguments to narrow it.
These --mode values name research interventions. The adapter translates each
intervention to one canonical core objective: joint,
action_conditioned_video, or video_conditioned_action. Model runtime APIs do
not accept the research aliases.
Conditional offline diagnostics generate one latent frame per model call even when the checkpoint was trained with randomized 1-to-4-frame future chunks. The evaluator advances one target frame and one cache frame each call, so it does not skip targets or let prediction errors accumulate across a four-frame diagnostic call. Counterfactual manifests record both the configured checkpoint chunk size and the effective per-mode diagnostic chunk size.
Use --help for windows, branches, and output paths. The reusable GJD policy
and counterfactual-action contracts continue to live in open_wam; only the
experiment orchestration and visualization live under scripts/.
LIBERO Inference¶
The maintained dual-expert evaluator loads one checkpoint and executes a closed-loop episode with the LingBot streaming VAE:
uv run --extra sim python scripts/run_libero_dual_expert_visualization.py \
--cfg /path/to/checkpoint_step_N/resolved_config.yaml \
--checkpoint /path/to/checkpoint_step_N \
--frontend-encode-mode lingbot_streaming_vae \
--dual-expert-inference-window-size 30 \
--benchmark libero_10 \
--task-id 0 \
--episode-idx 0 \
--max-timestep 800 \
--max-chunks 50 \
--startup-model-obs-frames 1 \
--startup-env-init-steps 5 \
--output-dir outputs/libero_dual_expert \
--runtime-device cuda:0 \
--action-device cuda:0 \
--frontend-device cuda:0 \
--decode-device cuda:0
The six standard non-GJD dual-expert rollout programs use an 800 timestep and
50 chunk limit. Conditional FDM/IDM programs are offline diagnostics by
default. The composition route below makes IDM usable online by supplying clean
future video; FDM remains offline because this route does not supply future
actions. The
--action-route generated_video_then_action composition route loads separate
producer and consumer sessions. Using the same VTA checkpoint for both roles is
the maintained manual VTA split; using a fixed IDM or IDM-enabled GJD consumer
keeps text-free IDM semantics with one clean history frame and the configured
future chunk (four latent frames and sixteen actions by default). See
Video/Action Composition for CLI examples,
extension contracts, and the strict parity gates.
GJD LIBERO rollout is a source-checkout integration and
defaults to 1500/100:
bash scripts/run_gjd_libero.sh rollout \
--architecture dual_expert \
--ablation mode_token \
--checkpoint /path/to/checkpoint_step_N \
--task-id 0 \
--episode-idx 0 \
--output-dir outputs/libero_gjd
For a task/episode sweep, use the loaded-once batch evaluator rather than a shell loop that reloads the checkpoint for every episode:
uv run --extra sim python scripts/run_libero_dual_expert_batch_visualization.py \
--cfg /path/to/checkpoint_step_N/resolved_config.yaml \
--checkpoint /path/to/checkpoint_step_N \
--benchmark libero_10 \
--task-ids 0-9 \
--episode-idxs 0-49 \
--seed-by-episode \
--frontend-encode-mode lingbot_streaming_vae \
--dual-expert-inference-window-size 30 \
--max-timestep 800 \
--max-chunks 50 \
--startup-model-obs-frames 1 \
--startup-env-init-steps 5 \
--output-dir outputs/libero_dual_expert_batch \
--save-rollout-video \
--runtime-device cuda:0 \
--action-device cuda:0 \
--frontend-device cuda:0 \
--decode-device cuda:0
The batch runner preserves the single-episode runtime semantics and starts fresh rollout state for each episode while retaining loaded model resources.
Parallel stream uses the shared realtime sandbox:
uv run --extra sim python scripts/run_libero_realtime_sandbox.py \
--cfg /path/to/checkpoint_step_N/resolved_config.yaml \
--checkpoint /path/to/checkpoint_step_N \
--task-id 0 \
--episode-idx 0 \
--output-dir outputs/libero_parallel_stream
Generic Simulator Rollout¶
Use the package simulator boundary for a built-in or extension-registered adapter:
uv run --extra sim openwam-sim-rollout \
--cfg configs/examples/<benchmark>.yaml \
--benchmark <registered-name> \
--target-action-hz 10
Application-owned adapters add --extension package.module and repeatable
--sim-option KEY=VALUE arguments. Standard result provenance is written by
default; use --provenance-mode full for a checkpoint SHA-256.
Closed-loop reports should record target and achieved action rate, fallback actions, task and episode identity, exact resolved config, checkpoint, success, and video path. Never compare realtime results without also fixing planner mode, diffusion steps, fallback policy, and horizon.
Numerical Characterization¶
Changes that can affect model numerics must verify immutable fixtures and checkpoint-backed goldens rather than regenerate expected values. The exact six-program dual-expert training, recurrent inference, four-GPU, full-state resume, and GJD gates are described in Testing.