Flow Matching#
本页内容6 个部分
查看概览图
从条件速度标签,
学到生成向量场
用线性条件路径讲清训练目标,也保留条件路径与边际 ODE 的区别。
线性条件路径
此处 x₀ 是噪声、x₁ 是数据。给定这一对端点,速度标签不随时间变。
回归边际速度场
不同端点配对在同一状态附近汇合,条件平均随位置和时间改变;生成 ODE 轨迹未必是直线。
-
1
选概率路径与端点耦合
线性插值只是常用选择 -
2
选时间采样与损失权重
uniform 或 sigmoid-normal 等 -
3
回归速度并解 ODE
求解器仍有离散化误差
Flow Matching 是更一般的向量场训练框架。少步质量由路径、训练误差和求解器共同决定;时间从噪声到数据还是反过来,是符号约定。
组件阅读路线 · 向量场 · ODE · Rectified Flow
1. 问题与向量场直觉#
1.1 Flow Matching 的动机#
从路径设计到向量场回归#
Flow Matching 是通过回归向量场学习概率流的框架,允许多种概率路径;本篇重点讨论噪声与数据的线性插值。它不要求模拟完整训练轨迹,因此训练可按时间点采样。
必须区分两个对象:
- 条件路径:给定一对 , 是匀速直线,速度为 。
- 边际生成流:网络只看到 ,MSE 最优速度是 。条件平均后的 ODE 轨迹通常不是直线。
只有当实际积分轨迹上的速度恒定时,Euler 一步才精确。线性插值训练不自动保证这一点;训练误差、配对、时间采样和求解器共同影响所需步数。标准 diffusion 同样可以使用 ODE 与少步求解器,不能把它固定等同于 1000 次推理。
1.2 向量场(Vector Field)和 ODE#
常微分方程(ODE)的形式#
Flow Matching 的数学核心是一个 ODE:
Flow ODE——向量场 决定了粒子在每个位置和时刻的运动方向与速度
其中 就是向量场:给定当前位置 和当前时刻 ,输出一个向量,告诉你"往哪个方向走、走多快"。
直觉类比:风场#
想象一个二维空间中的风场——每个位置都有一个风向和风速。如果你在某个位置松手放一个气球,它会随风飘。向量场 就是这个风场,而 ODE 描述了气球随时间的运动轨迹。
- :气球起始位置在噪声分布
- :气球最终到达数据分布
- 向量场 的目标:设计一个"风场",让从 中撒下的所有气球,在 时恰好落在真实数据的分布上。
时间方向注意!#
重要区别
本篇采用的时间约定:
• Diffusion: 是数据, 是噪声(加噪方向)
• Flow Matching: 是噪声, 是数据(生成方向)
不同论文和实现可以反过来定义时间,必须同时检查插值式、target 符号与积分方向;这不是两个方法的固有区别。
用 ODE Solver 生成数据#
生成数据的过程就是求解 ODE:从 出发,沿着向量场 积分到 。最简单的求解方法是 Euler 方法:
Euler 前向步进——每一步沿向量场方向走 的距离
例如把 分成 步,,依次计算 ,最终的 就是生成的样本。
1.3 从 Diffusion 到 Flow Matching:概念对比#
| 对比维度 | Diffusion(DDPM / SDE) | Flow Matching |
|---|---|---|
| 描述方式 | SDE(有随机项 )或 ODE | 这里采用确定性 ODE 采样 |
| 前向过程 | 逐步加噪,路径弯曲 | 直线插值 |
| 网络学的目标 | 噪声 或 score | 向量场 |
| 训练损失 | ||
| 时间方向 | 加噪, 去噪 | 生成(噪声→数据) |
| Noise schedule | 需要设计 | 线性插值固定一种路径;时间采样/重参数化仍需选择 |
| 采样方式 | SDE / ODE solver | 仅 ODE solver |
| 典型步数 | 随模型与 solver 改变 | 20-50 步 |
一句话总结
Diffusion 是"先搞乱再一步步清理"的过程;Flow Matching 是"直接画一条从噪声到数据的直线,让网络学会指路"的过程。
2. 条件路径与训练#
2.1 Conditional Flow Matching(CFM)的训练#
为什么需要 Conditional?#
如果我们想直接学整个边际向量场 ,需要知道 (所有时刻 的概率密度),这通常是不可解析的。
解决方案:Conditional Flow Matching——条件于一对 ,只在这一对数据上定义直线路径。可以证明,对所有 对求平均后,得到的梯度和直接训练边际向量场是一致的。
直线插值路径#
给定噪声 和数据 ,中间时刻 的状态通过线性插值得到:
直线插值—— 时为纯噪声 , 时为纯数据
条件向量场(Ground Truth)#
对上面的直线路径求时间导数:
条件向量场——一个常数!不随 变化,方向始终是从 指向
这就是 Flow Matching 训练简洁的根本原因:目标向量场是一个常数方向,就是"从噪声指向数据"。
训练损失函数#
CFM 损失——让网络 在每个 处预测正确的方向
和 Diffusion 训练目标对比#
对比
Diffusion:,网络学的是"加了什么噪声"
Flow Matching:,网络学的是"从噪声到数据的方向"
二者在形式上几乎一样——都是 MSE 回归!但回归的目标不同:一个是噪声 ,一个是位移 。
D 数字例子#
假设数据是 2D 点 ,采样噪声 ,随机时间 :
- 插值:
- 目标向量场:
- 网络输入 和 ,输出预测 ,目标是逼近
- 损失:
2.2 训练过程:伪代码与数字例子#
训练伪代码#
# Flow Matching (CFM) 训练
for step in range(num_steps):
x1 = sample_data_batch() # 1. 取真实数据
x0 = torch.randn_like(x1) # 2. 采样噪声 ~ N(0, I)
t = torch.rand(x1.shape[0], device=x1.device)
t_input = t
t = t.view(-1, *([1] * (x1.ndim - 1))) # 广播到非 batch 维
xt = (1 - t) * x0 + t * x1 # 4. 直线插值
v_target = x1 - x0 # 5. 目标向量场(常数方向)
v_pred = model(xt, t_input) # 6. 网络时间输入保留 [B]
loss = F.mse_loss(v_pred, v_target) # 7. MSE 损失
optimizer.zero_grad(set_to_none=True)
loss.backward() # 8. 反向传播
optimizer.step()
与 DDPM 训练并列对比#
| 步骤 | DDPM 训练 | Flow Matching 训练 |
|---|---|---|
| 1. 数据 | 取 (数据) | 取 (数据) |
| 2. 噪声 | ||
| 3. 时间 | (离散) | (连续) |
| 4. 加噪 / 插值 | ||
| 5. 目标 | (噪声) | (方向) |
| 6. 损失 |
关键简化
Flow Matching 训练只需一步线性插值得到 ,无需计算 、设计 noise schedule、或考虑离散时间步。整个流程更加简洁统一。
具体数字例子(1D)#
假设 (真实数据),(采样噪声),:
- 插值:
- 目标向量场:
- 网络输入 ,预测 (假设)
- 损失:
对比 DDPM(Day 4 的例子):同样的数据 ,DDPM 需要先查表算 ,然后做 ,多了一步查表和非线性变换。
3. 采样与 Rectified Flow#
3.1 采样过程(ODE 求解)#
采样 = 求解 ODE#
训练完成后,生成样本就是从 到 求解 ODE:
Euler 方法#
把 均匀分成 步,,迭代:
采样伪代码#
# Flow Matching 采样(Euler)
x = torch.randn(shape) # x_0 ~ N(0, I)
N = 25 # 步数(远少于 DDPM 的 1000)
dt = 1.0 / N
for i in range(N):
t = i * dt
v = model(x, t) # 网络预测向量场
x = x + dt * v # Euler 前进一步
# x 就是生成的样本(t=1 时的数据)
为什么步数可以更少?#
直线 vs 弯曲路径上的 Euler 误差
Euler 的局部截断误差与沿解轨迹的加速度有关,包括方向与速度大小的变化;仅看几何曲率并不充分。在匀速直线轨迹上,沿轨迹的 是常数,Euler 方法不管步长多大都没有截断误差。\
• Flow Matching:若学到的边际流更接近匀速直线,离散误差可能更小;条件直线不保证边际流的这一性质。 • Diffusion:有限步误差也取决于参数化与 solver,可采用专门的少步算法。
具体数字例子(1D,N=4 步)#
假设训练好的 完美学到了向量场,,目标 ,理论向量场 :
- 步 1:
- 步 2:
- 步 3:
- 步 4: ✓ 精确到达
即使只用 1 步:,同样精确!这就是直线路径的威力。
**面试要点:**如果面试官问"Flow Matching 为什么只需要 20 步就能生成高质量样本",关键答案是——路径接近直线,ODE solver 在直线上的截断误差接近零。
一步生成的极限#
如果路径完全是直线(每个噪声点和数据点之间没有交叉),理论上:
一步生成——,直接从 跳到
这就是 Consistency Model、一步蒸馏等工作的理论基础。
为什么实际上做不到一步?
训练时每对 各走一条直线,但推理时网络在 处看到的是多条路径的交叉点——同一个 可能来自"猫"、"狗"、"风景"等无数种 。网络只能预测这些路径的平均方向,而平均方向是弯曲的,不再是单条直线。
直觉上:
• 单对路径训练时:,常数,一步精确。
• 推理时:,是期望,路径隐式弯曲。
越靠近 (数据端),不同路径逐渐收敛、交叉减少,预测越准——这也是为什么 Flow Matching 在少步推理(8–20步)上远优于 DDPM,但还达不到真正一步。
真正的一步生成需要额外的蒸馏(Consistency Distillation)来直接学习 的映射,绕开 ODE 积分。
3.2 Rectified Flow(直线化)#
Optimal Transport 视角#
Flow Matching 虽然在每对 上走的是直线,但如果噪声和数据之间的配对不好(比如同一个噪声点被映射到了很远的数据点,而旁边的噪声点映射到了很近的数据点),整体路径会交叉。交叉的路径意味着向量场在某些区域需要指向不同方向——网络学起来困难,采样误差大。
最优传输(Optimal Transport)要求配对使得总运输距离最小——这会自然减少路径交叉。
Rectified Flow(Liu et al., 2023)#
Rectified Flow 通过**"reflow"**迭代让路径越来越直:
- 第一轮训练:随机配对 ,训练得到向量场
- 生成新配对:用 从噪声 生成对应的数据 ,得到更"合理"的 配对
- 第二轮训练:用 重新训练新的 ——由于配对更合理,路径交叉减少, 更接近直线
- 可重复多轮,路径越来越直
为什么路径越直越好?
直线路径上,向量场是常数 。Euler 方法在常数向量场上零截断误差——即使只走一步也能精确到达终点。
越直 → 可用步数越少 → 推理越快 → 极限就是一步生成。
实际应用#
SD3(Stable Diffusion 3)和 Flux 都采用了 Rectified Flow 作为训练框架。它们在实践中通常使用 20-50 步采样,已经能获得极高质量的生成结果。
Rectified Flow 可视化#
直观展示随机配对(路径交叉)vs Reflow 后(路径顺直)的区别
噪声点 x₀ ~ N(0,I)
数据点 x₁(两个簇)
当前位置 xₜ
时间 t = 0.00(拖动查看插值过程)
随机配对(路径交叉)#
噪声点和数据点随机匹配 → 路径大量交叉 → 向量场在交叉处方向矛盾 → 网络只能输出平均方向
Reflow 后(路径顺直)#
用训练好的模型重新配对 → 相近的噪声映射到相近的数据 → 路径几乎不交叉 → 可以更少步采样
▶ 播放动画
重新采样
####### 为什么交叉是问题
- 交叉点处 x_t 相同,但目标 x₁ 不同
- 网络输入相同,无法输出不同方向
- 只能输出期望方向 E[x₁-x₀ | x_t]
- 期望方向是弯曲的 → 需要更多步
####### Reflow 如何解决
- 用已训练模型跑推理得到新配对 (ε, x̂₁)
- 地理上相邻的点自然配对
- 路径不再交叉 → 向量场更一致
- 多轮 Reflow 后可蒸馏成一步生成
**面试要点:**Rectified Flow = 多轮"reflow"让路径更直 → Euler 误差更小 → 步数更少。SD3 和 Flux 的核心训练范式。
4. 比较、应用与理解检查#
4.1 Flow Matching vs Diffusion 深度对比#
数学关系#
Flow Matching 和 Diffusion 在理论上有深层联系:
Probability Flow ODE#
Diffusion 的 SDE 可以转化为一个等价的 ODE(Probability Flow ODE),这个 ODE 本质上就是一种 Flow。其向量场为:
Probability Flow ODE 的向量场—— 是漂移项, 是扩散系数, 是 score
连续时间极限下的等价性#
Diffusion 的 probability-flow ODE 是可用 FM 学习的一类流;只有选择兼容的概率路径、时间与状态参数化时才能建立具体对应。任意线性 FM 和任意 VP diffusion 并不自动描述同一个概率流。
向量场 v 和 score 的关系#
数学对应
对于 VP-SDE(DDPM 对应的连续时间版本),向量场和 score function 之间的关系为:
其中 是漂移项, 是扩散系数。
简单来说,向量场 = 漂移 - 半个扩散系数的平方 × score。
4.2 实际差异#
| 维度 | Diffusion | Flow Matching |
|---|---|---|
| 训练复杂度 | 需要设计 noise schedule | 无需,路径由线性插值决定 |
| 路径形状 | 弯曲(非线性加噪) | 直线(线性插值) |
| 收敛速度 | 较慢 | 较快(直线目标更简单) |
| 架构适配 | U-Net 为主 | 更适合 Transformer(DiT) |
| 采样步数 | 50-1000 | 20-50 |
4.3 趋势判断#
行业趋势
2024 年以后的新生成模型几乎全部采用 Flow Matching 框架:
• SD3(Stability AI)— Rectified Flow + MMDiT
• Flux(Black Forest Labs)— Rectified Flow + 双流 Transformer
• Sora(OpenAI,推测)— Flow Matching + DiT
• Lumina、Open-Sora 等开源项目也纷纷转向 Flow Matching
5. Flow Matching 在 SD3 / Flux 中的应用#
5.1 SD3 的训练框架#
Stable Diffusion 3 的核心创新之一就是将训练范式从 DDPM 切换到 Rectified Flow:
- 前向过程:线性插值 ,不再使用 和
- 训练目标:预测向量场
- 架构:MMDiT(Multimodal DiT),文本和图像 token 在同一个 Transformer 中处理
5.2 logit-normal 时间采样#
SD3 并没有使用均匀分布 ,而是使用 logit-normal 分布来采样训练时间 :
例如 时,采样较集中在中间;改变参数会改变这一性质。不是对 logit-normal 样本再套一次 sigmoid。
直觉理由: 接近 0(纯噪声)或 接近 1(纯数据)时,任务"太简单"——端点的回归难度依赖参数化,不能仅凭“纯噪声/纯数据”判断难易。中间时刻才是最需要学习的困难区域。
logit-normal 分布 PDF 图像#
拖动参数查看不同 μ、σ 下的形状,与均匀分布 U(0,1) 对比
μ = 0.0
σ = 1.0
logit-normal(μ, σ)
均匀分布 U(0,1)
SD3 实际用法 μ=0, σ=1
v-prediction 与 Flow Matching 的关系#
早在 Diffusion 时代,研究者已经发现 v-prediction 比 -prediction 在某些情况下效果更好。Flow Matching 的向量场 本质上就是一种 v-prediction 的推广——它天然地编码了从噪声到数据的方向信息。
SD3 / Flux 的推理配置#
| 配置 | SD 1.x / 2.x | SD3 | Flux |
|---|---|---|---|
| 训练范式 | DDPM (-pred) | Rectified Flow (-pred) | Rectified Flow (-pred) |
| Backbone | U-Net | MMDiT | 双流 Transformer |
| 采样器 | DDIM / DPM-Solver++ | Euler (Flow) | Euler (Flow) |
| 典型步数 | 20-50 | 28 | 20-50 |
| CFG scale | 7.5 | 4-7 | 3.5(Guidance Distill) |
**面试要点:**SD3 从三个方面创新:(1) Rectified Flow 训练 (2) MMDiT 架构 (3) logit-normal 时间采样。Flux 进一步优化为双流 Transformer + Guidance Distillation。
4.2 理解检查(8 道)#
Q1 Flow Matching 和 Diffusion 的核心区别是什么?#
参考回答
核心区别在三个方面:
路径形状:Diffusion 通过逐步加噪走一条弯曲路径,Flow Matching 通过线性插值 走直线路径。
训练目标:Diffusion 学预测噪声 ,Flow Matching 学预测向量场 (从噪声到数据的方向)。
少步质量是模型与采样器的实验结果,不能仅由条件路径线性推导;应在相同模型质量、NFE 和计算预算下比较。
Diffusion 的 probability-flow ODE 是可用 FM 学习的一类流;只有选择兼容的概率路径、时间与状态参数化时才能建立具体对应。任意线性 FM 和任意 VP diffusion 并不自动描述同一个概率流。
Q2 Flow Matching 的训练目标是什么?#
参考回答
Flow Matching(具体地说是 Conditional Flow Matching)的训练目标是:
其中 是噪声, 是数据, 是线性插值。网络 接收当前状态 和时间 ,输出预测方向;目标是逼近 ,即从噪声指向数据的常数方向。
这个目标的优美之处在于 ground truth 向量场是常数(不随 变化),所以比 Diffusion 的噪声预测更容易学习。
Q3 为什么 Flow Matching 只需要更少的采样步数?#
参考回答
根本原因是 Flow Matching 的路径接近直线。采样时使用 ODE solver(如 Euler 方法),Euler 方法的截断误差正比于路径的曲率。
在匀速直线轨迹上,Euler 可一步精确积分;一般学得的 FM 向量场不满足该条件,不能据此断言总比 diffusion 更直。
Rectified Flow 进一步通过 reflow 减少路径交叉,让路径更直、步数更少。典型步数:Flow Matching 20-50 步 vs DDPM 1000 步 vs DDIM 50 步。
Q4 Rectified Flow 是什么?为什么能加速?#
参考回答
Rectified Flow 是一种通过多轮迭代让传输路径更直的方法。流程:
(1) 随机配对 训练第一个模型
(2) 用 从原始噪声 生成 ,得到更合理的配对
(3) 用新配对训练 ,路径交叉减少、更直
(4) 重复直到路径足够直
能加速的原因是:路径越直 → 向量场越接近常数 → Euler 方法的截断误差越小 → 需要的步数越少。极限情况下路径完全是直线,一步就能完成生成。
SD3 和 Flux 都使用了 Rectified Flow,典型采样只需 20-30 步。
Q5 SD3 用了哪些技术?和 SD 1.x 的区别?#
参考回答
SD3 相对 SD 1.x 有三大关键升级:
(1) 训练范式:从 DDPM(-prediction + noise schedule)转为 Rectified Flow(-prediction + 线性插值),不再需要设计 。
(2) 骨干架构:从 U-Net 换成 MMDiT(Multimodal DiT),文本和图像 token 在同一个 Transformer 内通过联合 Attention 交互,比 Cross-Attention 更深度融合。
(3) 训练技巧:使用 logit-normal 时间采样(让训练更多关注中间难度的时间步),以及三个文本编码器(CLIP ×2 + T5-XXL)增强文本理解能力。
结果是生成质量、文本对齐度、推理效率全面提升。
Q6 向量场 和 score function 的关系?#
参考回答
对于 Diffusion 对应的 VP-SDE,向量场和 score function 有如下关系:
其中 是 SDE 的漂移项, 是扩散系数。
直觉理解:向量场 包含两部分信息——(1) 确定性的漂移方向 ,和 (2) 用 score function 修正的去噪方向。这说明 score-based model 和 flow-based model 本质上在学同一件事,只是参数化不同。
Q7 Flow Matching 的时间方向和 Diffusion 有什么不同?#
参考回答
Diffusion: 是干净数据, 是纯噪声。训练时是 (加噪),推理时是 (去噪)。
Flow Matching: 是纯噪声, 是干净数据。训练和推理的方向一致——都是 (从噪声到数据)。
Flow Matching 的时间方向更符合直觉(时间前进 = 数据生成),代码也更直观。但需要注意,不同论文的约定可能不同,阅读时要先确认时间定义。
Q8 如果面试官问"讲讲你对生成模型发展的理解",怎么组织回答?#
参考回答(3 分钟版本)
我会从四条主线来组织回答:
第一条线:生成范式——从 VAE 的隐变量建模,到 GAN 的对抗训练,再到 Diffusion 的迭代去噪,最后到 Flow Matching 的直线传输。核心趋势是生成质量越来越高、训练越来越稳定。
第二条线:骨干架构——从 CNN / U-Net 到 Transformer(DiT)。Transformer 的 scaling law 更好,参数量增大时效果持续提升,而且能和 NLP 的基础设施复用。
第三条线:效率优化——从像素空间到隐空间(Latent Diffusion),从 1000 步到 20 步(DDIM → Flow Matching → Rectified Flow → 蒸馏),甚至一步生成。
第四条线:模态扩展——从图像生成,到文本条件图像生成(CLIP + Cross-Attention),再到视频生成(时空 Attention + 3D VAE),目前正在向多模态统一(理解 + 生成)发展。
当前最前沿的系统(如 SD3、Flux、Sora)通常综合了所有主线的最新进展:Flow Matching + DiT + Latent Space + 多模态条件。
4.3 进阶讨论:Flow Matching vs DDPM 完整对比 + PyTorch 代码#
Flow Matching 和 DDPM 的六维对比(面试展开版)#
面试官问"flow matching 和 DDPM 有什么区别"时,需要从多个层次回答:
| 对比维度 | DDPM | Flow Matching | 为什么重要 |
|---|---|---|---|
| 1. 路径形状 | 弯曲(非线性加噪 ᾱt 衰减) | 直线(线性插值 (1−t)x0 + tx1) | 直线路径 → ODE solver 截断误差小 → 少步采样 |
| 2. 训练目标 | 预测噪声 ϵ | 预测速度场 v = x1 − x0 | v 是常数(不依赖 t),比 ϵ 更容易学 |
| 3. 时间定义 | t = 0 干净 → t = T 纯噪声 训练 0 → T,推理 T → 0 |
t = 0 纯噪声 → t = 1 干净 训练和推理方向一致 0 → 1 |
FM 方向更直觉,代码更简洁 |
| 4. 噪声调度 | 需要设计 βt schedule (linear / cosine / scaled linear) |
不需要,路径由线性插值决定 | 少一个超参数,训练更简单 |
| 5. 采样方式 | SDE(随机)或 ODE(DDIM,确定性) 50-1000 步 |
ODE(Euler 方法) 20-50 步 |
FM 天然是 ODE,不需要 DDIM 那样的"转换" |
| 6. 理论关系 | 选择兼容路径与参数化时可以建立对应;并非任意 FM 与 diffusion 的轨迹相同。 | 面试加分项:说明二者本质相同,差异在参数化 | |
面试题 Flow Matching 和 DDPM 有什么区别?(完整版回答)
Diffusion 的 probability-flow ODE 是可用 FM 学习的一类流;只有选择兼容的概率路径、时间与状态参数化时才能建立具体对应。任意线性 FM 和任意 VP diffusion 并不自动描述同一个概率流。
展开三个核心差异:
少步质量是模型与采样器的实验结果,不能仅由条件路径线性推导;应在相同模型质量、NFE 和计算预算下比较。
(2) 训练目标:DDPM 预测噪声 (与 无关但与 相关),FM 预测速度场 (常数,既不依赖 也不依赖中间状态)。常数目标更容易回归,收敛更快。
(3) 工程简化:FM 不需要设计 noise schedule(),不需要预计算 查表,时间用连续 而非离散 。整个训练代码少一半。
Diffusion 的 probability-flow ODE 是可用 FM 学习的一类流;只有选择兼容的概率路径、时间与状态参数化时才能建立具体对应。任意线性 FM 和任意 VP diffusion 并不自动描述同一个概率流。
PyTorch 代码:Flow Matching 的 velocity 计算#
面试官可能要求"写一下 flow matching 怎么求 velocity"。核心就三行,但要理解每一行的含义:
import torch
import torch.nn.functional as F
# ====== Flow Matching 核心:velocity 的定义和训练 ======
# 给定一对 (x0, x1),velocity 的定义极其简单:
# v = x1 - x0
# 就是从噪声指向数据的方向(常数向量,不依赖 t)
def compute_flow_matching_loss(model, x1, sigma_min=1e-5):
"""
Flow Matching 单步训练损失
Args:
model: 速度场网络 v_θ(x_t, t) → 预测速度
x1: 真实数据 [B, C, H, W](注意 FM 约定 x1 = 数据)
sigma_min: 最小噪声(避免 t=0 时完全是噪声)
Returns:
loss: MSE(v_pred, v_target)
"""
B = x1.shape[0]
# 1. 采样噪声 x0 ~ N(0, I)
x0 = torch.randn_like(x1)
# 2. 采样时间 t ~ U(0, 1)
t = torch.rand(B, device=x1.device)
# 3. 线性插值得到 x_t
# x_t = (1 - t) * x0 + t * x1
t_expand = t.view(B, 1, 1, 1) # [B,1,1,1] 方便广播
x_t = (1 - t_expand) * x0 + t_expand * x1
# 4. 目标 velocity = x1 - x0(常数!不依赖 t)
v_target = x1 - x0
# 5. 网络预测 velocity
v_pred = model(x_t, t) # [B, C, H, W]
# 6. MSE 损失
loss = F.mse_loss(v_pred, v_target)
return loss
# ====== 对比:DDPM 的同等操作 ======
def compute_ddpm_loss(model, x0, alpha_bar):
"""DDPM 训练损失(对比用)"""
B = x0.shape[0]
# 1. 采样噪声 ε ~ N(0, I)
noise = torch.randn_like(x0)
# 2. 采样离散时间步 t ~ {0, ..., T-1}
t = torch.randint(0, len(alpha_bar), (B,), device=x0.device)
# 3. 查表计算加噪(比 FM 复杂!)
a = alpha_bar[t].view(B, 1, 1, 1).sqrt()
b = (1 - alpha_bar[t]).view(B, 1, 1, 1).sqrt()
x_t = a * x0 + b * noise # 非线性加噪
# 4. 目标 = 噪声 ε(而非 velocity)
noise_pred = model(x_t, t)
# 5. MSE 损失
loss = F.mse_loss(noise_pred, noise)
return loss
代码层面的关键差异
对比两个函数就能看出 Flow Matching 的简洁性:
- 没有查表:FM 不需要预计算 ,没有
alpha_bar[t]这种操作 - 连续时间:
torch.rand(连续)vstorch.randint(离散) - 插值更简单:
(1-t)*x0 + t*x1vssqrt(ᾱ)*x0 + sqrt(1-ᾱ)*ε - 目标更直观:
v = x1 - x0(方向)vsε(噪声本身)
PyTorch 代码:Flow Matching 的完整采样过程#
@torch.no_grad()
def flow_matching_sample(model, shape, num_steps=50, device='cuda'):
"""
Flow Matching 采样(Euler ODE solver)
从 t=0(噪声)到 t=1(数据)
"""
# 初始化:纯噪声
x = torch.randn(shape, device=device) # x_0 ~ N(0, I)
dt = 1.0 / num_steps
for i in range(num_steps):
t_curr = torch.full((shape[0],), i * dt, device=device)
# 网络预测当前位置的速度场
v = model(x, t_curr)
# Euler 前进一步: x_{t+dt} = x_t + dt * v(x_t, t)
x = x + dt * v
return x # t=1 时的样本 ≈ 真实数据
# ====== 对比:用 DDPM 生成同等样本 ======
@torch.no_grad()
def ddpm_sample(model, shape, T=1000, betas=None, device='cuda'):
"""DDPM 采样:从 t=T(噪声)到 t=0(数据),每步要算很多系数"""
alphas = 1.0 - betas
alpha_bar = torch.cumprod(alphas, 0).to(device)
x = torch.randn(shape, device=device) # x_T ~ N(0, I)
for t in reversed(range(T)): # T-1, T-2, ..., 0
t_batch = torch.full((shape[0],), t, device=device, dtype=torch.long)
eps_pred = model(x, t_batch)
# 复杂的系数计算
alpha_t = alphas[t]
alpha_bar_t = alpha_bar[t]
beta_t = betas[t]
# 预测均值
mean = (1 / alpha_t.sqrt()) * (
x - (beta_t / (1 - alpha_bar_t).sqrt()) * eps_pred
)
# 加随机噪声(最后一步不加)
if t > 0:
noise = torch.randn_like(x)
x = mean + beta_t.sqrt() * noise
else:
x = mean
return x # 1000 步才到达!
**面试对比要点:**FM 采样就是 for 循环 + 一行 Euler 更新(x = x + dt * v),不需要任何系数查表。DDPM 每步要算 ,还要分情况加噪声。代码量差 3×,步数差 20×。
PyTorch 代码:Rectified Flow 的 velocity 训练(SD3 风格)#
SD3 / Flux 实际使用的是 Rectified Flow + logit-normal 时间采样:
def compute_rectified_flow_loss(model, x1, text_embeds):
"""SD3 风格的 Rectified Flow 训练"""
B = x1.shape[0]
# 1. 噪声
x0 = torch.randn_like(x1)
# 2. logit-normal 时间采样(SD3 的关键技巧)
# 让 t 集中在 0.5 附近——中间难度最值得学
u = torch.randn(B, device=x1.device) # ~ N(0,1)
t = torch.sigmoid(u) # → logit-normal
# 3. 线性插值
t_expand = t.view(B, 1, 1, 1)
x_t = (1 - t_expand) * x0 + t_expand * x1
# 4. velocity 目标(还是 x1 - x0,和基础 FM 一样)
v_target = x1 - x0
# 5. 条件速度场预测(MMDiT,输入包含文本条件)
v_pred = model(x_t, t, text_embeds)
# 6. MSE 损失
loss = F.mse_loss(v_pred, v_target)
return loss
原始资料:Flow Matching、Rectified Flow、SD3。Reflow 改善 transport coupling 不等于无条件求得全局最优传输;理论结论需保留原文假设。
延伸阅读#
- 采样器与 ODE Solver:生成时怎样在求解误差和模型调用成本之间取舍?
- 扩散与视频模型训练实践:生成模型训练中哪些目标、采样和显存设置需要一起检查?