上一章《潜空间扩散与 DiT》把扩散搬进潜空间,可路径还是弯的,跑几十到上千步。这一章掰直它。
但训练目标那条直线,和你实际走的轨迹不是同一条。
扩散模型每一步都在做一件事:估计当前位置「该往哪走」——方向不是一次算好的,从噪声开始,边走边猜。
数据只在一个位置(单峰)时,路径几乎是一条直线。真实数据是多峰的—— 猫、狗、汽车各占一块。噪声点最终要去哪?它一开始完全不知道。
你在浓雾里要去最近的几座城,看不见路。
扩散:先朝一个方向走,雾散一点、发现"那边好像有城"再拐过去——先直后弯。
图解 · 为什么"朝平均方向走"也会拐弯
弯曲不是因为模型不够强,而是问题本身模糊:同一个噪声点,早期看起来该去猫那边, 走到一半发现更像狗,被迫中途改变主意。reflow 治的就是这个:跑一遍记下每个噪声点实际去了哪, 再告诉模型「下次直奔那里」。
扩散学的是"预测噪声"。
路径是人选的、固定的直线(噪声和数据之间线性插值),网络只学"此刻往哪走"。
先把"采样"说清楚:站在当前位置问网络"往哪走",拿到方向 v,挪一小步
(x ← x + Δt·v),再问、再挪。步数就是问几次;步数少,每步迈得大,路一弯就走偏。
训练时,随机抽一个噪声点 ε、随机抽一个数据点 x₀,配成一对(不挑),连一条直线;网络学的就是这条直线的方向 x₀ − ε。
但同一个位置可能来自好几对配对,网络只能学到这些方向的平均——从当前位置笔直指向"平均终点",再除以"剩下的时间" 1 − t。
第 M 节把它写成公式卡。
互动 · 训练要的直线 vs 实际走出的轨迹
金色 = 数据分布(8 簇),灰点 = 噪声起点。
青虚线 = 训练配对要求的直线;绿实线 = 流匹配实际轨迹,粉线 = 扩散 DDIM。
绿线并不贴青虚线——它也是弯的。
这不是 bug:训练时每条配对直线都是直的,但采样用的"平均速度场"要对所有终点取平均,方向会漂移。
这个落差就是流匹配还要多步的原因,也是第 4 节 reflow 要补的。
数值积分有个朴素的规律:用折线去逼近曲线,弯得越厉害,需要的段数越多。 直线只需要一段,一个半圆需要十几段才能逼近。
互动 · 步数 vs 终点误差
横轴是步数,纵轴是"终点到最近簇中心的距离"(24 个起点平均,越低越靠近数据)。
粉线(扩散)从 1 步的 1.04 一路降;绿线(
这不是"流匹配更准",是"它的路更好走"——64 步时两条曲线停在同一个水平。
(这里取 24 个起点;和第 2 节同为 8 步的误差不直接比。)
注意 4 步的 0.1561 比"理想下限"还低——积分没走完,落点更接近平均答案:
这把尺子量的是离数据多远,不是质量。
Rectified Flow(整流流)提出了一个极其简单的迭代办法,三步:
互动 · 亲手迭代 reflow,看路径被拉直
直线度 = 起点到终点的直线距离 ÷ 实际走过的路程,完全笔直等于 1.00。 拖 reflow 滑块看它怎么逼近 1——逼近 1,就是"步数可以降下来"的过程(直线理论上一步能走完)。
每轮 reflow 都要重训一次模型,还要先生成一批配对; 第二轮配对是当前模型自己产的——第一轮缺陷会被固定。所以工程上通常只做 1~2 轮。
第 2 节说「网络学的是平均方向」。这一节把它写成公式,再把符号钉到图上:v、x、t、E。 图中两个箭头(目标方向 vs 学到的方向)的夹角,就是这一章全部故事的长度。
互动 · 两个箭头,一个夹角
训练时你要求橙箭头(每条配对一条直线);采样时只能给绿箭头(对所有终点取平均)。 数据单峰时两者重合;真实分布永远不止一个峰。拖时刻滑块看夹角—— 它是路径会弯的全部来源。
图解 · 「终点减起点」为什么是一个箭头
两个点相减,得到的是「从起点指向终点的那支箭」:把起点搬到原点,终点跟着平移,它落到的位置就是那支箭。
| 方法 | 它改了什么 | 步数 | 代表 |
|---|---|---|---|
| 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 年那三篇——
| 理由 | 说明 |
|---|---|
| ① 采样步数少 | 路径直了,同样的质量只需 20~50 步,蒸馏版 1~4 步。推理成本砍掉一个量级。 |
| ② 训练目标更简单 | 不需要噪声调度表、ᾱ 累积乘积、信噪比,训练目标就是回归速度向量。 |
| ③ 和 DiT 天然契合 | DiT 输出本来就是每个 patch 一个向量;让它输出速度,和语言模型输出 token 的 logits 同构。 |
| ④ 论文里的实测更好 | SD3 在同一套架构和算力下做了公平对比:整流流目标全面优于 DDPM。 |
① 时间步不能均匀采样:SD3 改成从对数正态分布里抽,让训练集中在中间「信息量最大」的时刻;两头太容易,学不到东西。
② 噪声尺度要按分辨率调,SD3 加了偏移参数补偿。这两个细节不带美感,去掉性能会明显掉。
| 代价 | 具体表现 | 什么时候真的会痛 → 换什么 |
|---|---|---|
| ① reflow 每轮都会损失一点精度 | 近似场替代真场,终点往数据外漂;下一轮配对自产,误差被固化 | 要极限精度(医疗、渲染)→ 少做 reflow,多步换质量 |
| ② 一步生成的质量仍然不行 | 一步要模型一次算完所有细节,对容量要求极高;FLUX 默认仍是 20~50 步 | 要实时出图又不想加步数 → 换蒸馏型号(FLUX.1-schnell、SD3.5 Turbo,1~4 步) |
| ③ 少步采样的多样性会下降 | 一步出图只能用平均答案,样本之间更像;这不是直线的锅,是少步蒸馏的代价 | 同一提示词要出多种结果 → 加随机性或多步采样 |
| ④ Reflow 要额外训练 | 每轮重训 + 生成配对数据,成本实打实;第二轮后边际收益迅速下降 | 训练预算紧 → 只做 1 轮,或跳过 reflow 上蒸馏 |
| ⑤ 实现细节敏感 | 时间步采样、噪声偏移、路径平滑影响都很大,调参空间比 DDPM 大 | 没有大规模训练经验 → 照抄成熟配方(SD3 的默认值) |
那为什么还是换了?推理成本每天都在付,调参成本是一次性的; 「步数 50→20」= 推理费用降到四成——换是必然的。
训练时让它学的,和采样时真正走的,不是同一条路。
回到第 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 都换了。
上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。