Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
8363327
[feat] Add r_embedder config flags to Wan (defaults preserve bit-iden…
Enderfga May 19, 2026
0e04210
[feat] Extend WanTimeTextImageEmbedding with optional r_timestep
Enderfga May 19, 2026
fba7170
[feat] Thread optional r_timestep through WanTransformer3DModel.forward
Enderfga May 19, 2026
aa5ce59
[feat] Add FlowMapEulerDiscreteScheduler
Enderfga May 19, 2026
ce23c5b
[feat] AnyFlowPretrainMethod skeleton + (t, r) per-batch sampling
Enderfga May 19, 2026
a8b99d2
[test] AnyFlow pretrain: cover (t, r) sampling distribution + validation
Enderfga May 19, 2026
f6d78fa
[feat] AnyFlow pretrain: central-difference target + full single_trai…
Enderfga May 19, 2026
314a018
[feat] AnyFlowMethod (on-policy DMD2 with multi-step Euler-flow rollout)
Enderfga May 19, 2026
e8516c2
[feat] AnyFlow YAML configs (pretrain + on-policy) for Wan 2.1 T2V 1.3B
Enderfga May 19, 2026
fd98371
[test] AnyFlow GPU smoke (pretrain + on-policy, 2 iters each)
Enderfga May 19, 2026
ef1bf25
[docs] AnyFlow algorithm + usage guide
Enderfga May 19, 2026
eed1a45
[test] AnyFlow embedder tests: initialize ReplicatedLinear weights fo…
Enderfga May 19, 2026
acef1a9
[verify] AnyFlow↔FastVideo numerical parity script
Enderfga May 19, 2026
bbb1f97
[demo] AnyFlow 14B T2V inference demo (NFE=4 + NFE=50)
Enderfga May 19, 2026
b9e231f
[misc] Drive AnyFlow helper script paths from env vars + ruff SIM108
Enderfga May 22, 2026
5cfcba0
[misc] Apply yapf/ruff pre-commit autofixes after rebase (main moved …
SolitaryThinker Jul 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions docs/distillation/anyflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# 🌊 AnyFlow Any-Step Video Distillation

**AnyFlow** ([paper](https://arxiv.org/abs/2605.13724), [project page](https://nvlabs.github.io/AnyFlow/), [official code](https://github.com/NVlabs/AnyFlow), [model weights](https://huggingface.co/collections/nvidia/anyflow)) is an any-step video diffusion framework built on flow maps. A single distilled checkpoint can be evaluated at NFE ∈ {1, 2, 4, 8, 16, 32} without retraining, and quality scales **monotonically** with steps — unlike consistency-based distillation, which often degrades as NFE grows.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The link to the AnyFlow paper appears to have a typo. The year-month part 2605 is likely incorrect for a 2024 paper. The correct link should probably point to arxiv.org/abs/2405.13724.

Suggested change
**AnyFlow** ([paper](https://arxiv.org/abs/2605.13724), [project page](https://nvlabs.github.io/AnyFlow/), [official code](https://github.com/NVlabs/AnyFlow), [model weights](https://huggingface.co/collections/nvidia/anyflow)) is an any-step video diffusion framework built on flow maps. A single distilled checkpoint can be evaluated at NFE ∈ {1, 2, 4, 8, 16, 32} without retraining, and quality scales **monotonically** with steps — unlike consistency-based distillation, which often degrades as NFE grows.
**AnyFlow** ([paper](https://arxiv.org/abs/2405.13724), [project page](https://nvlabs.github.io/AnyFlow/), [official code](https://github.com/NVlabs/AnyFlow), [model weights](https://huggingface.co/collections/nvidia/anyflow)) is an any-step video diffusion framework built on flow maps. A single distilled checkpoint can be evaluated at NFE ∈ {1, 2, 4, 8, 16, 32} without retraining, and quality scales **monotonically** with steps — unlike consistency-based distillation, which often degrades as NFE grows.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The arxiv ID is correct as written. AnyFlow was posted to arxiv this month (2026-05), so the prefix is 2605, not 2405. https://arxiv.org/abs/2605.13724 resolves to the right paper; https://arxiv.org/abs/2405.13724 is a different (2024) submission that this suggestion has been pattern-matched to.


The student network ``u_θ(x_t, t, r)`` predicts the *average velocity* from time ``t`` back to time ``r``, so one Euler step is

```
x_r = x_t - ((t - r) / N) · u_θ(x_t, t, r)
```

for any ``t > r``.

## 📊 Model Overview

NVIDIA publishes four checkpoints under [`nvidia/anyflow`](https://huggingface.co/collections/nvidia/anyflow):

- `nvidia/AnyFlow-Wan2.1-T2V-1.3B-Diffusers` — bidirectional T2V, Wan2.1 1.3B base
- `nvidia/AnyFlow-Wan2.1-T2V-14B-Diffusers` — bidirectional T2V, Wan2.1 14B base
- `nvidia/AnyFlow-FAR-Wan2.1-1.3B-Diffusers` — frame-autoregressive variant, 1.3B
- `nvidia/AnyFlow-FAR-Wan2.1-14B-Diffusers` — frame-autoregressive variant, 14B

FastVideo currently supports the bidirectional T2V variants for training; the FAR variants can be loaded for inference through the diffusers integration.

## ⚙️ Inference

For inference, load the published checkpoint directly through diffusers; FastVideo's training-side ``WanModel`` config maps the HF AnyFlow ``delta_embedder`` weights onto its internal layout via ``param_names_mapping`` so the same checkpoint can be used as the ``init_from`` for the on-policy YAML below.

## 🧠 Algorithm

Training runs in two stages. Both use the dual-timestep Wan backbone — enabled by ``pipeline.dit_config.r_embedder: true`` in the YAML, which allocates a sibling ``condition_embedder.delta_embedder`` and fuses its embedding with the standard timestep embedding via either an additive or a gated mixer.

### Stage 1 — Pretrain (flow-map central-difference)

Method: ``AnyFlowPretrainMethod`` (``fastvideo/train/methods/distribution_matching/anyflow_pretrain.py``)

For each batch, sample ``(t, r) ∈ [0, 1]`` as ``(max, min)`` of two uniform draws, then:

- a ``diffusion_ratio`` fraction (default 0.5) gets ``r = t`` — recovers plain flow matching;
- a ``consistency_ratio`` fraction (default 0.25) gets ``r = 0`` — forces consistency to clean data;
- the remainder is free.

The student forward at ``(t, r)`` is trained against the central-difference target

```
target = (eps - x_0) - (t - r) · dF/dt
```

where ``dF/dt`` is estimated from the student's own forward at ``(t ± δ, r)`` with the sample also moved along the flow trajectory by ``v_pred · (δ / N)``. Per-timestep weighting uses ``beta08`` (``w(t) = t · sqrt(1 - t)``, renormalized). A stop-gradient scale-balance keeps the non-diffusion branches' loss magnitude aligned with the diffusion branch.

### Stage 2 — On-policy DMD

Method: ``AnyFlowMethod`` (``fastvideo/train/methods/distribution_matching/anyflow.py``)

Inherits ``DMD2Method``. The student is rolled out for ``student_sample_steps`` Euler-flow steps from pure noise; one randomly-chosen step is gradient-enabled (broadcast from rank 0 so every worker agrees), the rest run under ``torch.no_grad``. With ``use_mean_velocity: true`` (default) the rollout uses ``r = t_next`` at each step, matching AnyFlow's ``WanAnyFlowPipeline.training_rollout``.

The inherited ``_dmd_loss`` (VSD with fake-score critic) consumes the rollout output and the teacher's CFG prediction. The optional pinned ``t_list_override`` lets configs reproduce the paper's hand-tuned 4-step schedule ``[999, 937, 833, 624, 0]``.

## 🚀 Training Scripts

### Stage 1 — pretrain

```bash
bash examples/train/run.sh \
examples/train/configs/distribution_matching/wan/anyflow_pretrain_t2v.yaml
```

**Key configuration** (in ``examples/train/configs/distribution_matching/wan/anyflow_pretrain_t2v.yaml``):

- Global batch size: 32 (8 GPUs × 4 per-GPU)
- Learning rate: 5e-5
- Flow shift: 5.0
- ``diffusion_ratio`` / ``consistency_ratio``: 0.5 / 0.25
- ``epsilon`` (finite-difference step): 5 (absolute train-timestep units)
- ``weight_type``: ``beta08``
- ``fuse_guidance_scale``: 3.0
- Training steps: 6000

### Stage 2 — on-policy

```bash
bash examples/train/run.sh \
examples/train/configs/distribution_matching/wan/anyflow_onpolicy_t2v.yaml \
--models.student.init_from outputs/wan2.1_anyflow_pretrain/checkpoint-final
```

(Or point ``models.student.init_from`` directly at ``nvidia/AnyFlow-Wan2.1-T2V-1.3B-Diffusers`` to bootstrap from the paper weights and skip Stage 1.)

**Key configuration**:

- Global batch size: 8 (8 GPUs × 1 per-GPU)
- Learning rate: 2e-6
- Flow shift: 5.0
- ``student_sample_steps``: 4
- ``t_list_override``: ``[999, 937, 833, 624, 0]``
- ``use_mean_velocity``: ``true`` (i.e. ``r = t_next`` during rollout)
- ``real_score_guidance_scale``: 3.0
- ``generator_update_interval``: 5 (DMD2 alternation)
- Training steps: 4000

## 🔌 Loading published AnyFlow checkpoints

The HF AnyFlow checkpoints expose ``condition_embedder.delta_embedder.*`` weights that FastVideo internally maps onto its ``condition_embedder.delta_embedder.mlp.*`` layout. This rename happens automatically through the regex in ``WanVideoArchConfig.param_names_mapping`` — no separate adapter is needed. The same regex is a no-op on plain Wan checkpoints (which don't contain any ``delta_embedder`` keys).

Set the YAML's ``pipeline.dit_config.r_embedder: true`` to allocate the ``delta_embedder`` module on the FastVideo side; when initializing from a plain Wan checkpoint the delta weights are deep-copied from ``time_embedder`` (matching AnyFlow's ``setup_flowmap_model()`` behavior).

## 🧭 Note on ``fuse_guidance_scale``

Stage 1 optionally fuses classifier-free guidance into the training target so the resulting checkpoint can be sampled at ``guidance_scale=1.0`` (no extra forward pass at inference time). The transformation is

```
noise_pred ← (noise_pred - (1 - g) · noise_pred_uncond) / g
```

with ``g = fuse_guidance_scale``. The negative prompt embedding comes from ``WanModel``'s ``ensure_negative_conditioning()`` — i.e. the dataset's configured ``sampling_param.negative_prompt``. Setting ``fuse_guidance_scale: 1.0`` skips the extra unconditional forward entirely.

The on-policy stage's ``real_score_guidance_scale`` (inherited from DMD2) follows the same parameterization conventions documented in [``dmd.md``](dmd.md#-note-on-real_score_guidance_scale).
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# AnyFlow on-policy DMD — Wan 2.1 T2V 1.3B.
#
# Stage 2 of the AnyFlow two-stage recipe. Continues from the pretrain
# checkpoint; refines the student via DMD2 with a multi-step Euler-flow
# rollout from pure noise. Teacher provides the real score, critic
# learns the fake score; both inherited from DMD2Method.
#
# Replace <PATH_TO_PRETRAIN_CKPT> with the output of the pretrain stage,
# or with the NVIDIA-released checkpoint
# nvidia/AnyFlow-Wan2.1-T2V-1.3B-Diffusers to bootstrap directly from
# the paper weights (the delta_embedder rename is handled by the
# param_names_mapping in WanVideoArchConfig).

models:
student:
_target_: fastvideo.train.models.wan.WanModel
init_from: <PATH_TO_PRETRAIN_CKPT>
trainable: true
teacher:
_target_: fastvideo.train.models.wan.WanModel
init_from: Wan-AI/Wan2.1-T2V-14B-Diffusers
trainable: false
disable_custom_init_weights: true
critic:
_target_: fastvideo.train.models.wan.WanModel
init_from: Wan-AI/Wan2.1-T2V-1.3B-Diffusers
trainable: true
disable_custom_init_weights: true

method:
_target_: fastvideo.train.methods.distribution_matching.anyflow.AnyFlowMethod
rollout_mode: simulate
generator_update_interval: 5
real_score_guidance_scale: 3.0
dmd_denoising_steps: [999, 937, 833, 624]
warp_denoising_step: false

# AnyFlow rollout knobs.
student_sample_steps: 4
use_mean_velocity: true
t_list_override: [999.0, 937.0, 833.0, 624.0, 0.0]
dmd_score_r_value: 0.0 # DMD scoring conditioning is at r=0 (consistency target).

# Critic optimizer (DMD2 inherited).
fake_score_learning_rate: 8.0e-6
fake_score_betas: [0.0, 0.999]
fake_score_lr_scheduler: constant

attn_kind: vsa

training:
distributed:
num_gpus: 8
sp_size: 1
tp_size: 1
hsdp_replicate_dim: 1
hsdp_shard_dim: 8

data:
data_path: data/preprocessed
dataloader_num_workers: 4
train_batch_size: 1
training_cfg_rate: 0.0
seed: 1000
num_latent_t: 21
num_height: 480
num_width: 832
num_frames: 81

optimizer:
learning_rate: 2.0e-6
betas: [0.0, 0.999]
weight_decay: 0.01
lr_scheduler: constant
lr_warmup_steps: 0

loop:
max_train_steps: 4000
gradient_accumulation_steps: 1

checkpoint:
output_dir: outputs/wan2.1_anyflow_onpolicy
training_state_checkpointing_steps: 500
checkpoints_total_limit: 3
resume_from_checkpoint: latest

tracker:
project_name: anyflow-wan
run_name: wan2.1_t2v_anyflow_onpolicy

model:
enable_gradient_checkpointing_type: full

callbacks:
grad_clip:
_target_: fastvideo.train.callbacks.grad_clip.GradNormClipCallback
max_grad_norm: 1.0

pipeline:
flow_shift: 5.0
dit_config:
r_embedder: true
r_embedder_fusion: gated
r_embedder_gate_value: 0.25
r_embedder_deltatime_type: r
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# AnyFlow pretrain (flow-map central-difference) — Wan 2.1 T2V 1.3B.
#
# Stage 1 of the AnyFlow two-stage recipe. Trains the dual-timestep
# u_θ(x_t, t, r) on the central-difference target so the same checkpoint
# can be sampled at arbitrary NFE in the on-policy stage.
#
# Initialize from base Wan 2.1 T2V 1.3B. No teacher or critic at this
# stage; AnyFlowPretrainMethod owns a single student + one optimizer.

models:
student:
_target_: fastvideo.train.models.wan.WanModel
init_from: Wan-AI/Wan2.1-T2V-1.3B-Diffusers
trainable: true

method:
_target_: fastvideo.train.methods.distribution_matching.anyflow_pretrain.AnyFlowPretrainMethod
diffusion_ratio: 0.5
consistency_ratio: 0.25
epsilon: 5 # finite-difference step in absolute train-timestep units
weight_type: beta08 # per-timestep loss weight = t * sqrt(1 - t), renormalized
fuse_guidance_scale: 3.0
# shift is taken from pipeline.flow_shift below.

training:
distributed:
num_gpus: 8
sp_size: 1
tp_size: 1
hsdp_replicate_dim: 1
hsdp_shard_dim: 8

data:
data_path: data/preprocessed
dataloader_num_workers: 4
train_batch_size: 4
training_cfg_rate: 0.0
seed: 1000
num_latent_t: 21
num_height: 480
num_width: 832
num_frames: 81

optimizer:
learning_rate: 5.0e-5
betas: [0.9, 0.999]
weight_decay: 0.0
lr_scheduler: constant
lr_warmup_steps: 0

loop:
max_train_steps: 6000
gradient_accumulation_steps: 1

checkpoint:
output_dir: outputs/wan2.1_anyflow_pretrain
training_state_checkpointing_steps: 500
checkpoints_total_limit: 3
resume_from_checkpoint: latest

tracker:
project_name: anyflow-wan
run_name: wan2.1_t2v_anyflow_pretrain

model:
enable_gradient_checkpointing_type: full

callbacks:
grad_clip:
_target_: fastvideo.train.callbacks.grad_clip.GradNormClipCallback
max_grad_norm: 1.0

pipeline:
flow_shift: 5.0
dit_config:
# Enable AnyFlow dual-timestep conditioning. The student loads from
# base Wan 2.1 — its checkpoint has no delta_embedder weights, so they
# get initialized identically to time_embedder via deep-copy in
# WanTimeTextImageEmbedding.__init__.
r_embedder: true
r_embedder_fusion: gated
r_embedder_gate_value: 0.25
r_embedder_deltatime_type: r
14 changes: 14 additions & 0 deletions fastvideo/configs/models/dits/wanvideo.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
from dataclasses import dataclass, field
from typing import Literal

from fastvideo.configs.models.dits.base import DiTArchConfig, DiTConfig

Expand All @@ -19,6 +20,11 @@ class WanVideoArchConfig(DiTArchConfig):
r"^condition_embedder\.text_embedder\.linear_2\.(.*)$": r"condition_embedder.text_embedder.fc_out.\1",
r"^condition_embedder\.time_embedder\.linear_1\.(.*)$": r"condition_embedder.time_embedder.mlp.fc_in.\1",
r"^condition_embedder\.time_embedder\.linear_2\.(.*)$": r"condition_embedder.time_embedder.mlp.fc_out.\1",
# AnyFlow dual-timestep checkpoints expose delta_embedder weights with the
# same internal layout as time_embedder. The regex is harmless on plain
# Wan checkpoints (no delta_embedder keys to match).
r"^condition_embedder\.delta_embedder\.linear_1\.(.*)$": r"condition_embedder.delta_embedder.mlp.fc_in.\1",
r"^condition_embedder\.delta_embedder\.linear_2\.(.*)$": r"condition_embedder.delta_embedder.mlp.fc_out.\1",
r"^condition_embedder\.time_proj\.(.*)$": r"condition_embedder.time_modulation.linear.\1",
r"^condition_embedder\.image_embedder\.ff\.net\.0\.proj\.(.*)$":
r"condition_embedder.image_embedder.ff.fc_in.\1",
Expand Down Expand Up @@ -86,6 +92,14 @@ class WanVideoArchConfig(DiTArchConfig):
# "relativistic" keeps long rollouts in-distribution; a no-op unless sink_size > 0 and local_attn_size > 0.
rope_cache_policy: str = "absolute"

# AnyFlow dual-timestep conditioning. Defaults preserve bit-identity with
# the legacy single-timestep forward (no delta_embedder allocated, no
# extra computation on the embedder forward path).
r_embedder: bool = False
r_embedder_fusion: Literal["additive", "gated"] = "additive"
r_embedder_gate_value: float = 0.25
r_embedder_deltatime_type: Literal["r", "t-r"] = "r"

def __post_init__(self):
super().__post_init__()
self.out_channels = self.out_channels or self.in_channels
Expand Down
Loading
Loading