☰
Score-based Models:扩散模型背后隐藏的核心原理与工程实践
2026/10/22 18:42:16 网站建设 项目流程

先下个结论:Score-based Models,就是近两年把生成模型搞出大动静的那条技术路线里,藏得最深的一个抽象核心。如果你已经在用扩散模型生成图像、音频或者分子结构,那你其实已经在和它打交道,只是很多教程直接拿扩散模型讲,把score这套底层机制当成黑盒跳过去了。这个项目标题本身不带任何场景描述,我做这个方向时就是从几篇经典工作倒推出来的,所以这篇就按我理解它的顺序来讲,从“到底学的是什么”一直拆到“怎么调参才不至于崩”。

这个东西适合谁?适合已经跑通扩散模型但总觉得哪里没想透的人,也适合刚接触生成模型、想知道GAN之外还有什么路可以选的人。它不需要你有多深的数学功底,只要你愿意把公式里的每一项都落到“网络到底在预测什么”这个层面,就能看明白。

1. 先搞清楚Score函数到底是个什么东西

1.1 与其直接猜概率,不如猜概率的“坡度”

生成模型的核心任务,是让机器学会一个数据分布。传统思路有两条:一条像GAN那样,让网络直接产出一个样本,再用判别器逼着这个样本越来越像真的;另一条像VAE那样,先假设一个简单的分布(比如高斯),再把真实分布硬塞进去编码解码。两条路都有一个共同点:都想要一个能算出“这个样本有多大概率”的函数。

问题在于,真实数据的概率分布是一个极高维的曲面,高维空间里大部分地方概率都趋近于零,你根本没法在这么大的空间里一格格去数数,然后归一化出一个概率密度。就算你硬把网络训练成了概率预测器,计算归一化常数也有一步在整个生成过程中跑不动的积分。

Score-based Models换了个角度:不直接学概率本身,而是学概率对输入求导的结果,也就是概率密度函数的梯度。

这个名字里的score一词,指的就是这样一条向量场:它在每个数据点上都指向概率密度上升最快的方向。对一张真实图片附近的点来说,score会把它推向图像流形的中心;对远离数据的杂乱噪声点来说,score很小、方向也不明确,因为周围概率密度变化平缓。

我习惯用一个地形图类比:概率密度就是一座山的高度,score就是山体上每个位置的最大坡度向量。你要找山顶,不需要知道这座山绝对海拔是多少,只需要每一步都顺着坡度往上爬。score函数就是你要的那根登山杖。

1.2 为什么学梯度比学概率更容易落地

要学概率密度,你得面对那个恐怖的归一化常数。而score里面这个常数直接被求导吃掉了。假设真实概率密度是$\pi(x)$,那么score函数定义为:

$\nabla_x \log \pi(x) = \frac{1}{\pi(x)} \nabla_x \pi(x)$

分母上的$\pi(x)$确实在,但注意我们根本没打算显式计算它,我们只是让神经网络直接拟合这个梯度的值。网络不会输出一个概率数,而是输出一个和输入x维度相同的向量。比如一张三通道256x256的图,网络输出就是3x256x256的向量场。

这个思路带来的直接好处是:不需要知道归一化常数是多少,就能完整刻画数据的分布形态。因为一个分布用密度函数描述也好,用梯度场描述也好,信息量是等价的——只不过一个给出高度,一个给出坡度。生成的时候,从一堆随机噪声出发,顺着坡往上爬,就能爬到数据流形的高概率区。

1.3 一个完整的Score-based Model需要回答三个问题

任何一个基于score的生成模型,拆开来看都是在解决三件事:

  • 用什么目标函数让网络学会“真实数据分布”的score?
  • 学到的score怎么用来生成新样本?
  • 训练和采样过程中,怎么保证数值稳定、不崩?

这三个问题互相之间牵扯很深。比如你辛辛苦苦让网络学了某一个噪声水平下的score,直接用朗之万动力学去采样,大概率什么都采不出来,因为真实数据流形所在的区域,score指向非常微弱,从任意随机点出发,根本走不到山顶。这就需要把问题放到多尺度噪声里去重新审视——这是后面第四节的重点。

所以我的建议是:不要一上来就啃SDE那套形式化框架,先把上面三个问题的第一个搞清楚,训练目标没搞明白,后面所有采样技巧都是空中楼阁。

2. 训练目标:怎么让网络学会真实的Score函数

2.1 Score Matching和Fisher散度

最直觉的监督信号就是“预测值 vs 真实值”的L2距离,但麻烦在于真实的score函数我们是不知道的——这正是我们要学的东西,不能用未知目标来监督自己。

Score Matching给出一个妙招:最小化网络输出和真实score之间的Fisher散度。Fisher散度长这样:

$J(\theta) = \frac{1}{2} \mathbb{E}{p(x)} \left[ | s\theta(x) - \nabla_x \log p(x) |^2 \right]$

这式子没法直接算,因为里面还是有真实score。但Score Matching定理证明,在期望下标下,这个目标等价于另一个只包含网络和真实数据样本的式子:

$\tilde{J}(\theta) = \mathbb{E}{p(x)} \left[ \mathrm{tr}(\nabla_x s\theta(x)) + \frac{1}{2} | s_\theta(x) |^2 \right]$

就是说,你只需要让网络在真实数据点上的输出接近零向量,同时让网络输出的导数(对输入的雅可比矩阵的迹)越小越好。直观上这就是在说:真实数据点应该在概率密度的山顶附近,所以那里的梯度应该是零或者指向山顶,同时这个梯度场不能变化得太剧烈。

从实操角度讲,这个目标初版非常难用。求$\mathrm{tr}(\nabla_x s_\theta(x))$意味着要对输入求二阶导,对一张图片来说雅可比矩阵维度极高,而且自动求导二阶传播消耗巨大。我第一次跑这个目标时,一张32x32的小图,batch size设8,显存直接爆掉,训练速度慢到离谱。

所以实际工程里基本没人直接用原始Score Matching。

2.2 Denoising Score Matching:给原始目标打个补丁

Denoising Score Matching绕开了难算的迹。核心思路非常朴素:故意往数据里加高斯噪声,然后让网络去预测“加了噪声的数据点”到“真实数据点”的方向和距离。

假设加噪后的分布是$q_\sigma(\tilde{x} | x) = \mathcal{N}(\tilde{x}; x, \sigma^2 I)$,Denoising Score Matching的目标是:

$\arg\min_\theta \mathbb{E}{q\sigma(\tilde{x}, x)} \left[ | s_\theta(\tilde{x}) - \nabla_{\tilde{x}} \log q_\sigma(\tilde{x} | x) |^2 \right]$

注意,这里的监督信号是“条件分布下的score”,它是有闭式解的,等于$-\frac{\tilde{x} - x}{\sigma^2}$。也就是让网络预测加噪数据指向干净数据的那条向量,再除以噪声方差。

这个做法漂亮在两点。第一,不需要对网络求二阶导,只需要求一个简单的L2 loss。第二,加噪这个行为本身就是训练样本的增广,同一个干净样本在不同噪声水平下可以产生无数训练对,数据效率高了不少。

2.3 网络输出要怎么理解

我自己第一次实践时最困惑的是:网络输出的向量,到底对应哪个“方向”?

搞清楚这一点,要从score的定义和加噪的关系说起。网络$s_\theta(\tilde{x})$学的是$\nabla_{\tilde{x}} \log q_\sigma(\tilde{x})$,其中$q_\sigma$是加噪后的边缘分布。Denoising Score Matching告诉我们,最优解是$(\tilde{x} - x)/\sigma^2$,也就是从加噪点指向干净点的向量,除以噪声方差。

你仔细看会发现:网络本质上在做的,是在估计“我比干净数据多出来的那部分噪声”。如果把$\tilde{x} - x$看成噪声向量,那么score就是噪声向量反向后除以$\sigma^2$。所以很多实现里,干脆让网络直接预测噪声,然后把score用“噪声除以方差”来逼近。这也是为什么后来扩散模型的训练代码里,网络输出直接就是噪声图像。

提示:不要把score和噪声预测割裂看待。它们只是一枚硬币的两面,score乘上负的$\sigma^2$就是网络预测的噪声。这个换算关系在你写采样代码时会经常用到。

2.4 给不同噪声等级配不同权重的训练细节

单个噪声水平$\sigma$下的denoising score matching可以稳定学到一个带噪数据的score,但实际采样时,我们需要的是一系列噪声水平。如果你用多个$\sigma$,就必须考虑:大噪声样本和小噪声样本的loss量级不一样。

噪声越大,score的幅值越小(除以$\sigma^2$嘛),loss天然被压得很小;噪声越小,score幅值越大,loss被放大。如果不做处理,网络会本能地偏向学好小噪声的样本,把大噪声下的行为忽略掉。结果就是采样后期路径对不上,从纯噪声出发根本走不进数据区域。

主流做法是把loss按$\sigma^2$加权:

$\mathbb{E}{x, \tilde{x}, \sigma} \left[ \lambda(\sigma) | s\theta(\tilde{x}, \sigma) - (x - \tilde{x})/\sigma^2 |^2 \right], \quad \lambda(\sigma) = \sigma^2$

这样做的效果是:每个噪声尺度上的loss量级大致一致,网络不会偏心。

我在实验里验证过:不配权的版本在纯色背景类数据上还能凑合跑通,一换到纹理比较丰富的真实图像数据,生成的图要么糊成一片,要么全是那种“像素噪点风”,几乎没有中间态。配权之后,各尺度切换才顺滑起来。

3. 采样过程:用朗之万动力学把Score变成样本

3.1 朗之万采样器的直观和数学

训练网络学会score之后,生成样本就成了一个“在场地形图上找高点”的过程。但直接做最速上升会有问题:梯度上升会走向确定性,导致每次生成的都是同一个最优样本,多样性丢失。而且真实数据的score场并不是凸函数地形,直接上升很容易卡在局部极值附近。

朗之万动力学在梯度上升的基础上加了一个随时间衰减的随机噪声项。更新公式是:

$x_{t+1} = x_t + \frac{\epsilon}{2} \nabla_x \log p(x_t) + \sqrt{\epsilon} z_t$

其中$z_t$是标准正态分布采样。理解这个式子可以分成两块:第一块的$\epsilon/2$是在沿score方向稳步推进,相当于“往山顶走”;第二块的$\sqrt{\epsilon}$是随机扰动项,让轨迹有机会跳出局部区域,保持探索性。噪声项的系数是$\sqrt{\epsilon}$而不是$\epsilon$,这是随机微分方程离散化里有深刻原因的——步长越小,随机项的相对作用越大,轨迹越“抖”,但只要迭代够多,最终分布会收敛到真实概率分布。

3.2 步长和迭代次数的权衡

你会遇到一个很现实的问题:步长$\epsilon$选多少、迭代多少次才够?

选太小,网络要走成千上万步才能采到一个样本;选太大,轨迹直接就飞了,score场在小步长假设下成立的近似被打破。

我踩过最大的坑是在一维玩具分布上试,数据是两个相距很远的高斯峰。步长设大了,样本在两个峰之间来回横跳,生成结果方差极大;步长设小了,采样始终停留在一个峰附近,另一个峰从来不被访问,多样性指标看起来稀烂。

后来我发现一个经验法则:先设定最大步长不超过训练时最小噪声水平的几倍,然后让迭代步数反比于步长的平方——这样做虽然总计算量基本恒定,但轨迹收敛质量会稳定很多。省时间的话,从$\epsilon=0.02$开始试,逐步加一倍,看生成质量的FID是上升还是下降。

3.3 为什么单噪声水平的采样一定会遇到“低密度区陷阱”

这其实要回到流形假设。真实数据的分布,在像素空间里是一个低维流形,流形之外的区域概率密度极低。在那些区域,score函数的值接近于零,因为平地没有坡度。

如果你只在单一的大噪声下训练score,那么采样时,初始点全是高维空间里的纯噪声,它们基本都在流形之外。这时候score场几乎没有有用信号,你只能靠朗之万项里的随机噪声瞎逛,逛到什么时候是头?无穷无尽。这就是低密度区陷阱:采样器在低密度区域徘徊,概率分布还没收敛到数据流形,你人已经等不及了。

解决办法,就是这篇文章的重头戏——把score模型从“单尺度”升级成“多尺度”,先在大噪声下快速把样本从纯噪声带到“大概有点数据影子”的区域,再一步步降低噪声,把样本精细到流形上。这个过程听着是不是很像扩散模型的去噪过程?本来就该是一回事。

4. 从静态走向动态:基于时间的Score模型与SDE统一视角

4.1 噪声等级和时间的等价关系

如果一组离散的噪声等级$\sigma_1 > \sigma_2 > \dots > \sigma_N$,它其实就是在标定一条从纯噪声逐步通往真实数据的退化路径。把离散的等级插值成连续变量,这个连续变量就可以解释成“时间”。时间越靠前,数据被破坏得越厉害;时间越靠后,数据越接近原始分布。

于是原来那个“不同噪声等级共享参数”的模型,就变成了一个显式接收时间输入$t$的条件网络:$s_\theta(x, t)$。输入一张带噪图和一个时间标签,输出对应的score向量。这个小小的改动,是Score-based Models从“理论说得通”变成“工程跑得通”的关键。因为它把一个静态无条件的向量场,变成了一条连续可调的动力学路径。

4.2 前向扰动和反向生成:SDE的视角

一旦时间连续化,整个加噪过程就看做一个随机微分方程(SDE):

$dx = f(x, t)dt + g(t)dw$

这里$f$决定数据的均值漂移方向,$g(t)$决定噪声强度随时间的变化。这就是前向SDE:从干净数据出发,被慢慢注入噪声,最终变成标准高斯噪声。

神奇的地方在于,Anderson定理告诉我们:已知前向SDE的结构,就能推导出对应的反向SDE,从噪声倒推回到数据。而反向SDE里,恰好就需要score函数$\nabla_x \log p_t(x)$来指导方向。所以我们训练网络学score,本质上就是在学反向SDE里的漂移项。

这样一个统一视角把所有基于扩散的生成模型都装进了一个框架:所谓方差保持(VP)就是让噪声强度随时间增长但保持总方差有界;所谓方差爆炸(VE)就是让方差直接随时间无界增长。我最初接触时觉得VP更精致,因为理论上数值稳定;但实际实验里VE在不少高质量图像生成任务上表现更暴力。别迷信哪一种,先跑小规模对比再决定。

4.3 和DDPM、NCSN这些具体实现的关系

很多人分不清Score-based Models和扩散模型的区别,这里我一句话说清楚:扩散模型(DDPM类的做法)是Score-based框架下的一种具体离散化方案,它用的是VP路径和特定的噪声调度;而较早的NCSN思路则更接近VE路径,直接用一系列离散噪声等级。

我后来琢磨出来的理解是:DDPM训练时让网络预测带噪数据里的纯噪声,其实等价于让网络学带噪数据的score乘上负的方差系数。所以网上那些“预测噪声”和“预测score”的代码,表面看loss不一样,本质学的是同一个信息。做底层研究或是不想被具体实现束缚的读者,从这个层面统一理解会省非常多事。

5. 实操指南:手把手搭一个可用的Score-based生成模型

5.1 网络骨干和输入条件化的选择

Score-based Models本身不指定要用什么网络结构,因为是给任意连续数据做向量场回归,骨干网络可以自由换。我做图像实验时最常用的还是U-Net变体,因为它的下采样-上采样结构天然适合捕捉多尺度信息,而且能输出和输入同尺寸的向量场。

输出层需要注意:很多人喜欢在最后一层接一个sigmoid或者tanh把输出压到[-1, 1],这在分类任务里正常,但在score模型里是有害的。向量场的范围没有天然上界,网络该输出多大就该输出多大,你硬压到有限区间,学到的score会被截断,采样时坍缩。我一般最后一层就是普通卷积不做激活。

时间输入(或者噪声等级输入)用正弦位置编码嵌入,然后加到网络的每层特征上,这个做法后来被沿用到了几乎所有扩散模型里。别偷懒只在最前面拼一下,后面每层都要注入条件信息,否则网络很难对不同噪声等级的行为做出区分。

5.2 训练配置和Loss计算的完整代码参考

下面是一段我实际在某个小型图像数据集上跑过的训练循环核心代码,用的就是Denoising Score Matching加$\sigma^2$加权。数据假设已经归一化到[-1, 1]。

import torch import torch.nn.functional as F def train_step(model, x, optimizer, sigmas, prob_dist): # x: [B, C, H, W],真实数据batch batch_size = x.shape[0] # 从预设噪声等级分布中采样,这里用log-uniform分布 sigma = sigmas.random_sample(batch_size).view(batch_size, 1, 1, 1).to(x.device) # 生成噪声 noise = torch.randn_like(x) # 加噪后的数据 x_noisy = x + noise * sigma # 让网络以带噪图像和噪声等级作为输入,输出score估计 score_est = model(x_noisy, sigma=sigma.squeeze()) # Denoising Score Matching的目标:网络输出应逼近 (x - x_noisy) / sigma^2 target = (x - x_noisy) / (sigma ** 2) # sigma^2加权,让各噪声等级的loss量级一致 loss = (sigma ** 2) * F.mse_loss(score_est, target) optimizer.zero_grad() loss.backward() optimizer.step() return loss.item()

注意几个关键点:sigma要用log-uniform在预设区间里采样,不能均匀采样。因为噪声等级跨越好几个数量级,均匀采样会大量选中大噪声,小噪声被饿死。损失函数的mse不要自己在batch维度上mean之前做奇怪的手脚,直接对每个维度做MSE再让sigma权重作用就够了。

EMA(指数移动平均)在这个模型里几乎必加。我见过最夸张的情况:不加EMA,FID从10到30之间反复横跳;加了EMA,FID稳定在12左右。具体操作是,用一个小一点的学习率维护一份滑动平均权重,每隔几百步把它同步给评估用的模型。

5.3 采样代码:预测器-校正器思路

单纯用朗之万从纯噪声一直漫漫迭代也行,但效率很低。工程上更常见的是“预测器-校正器”混合法:先用一个确定性或者半确定性的步骤快速推进(预测器),再用几步朗之万修正score场偏差(校正器)。

在我的实现里,简化版是这样:

def sample(model, sigmas, shape, steps=200): model.eval() # 从纯噪声出发 x = torch.randn(shape, device=sigmas.device) * sigmas[0] # 从大噪声到小噪声逐步推进 for i in range(len(sigmas)): sigma = sigmas[i] dt = sigmas[i+1] / sigma if i < len(sigmas)-1 else 0.01 # 预测器步骤:沿score走一步 with torch.no_grad(): score = model(x, sigma=sigma) x = x + 0.5 * dt * sigma * sigma * score # 校正器步骤:多步朗之万 for _ in range(2): with torch.no_grad(): score = model(x, sigma=sigma) noise = torch.randn_like(x) * sigma * 0.1 x = x + 0.1 * sigma * sigma * score + noise return x

这段代码不是最优性能版本,但是最直白。预测器部分里的$dt * \sigma^2$是反向SDE离散化时带来的系数,实际上是score和噪声方差乘积对时间的积分效应。校正器里的噪声项不能省略,它保证轨迹不会僵死在一个点。

5.4 训练不收敛的第一排查顺序

我发现很多人遇到loss不下降,第一反应就是调学习率、改网络结构,其实应该按顺序排查数据归一化。输入数据没有归一化到零均值单位方差的话,score的尺度会乱七八糟,loss曲线看起来在下降但生成质量完全不对。其次看梯度范数:打日志记录梯度的L2 norm,如果梯度范数量级超过几十,基本可以判断score输出尺度过大,检查一下是不是最后一层没有合理的初始化或者loss权重配错。

还有一个很容易被忽略的点:模型输出score的符号方向。如果你把符号搞反了,训练loss不会升高到离谱,但采样结果直接发散成高斯噪声或者变成纯黑纯白图像。我第一次调的时候,生成的图永远是一坨均匀灰色,查了一整天发现是U-Net的跳跃连接里把某个恒等映射加反了,score方向整体翻转了。这种低级错误非常好排查,但也非常费时间。

6. 常见问题与排查技巧实录

6.1 生成结果全是某个固定“模板”图像

这个症状通常意味着多样性崩溃。常见原因有三个:一是EMA没开,模型权重在训练后期震荡太厉害;二是朗之万采样里的噪声项被省略了——如果你把采样代码里的随机扰动项去掉,那么给定同一个初始噪声,后续步骤几乎是确定性的爬坡路径,很容易同时落到模式峰顶;三是噪声等级范围的覆盖不够,模型没有学到从小噪声到中等噪声区域足够丰富的score结构,导致所有轨迹最后聚到同一个极端。

我修过最多的案例是第二种。很多初学者会想“反正有score在指导,噪声项是不是可以不加”,结果一加就崩。请记住,朗之万里的噪声项不是额外扰动,是采样器理论存在的必要成分。

6.2 Loss下降正常但生成的是模糊噪声

这个情况大概率是“网络把score学到了,但采样路径走错了”。我遇到过网络在某一批数据上训练得很好,但采样时步长设置太大,轨迹像喝醉了一样乱飞,采出来的东西看起来就是随机色块。

解决方法是把采样步长缩小一个数量级,同时增大迭代步数。我之前用某个人像数据集,步长从0.05缩到0.005之后,FID直接下降了一半多。

6.3 训练时间和显存爆炸

Score模型比GAN要重得多,这是它的天然劣势。如果你只是做小规模验证,建议先用32x32或者64x64的尺寸,把训练损失、梯度norm、采样质量都跑通后,再放大到高分辨率。不要一上来就套128x128,你的显存会瞬间清零。

显存不够时的branch:减小batch size不一定最优,可以切patch训练。但要注意patch训练会导致score场边界效应,需要在patch边界做重叠处理,否则生成图像边缘会出现奇怪的条带。我用过一个工程技巧:把整图切成重叠的块,对每个块单独算score,最后在重叠区域取加权平均。代价是采样速度变慢,但省显存非常有效。

6.4 网络训练不稳定时怎么看日志

我的习惯是固定打印三个指标:总loss、梯度L2范数、各个sigma区间内loss的分位值。如果小sigma区间的loss突然暴涨,大概率是训练步长过大导致网络在小噪声区域过拟合加噪数据的细节;如果大sigma区间的loss始终下不来,先检查噪声调度区间是不是设得过小,导致完全没有高噪声样本供网络学。

有条件的话,在训练过程中定期跑一次中间采样,把生成图存下来。这个“可视化采样日志”比任何指标都直观。我见过很多loss曲线一路下降但采样图一直崩的情况,原因只有采样环节才能看出来。

7. 这个模型能做什么:应用场景与选型建议

7.1 连续型数据的天然优势

Score-based Models学的就是一个连续向量场,对于图像、连续音频波形、三维点云坐标、分子构象这些天然连续的数据,它的优势非常明显。你不用像GAN那样担心训练不稳定,也不用像VAE那样被迫牺牲清晰度。对图像生成,它就是目前最强一类方案的基石之一;对分子构象,它能直接在三维坐标上做连续扰动,生成多样且能量合理的结构。

如果你在做的任务是连续数据且需要高多样性,score类模型通常比GAN更稳。代价就是推理速度慢,几十上百步迭代没法像GAN一样一次前向就出图。

7.2 条件生成和可控生成怎么做

最偷懒且好用的办法是“引导注入”:训练时把条件信息(类别标签、文本嵌入、掩码图)输进网络,采样时利用一个预训练分类器的梯度,对score场做偏移:

$\nabla_x \log p_t(x|c) = \nabla_x \log p_t(x) + \alpha \nabla_x \log p_t(c|x)$

这个公式的意思是,无条件score负责“让样本看起来像真实数据”,分类器梯度负责“让样本满足条件”。$\alpha$越大,条件满足度越高,多样性越低。这个思路后来被大量扩展,但核心从来没变过——它建立在score模型灵活的“场叠加”特性上。别的生成模型想做这种矢量场级控制,绕了好大一圈。

7.3 目前最大的短板:离散数据

Score函数天然定义在连续空间里。对于离散序列(文本、代码、离散化token序列),梯度的概念不再直接适用。虽然可以通过连续松弛把离散点嵌入连续空间再学score,但效果远不如图像领域那么惊艳。这也是为什么大语言模型领域没有全面倒向扩散方法的主要原因之一。

如果你要做纯文本生成,我的建议是:除非你有明确的非自回归、并行生成需求,否则别硬用score模型。零样本打磨、语言流利度这些指标,还是自回归模型更强。二者的混合方案是现在很多研究在探索的方向,还没有一个统治性的成熟范式。

7.4 与其他生成模型的横向对比

拿4个维度说感受:

  • 训练稳定性:score最稳,GAN最娇气。我做项目时基本可以“开瓶水放着跑”,GAN则要守在旁边随时准备调参。
  • 样本多样性:score和GAN都能做到很丰富,VAE差一大截。
  • 推理速度:GAN最快,一次前向;score最慢,几十步起步。
  • 可控性:score在结构上适合做条件生成,GAN则要靠辅助网络或损失函数来约束。

还有一点经常被忽略的是内存占用:score模型训练和采样都极其吃显存,比同等参数量的GAN要高不少。如果你只有一张消费级显卡,建议先在小图尺寸上把流程跑通,再考虑怎么省显存。我在某实验室那阶段就用过一张中等显存卡,把批量降到4,靠梯度累积跑通了一个小规模图像生成实验,效果打不过大卡版本,但整个链路验证是没问题的。

写在最后的一点个人经验

Score-based Models这个标题背后的内容量,比字面上看起来大得多。我觉得它最值得学的地方不是那些SDE公式推导,而是“怎么把一个看起来不可解的概率问题,转化成可监督的向量场回归问题”这一整套思维范式。每次我遇到新的生成任务,第一反应都是想数据流形长什么样、加噪声后score场会如何变化——这个习惯就是被score模型训练出来的。

如果强迫我给新手一个最快的上手路径:先在一个低维玩具分布(两个高斯混合)上实现否定态,把训练和采样每个环节可视化,再上图像。不要一上来就复现大模型,你会在调试过程中累死。玩具分布跑通之后,你会发现理解扩散模型、理解引导、理解各种变体,全都顺了。

最后分享一个小技巧:训练时把噪声等级的范围和采样的初始噪声范围严格对应上。我发现很多采样问题不是模型没学好,而是采样器初始的噪声方差远大于训练时见过的最大噪声,导致模型面对“陌生输入”瞎输出。这个问题我前后踩了三轮才意识到,真不是网络的问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询