<!-- Copyright 2026 The AnyFlow Team, NVIDIA Corp., and The HuggingFace Team. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
the License for the specific language governing permissions and limitations under the License.
-->

# AnyFlow

[AnyFlow](https://huggingface.co/papers/2605.13724) 是一个视频扩散**蒸馏**框架，把预训练的 Wan2.1 教师
模型蒸馏成在标准 Euler 采样下支持*任意步数 (any-step)* 的学生模型。同一个蒸馏出来的 checkpoint 可以
在 1、2、4、8、16... NFE 下推理，**质量随步数单调提升** —— 这一点和 consistency models 不同，后者
NFE 增加反而经常掉点。

核心思路是学习 **flow map** $\Phi_{r\leftarrow t}: \mathbf{z}_t \to \mathbf{z}_r$（任意 $1 \ge t \ge r \ge 0$），
而不是 consistency models 学的固定端点映射 $\mathbf{z}_t \to \mathbf{z}_0$。Flow map 的可组合性消除了
采样步之间的 re-noising；on-policy 蒸馏阶段额外用 **DMD 反向散度监督** + **Flow-Map backward simulation**
（3 段 shortcut）补上 consistency 蒸馏遗留的 exposure-bias 缺口。

AnyFlow 由 NVIDIA、新加坡国立大学（NUS）和 MIT 合作完成，作者为 Yuchao Gu、Guian Fang、Yuxin Jiang、Weijia Mao、Song Han、Han Cai、Mike Zheng Shou。原始训练代码在 [`NVlabs/AnyFlow`](https://github.com/NVlabs/AnyFlow)，项目主页是 [nvlabs.github.io/AnyFlow](https://nvlabs.github.io/AnyFlow)，4 个发布 checkpoint 归在 [`nvidia/anyflow`](https://huggingface.co/collections/nvidia/anyflow) Hugging Face collection 里。

本文档梳理实战要点：怎么选 pipeline、怎么用 any-step 采样、怎么把 AnyFlow 嵌进 T2V / I2V / V2V 工作流。

## Bidirectional 还是 Causal —— 怎么选 pipeline

AnyFlow 提供两个 pipeline 形态，scheduler 和蒸馏方法相同，区别在于**怎么对帧采样**：

- [`AnyFlowPipeline`](../api/pipelines/anyflow#anyflowpipeline) —— **bidirectional** T2V。一次性对整个
  视频张量去噪，全局自注意力。**纯 prompt 输入、不要流式输出**时选这个。
- [`AnyFlowFARPipeline`](../api/pipelines/anyflow#anyflowfarpipeline) —— **causal (FAR)**。
  按 chunk 分段去噪，块稀疏因果注意力 + 跨 chunk 复用 KV cache。**图生视频 (I2V)**、**视频续写 (V2V)**、
  或任何受益于逐帧自回归采样的场景选这个。同一个模型通过 `video`（像素空间）或 `video_latents`
  （已编码 latent）这两个互斥 kwarg 来切换三种任务模式。

简化对照表：

| 场景 | Pipeline | 调用方式 |
|------|----------|----------|
| 纯文生视频，固定 NFE 求最大质量 | `AnyFlowPipeline` | `pipe(prompt, ...)` |
| 图生视频（首帧给定） | `AnyFlowFARPipeline` | `pipe(prompt, video=<单帧 tensor>, ...)` |
| 视频续写 / V2V | `AnyFlowFARPipeline` | `pipe(prompt, video=<多帧 tensor>, ...)` |
| 流式 / 渐进式生成 | `AnyFlowFARPipeline` | — |

高分辨率下 bidirectional 单 token 更快；causal 牺牲一点单步速度，换来在所有 latent 帧分配前就能开始
采样的能力，对超长序列尤其有用。

## 加载 checkpoint

NVIDIA 发布了 4 个 AnyFlow checkpoint，pipeline × 规模各一份：

```py
import torch
from diffusers import AnyFlowPipeline, AnyFlowFARPipeline

# Bidirectional, 轻量
pipe = AnyFlowPipeline.from_pretrained(
    "nvidia/AnyFlow-Wan2.1-T2V-1.3B-Diffusers", dtype=torch.bfloat16
).to("cuda")

# Bidirectional, 满血
pipe = AnyFlowPipeline.from_pretrained(
    "nvidia/AnyFlow-Wan2.1-T2V-14B-Diffusers", dtype=torch.bfloat16
).to("cuda")

# Causal (FAR), 1.3B
pipe = AnyFlowFARPipeline.from_pretrained(
    "nvidia/AnyFlow-FAR-Wan2.1-1.3B-Diffusers", dtype=torch.bfloat16
).to("cuda")

# Causal (FAR), 14B
pipe = AnyFlowFARPipeline.from_pretrained(
    "nvidia/AnyFlow-FAR-Wan2.1-14B-Diffusers", dtype=torch.bfloat16
).to("cuda")
```

四个 checkpoint 共用同一份 [`FlowMapEulerDiscreteScheduler`](../api/schedulers/flow_map_euler_discrete)，
默认 `shift=5.0`。

## Any-step 采样

AnyFlow 最关键的特性是同一个 checkpoint **不需重新调度**，NFE 越大质量越高。固定 prompt、扫一下步数
就能看出模型怎么在延迟和保真度之间权衡：

```py
import torch
from diffusers import AnyFlowPipeline
from diffusers.utils import export_to_video

pipe = AnyFlowPipeline.from_pretrained(
    "nvidia/AnyFlow-Wan2.1-T2V-1.3B-Diffusers", dtype=torch.bfloat16
).to("cuda")

prompt = "森林里一只小熊猫在啃竹子，电影感光照"

for nfe in [1, 2, 4, 8, 16, 32]:
    # 每轮重建 generator —— 这样跨步数对比时唯一变量是 NFE。
    generator = torch.Generator("cuda").manual_seed(0)
    video = pipe(prompt, num_inference_steps=nfe, num_frames=81, generator=generator).frames[0]
    export_to_video(video, f"out_nfe{nfe}.mp4", fps=16)
```

paper 的 Tab 3 / Fig 1 表明：每个 AnyFlow checkpoint 在 4 → 32 NFE 范围 VBench Quality 都单调上升，而
consistency 类基线（rCM、Self-Forcing）在同区间反而掉点。

> [!TIP]
> Classifier-free guidance (CFG) 已经在训练阶段融进权重。pipeline 推理
> 时**不会**再跑一次 unconditional 前向 —— guidance 直接由蒸馏后的权重带出。release 出来的 checkpoint
> 都用默认的 `guidance_scale=1.0` 即可。

## 图生视频 与 视频续写

Causal pipeline 用同一个蒸馏模型支持三种任务模式，**通过 `video` / `video_latents` 二选一来选**：

- `video` —— 像素空间张量，形状 `(B, T, C, H, W)` ∈ `[0, 1]`，pipeline 内部会过一遍 `VideoProcessor`
  + VAE 编码；
- `video_latents` —— 已经在模型布局下的 latent，跳过 VAE 编码；
- 两者都不传 —— 纯文生视频；
- 两者同时传 —— 抛 `ValueError`（互斥）。

Context tensor 的帧数必须满足 `T = 4n + 1`，跟 VAE 时间步长对齐。

> [!IMPORTANT]
> FAR pipeline 是分块 (chunk) rollout，`num_frames` 必须配合 chunk 调度。发布的 checkpoint 在
> transformer config 里写入 `chunk_partition=[1, 3, 3, 3, 3, 3, 3, 2]`（求和 21），对应标准
> `num_frames=81`（21 = (81 − 1) // 4 + 1）。改 `num_frames` 时**必须**显式传匹配的 `chunk_partition`，
> 使其求和等于 `(num_frames - 1) // 4 + 1`，否则 pipeline 会抛 `ValueError`。比如 `num_frames=33` 对应
> 9 个 latent 帧，可用 `chunk_partition=[1, 4, 4]`。

```py
import numpy as np
import torch
from diffusers import AnyFlowFARPipeline
from diffusers.utils import export_to_video, load_image, load_video

pipe = AnyFlowFARPipeline.from_pretrained(
    "nvidia/AnyFlow-FAR-Wan2.1-1.3B-Diffusers", dtype=torch.bfloat16
).to("cuda")


def to_video_tensor(images, height=480, width=832):
    """把 PIL 列表转成 FAR pipeline 需要的 (B, T, C, H, W) [0, 1] 张量。"""
    frames = np.stack([np.asarray(img.resize((width, height))) for img in images]).astype("float32") / 255.0
    # frames: (T, H, W, C) → (T, C, H, W) → 加 batch 维 → (1, T, C, H, W)
    return torch.from_numpy(frames).permute(0, 3, 1, 2).unsqueeze(0)


# 1) 文生视频（无 context）。81 帧匹配默认 chunk_partition。
video = pipe(prompt="一只猫在夕阳下冲浪", num_inference_steps=4, num_frames=81).frames[0]
export_to_video(video, "t2v.mp4", fps=16)

# 2) 图生视频 —— 单帧 context 经过 VAE 是 1 个 latent，正好对上默认 chunk_partition 的第一项 (`[1, ...]`)。
first_frame = load_image("path/to/first_frame.png")
context_tensor = to_video_tensor([first_frame]).to("cuda")  # (1, 1, 3, 480, 832), [0, 1]
video = pipe(
    prompt="一只猫走过阳光下的草坪",
    video=context_tensor,
    num_inference_steps=4,
    num_frames=81,
).frames[0]
export_to_video(video, "i2v.mp4", fps=16)

# 3) 视频续写。9 帧 raw context → 3 个 latent context；显式覆盖 chunk_partition，让第一块正好覆盖 context。
context_frames = load_video("path/to/context.mp4")[:9]  # 9 = 4·2 + 1
context_tensor = to_video_tensor(context_frames).to("cuda")  # (1, 9, 3, 480, 832)
video = pipe(
    prompt="继续这个故事",
    video=context_tensor,
    num_inference_steps=4,
    num_frames=81,
    chunk_partition=[3, 3, 3, 3, 3, 3, 3],  # 7 个 chunk × 3 = 21 latent；首块就是 context
).frames[0]
export_to_video(video, "v2v.mp4", fps=16)
```

底层 patchify chunk 调度根据 `video` / `video_latents` 是否给定自动调整：纯文生用 kernel 2 (full) 和
4 (compressed)；有 context 时第一个 chunk 改成 kernel 1，让条件帧保留全分辨率。

如果你已经有 VAE 编码过的 latent，可以直接传 `video_latents=<tensor>` 跳过 `vae_encode` 步骤
（和 `video` 互斥）。

## LoRA 微调

两个 pipeline 都复用 [`WanLoraLoaderMixin`](../api/loaders/lora)，因此为对应 Wan2.1 backbone 训练的
LoRA adapter 直接加载即可：

```py
pipe.load_lora_weights("path/or/repo/with/wan_lora")
```

如果要做**继续 on-policy 蒸馏微调**（用论文里相同的 DMD 反向散度监督配方训新 LoRA），请参考原始
AnyFlow 训练框架 [`NVlabs/AnyFlow`](https://github.com/NVlabs/AnyFlow)，这套训练流程不在
diffusers 范围内。

## 常见坑

- **永远 `guidance_scale=1.0`。** 蒸馏后的 checkpoint 已经把 CFG 融进权重。设 `> 1` 会多跑一遍
  unconditional 前向、延迟翻倍、质量微降。
- **Bidirectional pipeline 不支持流式。** 所有 `num_frames` 一起去噪。需要边采边播请用 causal pipeline。
- **Causal pipeline KV cache 假设 chunk 调度跨调用一致。** 中途重建 cache 不被 release 模型支持。
- **`num_frames` 必须满足 VAE 时间步长。** release checkpoint 用 `(N - 1) % 4 == 0` 的值（如 9、17、33、81）。

## 引用

```bibtex
@misc{gu2026anyflowanystepvideodiffusion,
  title={AnyFlow: Any-Step Video Diffusion Model with On-Policy Flow Map Distillation},
  author={Yuchao Gu and Guian Fang and Yuxin Jiang and Weijia Mao and Song Han and Han Cai and Mike Zheng Shou},
  year={2026},
  eprint={2605.13724},
  archivePrefix={arXiv},
  primaryClass={cs.CV},
  url={https://arxiv.org/abs/2605.13724},
}

@article{gu2025long,
  title={Long-Context Autoregressive Video Modeling with Next-Frame Prediction},
  author={Gu, Yuchao and Mao, Weijia and Shou, Mike Zheng},
  journal={arXiv preprint arXiv:2503.19325},
  year={2025}
}
```
