
c-Rectified flow 是生成模型领域里把扩散模型的概率流 ODE、最优传输中的代价函数和整流rectify思想放到同一个框架下的研究方向。它回答的核心问题不是如何生成更真实的样本而是如何在给定采样步数、给定样本量和给定代价函数 c 的情况下同时给出计算层面的收敛保证和统计层面的误差控制。在常见扩散模型里从纯噪声到真实样本的路径是一条随机微分方程SDE的轨迹逆过程需要大量采样步数。整流流尝试把这条弯弯曲曲的轨迹拉直用一条近似直线的概率流 ODE 从源分布走向目标分布。c-Rectified flow 又往前走了一步它不再假设这条直线对应的配对是任意随机配对而是让配对本身服从某个带代价的优化目标。于是整个生成过程既有扩散模型的流式表达又有最优传输的分配结构还保留了少步采样带来的计算优势。这篇文章会把 c-Rectified flow 拆成三个层面来看首先说清它要解决什么问题其次解释计算保证和统计保证各自指什么最后给出一套可以运行的简化实现、评估方法和排错清单。基于原始材料只提供了标题和少量热词文中的实现部分会采用“通用工程示例”的方式落地前需要根据你自己的数据形态、网络结构和代价函数做调整。1. 从整流流到 c-Rectified flow问题定位与核心直觉1.1 整流流解决的是“采样步数和路径弯曲”的矛盾扩散模型的生成过程可以理解为一个逆时间过程先从噪声分布采样一个点再沿着学习到的分数场或速度场逐步移动到数据分布。问题在于真实数据分布往往位于一个高维流形附近噪声分布到数据分布之间的概率路径通常是弯曲的。用离散 ODE 模拟这条弯曲路径时步数越少误差越大步数越多计算开销越高。整流流的基本做法非常直接。给定源分布 p0 和目标分布 p1先构造一对随机变量 (X0, X1)其中 X0 服从 p0X1 服从 p1。然后定义线性插值路径X_t (1 - t) * X0 t * X1其中 t 从 0 变化到 1。这条路径的端点分别是噪声和数据中间点由线性插值生成。对应的速度场目标是 X1 - X0它是一个与时间无关的常数方向。整流流训练一个神经网络 v_theta(x_t, t) 去拟合这个方向损失函数写成L E [ || v_theta(X_t, t) - (X1 - X0) ||^2 ]训练完成后生成阶段只需要从 X0 出发按照这条近直线路径做几步 Euler 积分就能到达数据分布附近。和 SDE 采样相比整流流的优势在于路径更直、积分步数可以大幅减少。这里要注意整流流是否真的能减少传输代价取决于初始配对 (X0, X1) 的选取。如果配对是纯随机的比如把一批噪声和一批数据各自随机打乱后配对路径虽然也是直线但这种直线可能交叉严重生成的分布质量并不稳定。1.2 重新整流为什么只解决了一半问题重新整流reflow是整流流的一种迭代改进方式。第一次训练得到一个速度场后生成阶段用这个速度场重新采样一组 (X0, X1)。这一步的关键是这组新配对不是随机配对的而是由已学到的流所定义的传输关系。接下来用新配对重新训练速度场循环多次后配对逐渐向某种更“对齐”的匹配关系收敛。从最优传输角度看重新整流实际上是在逼近一个二次代价下的最优传输耦合。因为每一步重新采样时模型会把 X0 送到最对应的 X1路径交叉减少直线插值的合理性增强。代价函数如果使用欧氏距离平方那最终逼近的耦合就接近 2-Wasserstein 意义下的最优传输。但实际任务里欧氏距离平方并不总是正确的度量。比如图像生成中像素域的高斯代价和人眼感知不直接对应语音或时序数据中较小的时序偏移可能比大幅幅度错误更可接受分子生成、推荐排序、点云配准等场景中代价函数往往由业务逻辑决定而不是由默认的欧氏距离决定。重新整流过程默认使用二次代价这就在理论和实际之间留出一道缝。1.3 c-Rectified flow 把代价函数放进配对目标c-Rectified flow 的核心改变是不再默认使用欧氏距离平方而是显式引入一个代价函数 c(x, y)让配对过程在这个代价意义下寻找更优的耦合。给定两个边缘分布 p0 和 p1最优传输问题关心的是所有以 p0、p1 为边缘分布的联合分布 Gamma 中找到使 E[ c(X0, X1) ] 最小的那个联合分布。c 越小说明平均传输成本越低。c-Rectified flow 把这一目标放到整流框架中训练速度场时采样配对不仅来自随机或流诱导的耦合还要受到代价函数 c 的约束。于是整个问题变成两层外层在所有边缘匹配中优化代价函数 c即选出一个更优的配对分布 gamma_c。内层给定配对分布 gamma_c 后训练速度场去拟合线性插值路径上的目标速度 X1 - X0。这两层互相依赖。速度场好重新采样得到的配对才可靠配对可靠训练速度场才稳定。c-Rectified flow 的“计算与统计保证”正是在分析这两层循环能否收敛、以多快速度收敛、以及需要多少样本才能把估计误差控制住。2. 计算保证和统计保证分别解决什么问题2.1 计算保证关注的是“步数有限时误差有多大”在生成模型里计算成本主要来自两个方面一是训练二是采样。c-Rectified flow 的计算保证更关注采样阶段的离散步数。理论分析通常假设真实速度场满足一定的正则性比如 Lipschitz 连续、有界、或者对 t 有足够的光滑性然后给出用 N 步 Euler 或 Runge-Kutta 方法积分时最终分布和真实目标分布之间的距离上界。这类误差界通常由三项组成初始化误差生成起点 X0 是否严格来自 p0。离散化误差步长 h 1/N 越大单步线性近似引入的误差越大。传播误差中间步骤的误差会不会被放大依赖速度场的 Lipschitz 常数。如果速度场是完美直线那么任意步长下 Euler 法都精确。如果路径有弯曲那么步长必须小到能分辨弯曲尺度。c-Rectified flow 通过代价函数引导配对让路径交叉减少、整体路径趋向更平直这时相同步数下的离散化误差会比随机配对更小。这也是为什么计算保证和代价函数选取直接相关。前面提到“c 的选取”不是细节问题。凸性较强的代价函数更容易诱导出结构稳定的配对路径更直非凸、不平滑的代价函数会让配对产生剧烈跳变速度场更复杂离散化误差随之上升。因此计算保证的有效边界必须写明对 c 的假设比如 Lipschitz 常数、凸性或者上下界。2.2 统计保证关注的是“样本有限时估计偏差有多大”统计保证处理的是手里只有 m 个数据样本无法精确知道真实边缘分布 p0 和 p1更无法精确计算最优耦合。所有训练过程都基于经验分布。给定有限样本时经验分布和真实分布之间本身就有随机误差。这个误差传到配对估计上会让最优传输耦合的估计偏离真实最优值。统计保证通常给出的是偏差和方差的上界例如传输代价估计值 E_hat[c] 与真实最优代价 E[c*] 的差距。生成分布与真实目标分布之间的分布距离。估计的配对与真实最优配对之间在某种度量下的距离。这里有一个在最优传输领域反复出现的权衡理论上越精确的配对估计往往对样本误差越敏感。完全不加入熵正则或平滑项时最优传输映射可能过度拟合训练样本加入正则后耦合估计偏差变大但方差变小。c-Rectified flow 在统计保证部分需要回答的正是引入代价函数 c 和配套正则项之后误差项能否以可接受的速率随样本量 m 增加而下降。2.3 两类保证必须放到同一条流程中看很多人会把计算保证和统计保证分开理解计算保证是采样步数层面统计保证是样本量层面。但 c-Rectified flow 的实践过程里它们互相影响。一个典型场景固定样本量增大采样步数。理论上统计误差不会因为采样步数增加而下降因为样本量没变。但步数过少导致生成分布偏离在统计评估中会表现为生成分布和真实目标分布的距离偏大。这种偏大并非统计估计误差而是计算误差。反向也成立样本量增大计算步数不变经验代价可以下降但速度场复杂度可能上升离散化误差未必同步减小。因此复现论文或比较方法时必须同时记录步数和样本量并在报告中分别区分传输代价的下降是来自配对优化还是来自步数增加。理解这一点后再看 c-Rectified flow 的计算与统计保证就不会只是背几条误差公式而是能用来指导参数选择样本少时增加正则强度步数少时优先采用路径更直的配对二者不能在报告中混为一谈。3. 核心算法与理论工具配对、对偶和正则化3.1 用概率流 ODE 表达生成过程c-Rectified flow 的生成过程仍然建立在概率流 ODE 上。如果源分布到目标分布之间的路径由速度场 v(x_t, t) 决定那么生成阶段从 X0 到 X1 的更新可以写作离散形式x_{n1} x_n (1 / N) * v_theta(x_n, t_n)这里的本质不是让每一步都最优而是让整条路径的端点落在目标分布附近。和扩散模型相比速度场更接近“从当前点指向最终目标的直接移动方向”因此单步预测的意思更清楚。3.2 c 变换和对偶形式在最优传输理论中给定代价函数 c对偶问题往往比原始问题更容易算法化。Kantorovich 对偶形式会引入两个势函数 f 和 g让传输代价可以表达成在约束 f(x) g(y) c(x, y) 下的最大化问题。c-Rectified flow 的分析中常会用到 c 变换即把某个函数通过代价函数转换成另一个函数的形式。具体到实现有两种常见落地思路第一种是显式用对偶网络表示势函数每次迭代既更新速度场又更新势函数让配对逼近最优条件。第二种是直接对配对矩阵做正则化检索比如使用 Sinkhorn 算法近似最优传输耦合再用这个耦合采样配对训练速度场。第二种思路工程上更容易实现代价是只能得到近似解。正则参数越大求解越稳定但与真实最优传输解的偏差也越大。这部分在下一节代码实现中会体现出来。3.3 熵正则与耦合平滑纯最优传输问题在样本量有限时容易过拟合解决方式是加入熵正则化min E[ c(X0, X1) ] - epsilon * H(gamma)其中 H 是配对分布的熵。epsilon 越大配对分布越平滑越接近独立随机配对epsilon 越小配对越尖锐代价越低但统计方差更高。在 c-Rectified flow 中熵正则同时承担两个作用一是在训练早期避免配对过度跳跃二是在统计估计中控制方差。实际操作中epsilon 通常随训练轮数递减类似退火让模型先稳定再精调。3.4 算法骨架一次完整的 c-Rectified flow 训练可以分成三个阶段。第一阶段用当前配对策略生成一批配对 (X0, X1)记录每个样本对的代价 c。如果这是第一轮配对可以来自随机匹配或独立采样。这个阶段不需要网络参与只负责准备数据。第二阶段根据已经计算好的代价和熵正则参数更新配对权重。工程上可以直接用 softmax 加权重采样也可以用 Sinkhorn 计算一个近似最优传输矩阵。第三阶段用新配对训练速度场更新网络参数。训练完成后再用当前网络做一次生成生成结果参与下一轮配对更新。下面用一个最小实现把这三个阶段串起来。4. 最小可运行实现PyTorch 版 c-Rectified flow4.1 环境准备实现不依赖特殊库只需要 PyTorch 和常见科学计算库。论文复现时建议核对具体版本本文示例使用通用结构依赖作用建议说明Python 3.9 或以上运行环境低版本可能缺少类型注解支持PyTorch 2.x网络训练与自动微分2.x 对 CPU/GPU 调度更友好NumPy数据生成和转换基础依赖Matplotlib可视化路径与代价曲线便于检查收敛POT 库Sinkhorn 等最优传输计算可选手写实现也可替代如果使用 GPU建议显存不低于 4GB。二维 toy 数据在 CPU 上也能跑通但学习效率和样本量会受明显限制。4.2 生成二维示例数据为了能直观看到配对和传输代价使用两个分布。源分布使用混合高斯目标分布使用两个 U 型扇区。下面的函数生成少量样本观察分布形状。import torch import numpy as np def sample_source(n): 源分布两个高斯块的中心分布在左右两侧。 centers torch.tensor([[-2.0, 0.0], [2.0, 0.0]]) idx torch.randint(0, 2, (n,)) x0 centers[idx] 0.3 * torch.randn(n, 2) return x0 def sample_target(n): 目标分布两个弧形扇区用于观察传输路径是否交叉。 theta torch.rand(n) * np.pi r 0.6 0.4 * torch.rand(n) x r * torch.cos(theta) y r * torch.sin(theta) sign torch.randint(0, 2, (n,)) * 2 - 1 x1 torch.stack([x, sign * y], dim1) return x1这里源分布和目标分布没有真的一一对应关系配对策略将决定每个噪声点去往目标空间的哪个位置。随机配对会让路径交叉代价函数引导下的配对会让路径尽量不交叉。4.3 定义代价函数代价函数是最能体现任务语义的部分。这里使用平方欧氏距离加上少量正则说明 c 的写法def transport_cost(x0, x1): 输入 x0, x1: [batch, dim] 返回每个样本对的传输代价形状 [batch] diff x0 - x1 cost (diff ** 2).sum(dim1) return cost如果需要改变代价语义比如更关注方向变化可以增加一个方向项def transport_cost_with_dir(x0, x1): dist (x0 - x1).pow(2).sum(dim1) dir_diff torch.nn.functional.cosine_similarity(x1 - x0, torch.ones_like(x1), dim1) return dist 0.1 * (1 - dir_diff)注意代价函数本身必须可微才能用于后续网络辅助的配对优化如果业务代价不可微可以先构造一个可微的代理函数。生产环境尽量不要在代偿函数里加入大量离散逻辑否则梯度会不稳定。4.4 用 softmax 加权重采样生成配对配对核心逻辑先计算当前批次中每个源点与所有目标点的代价矩阵再按负代价做 softmax 采样。温度 alpha 控制采样的尖锐程度。def build_paired_batch(x0, x1, alpha10.0): x0: [N, d] x1: [M, d] 返回配对后的 x0 和对应 x1保持 x0 顺序。 如果 M 不等于 N则按比例从 x1 中采样。 cost_matrix torch.cdist(x0, x1, p2) ** 2 # [N, M] logits -alpha * cost_matrix prob torch.softmax(logits, dim1) # 每个源点选择目标点的概率 idx torch.multinomial(prob, 1).squeeze(1) # [N] return x0, x1[idx]alpha 过小时配对接近随机alpha 过大时每次都以最近邻为目标路径很少交叉但样本多样性可能下降。这里体现的就是上一节提到的熵正则和尖锐匹配之间的权衡。实际中可以先设 alpha 10 观察代价下降再逐步加大。4.5 定义速度和网络速度场使用简单的多层感知机。输入是当前插值点 x 和时间 t输出是预测速度。由于这里维度是 2网络不需要太重。import torch.nn as nn class VelocityNet(nn.Module): def __init__(self, dim2, hidden128): super().__init__() self.net nn.Sequential( nn.Linear(dim 1, hidden), nn.SiLU(), nn.Linear(hidden, hidden), nn.SiLU(), nn.Linear(hidden, hidden), nn.SiLU(), nn.Linear(hidden, dim) ) def forward(self, x, t): t t.view(-1, 1) h torch.cat([x, t], dim1) return self.net(h)时间 t 作为额外特征输入网络目的是让模型能区分靠近源分布和靠近目标分布时的不同移动策略。训练时t 要折叠在 [0, 1] 内。4.6 训练循环训练循环分三步先根据当前配对策略生成一批配对然后随机采样 t 构造插值点最后计算速度拟合损失。这里把配对更新和速度训练放在同一个循环里工程上更简洁但理论分析时要把它们看作不同阶段的交替。def train_c_rectified_flow(net, opt, n_iters5000, batch_size256, alpha10.0, seed0): torch.manual_seed(seed) loss_history [] cost_history [] for step in range(n_iters): # 1. 采样源点和目标点 x0 sample_source(batch_size) x1 sample_target(batch_size) # 2. 按传输代价重新配对 x0_paired, x1_paired build_paired_batch(x0, x1, alphaalpha) # 3. 随机采样 t t torch.rand(batch_size) xt (1 - t).unsqueeze(1) * x0_paired t.unsqueeze(1) * x1_paired # 4. 预测速度 pred net(xt, t) # 5. 目标速度 target_vel x1_paired - x0_paired # 6. 损失 MSE loss ((pred - target_vel) ** 2).mean() opt.zero_grad() loss.backward() opt.step() # 记录 loss_history.append(loss.item()) with torch.no_grad(): cost_history.append(transport_cost(x0_paired, x1_paired).mean().item()) if step % 500 0: print(fstep {step}, loss{loss.item():.5f}, cost{cost_history[-1]:.5f}) return loss_history, cost_history这个循环里的 alpha 是固定值。更合理的做法是按迭代次数退火比如前 2000 步用 alpha 5之后逐步增加到 20。要注意 softmax 加权重采样是离散采样无法把代价梯度回传到配对矩阵本身如果需要严格按优化视角更新配对应换用 Sinkhorn 或对偶网络。4.7 ODE 采样验证训练完成后用 Euler 法从源分布采样一批点步数可以设置为 10、20、50 等观察生成分布是否接近目标分布。torch.no_grad() def sample_by_ode(net, num_samples2048, steps20): x sample_source(num_samples) dt 1.0 / steps for i in range(steps): t torch.full((num_samples,), i * dt) v net(x, t) x x v * dt return x采样后分别计算生成样本与目标样本之间的传输代价和分布距离观察不同步数下的变化。如果步数从 50 降到 5传输代价上升很少说明速度场已经被整流得比较直如果代价显著上升说明配对问题还没有解决。5. 数值验证与参数选择从二维 toy 到生产倾向5.1 评估指标要同时看传输代价和分布质量最直接的指标是平均传输代价 E[c(X0, X1)]。这个值越低说明配对越接近代价最优的耦合。但它不能反映生成分布是否真的靠近目标分布因为即使所有配对都走最近邻也可能只覆盖目标分布的一部分。因此需要同时观察分布距离Wasserstein-2 距离在二次代价下衡量生成分布和目标分布的差异。最大均值差异MMD适合小样本分布比较。FID高维生成任务中常用但对二维 toy 数据参考价值有限。能量分数或负对数似然如果想评估概率预测的校准性。如果是带标签数据还可以计算配对交叉熵评估“源点是否被送到正确类别区域”。这种业务性指标有时比抽象的分布距离更能说明代价函数设置是否合理。建议记录一张评估表步数平均传输代价W2生成样本是否覆盖所有目标簇是否出现路径交叉50.820.41部分覆盖少量200.750.32覆盖完整无明显交叉500.730.30覆盖完整无明显交叉如果从 20 步增加到 50 步指标变化微弱说明可以采用 20 步作为生产步数如果 5 步和 20 步差异巨大说明速度场还不够直应该先加强配对优化而不是盲目增加步数。5.2 参数影响速查参数调小的影响调大的影响推荐做法alpha配对温度配对接近随机路径交叉多代价下降慢配对接近最近邻代价低但多样性可能不足从小到大退火epsilon熵正则耦合尖锐统计方差大耦合平滑但偏离最优传输解前期大后期小训练轮数速度场欠拟合可能过拟合训练配对泛化下降观察代价曲线的收敛平台批量大小配对矩阵不稳定估计方差大配对矩阵稳定但显存开销大至少 256理想 1024采样步数误差增大生成质量下降误差减小计算开销增加从 20 开始按指标调整5.3 可视化检查路径二维数据上可以画出几条代表性的插值路径。具体做法是固定采样几个源点每隔固定步长记录一次位置把所有中间点连起来。如果路径之间交叉严重说明配对不够优如果路径拥挤在某个区域说明代价函数或正则化没有平衡好覆盖性。路径可视化只能作为辅助判断不能替代指标。真正的生产环境里路径维度往往达到几千甚至更高可视化没有意义应该改用传输代价和分布距离的曲线。6. 常见问题排查与最佳实践6.1 从现象到根因的排查表问题现象常见原因检查方式处理建议训练初期损失下降很快后期震荡alpha 固定太大配对切换过于频繁输出每个 batch 的配对代价方差使用退火 temperature传输代价持续下降但生成分布仍不覆盖目标配对过度靠近部分目标点从未被选中统计每个目标点被选中的次数降低 alpha或在采样时加入最小概率采样步数增加但指标几乎不变速度场已经接近直线或网络容量不足同时减小比较 20 步和 100 步的输出差异若网络容量受限先增大网络再判断不同随机种子结果差异明显配对重采样在高方差模式下运行使用多个 seed 跑 5 次计算平均和标准差修正随机种子并考虑减少 alpha 的最终值高维数据上代价函数不稳定高维欧氏距离区分度下降打印代价分布直方图换成更贴合任务的代价函数或加归一化生成样本出现离群点ODE 离散误差在路径弯曲处放大减小步数并观察离群点是否减少增加步数或者对代价函数增加 Lipschitz 约束6.2 可复用检查清单发布或提交复现实验前建议按以下清单逐项确认源分布和目标分布的边缘约束是否严格满足不能因为重采样破坏边缘。代价函数是否可微是否对尺度变化敏感。alpha 和 epsilon 是否写入训练记录方便后续复现。传输代价下降曲线和分布距离曲线是否同步记录。是否在固定步数下比较不同配对策略而不是同时改变多个变量。是否保留多个随机种子的结果确认差异在可接受范围。最终采样生成阶段是否使用训练时同一套速度场和同一套步数策略。高维任务里是否验证配对分布没有出现模式坍塌或局部过度集中。6.3 从 toy 代码走向完整项目实践的扩展方向这里的二维实现把配对更新和速度训练放在同一个循环里适合理解算法主体。真实项目里至少要注意以下几点。第一配对更新的频率和训练频率要分离。更稳定的做法是先训练一个基础整流流再固定网络做几轮重新配对最后在更优配对下重新训练。一次性循环虽然简单但在高维空间中容易把配对噪声带入速度场训练。第二代价函数不是越高深越好。在业务场景里应该先和业务方明确“什么样的传输是便宜的”再把它翻译成可微函数。如果只用一个加权平方距离得到的 c-Rectified flow 本质上仍然接近二次代价下的重新整流。第三可以与 Schrödinger bridge 方向结合。熵正则化后的 c-Rectified flow 本身就可以看成对带噪声最优传输问题的逼近。当源分布和噪声尺度需要同时建模时把噪声项纳入过程会更自然。第四高维采样时要注意 ODE 积分器的选择。Euler 方法只是起点。二阶 Heun 方法、自适应步长方法能显著减少离散步数代价是每步两次前向计算。在 5 步和 10 步这类极低步数场景下二阶方法通常比单纯增加步数更划算。c-Rectified flow 的价值不在于把名字变得复杂而在于它迫使实践者回答两个问题我的生成路径希望最小化到什么代价在有限样本和有限步数下这个最小化能做到什么程度想清楚这两个问题再回到网络结构和训练细节方向就会清晰很多。新手可以先从二维 toy 把配对、代价、步数三者之间的关系跑通再逐步替换成自己的数据和业务代价函数。