生成模型 / 扩散与 Flow Matching 原理笔记进阶 22 分钟阅读 查看源文件 ↗

Flow Matching#

本页内容6 个部分
查看概览图
FOUNDATIONS · 06

从条件速度标签,
学到生成向量场

用线性条件路径讲清训练目标,也保留条件路径与边际 ODE 的区别。

PAIR给定一对端点

线性条件路径

路径x_t = (1−t)x₀ + tx₁速度标签u_t = x₁ − x₀

此处 x₀ 是噪声、x₁ 是数据。给定这一对端点,速度标签不随时间变。

条件平均 标签 → 速度场
FIELD模型看到 x_t 与 t

回归边际速度场

平方误差最优解v*(x,t) = E[x₁−x₀ | x_t=x]

不同端点配对在同一状态附近汇合,条件平均随位置和时间改变;生成 ODE 轨迹未必是直线。

  1. 1
    选概率路径与端点耦合 线性插值只是常用选择
  2. 2
    选时间采样与损失权重 uniform 或 sigmoid-normal 等
  3. 3
    回归速度并解 ODE 求解器仍有离散化误差
别混淆
条件直线 ≠ 生成轨迹必然直

Flow Matching 是更一般的向量场训练框架。少步质量由路径、训练误差和求解器共同决定;时间从噪声到数据还是反过来,是符号约定。

条件路径边际速度场数值积分仍有误差

组件阅读路线 · 向量场 · ODE · Rectified Flow

1. 问题与向量场直觉#

1.1 Flow Matching 的动机#

从路径设计到向量场回归#

Flow Matching 是通过回归向量场学习概率流的框架,允许多种概率路径;本篇重点讨论噪声与数据的线性插值。它不要求模拟完整训练轨迹,因此训练可按时间点采样。

必须区分两个对象:

  • 条件路径:给定一对 (x0,x1)(x_0,x_1),xt=(1−t)x0+tx1x_t=(1-t)x_0+tx_1 是匀速直线,速度为 x1−x0x_1-x_0。
  • 边际生成流:网络只看到 (x,t)(x,t),MSE 最优速度是 v∗(x,t)=E[x1−x0∣xt=x]v^*(x,t)=\mathbb E[x_1-x_0\mid x_t=x]。条件平均后的 ODE 轨迹通常不是直线。

只有当实际积分轨迹上的速度恒定时,Euler 一步才精确。线性插值训练不自动保证这一点;训练误差、配对、时间采样和求解器共同影响所需步数。标准 diffusion 同样可以使用 ODE 与少步求解器,不能把它固定等同于 1000 次推理。

1.2 向量场(Vector Field)和 ODE#

常微分方程(ODE)的形式#

Flow Matching 的数学核心是一个 ODE:

dxdt=vθ(x,t)\frac{dx}{dt} = v_\theta(x, t)

Flow ODE——向量场 vθv_\theta 决定了粒子在每个位置和时刻的运动方向与速度

其中 vθ(x,t)v_\theta(x, t) 就是向量场:给定当前位置 xx 和当前时刻 tt,输出一个向量,告诉你"往哪个方向走、走多快"。

直觉类比:风场#

想象一个二维空间中的风场——每个位置都有一个风向和风速。如果你在某个位置松手放一个气球,它会随风飘。向量场 vθv_\theta 就是这个风场,而 ODE 描述了气球随时间的运动轨迹。

  • t=0t = 0:气球起始位置在噪声分布 p0=N(0,I)p_0 = \mathcal{N}(0, I)
  • t=1t = 1:气球最终到达数据分布 p1=pdatap_1 = p_{\text{data}}
  • 向量场 vθv_\theta 的目标:设计一个"风场",让从 N(0,I)\mathcal{N}(0,I) 中撒下的所有气球,在 t=1t=1 时恰好落在真实数据的分布上。

时间方向注意!#

重要区别

本篇采用的时间约定:
• Diffusion:t=0t=0 是数据,t=Tt=T 是噪声(加噪方向)
• Flow Matching:t=0t=0 是噪声,t=1t=1 是数据(生成方向)
不同论文和实现可以反过来定义时间,必须同时检查插值式、target 符号与积分方向;这不是两个方法的固有区别。

用 ODE Solver 生成数据#

生成数据的过程就是求解 ODE:从 x0∼N(0,I)x_0 \sim \mathcal{N}(0, I) 出发,沿着向量场 vθv_\theta 积分到 t=1t=1。最简单的求解方法是 Euler 方法:

xt+Δt=xt+Δt⋅vθ(xt,t)x_{t+\Delta t} = x_t + \Delta t \cdot v_\theta(x_t, t)

Euler 前向步进——每一步沿向量场方向走 Δt\Delta t 的距离

例如把 [0,1][0,1] 分成 NN 步,Δt=1/N\Delta t = 1/N,依次计算 x1/N,x2/N,…,x1x_{1/N}, x_{2/N}, \dots, x_1,最终的 x1x_1 就是生成的样本。

1.3 从 Diffusion 到 Flow Matching:概念对比#

对比维度 Diffusion(DDPM / SDE) Flow Matching
描述方式 SDE(有随机项 dwdw)或 ODE 这里采用确定性 ODE 采样
前向过程 逐步加噪,路径弯曲 直线插值 xt=(1−t)x0+tx1x_t = (1-t)x_0 + t x_1
网络学的目标 噪声 ϵ\epsilon 或 score ∇xlog⁡pt\nabla_x \log p_t 向量场 v(x,t)v(x, t)
训练损失 ∣ϵθ−ϵ∣2|\epsilon_\theta - \epsilon|^2 ∣vθ−(x1−x0)∣2|v_\theta - (x_1 - x_0)|^2
时间方向 t:0→Tt: 0 \to T 加噪,T→0T \to 0 去噪 t:0→1t: 0 \to 1 生成(噪声→数据)
Noise schedule 需要设计 βt\beta_t 线性插值固定一种路径;时间采样/重参数化仍需选择
采样方式 SDE / ODE solver 仅 ODE solver
典型步数 随模型与 solver 改变 20-50 步

一句话总结

Diffusion 是"先搞乱再一步步清理"的过程;Flow Matching 是"直接画一条从噪声到数据的直线,让网络学会指路"的过程。

2. 条件路径与训练#

2.1 Conditional Flow Matching(CFM)的训练#

为什么需要 Conditional?#

如果我们想直接学整个边际向量场 v(x,t)v(x, t),需要知道 pt(x)p_t(x)(所有时刻 tt 的概率密度),这通常是不可解析的。

解决方案:Conditional Flow Matching——条件于一对 (x0,x1)(x_0, x_1),只在这一对数据上定义直线路径。可以证明,对所有 (x0,x1)(x_0, x_1) 对求平均后,得到的梯度和直接训练边际向量场是一致的。

直线插值路径#

给定噪声 x0∼N(0,I)x_0 \sim \mathcal{N}(0, I) 和数据 x1∼pdatax_1 \sim p_{\text{data}},中间时刻 t∈[0,1]t \in [0, 1] 的状态通过线性插值得到:

xt=(1−t)⋅x0+t⋅x1x_t = (1 - t) \cdot x_0 + t \cdot x_1

直线插值——t=0t=0 时为纯噪声 x0x_0,t=1t=1 时为纯数据 x1x_1

条件向量场(Ground Truth)#

对上面的直线路径求时间导数:

v(xt,t∣x0,x1)=dxtdt=x1−x0v(x_t, t \mid x_0, x_1) = \frac{dx_t}{dt} = x_1 - x_0

条件向量场——一个常数!不随 tt 变化,方向始终是从 x0x_0 指向 x1x_1

这就是 Flow Matching 训练简洁的根本原因:目标向量场是一个常数方向,就是"从噪声指向数据"。

训练损失函数#

L=Et∼U(0,1),  x0∼N(0,I),  x1∼pdata[∥vθ(xt,t)−(x1−x0)∥2]\mathcal{L} = \mathbb{E}_{t \sim U(0,1),\; x_0 \sim \mathcal{N}(0,I),\; x_1 \sim p_{\text{data}}} \left[\|v_\theta(x_t, t) - (x_1 - x_0)\|^2\right]

CFM 损失——让网络 vθv_\theta 在每个 (xt,t)(x_t, t) 处预测正确的方向 x1−x0x_1 - x_0

和 Diffusion 训练目标对比#

对比

Diffusion:L=∥ϵθ(xt,t)−ϵ∥2\mathcal{L} = \|\epsilon_\theta(x_t, t) - \epsilon\|^2,网络学的是"加了什么噪声"
Flow Matching:L=∥vθ(xt,t)−(x1−x0)∥2\mathcal{L} = \|v_\theta(x_t, t) - (x_1 - x_0)\|^2,网络学的是"从噪声到数据的方向"

二者在形式上几乎一样——都是 MSE 回归!但回归的目标不同:一个是噪声 ϵ\epsilon,一个是位移 x1−x0x_1 - x_0。

D 数字例子#

假设数据是 2D 点 x1=(3.0,  4.0)x_1 = (3.0,\; 4.0),采样噪声 x0=(−1.0,  0.5)x_0 = (-1.0,\; 0.5),随机时间 t=0.3t = 0.3:

  • 插值:xt=(1−0.3)⋅(−1.0,  0.5)+0.3⋅(3.0,  4.0)=0.7⋅(−1,  0.5)+0.3⋅(3,  4)=(−0.7+0.9,  0.35+1.2)=(0.2,  1.55)x_t = (1 - 0.3) \cdot (-1.0,\; 0.5) + 0.3 \cdot (3.0,\; 4.0) = 0.7 \cdot (-1,\; 0.5) + 0.3 \cdot (3,\; 4) = (-0.7 + 0.9,\; 0.35 + 1.2) = (0.2,\; 1.55)
  • 目标向量场:v=x1−x0=(3−(−1),  4−0.5)=(4.0,  3.5)v = x_1 - x_0 = (3 - (-1),\; 4 - 0.5) = (4.0,\; 3.5)
  • 网络输入 xt=(0.2,1.55)x_t = (0.2, 1.55) 和 t=0.3t = 0.3,输出预测 vθv_\theta,目标是逼近 (4.0,3.5)(4.0, 3.5)
  • 损失:L=∥vθ−(4.0,3.5)∥2L = \|v_\theta - (4.0, 3.5)\|^2

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. 数据 取 x0x_0(数据) 取 x1x_1(数据)
2. 噪声 ϵ∼N(0,I)\epsilon \sim \mathcal{N}(0,I) x0∼N(0,I)x_0 \sim \mathcal{N}(0,I)
3. 时间 t∼{1,…,T}t \sim \{1, \dots, T\}(离散) t∼U(0,1)t \sim U(0,1)(连续)
4. 加噪 / 插值 xt=αˉt x0+1−αˉt ϵx_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1-\bar\alpha_t}\, \epsilon xt=(1−t) x0+t x1x_t = (1-t)\, x_0 + t\, x_1
5. 目标 ϵ\epsilon(噪声) x1−x0x_1 - x_0(方向)
6. 损失 ∣ϵθ(xt,t)−ϵ∣2|\epsilon_\theta(x_t, t) - \epsilon|^2 ∣vθ(xt,t)−(x1−x0)∣2|v_\theta(x_t, t) - (x_1 - x_0)|^2

关键简化

Flow Matching 训练只需一步线性插值得到 xtx_t,无需计算 αˉt\bar\alpha_t、设计 noise schedule、或考虑离散时间步。整个流程更加简洁统一。

具体数字例子(1D)#

假设 x1=5.0x_1 = 5.0(真实数据),x0=−1.2x_0 = -1.2(采样噪声),t=0.6t = 0.6:

  • 插值:xt=(1−0.6)×(−1.2)+0.6×5.0=0.4×(−1.2)+0.6×5.0=−0.48+3.0=2.52x_t = (1-0.6) \times (-1.2) + 0.6 \times 5.0 = 0.4 \times (-1.2) + 0.6 \times 5.0 = -0.48 + 3.0 = 2.52
  • 目标向量场:v=x1−x0=5.0−(−1.2)=6.2v = x_1 - x_0 = 5.0 - (-1.2) = 6.2
  • 网络输入 (xt,t)=(2.52,0.6)(x_t, t) = (2.52, 0.6),预测 vθ=5.8v_\theta = 5.8(假设)
  • 损失:(5.8−6.2)2=0.16(5.8 - 6.2)^2 = 0.16

对比 DDPM(Day 4 的例子):同样的数据 x0=5.0x_0 = 5.0,DDPM 需要先查表算 αˉt\bar\alpha_t,然后做 xt=αˉt x0+1−αˉt ϵx_t = \sqrt{\bar\alpha_t}\, x_0 + \sqrt{1-\bar\alpha_t}\, \epsilon,多了一步查表和非线性变换。

3. 采样与 Rectified Flow#

3.1 采样过程(ODE 求解)#

采样 = 求解 ODE#

训练完成后,生成样本就是从 t=0t=0 到 t=1t=1 求解 ODE:

dxdt=vθ(x,t),x0∼N(0,I)\frac{dx}{dt} = v_\theta(x, t), \quad x_0 \sim \mathcal{N}(0, I)

Euler 方法#

把 [0,1][0, 1] 均匀分成 NN 步,Δt=1/N\Delta t = 1/N,迭代:

xt+Δt=xt+Δt⋅vθ(xt,t)x_{t + \Delta t} = x_t + \Delta t \cdot v_\theta(x_t, t)

采样伪代码#

# 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 的局部截断误差与沿解轨迹的加速度有关,包括方向与速度大小的变化;仅看几何曲率并不充分。在匀速直线轨迹上,沿轨迹的 vθv_\theta 是常数,Euler 方法不管步长多大都没有截断误差。\

• Flow Matching:若学到的边际流更接近匀速直线,离散误差可能更小;条件直线不保证边际流的这一性质。 • Diffusion:有限步误差也取决于参数化与 solver,可采用专门的少步算法。

具体数字例子(1D,N=4 步)#

假设训练好的 vθv_\theta 完美学到了向量场,x0=−2.0x_0 = -2.0,目标 x1=6.0x_1 = 6.0,理论向量场 v=8.0v = 8.0:

  • Δt=1/4=0.25\Delta t = 1/4 = 0.25
  • 步 1:x0.25=−2.0+0.25×8.0=0.0x_{0.25} = -2.0 + 0.25 \times 8.0 = 0.0
  • 步 2:x0.50=0.0+0.25×8.0=2.0x_{0.50} = 0.0 + 0.25 \times 8.0 = 2.0
  • 步 3:x0.75=2.0+0.25×8.0=4.0x_{0.75} = 2.0 + 0.25 \times 8.0 = 4.0
  • 步 4:x1.0=4.0+0.25×8.0=6.0x_{1.0} = 4.0 + 0.25 \times 8.0 = 6.0 ✓ 精确到达

即使只用 1 步:x1=−2.0+1.0×8.0=6.0x_1 = -2.0 + 1.0 \times 8.0 = 6.0,同样精确!这就是直线路径的威力。

**面试要点:**如果面试官问"Flow Matching 为什么只需要 20 步就能生成高质量样本",关键答案是——路径接近直线,ODE solver 在直线上的截断误差接近零。

一步生成的极限#

如果路径完全是直线(每个噪声点和数据点之间没有交叉),理论上:

x1=x0+1⋅vθ(x0,0)x_1 = x_0 + 1 \cdot v_\theta(x_0, 0)

一步生成——Δt=1\Delta t = 1,直接从 t=0t=0 跳到 t=1t=1

这就是 Consistency Model、一步蒸馏等工作的理论基础。

为什么实际上做不到一步?

训练时每对 (x0,x1)(x_0, x_1) 各走一条直线,但推理时网络在 xtx_t 处看到的是多条路径的交叉点——同一个 xtx_t 可能来自"猫"、"狗"、"风景"等无数种 x1x_1。网络只能预测这些路径的平均方向,而平均方向是弯曲的,不再是单条直线。

直觉上:
  • 单对路径训练时:v=x1−x0v = x_1 - x_0,常数,一步精确。
  • 推理时:vθ(xt,t)=E[x1−x0∣xt]v_\theta(x_t, t) = \mathbb{E}[x_1 - x_0 \mid x_t],是期望,路径隐式弯曲。

越靠近 t=1t=1(数据端),不同路径逐渐收敛、交叉减少,预测越准——这也是为什么 Flow Matching 在少步推理(8–20步)上远优于 DDPM,但还达不到真正一步。

真正的一步生成需要额外的蒸馏(Consistency Distillation)来直接学习 x0→x1x_0 \to x_1 的映射,绕开 ODE 积分。

3.2 Rectified Flow(直线化)#

Optimal Transport 视角#

Flow Matching 虽然在每对 (x0,x1)(x_0, x_1) 上走的是直线,但如果噪声和数据之间的配对不好(比如同一个噪声点被映射到了很远的数据点,而旁边的噪声点映射到了很近的数据点),整体路径会交叉。交叉的路径意味着向量场在某些区域需要指向不同方向——网络学起来困难,采样误差大。

最优传输(Optimal Transport)要求配对使得总运输距离最小——这会自然减少路径交叉。

Rectified Flow(Liu et al., 2023)#

Rectified Flow 通过**"reflow"**迭代让路径越来越直:

  1. 第一轮训练:随机配对 (x0,x1)(x_0, x_1),训练得到向量场 v1v_1
  2. 生成新配对:用 v1v_1 从噪声 x0x_0 生成对应的数据 x^1=ODE(x0,v1)\hat{x}_1 = \text{ODE}(x_0, v_1),得到更"合理"的 (x0,x^1)(x_0, \hat{x}_1) 配对
  3. 第二轮训练:用 (x0,x^1)(x_0, \hat{x}_1) 重新训练新的 v2v_2——由于配对更合理,路径交叉减少,v2v_2 更接近直线
  4. 可重复多轮,路径越来越直

为什么路径越直越好?

直线路径上,向量场是常数 v=x1−x0v = x_1 - x_0。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。其向量场为:

vPF-ODE(x,t)=f(x,t)−12g(t)2∇xlog⁡pt(x)v_{\text{PF-ODE}}(x, t) = f(x, t) - \frac{1}{2}g(t)^2 \nabla_x \log p_t(x)

Probability Flow ODE 的向量场——ff 是漂移项,gg 是扩散系数,∇xlog⁡pt\nabla_x \log p_t 是 score

连续时间极限下的等价性#

Diffusion 的 probability-flow ODE 是可用 FM 学习的一类流;只有选择兼容的概率路径、时间与状态参数化时才能建立具体对应。任意线性 FM 和任意 VP diffusion 并不自动描述同一个概率流。

向量场 v 和 score 的关系#

数学对应

对于 VP-SDE(DDPM 对应的连续时间版本),向量场和 score function 之间的关系为:

v(x,t)=f(x,t)−12g(t)2∇xlog⁡pt(x)v(x, t) = f(x, t) - \frac{1}{2}g(t)^2 \nabla_x \log p_t(x)

其中 f(x,t)=−12β(t)xf(x,t) = -\frac{1}{2}\beta(t)x 是漂移项,g(t)=β(t)g(t) = \sqrt{\beta(t)} 是扩散系数。

简单来说,向量场 = 漂移 - 半个扩散系数的平方 × score。

4.2 实际差异#

维度 Diffusion Flow Matching
训练复杂度 需要设计 noise schedule βt\beta_t 无需,路径由线性插值决定
路径形状 弯曲(非线性加噪) 直线(线性插值)
收敛速度 较慢 较快(直线目标更简单)
架构适配 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:

  • 前向过程:线性插值 xt=(1−t)ϵ+tx1x_t = (1-t) \epsilon + t x_1,不再使用 αˉt\bar\alpha_t 和 βt\beta_t
  • 训练目标:预测向量场 v=x1−ϵv = x_1 - \epsilon
  • 架构:MMDiT(Multimodal DiT),文本和图像 token 在同一个 Transformer 中处理

5.2 logit-normal 时间采样#

SD3 并没有使用均匀分布 t∼U(0,1)t \sim U(0, 1),而是使用 logit-normal 分布来采样训练时间 tt:

z∼N(μ,σz2),t=sigmoid⁡(z)=11+e−z.z\sim\mathcal N(\mu,\sigma_z^2),\qquad t=\operatorname{sigmoid}(z)=\frac{1}{1+e^{-z}}.

例如 μ=0,σz=1\mu=0,\sigma_z=1 时,采样较集中在中间;改变参数会改变这一性质。不是对 logit-normal 样本再套一次 sigmoid。

直觉理由:tt 接近 0(纯噪声)或 tt 接近 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 比 ϵ\epsilon-prediction 在某些情况下效果更好。Flow Matching 的向量场 v=x1−x0v = x_1 - x_0 本质上就是一种 v-prediction 的推广——它天然地编码了从噪声到数据的方向信息。

SD3 / Flux 的推理配置#

配置 SD 1.x / 2.x SD3 Flux
训练范式 DDPM (ϵ\epsilon-pred) Rectified Flow (vv-pred) Rectified Flow (vv-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 通过线性插值 xt=(1−t)x0+tx1x_t = (1-t)x_0 + tx_1 走直线路径。

训练目标:Diffusion 学预测噪声 ϵ\epsilon,Flow Matching 学预测向量场 v=x1−x0v = x_1 - x_0(从噪声到数据的方向)。

少步质量是模型与采样器的实验结果,不能仅由条件路径线性推导;应在相同模型质量、NFE 和计算预算下比较。

Diffusion 的 probability-flow ODE 是可用 FM 学习的一类流;只有选择兼容的概率路径、时间与状态参数化时才能建立具体对应。任意线性 FM 和任意 VP diffusion 并不自动描述同一个概率流。

Q2 Flow Matching 的训练目标是什么?#

参考回答

Flow Matching(具体地说是 Conditional Flow Matching)的训练目标是:

L=Et,x0,x1[∥vθ(xt,t)−(x1−x0)∥2]\mathcal{L} = \mathbb{E}_{t,x_0,x_1} \left[\|v_\theta(x_t, t) - (x_1 - x_0)\|^2\right]

其中 x0∼N(0,I)x_0 \sim \mathcal{N}(0,I) 是噪声,x1∼pdatax_1 \sim p_{\text{data}} 是数据,xt=(1−t)x0+tx1x_t = (1-t)x_0 + tx_1 是线性插值。网络 vθv_\theta 接收当前状态 xtx_t 和时间 tt,输出预测方向;目标是逼近 x1−x0x_1 - x_0,即从噪声指向数据的常数方向。

这个目标的优美之处在于 ground truth 向量场是常数(不随 tt 变化),所以比 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) 随机配对 (x0,x1)(x_0, x_1) 训练第一个模型 v1v_1
(2) 用 v1v_1 从原始噪声 x0x_0 生成 x^1\hat{x}_1,得到更合理的配对
(3) 用新配对训练 v2v_2,路径交叉减少、更直
(4) 重复直到路径足够直

能加速的原因是:路径越直 → 向量场越接近常数 → Euler 方法的截断误差越小 → 需要的步数越少。极限情况下路径完全是直线,一步就能完成生成。

SD3 和 Flux 都使用了 Rectified Flow,典型采样只需 20-30 步。

Q5 SD3 用了哪些技术?和 SD 1.x 的区别?#

参考回答

SD3 相对 SD 1.x 有三大关键升级:

(1) 训练范式:从 DDPM(ϵ\epsilon-prediction + noise schedule)转为 Rectified Flow(vv-prediction + 线性插值),不再需要设计 βt\beta_t。

(2) 骨干架构:从 U-Net 换成 MMDiT(Multimodal DiT),文本和图像 token 在同一个 Transformer 内通过联合 Attention 交互,比 Cross-Attention 更深度融合。

(3) 训练技巧:使用 logit-normal 时间采样(让训练更多关注中间难度的时间步),以及三个文本编码器(CLIP ×2 + T5-XXL)增强文本理解能力。

结果是生成质量、文本对齐度、推理效率全面提升。

Q6 向量场 vv 和 score function 的关系?#

参考回答

对于 Diffusion 对应的 VP-SDE,向量场和 score function 有如下关系:

v(x,t)=f(x,t)−12g(t)2∇xlog⁡pt(x)v(x, t) = f(x, t) - \frac{1}{2}g(t)^2 \nabla_x \log p_t(x)

其中 f(x,t)=−12β(t)xf(x, t) = -\frac{1}{2}\beta(t)x 是 SDE 的漂移项,g(t)=β(t)g(t) = \sqrt{\beta(t)} 是扩散系数。

直觉理解:向量场 vv 包含两部分信息——(1) 确定性的漂移方向 ff,和 (2) 用 score function 修正的去噪方向。这说明 score-based model 和 flow-based model 本质上在学同一件事,只是参数化不同。

Q7 Flow Matching 的时间方向和 Diffusion 有什么不同?#

参考回答

Diffusion:t=0t=0 是干净数据,t=Tt=T 是纯噪声。训练时是 0→T0 \to T(加噪),推理时是 T→0T \to 0(去噪)。

Flow Matching:t=0t=0 是纯噪声,t=1t=1 是干净数据。训练和推理的方向一致——都是 0→10 \to 1(从噪声到数据)。

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 预测噪声 ϵ\epsilon(与 tt 无关但与 x0x_0 相关),FM 预测速度场 v=x1−x0v = x_1 - x_0(常数,既不依赖 tt 也不依赖中间状态)。常数目标更容易回归,收敛更快。

(3) 工程简化:FM 不需要设计 noise schedule(βt\beta_t),不需要预计算 αˉt\bar\alpha_t 查表,时间用连续 t∈[0,1]t \in [0,1] 而非离散 t∈{1,...,1000}t \in \{1,...,1000\}。整个训练代码少一半。

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 不需要预计算 αˉt\bar\alpha_t,没有 alpha_bar[t] 这种操作
  • 连续时间:torch.rand(连续)vs torch.randint(离散)
  • 插值更简单:(1-t)*x0 + t*x1 vs sqrt(ᾱ)*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 每步要算 αt,αˉt,βt\alpha_t, \bar\alpha_t, \beta_t,还要分情况加噪声。代码量差 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 不等于无条件求得全局最优传输;理论结论需保留原文假设。

延伸阅读#

相关阅读路线

理解生成模型 ↗