说句实在话,我第一次认真读Score-based Models相关的工作时,并没有觉得它有多惊艳——那时候大家的目光都在GANs和VAE上,生成一张像样的图,很少有人会绕弯子去想"我能不能直接估计分布对输入的导数"。直到自己动手复现了一个高维数据集上的生成实验,才发现这条路远比看上去更接近生成问题的本质,而且它把一堆原本割裂的概念(扩散模型、朗之万采样、连续时间SDE)以一种非常优雅的方式串在了一起。这个领域值得每一个做生成模型的人花时间搞清楚,不只是为了看懂某篇论文,更是为了在实践里拿到一套可以灵活使用的工具箱。
这篇文章我打算用一种更接近"复现手记"的方式来写:先讲清楚Score-based Models到底在解决什么问题、为什么从梯度下手是聪明的做法,再带你走一遍训练和采样的全链路,包括那些论文里不会写、但实际操作时一定会撞上的细节。适合正在看相关论文但还没动手跑通实验的研究生,也适合已经用过DDPM但想从更统一视角理解生成模型的开发者。我会尽量把原理和实操混在一起讲,因为光懂公式救不了模型不收敛,光会调参不补原理也走不远。
1. 为什么学数据密度梯度比直接学密度本身更聪明
1.1 Score函数到底长什么样
Score-based Models的核心学习目标叫做score,严格定义是数据分布对数密度关于输入向量的梯度。这里有一个很微妙的点:我们通常习惯于让模型去拟合一个分布本身,也就是p(x),但拟合分布要先保证它满足概率密度函数的归一化条件,也就是整个空间上的积分等于1。这个归一化常数在真实数据集上几乎没法算——高维空间下那个积分就是典型的难解问题。
而score函数完全绕开了这一层麻烦。我只需要让网络输出一个向量,它去逼近某个分布密度的梯度方向。梯度是相对量,不依赖那个归一化常数,所以模型要学的这个目标天然是可以直接优化的。从信息论角度看,分布一旦确定,梯度也就确定了;反过来,知道了一个函数在足够多位置上的梯度,也等于恢复了这个函数的相对形状。对生成任务来说,"知道去哪里采样概率高"这句话本身就和求导是同一个意思。
1.2 从能量视角理解生成过程
如果把对数密度乘个负号,就得到了一个能量函数。score就是负的能量梯度,指向的是能量下降最快的方向。这个视角下,生成样本的过程就特别像物理世界里的粒子运动:一个粒子在能量地形上受到某种力的驱动,能量低的地方对应数据分布密度高的位置,也就是真实样本所在的区域。
理解这个视角对后续调参非常有用。比如在做采样的时候,如果初始点选得特别不好,粒子就要走过一大片能量很高的"无人区",这些地方score的估计通常也很差,粒子就可能被困住或者在原地打转。这也是为什么后来几乎所有的采样策略都强调要控制步长、要在多尺度噪声下退火——本质上就是在帮粒子找一条靠谱的路径,让它别一头撞进能量地形的死角。
1.3 梯度信息为什么能支撑生成
有人可能会问,光有梯度没有密度值,怎么判断什么时候该停?生成任务里采样停止的条件并不依赖绝对密度,而是依赖这个随机过程是否收敛到稳态分布。只要score估计得足够准,按照朗之万动力学持续迭代,得到的轨迹分布最终就会收敛到目标分布。整个过程里不需要计算密度值,只需要每一步的梯度方向和一个控制随机性的噪声项。
我自己的理解是,这等于把生成问题从"求一个复杂的归一化分布"转换成了"在局部梯度信息下做随机游走"。无论空间多大、维度多高,局部信息都是可以计算的,这为高维生成提供了可操作的路径。同时也因为只依赖局部信息,训练目标变得异常干净,我只需要最小化每一点处模型输出和真实梯度之间的差异即可。
2. 训练障碍与分数匹配的三板斧
2.1 朴素Score Matching为什么算不了
最直观的训练方式是最小化模型输出s_θ(x)与真实score之间的均方误差,但真实score通常没有闭式表达式,因为对数密度本身未知。原始的Score Matching技巧在于通过分部积分把这个目标改写成只含s_θ(x)及其雅可比矩阵的形式,听起来很妙也很通用。
但是真正在工程里用起来就会发现两个非常现实的问题:其一,需要计算s_θ(x)对输入x的雅可比矩阵,高维图像数据下这个计算量和显存占用都很可怕;其二,这个目标在低密度区域的方差很大,数据维度稍微上来一点,训练就容易震荡甚至发散。我最早在某个模拟数据集上跑这个原始目标的时候,损失曲线看起来像心电图的病理波形,几乎没有任何收敛迹象。所以现在回想起来,后续那些改进方案本质上都是在给这个朴素目标打补丁。
2.2 Denoising Score Matching:把难问题翻译成去噪问题
Denoising Score Matching是目前最主流实用的训练目标,核心思路是先给数据加高斯噪声,让目标分布变成一个被模糊过的分布,再让模型去匹配这个被模糊分布的score。最关键的性质是:对于加噪后的分布,score函数可以直接用去噪模型来表示——模型只需要预测出加进去的噪声向量,再乘一个系数,就是score。
为什么这招管用?因为"预测噪声"是一个回归任务,每个样本对应的标签是明确的,监督信号强、收敛快。所以在实际训练里,我不需要操心任何关于归一化常数的问题,也不需要对输入求二阶导,只需要喂入加噪声的样本、让模型输出去噪结果,然后算一个简单的MSE。我第一次跑通这个损失函数的时候,直观感受就是:训练曲线终于像一个正常的回归任务了,而不是像在猜一个看不见答案的谜语。
2.3 Sliced Score Matching等替代方案
Denoising是目前的主导方案,但它要求对数据加噪,等于间接改变了学习目标,有些场景里这不是你想要的。Sliced Score Matching的出发点是:我不需要匹配完整的梯度向量,只匹配它在若干随机方向上的投影就足够了。因为完整梯度向量是高维超球面上一个点,投影匹配覆盖足够多的方向后,这个点也基本被固定了。
Sliced方案在这几个方向里有它独特的价值:不想引入加噪那一套、希望在原始分布上直接做匹配时,它比朴素Score Matching更可控。我自己的经验是,Sliced Score Matching训练起来可以做到相当稳定,收敛速度也还行,就是实现时要注意随机投影方向的数量和方差控制。如果只是做最常规的图像生成,Denoising方案通常就能交出很好的成绩,Sliced更适合用来研究噪声和分布匹配之间关系的那些偏门场景。
3. 朗之万采样:理论漂亮,实操全是细节
3.1 朗之万动力学在生成里扮演的角色
朗之万动力学是Score-based Models在采样阶段的标准引擎。它的更新规则很简洁:每一步在当前点叠加一个从score函数获得的确定性位移,再加上一个高斯随机扰动。确定性位移推动粒子往数据密度高的地方走,随机扰动防止它被困在局部高点,保证探索性。
这个流程物理图像极其清晰,但在实际推演时要注意一个关键点:这里的"步数"不是越多越好,也不是越少越好。步数太少,粒子还没走到分布的高密度区域,出来的样本大概率是模糊的或者杂乱的;步数太多,采样的计算代价成倍增加,生成一张图要跑几百步,完整跑一轮实验就得等到天荒地老。
3.2 步长、步数和初始化
步长是这个过程中的灵魂超参。一个最容易踩的坑是步长选得太自信,结果粒子在能量面边缘反复震荡,生成的样本好像在几个模式之间抽搐;步长选得太保守,则导致粒子爬得太慢,在有限步数里根本到不了该去的地方。Papers里常见的策略是按噪声等级设计步长序列,大噪声阶段允许走大步,小噪声阶段必须放小步,这样才能稳定逼近真实分布。
初始化也是一个容易被忽略的点。在不少实现里,采样直接从纯噪声出发,这样做的好处是随机性强、样本多样性高,代价是采样轨迹很长。有些场景会考虑用部分真实样本做初始化,虽然能加速收敛,但会损失一些多样性,甚至导致训练分布和采样分布不一致。我个人的偏好是,在标准图像生成任务里坚持从纯噪声出发,用足够长的退火轨迹保证质量,而在某些对速度要求更高的场景再考虑初始化策略。
3.3 多尺度噪声与退火采样
单尺度噪声面临的尴尬是:噪声大了,采样容易出模糊的假样本;噪声小了,score在低密度区域又不准。退火采样是一个直接的解决思路:先在很大的噪声尺度上做采样,让粒子在高斯模糊后的分布上快速找到大概的位置,然后逐步降低噪声尺度,不断细化和精修轨迹。
这个过程在直观上像先画一个粗糙的轮廓,再一遍遍细化细节。实际跑参考文献里的实现时,我对噪声尺度数量(通常几十个级别)和每个级别上的采样步数(往往上百步)做了分组实验,发现中间段噪声尺度对最终质量影响最大——这一段既是分布形状的主要界定者,也是score估计误差的放大区域。如果某个噪声等级上采样步数太少,后续所有小噪声等级都会被带偏,所以宁可在大中噪声段多花点计算,也别抠那几步。
4. 从Score视角到SDE统一框架:扩散模型原来是一家人
4.1 VE-SDE与VP-SDE:两套噪声策略
把score-based models放进连续时间框架之后,加噪过程可以被写成随机微分方程。这里有两个常见的特化设定:VE-SDE会让噪声方差随时间不断增大,最后噪声盖过信号,扩散过程是"由清晰到模糊";VP-SDE则让信号方差随时间逐渐减小,同时噪声方差控制在一个固定范围内,扩散过程更像"由有序到无序",这种设定下加噪过程的边际分布可以直接推导,因此天然和DDPM那套离散扩散框架对应得上。
我对这两套设定最大的感受是:VP-SDE因为和既有扩散模型工具的兼容性极好,工程上落地更顺;VE-SDE则在某些分布尾部特别稀疏的数据上有更好表现,因为它允许更大范围的噪声探索。选择哪套没有绝对答案,最好根据数据特性来定,并且把噪声调度函数当成模型的一部分一起优化。
4.2 概率流ODE与确定性采样
当扩散过程的所有边际分布已知时,可以推导出一个确定性的常微分方程,它和原SDE拥有完全相同的边际分布。这个ODE被称为概率流ODE。它的意义很实在:采样时可以完全丢开随机性,用确定性积分求得样本,这意味着同一个起点只会得到同一个结果,生成过程变得可复现,也方便在潜空间上做插值和编辑。
我从实践角度特别看重概率流ODE的一点是,它能和各类高阶数值积分器配合使用,把原本需要几百步的采样压缩到几十步甚至更少,同时保持不错的视觉质量。这一下子就把采样效率从"勉强能用"拉到了"工程可用"的水平。不少实现了快速采样的工作,核心就是在这个ODE上加速,而非改变网络结构。
4.3 统一框架对实际项目为什么重要
统一框架带来的最大好处,是让我可以把模型训练和采样策略解耦。这意味着我在训练阶段只需要专注一个共享的score网络,采样阶段却可以根据需求自由选择:要多样性就选SDE,要效率就选概率流ODE,要进度可控就选多步确定性积分。这种灵活性在实际项目里非常珍贵,因为业务需求经常在"质量优先"和"速度优先"之间切换,有了统一视角就不需要为每一种需求重新训练一套模型。
另外一个隐藏的红利是代码层面的共享。我在某个模拟项目的实现里,仅用一套基础训练代码,就分别支持了VE-SDE、VP-SDE和对应的ODE采样,不同设定之间的切换只是一个参数配置的问题。这种代码架构上的收益往往比算法本身还实用,因为我们都知道,模型迭代里最难缠的其实不是模型结构本身,而是那一片需要反复重构的训练和采样管线。
5. 训练稳定性和超参调试:踩出来的经验合集
5.1 损失函数和噪声调度的搭配
训练目标的实现细节往往是复现效果差别的最大来源。同一个理论目标,换一种离散化方式,写出来的损失函数可能有微妙偏差。以Denoising Score Matching为例,很多实现直接把模型输出定义为噪声预测,然后用简单的均方误差做损失;也有实现把模型输出定义为score本身,再去乘上一个和噪声方差相关的加权系数再算损失。这两种写法理论等价,但实际收敛曲线完全不同,尤其是不同噪声等级的梯度量级差别极大,不设置合适的加权函数会导致模型只学会了对某一尺度噪声的响应,其他尺度几乎失效。
我做实验的习惯是先把每个噪声尺度的损失单独记录下来,观察到底哪个尺度贡献了主要梯度。如果某一级的损失长期居高不下,优先怀疑加权系数不够;如果某一级损失趋近于零,则要怀疑该级样本数量过少或者噪声分布太极端。这套诊断流程救了我不少次,因为训练大多数时候不是网络容量不够,而是目标在各尺度间不平衡。
5.2 EMA与学习率:被低估的稳定性来源
Score-based Models的训练非常依赖指数滑动平均(EMA)机制。以我的经验,不开启EMA的模型在采样时经常出现细节抖动,明明训练损失已经很低,采样出来的样本却像蒙了一层雾。开启EMA并设置合适的衰减率后,样本质量会有肉眼可见的提升。这个机制的本质是对网络参数的周期性平均,等效于给模型做顺滑,能显著抑制score估计中的随机波动。
学习率的选择也要比普通分类任务更谨慎。由于目标涉及噪声多尺度平衡,学习率稍微调大一点,小噪声尺度的响应就会开始漂移,最终表现为生成样本边缘过锐或带有伪影。我自己多次实验后倾向于采用warmup策略,先在几百步内把学习率从零斜坡上升到一个初始值,之后再按余弦曲线缓慢衰减,这样能让不同尺度的score学习进度更加同步。
5.3 评估与监控:别只盯着FID看
用FID这类指标做量化评估没有错,但在调试阶段只盯单一指标会误导方向。FID对样本的全局分布特征比较敏感,但对局部伪影宽容度较高,屡次出现"FID还行但肉眼一看全崩了"的情况。所以我的习惯是定量指标和肉眼检查同时进行,每隔固定步数采样一批样本并快速浏览。
另外,我会额外监控采样轨迹的能量变化——即每一步score函数输出的范数大小。如果采样后期score范数仍然异常巨大,说明模型在低密度区域的估计不可靠,这种问题靠增加采样步数解决不了,必须回到训练阶段去修噪声调度或者数据增强。比多跑几十步采样更有效的,往往是做出这种回到训练根源的诊断。
6. 一个模拟项目的完整复盘
6.1 项目背景与设定
我拿一个模拟数据集(简化版本的高维混合分布)来说明整个流程,因为真实数据集和这个流程没有本质区别,只是可视化上更抽象。数据集由若干高斯分量混合构成,分量之间有重叠也有完全分离的区域。这种设定能很快暴露模型在低密度区域的行为——如果score学得不到位,采样结果会在一部分模式上集中,其他模式被弃置。
训练采用了一个中等规模的U型网络结构,输入加噪样本和噪声等级,输出噪声预测。损失函数使用加权的Denoising Score Matching,噪声等级从很小跨度到接近数据尺度,一共划分了三十个离散等级。整个训练过程用固定的随机种子,避免因采样随机性干扰评估结果。
6.2 调试链路里真正起作用的三处修改
第一处修改是把噪声等级的对数空间从均匀采样改成了按数据尺度分段的非均匀采样——靠近数据原始尺度的等级要多采样,远离的可以稀疏一些。这一改,小噪声等级对应的损失立刻下降,最终生成样本的细节保真度大幅提升。
第二处是给采样阶段加了预测校正流程:先走一步朗之万更新,再用score的梯度方向做一步校正,交叉进行。这比单纯增加采样步数对模式的覆盖更有效。第三处则是对EMA衰减率做了细致搜索,发现偏大一点的衰减率在这个任务里能明显抑制采样噪声。
6.3 与GAN方案对比后的实际体会
针对这个模拟项目,我还用同一套评估协议对比了GAN方案的实现。GANs在模式覆盖上天然容易发生模式坍缩,训练不充分的时候若干模式被完全忽视;Score-based Models则展现出一个让人欣慰的特性——即使训练不完美,采样结果仍然能覆盖全部模式,只是部分模式边界会比较模糊。这说明score学习到的是一条连续的梯度场,而不是一个脆弱的博弈均衡,因此在有限的训练预算下更稳健。
当然也有代价——Score-based Models的采样计算量明显更大,同一批样本的生成耗时大约是GAN方案的五到十倍。所以真要选型的时候,并不存在完胜的方案,一切取决于你更在意稳定性还是效率。我的结论是:凡是分布复杂、模式众多、训练预算有限的场景,Score-based路线往往是更稳妥的长线投资;而在线生成、资源受限、单峰或多峰简单分布的场景,GAN系或者更轻量的方案可能依然更合适。
如果非要在最后分享一条实战经验,我觉得是:不要被"score是特殊的高维技巧"这种想法吓退。它的本质就是一个回归问题——预测噪声、对齐梯度、控制噪声调度,和你在别的任务里做的那些回归工作没有本质差别。真正拉开效果差距的,不是哪种理论更高深,而是你有没有精细对待噪声尺度的划分、损失加权和采样步长这三件小事。