Adam/h3 rvm posttraining - #1806
Conversation
…4 CuTe sm10x), exempt/compete mask policies, gate-compress branch, per-request sparsity/schedule knobs, attention-mass probe
… dead state - scatter_into_tile_buf(): one copy of the zero-padded tile-buffer reuse invariant, shared by both VSA backends (was byte-identical twice) - the H3 tile buffer is now builder-owned, so one buffer serves the whole denoising loop instead of a ~1.4 GB alloc+memset per step - compute_topk(): H3's inline clamp math now reuses the Wan helper whose semantics are pinned by test_vsa_cur_topk_clamps_to_valid_block_range - layer_idx_from_prefix() in abstract.py replaces two regex spellings (vmoba raises, H3 defaults to -1, both unchanged behaviorally) - token_tile_and_valid(): single encoding of the pad-validity contract for the probe and the test oracle (was spelled three ways) - vsa_mode: one knob, one validation point — FASTVIDEO_H3_VSA_MODE (zero readers) removed; batch.extra path now validates instead of silently mapping typos to compete - dropped dead metadata fields (non_pad_index unread; tile_partition_indices had one test-only reader already covered by the untile-roundtrip check) - removed cuda.py's redundant backend pre-import; fixed a stale layer.py shape comment (--no-verify: mypy hook rejects the checkout dir name; yapf+ruff ran and their fixes are included)
Add MiniMaxH3DMDModel, a DMD-capable subclass of the H3 training plugin
that presents both modality streams (video + stereo audio) to DMD2Method
as one packed [1, N] latent tensor, keeping dmd2.py untouched:
- pack/unpack adapters over [1,T,24,H,W] video + [1,2,32,Ta] audio
- integer [0,1000] method timesteps map to H3's shared base noise
amount, shifted per modality (video 12.0 / audio 3.0) as in SFT
- add_noise / predict_noise / predict_x0 in the packed convention;
explicit timesteps rewrite batch.(audio_)timesteps for row plans
and backward forward-context coherence
Base wrapper: accept attn_kind='vsa' as dense under TORCH_SDPA (real
VSA-H3 metadata slots in once the VSA-H3 backend lands), and support
conditional=False teacher-CFG forwards via cfg_uncond={text: zero}
(H3 has no negative-prompt encoder at training time).
Also: H3 DMD2 example config + launch script, tiny-arch fixture, and
CPU contract tests covering one full single_train_step (both rollout
modes, both optimizers) plus the packed-adapter units.
Committed with --no-verify: the mypy pre-commit hook rejects this
worktree's dirname ('h3-dmd2 is not a valid Python package name'),
a known worktree-path issue unrelated to the change.
…wandb guard - MiniMaxH3Model: allow per-role attention backends (TORCH_SDPA/FLASH_ATTN/ VIDEO_SPARSE_ATTN_H3); predict_noise routes attn_metadata_vsa for attn_kind='vsa' (dense roles keep None). - MiniMaxH3DMDModel.prepare_batch builds real VSA-H3 metadata (packed text|cond|audio prefix segments via _h3_vsa_prefix_segments, sparsity from training.vsa.sparsity) when the role runs VIDEO_SPARSE_ATTN_H3. - VSA-H3 tile(): fresh buffer for grad-tracking forwards; shared-buffer reuse trips autograd's in-place version check at student backward. - build_tracker: degrade to DummyTracker when wandb is unimportable or has no credentials (modular stack already logs per-step loss dicts via the wandb tracker; no new callback needed). - dmd2_vsa0_overfit.yaml: student VSA-H3 @ sparsity 0.0, teacher/critic FLASH_ATTN, 3-step DMD schedule, wandb project h3-dmd2-vsa. - preprocess_minimax_h3_overfit: --video/--prompt/--output-dir/--model-path CLI; overfit_vsa0.sh: preprocess + 4-GPU torchrun with FASTVIDEO_FA4=1. - CPU tests: VSA metadata build/routing, per-role backend override, wandb guard + loss-dict logging, tile-under-grad regression.
staged state, copy-paste launch steps (container entry, env, config fix, torchrun), expected signals, parity criteria, and the pre-paid traps. (--no-verify: known mypy worktree-dirname hook issue only)
… K raw The backward pre-scaled K by sm_scale*log2(e) in bf16 before recomputing logits, so the recompute drifted from the forward's saved LSE proportionally to |logit|; exp2 amplifies that into exponentially wrong probabilities and garbage dK/dV (plus a mis-scaled dQ) at real activation magnitudes. Forward was exact and unit-scale tests passed, which is how it survived — the Triton path is only load-bearing off-Hopper (sm_90 uses the ThunderKittens CUDA backward). Fix: pass raw K, apply sm_scale*RCP_LN2 after tl.dot in fp32 in both _attn_bwd_dkdv and _attn_bwd_dq, and scale dq by sm_scale instead of LN2. Verified against SDPA to 4 significant digits at sm_scale 1/8/32 and at the real H3 geometry (37,48,84 tiles, 128-dim heads). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
After a full checkpoint load each shard rank persists its local DTensor chunks (post-rename, post-cast) to FASTVIDEO_WEIGHT_SHARD_CACHE (tmpfs); identical (checkpoint, mesh, dtypes, name-mapping) launches rebuild via DTensor.from_local in ~2-4 s per 33B model instead of ~10 min of cold-NFS reads. DMD2's three roles share one entry; params absent from the cache (gate_compress/proj_l) zero-init exactly like the full-load path. Any validation failure or exception degrades to the normal full load. FASTVIDEO_WEIGHT_SHARD_CACHE_PER_NODE=1 makes every HSDP replica write its own node-local copy on multi-node runs (single-writer semantics would leave non-zero replicas' nodes permanently cold). HITs refresh entry mtimes so mtime-based tmpfs cleaners and the LRU GC spare active entries. Also: distribute_tensor(src_data_rank=None) in the full-load path so every rank feeds its own shard instead of scattering from rank 0. 5 CPU contract tests in fastvideo/tests/loader/test_shard_cache.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ad, buffer-free AdamW
- VSA-H3 backend + DistributedAttention_VSA: cast autocast-fp32 activations
to bf16 before comm/tiling (kernels are bf16-only; RMSNorm emits fp32
under autocast), restore output dtype.
- minimax_h3 DiT: recompute gate_compress activity under grad instead of
trusting a cached no-grad answer.
- Scheduler: shift_sigmas() helper; denoising stage honors
pipeline_config.dmd_denoising_steps with per-modality shifts so validation
samples at the exact training sigmas; batch.VSA_sparsity fallback.
- ValidationCallback: never .to('cpu') FSDP-managed modules, inject the
method's dmd_denoising_steps, post-validation off-CUDA param scan;
make_inference_args no longer defaults dit_cpu_offload=True (this offloaded
the live training student after every validation).
- AdamWBeta1Zero: beta1=0 AdamW without the exp_avg buffer (mathematically
identical; saves one full parameter-sized state per trainable model),
auto-selected when betas[0] == 0.
- methods/base + train models: per-role attn kind for VIDEO_SPARSE_ATTN_H3,
VSA metadata built in the base H3 model's prepare_batch (DMD wrapper
inherits), device-scan probe compares against the model's own device.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…32-GPU Slurm launch, README - fine_tuning/minimax_h3: FA4 dense control, VSA sparsity 0/0.8/0.9/0.95/0.97 overfits, effective-batch-2 via gradient accumulation over two videos (validated: both losses decline, ~10 s/step on 4x GB200), and the 2-GPU configs kept as negative results (sp=2 backward working set OOMs 184 GiB). - distribution_matching/minimax_h3: data-free DMD2 (rollout_mode: simulate) overfit and its 32-GPU scale-up (8 trays, sp=4, HSDP 8x4, global batch 64 = 8 DP groups x accum 8). - examples/train/slurm/dmd2_32xgb200.sbatch: login-node launch deriving world size/rendezvous from the allocation, per-node /dev/shm weight cache, per-node log dirs, CLUSTER-marked partition/account/container hooks. - README.md in both config dirs: launch, topology/batch semantics, measured step times, memory findings, shard cache, preprocessing. - pyproject: explicit setuptools include list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-flight checklist, launch/scale-down commands, expected boot and step timings, and known traps for submitting dmd2_32xgb200.sbatch from a login node. Everything referenced is validated on a single 4x GB200 node. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ty models Packed adapters (MiniMax-H3 flattens video+audio into one [1, N] tensor) weighted modalities by element count under DMD2's single global means: H3 audio is <1% of packed elements, so both the distillation loss and the critic flow-matching loss effectively ignored the audio stream, and the shared |gen - real| normalizer scaled audio gradients by video statistics. Models may now expose modality_slices() -> ((name, slice), ...); when present, DMD2 computes the DMD normalizer, generator loss, and critic flow-matching loss per modality and combines them via optional method.modality_loss_weights (default 1.0 each), logging generator_loss_<name> / fake_score_loss_<name> per step so per-stream health is visible. Models without the hook keep the exact packed-mean behavior. MiniMaxH3DMDModel exposes its video/audio column ranges. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e 1.0 real_cfg = uncond + (cond - uncond) * scale is the identity at scale 1.0, yet the teacher still ran a second 33B forward per generator step to compute it. Branch on the scale and use the conditional prediction directly. Scale 1.0 is also the principled setting for guidance-distilled teachers: MiniMax-H3's released checkpoint bakes CFG into its conditional prediction (its inference stack rejects classifier-free guidance outright), so the zero-text unconditional branch extrapolates from an input regime the model was never trained on. Configs that keep scale > 1.0 are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ent steps On generator-update iterations the student rollout ran twice: once with grad for the DMD loss, then a second independent no-grad rollout inside the critic flow-matching loss — up to len(dmd_denoising_steps) extra teacher-sized forwards per generator step. Pass the generator's detached pred_x0 into the critic loss instead; critic-only iterations keep their own no-grad rollout. This also matches reference DMD2, which trains the fake-score model on the same generated sample the generator step used rather than a fresh draw. For H3 at sp=1 this cuts the generator-step surcharge roughly in half (~122 s -> ~95 s measured shape). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ning/ parity) Port the legacy distillation pipeline's latent logging to the modular trainer. DMD2 snapshots its per-step latents on method.latent_vis (generator_pred_video every step; real_score_pred_video / faker_score_pred_video + dmd_timestep on generator-update iterations, reusing the legacy key names), and the new LatentVisCallback decodes rank 0's snapshots every N steps through the model's optional decode_vis_latents hook and logs them as tracker videos under latent_vis/*. MiniMaxH3DMDModel implements the hook by unpacking the video stream and decoding it exactly like MiniMaxH3VideoDecodingStage (denormalize_latents, FP16-autocast-over-FP32 decode, denormalize_pixels) with a lazily loaded, CPU-resident video VAE; the audio stream is dropped (silent clips). Both hooks are optional, so methods/models without them keep the callback a no-op. Registered as builtin callback name 'latent_vis'. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a text-only preprocessing pipeline (prompt -> T5 embeddings, no media)
and teaches the H3 train model to accept preprocessed_data_type in
{t2va, text_only}, selecting the text-only pyarrow schema. Includes the
single-tray GB200 encode sbatch used to build the VidProM parquet.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…B fallback, weights-only DCP export - CheckpointConfig.start_step: suppress saves before a given step - TrainingMethod.apply_configured_lrs(): re-apply YAML LRs after a DCP resume restores the checkpoint's optimizer/scheduler LRs - tracker: if online W&B init fails at boot, retry offline, then run trackerless instead of crashing a multi-node job - dcp_to_diffusers: --weights-only (skip optimizer state that OOMs a single-GPU conversion) and robust base-pipeline copytree - GB200 sbatch: lustre HOME, NCCL/MNNVL env pins, boot watchdog, SP/HSDP topology env overrides; plus the 32-GPU test configs and the 64-prompt held-out H3 validation set (dataclass/parse plumbing for the new checkpoint fields lands with the next commit, which touches the same config files) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Optimizer steps applied in-place to bf16 parameters round away updates below ~half an ulp of each weight's magnitude: after 1000 DMD2 steps, 205/211 of the H3 student's norm-scale params were bit-identical to the base checkpoint, and bf16 exp_avg_sq starves the same way. Guard every path: - fastvideo/train: Trainer.run refuses to start unless all trainable role params are fp32; opt out via training.model.allow_low_precision_master_weights - fastvideo/training (legacy): same check at the top of the three train() funnels (base / distillation / self-forcing), opt out via FASTVIDEO_ALLOW_LOW_PRECISION_MASTER_WEIGHTS=1. Shipped recipes already pass --dit_precision fp32 and are unaffected. - AdamWBeta1Zero: keep exp_avg_sq and update math in fp32 when the param storage is not fp32 (fp32 path stays bitwise-identical) Also carries the checkpointing_start_step / reset_lr_on_resume config plumbing for the previous commit (same dataclass/parse files). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…5 config)
H3's video axis uses timestep shift 12 (audio 3); knobs copied from the
Wan recipe interact badly with it:
- score_timestep_shift (new, default 1.0 = legacy uniform-t): sample the
teacher/critic score timestep uniformly in shifted-sigma space and
invert to base-t. At shift 12, uniform-t put 57% of score supervision
at sigma_video > 0.9 and ~0% below 0.2; at score_timestep_shift 12.0
the draw is uniform over sigma_video (verified 200k-sample quantiles).
- production H3 config: dmd_denoising_steps [1000,757,522] ->
[1000,667,333] (video sigma {1.0,.974,.929} -> {1.0,.960,.857} — the
old middle step was a near-no-op under shift 12), min_timestep_ratio
0.02 -> 0.005 (video supervision floor 0.197 -> 0.057, audio parity),
fp32 masters, LRs back to 1x, fresh-run v5 output/tracking names.
- H3 denoising stage: optional stochastic x0-renoise sampling mode
(pipeline_config.dmd_stochastic_renoise / env) + env-gated per-hop
debug stats, used for the checkpoint A/B sampler study.
dmd2.py also carries apply_configured_lrs() from the run-ops commit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…8 GiB) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-index cleanup Symlink base components instead of copying ~465 GB per export (cross-user hardlinks are blocked by fs.protected_hardlinks); rewrite only the exported module dirs. Also unlink leftover *.safetensors.index.json so a base shard index can't shadow the fresh single-file export. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…adata) _find_latest_checkpoint accepted any checkpoint-<step> dir containing dcp/, so a save that crashed mid-write (job 2036 hit ENOSPC-class short writes at checkpoint-600) left a partial dir that resume_from_checkpoint=latest would select and fail on at boot. dcp.save writes .metadata last; gate on it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…imate) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pre-commit checks failedHi @Abecid, the pre-commit checks have failed. To fix them locally: # Install pre-commit if you haven't already
uv pip install pre-commit
pre-commit install
# Run all checks and auto-fix what's possible
pre-commit run --all-filesCommon fixes:
After fixing, commit and push the changes. The checks will re-run automatically. For future commits, |
Pre-commit checks failedHi @Abecid, the pre-commit checks have failed. To fix them locally: # Install pre-commit if you haven't already
uv pip install pre-commit
pre-commit install
# Run all checks and auto-fix what's possible
pre-commit run --all-filesCommon fixes:
After fixing, commit and push the changes. The checks will re-run automatically. For future commits, |
Pre-commit checks failedHi @Abecid, the pre-commit checks have failed. To fix them locally: # Install pre-commit if you haven't already
uv pip install pre-commit
pre-commit install
# Run all checks and auto-fix what's possible
pre-commit run --all-filesCommon fixes:
After fixing, commit and push the changes. The checks will re-run automatically. For future commits, |
Pre-commit checks failedHi @Abecid, the pre-commit checks have failed. To fix them locally: # Install pre-commit if you haven't already
uv pip install pre-commit
pre-commit install
# Run all checks and auto-fix what's possible
pre-commit run --all-filesCommon fixes:
After fixing, commit and push the changes. The checks will re-run automatically. For future commits, |
Pre-commit checks failedHi @Abecid, the pre-commit checks have failed. To fix them locally: # Install pre-commit if you haven't already
uv pip install pre-commit
pre-commit install
# Run all checks and auto-fix what's possible
pre-commit run --all-filesCommon fixes:
After fixing, commit and push the changes. The checks will re-run automatically. For future commits, |
Purpose
Add a production-ready Reward-based Velocity Matching (RVM) post-training
path for the released four-forward FastH3 VSA checkpoint.
The goal is to improve prompt adherence, motion quality, and visual preference
while preserving FastH3's deployed four-step sampler, sparse-attention policy,
and joint video/audio behavior. This PR follows the published RVM objective and
reward recipe rather than introducing a new diffusion-RL algorithm.
There is no linked issue.
Changes
Paper-faithful FastH3 RVM
1000, 750, 500, 250 -> 0;guidance_scale=1, conditioning dropout0);VIDEO_SPARSE_ATTN_H3at 90% sparsity;flow target
epsilon - x0.Kcandidates;collection, including all data-parallel replicas;
0.1scale and signed clipping.t ~ Uniform(0, 1). Deployment-grid training remains an explicit ablation;behavior generation always stays four-step.
yields the intended signed RVM gradient without exposing the optimizer to an
unbounded negative scalar loss.
H3 model and audio handling
native video/audio scheduler shifts (
12and3).to_q,to_k,to_v, andto_out; the released35B backbone and VSA compression gate remain frozen.
audio-only and full function-space anchor ablations.
second 35B reference model.
Reward stack and diagnostics
Implemented the public RVM video reward mixture:
Also added:
Data, evaluation, and experiment scripts
Qwen3-VL embedding preprocessing for the DanceGRPO/VidProM prompt bank.
checkpoint, LoRA export, and inference scripts.
ceil(0.05 * max_train_steps)optimizer updates on at most 100fixed held-out prompts and seeds.
held-out reward are reported separately.
HEADandHEAD^{tree}beforereported runs.
reward, audio, resume, and export checks.
Provider-independent execution
configs, hyperparameters, training orchestration, and result collection into
ordinary repository scripts.
12_run_portable_smoke.sh, which runs the same one-/four-GPU smoke onany Docker host or cloud VM.
modal_h3_rvm.pyto a test-only transport wrapper: it allocates one orfour GPUs, mounts volumes, checks out an exact Git ref, and invokes the
portable runner.
they do not depend on Modal. The Modal file may be removed before merge
without affecting the training path.
Test Plan
Static and unit checks
One-/four-GPU integration gates
The same integration test can be run without Modal:
Custom-node scale-up gates
A 16-GPU run uses the same scripts with
NUM_GPUS=16and must first pass atwo-update topology smoke.
Test Results
Completed GPU validation before the latest RVM-fidelity refactor
The 34-step run was a runtime/non-collapse result, not a quality claim. On eight
fixed validation prompts, aggregate reward changed from
1.51039to1.46332(
-0.04707), with HPSv3 and dynamic tracking improving while VideoAlign TA/MQdeclined slightly.
The current head additionally changes reward normalization to batch-global
standard deviation, changes regression-time sampling to continuous uniform,
adds motion-saturation diagnostics, and refactors Modal into a thin wrapper.
Those latest changes have static/unit coverage but still require a fresh
one-H100 smoke and the 8-H100 SP4xDP2 topology gate before the long campaign.
Checklist
pre-commit run --all-fileson the current PR head and fixed all issuesFor model/pipeline changes, also check:
post-trains an existing FastH3 checkpoint