Buckets:

HuggingFaceDocBuilder's picture
|
download
raw
10.2 kB
# 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", torch_dtype=torch.bfloat16
).to("cuda")
# Bidirectional, 满血
pipe = AnyFlowPipeline.from_pretrained(
"nvidia/AnyFlow-Wan2.1-T2V-14B-Diffusers", torch_dtype=torch.bfloat16
).to("cuda")
# Causal (FAR), 1.3B
pipe = AnyFlowFARPipeline.from_pretrained(
"nvidia/AnyFlow-FAR-Wan2.1-1.3B-Diffusers", torch_dtype=torch.bfloat16
).to("cuda")
# Causal (FAR), 14B
pipe = AnyFlowFARPipeline.from_pretrained(
"nvidia/AnyFlow-FAR-Wan2.1-14B-Diffusers", torch_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", torch_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", torch_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}
}
```

Xet Storage Details

Size:
10.2 kB
·
Xet hash:
99de32cb8c5629aa89c2a77ec1901da6fd012041dede6b5b1c599146e113d576

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.