阶段 4 · 学会创造

流匹配:"学一条直线"
和"走一条直线"
是两回事

上一章《潜空间扩散与 DiT》把扩散搬进潜空间,可路径还是弯的,跑几十到上千步。这一章掰直它。 流匹配换个目标:把路径定成一条直线,网络只学方向,听起来一步能到。
但训练目标那条直线,和你实际走的轨迹不是同一条。

1

先看清问题:路径到底有多弯

扩散模型每一步都在做一件事:估计当前位置「该往哪走」——方向不是一次算好的,从噪声开始,边走边猜。

数据只在一个位置(单峰)时,路径几乎是一条直线。真实数据是多峰的—— 猫、狗、汽车各占一块。噪声点最终要去哪?它一开始完全不知道。

🎯 类比

你在浓雾里要去最近的几座城,看不见路。
扩散:先朝一个方向走,雾散一点、发现"那边好像有城"再拐过去——先直后弯。
流匹配:依然看不清目的地,但每步都朝几座城合起来的平均方向走; 走近了才拐向其中一座——路短得多,可还是弯的。

图解 · 为什么"朝平均方向走"也会拐弯

弯曲不是因为模型不够强,而是问题本身模糊:同一个噪声点,早期看起来该去猫那边, 走到一半发现更像狗,被迫中途改变主意。reflow 治的就是这个:跑一遍记下每个噪声点实际去了哪, 再告诉模型「下次直奔那里」。

2

流匹配只有一句话

扩散学的是"预测噪声"。流匹配换了个目标,更简单:

扩散  xt = √αt·x₀ + √(1−αt)·ε  → 让网络猜 ε

流匹配 xt = (1−t)·ε + t·x₀  → 让网络猜 x₀ − ε

路径是人选的、固定的直线(噪声和数据之间线性插值),网络只学"此刻往哪走"。

先把"采样"说清楚:站在当前位置问网络"往哪走",拿到方向 v,挪一小步 (x ← x + Δt·v),再问、再挪。步数就是问几次;步数少,每步迈得大,路一弯就走偏。
训练时,随机抽一个噪声点 ε、随机抽一个数据点 x₀,配成一对(不挑),连一条直线;网络学的就是这条直线的方向 x₀ − ε。 但同一个位置可能来自好几对配对,网络只能学到这些方向的平均——从当前位置笔直指向"平均终点",再除以"剩下的时间" 1 − t。 第 M 节把它写成公式卡。

互动 · 训练要的直线 vs 实际走出的轨迹

💡 怎么看这张图

金色 = 数据分布(8 簇),灰点 = 噪声起点。
青虚线 = 训练配对要求的直线;绿实线 = 流匹配实际轨迹,粉线 = 扩散 DDIM。

绿线并不贴青虚线——它也是弯的。 这不是 bug:训练时每条配对直线都是直的,但采样用的"平均速度场"要对所有终点取平均,方向会漂移。
这个落差就是流匹配还要多步的原因,也是第 4 节 reflow 要补的。

3

为什么弯曲会浪费步数

数值积分有个朴素的规律:用折线去逼近曲线,弯得越厉害,需要的段数越多。 直线只需要一段,一个半圆需要十几段才能逼近。

互动 · 步数 vs 终点误差

横轴是步数,纵轴是"终点到最近簇中心的距离"(24 个起点平均,越低越靠近数据)。 粉线(扩散)从 1 步的 1.04 一路降;绿线(流匹配)4 步之后在 0.16~0.18 附近, 4 → 8 步反而略涨(0.1561 → 0.1804),是积分没走完的假象。
这不是"流匹配更准",是"它的路更好走"——64 步时两条曲线停在同一个水平。 (这里取 24 个起点;和第 2 节同为 8 步的误差不直接比。)
注意 4 步的 0.1561 比"理想下限"还低——积分没走完,落点更接近平均答案: 这把尺子量的是离数据多远,不是质量。

4

Reflow:把弯的掰直

流匹配比扩散直,但没完全直:它的速度场是对所有可能终点取平均的 (论文里叫边缘速度场),平均方向会漂移。

Rectified Flow(整流流)提出了一个极其简单的迭代办法,三步:

① 跑一遍,记录配对 从噪声 ε 出发积分一条轨迹,看它落在哪,记作 y——得到配对 (ε, y)。
② 重新训练:这次直接连直线 用这批新配对,把线性插值路径改成 (1−t)·ε + t·y,目标速度改成 y − ε。
③ 重复 每重复一次,路径更直一点;足够多次后一步采样就能达到多步的质量—— 这就是蒸馏(多步模型当老师,训一个少步的学生模仿它)。

互动 · 亲手迭代 reflow,看路径被拉直

直线度 = 起点到终点的直线距离 ÷ 实际走过的路程,完全笔直等于 1.00。 拖 reflow 滑块看它怎么逼近 1——逼近 1,就是"步数可以降下来"的过程(直线理论上一步能走完)。

⚠️ 但 reflow 有个隐藏的代价

每轮 reflow 都要重训一次模型,还要先生成一批配对; 第二轮配对是当前模型自己产的——第一轮缺陷会被固定。所以工程上通常只做 1~2 轮。

M

数学 · 每个符号在图上都有一块

第 2 节说「网络学的是平均方向」。这一节把它写成公式,再把符号钉到图上:v、x、t、E。 图中两个箭头(目标方向 vs 学到的方向)的夹角,就是这一章全部故事的长度。

互动 · 两个箭头,一个夹角

💡 把第 2 节那张图拆成一个点来看

训练时你要求橙箭头(每条配对一条直线);采样时只能给绿箭头(对所有终点取平均)。 数据单峰时两者重合;真实分布永远不止一个峰。拖时刻滑块看夹角—— 它是路径会弯的全部来源。

图解 · 「终点减起点」为什么是一个箭头

两个点相减,得到的是「从起点指向终点的那支箭」:把起点搬到原点,终点跟着平移,它落到的位置就是那支箭。

5

从随机扩散到确定性直线

方法它改了什么步数代表
DDPM
2020
预测噪声 ε,采样走随机 SDE,每步注噪 1000 DDPM、早期 SD
DDIM
2021
同样的噪声目标,改成确定性 ODE——路径仍然弯 20 ~ 100 SD 1.5 / SDXL
DPM-Solver
2022
不换路径,换积分器:高阶积分逼近同一条弯路 10 ~ 20 各种加速插件
流匹配 / 随机插值
2022
换目标也换路径:预测速度 v = x₀ − ε,路径改成直线插值 20 ~ 50 2022 年的原始论文;这条路线后被 SD3 / FLUX 采用(见第 6 节)
整流流(RF 目标)
2022 → 2024
同上,把"路径固定为线性插值"写死;不做 reflow 仍弯 20 ~ 50(dev)
4(蒸馏版)
SD3、SD3.5、FLUX
Reflow 迭代蒸馏
2022 → 2023
用自产的新配对反复重训,一轮轮拉直 1 InstaFlow(把 SD 1.5 蒸到 1 步采样)
一致性 / 蒸馏类
2023 →
直接学"任意点到终点"的映射,一步到位 1 ~ 4 LCM、Turbo、Lightning

2022 年那三篇——流匹配(Lipman 等)、整流流(Liu 等)、随机插值(Albergo & Vanden-Eijnden)—— 几乎同年独立提出,用不同框架说同一件事:把「扩散」重述成「沿一条路径把噪声运输成数据」。 三条路线覆盖面不同,但「路径可以自己设计」是共通的。

6

为什么 SD3 和 FLUX 都换了

理由说明
① 采样步数少 路径直了,同样的质量只需 20~50 步,蒸馏版 1~4 步。推理成本砍掉一个量级。
② 训练目标更简单 不需要噪声调度表、ᾱ 累积乘积、信噪比,训练目标就是回归速度向量。
③ 和 DiT 天然契合 DiT 输出本来就是每个 patch 一个向量;让它输出速度,和语言模型输出 token 的 logits 同构。
④ 论文里的实测更好 SD3 在同一套架构和算力下做了公平对比:整流流目标全面优于 DDPM。
⚠️ 但有两个不显眼却关键的实现细节

① 时间步不能均匀采样:SD3 改成从对数正态分布里抽,让训练集中在中间「信息量最大」的时刻;两头太容易,学不到东西。
② 噪声尺度要按分辨率调,SD3 加了偏移参数补偿。这两个细节不带美感,去掉性能会明显掉。

7

它并不是免费的午餐

代价具体表现什么时候真的会痛 → 换什么
① reflow 每轮都会损失一点精度 近似场替代真场,终点往数据外漂;下一轮配对自产,误差被固化 要极限精度(医疗、渲染)→ 少做 reflow,多步换质量
② 一步生成的质量仍然不行 一步要模型一次算完所有细节,对容量要求极高;FLUX 默认仍是 20~50 步 要实时出图又不想加步数 → 换蒸馏型号(FLUX.1-schnell、SD3.5 Turbo,1~4 步)
③ 少步采样的多样性会下降 一步出图只能用平均答案,样本之间更像;这不是直线的锅,是少步蒸馏的代价 同一提示词要出多种结果 → 加随机性或多步采样
④ Reflow 要额外训练 每轮重训 + 生成配对数据,成本实打实;第二轮后边际收益迅速下降 训练预算紧 → 只做 1 轮,或跳过 reflow 上蒸馏
⑤ 实现细节敏感 时间步采样、噪声偏移、路径平滑影响都很大,调参空间比 DDPM 大 没有大规模训练经验 → 照抄成熟配方(SD3 的默认值)

那为什么还是换了?推理成本每天都在付,调参成本是一次性的; 「步数 50→20」= 推理费用降到四成——换是必然的。

8

小结

训练时让它学的,和采样时真正走的,不是同一条路。

它对应哪条线 ② 没有免费午餐——它假设「走直线最好」(第 2 节);平均出来还是弯的,靠 reflow 掰直,还赔上多样性(第 4、7 节)。
一句话 把「教它走哪条路」和「它实际走哪条路」拆开看:两者不相等,reflow 就是补落差。
它牺牲了什么 轨迹自由度和终点精度:一个起点只通一个终点,少步采样的样本更像;每轮 reflow 误差都往上抬(第 4 节)。
🎬 自己验一遍

回到第 2 节,把「采样步数」从 8 拖到 60——绿线更光滑,但不会贴到青虚线上: 步数只决定弯路走得多精确,不决定它直不直。
让它贴上,得去第 4 节拖「Reflow 轮次」——那一次变的是路径本身。 两个滑块的区别,就是这一章的全部内容。

它在暗线里站在哪

这一章落在下面几条暗线上。

暗线这一章的回答
什么被牺牲了 步数 50→20,推理账单降到四成。牺牲:路径自由度、样本多样性、终点精度。
参数账本 SD3 Medium 2B / Large 8B、FLUX.1-dev 12B。1024² 的图在潜空间是 128×128,按 2×2 切块约 4096 个 token,单次前向约 6.6×10¹³ FLOPs——少跑一步就省一笔。
它假设了什么 ① 噪声到数据有一条单一最优路线;② 路径越直、步数越少(经验,不是定理);③ 模型自产的配对可信。这就是本章的归纳偏置。
违背了哪个直觉 「你训练什么就得到什么」——错。学到的是对终点取期望后的速度场:单条路径是直的,平均场是弯的。

为什么不直接学「噪声 → 终点」的一步映射?那要把所有弯压进一次输出—— 下一章《一致性模型与蒸馏》接着讲。

一句话带走流匹配与整流流

扩散模型的路径先直后弯——它一开始不知道要去哪个模式,边走边改主意;弯 = 要更多步数。
流匹配换成「噪声到数据的线性插值」,让网络学「从当前位置指向平均终点」——更简单、更直、步数更少。
整流流再进一步:跑一遍记录每个噪声点实际去了哪,再重训让它下次直奔那里,一轮轮掰直。
代价是终点误差从 0.183 涨到 0.254(下限 0.163)、多样性下降、reflow 要额外训练。 但推理成本少一个量级——所以 SD3 和 FLUX 都换了。

9

拓展阅读

上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。

📄 这一章的说法从哪来

💻 工业界怎么写

∑ 更严格的形式