news 2026/10/27 11:23:51

从分数匹配到朗之万采样:NCSN图像生成全流程拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从分数匹配到朗之万采样:NCSN图像生成全流程拆解

前阵子有位朋友在复现图像生成模型时卡住了,他把论文里的公式抄了三遍,代码也照着写了一遍,结果训练损失一路往下掉,采样出来的图却是一团彩色噪点。我去看他代码,问题出在噪声级别和采样步长的配比上——这套东西的坑几乎全藏在细节里。所以这篇我想把 NCSN 从头到尾掰一遍。NCSN 全称 Noise Conditional Score Network,中文一般叫噪声条件分数网络,是分数匹配类生成模型里绕不开的一块基石。它做的事情说起来不复杂:学一个网络去估计数据分布对数密度的梯度,然后用朗之万动力学把随机噪声一步步推到数据分布上去,最终生成图像。适合谁看?如果你已经知道扩散模型大体上是怎么加噪去噪的,但每次看到“分数”“score matching”“Langevin”这几个词就自动跳过,那这篇就是为你写的。我会把公式背后的直觉、参数的来由、代码里那些没人告诉你的小动作全部摊开讲,争取让你读完能自己撸一个能出图的最小版本。

1. 整体思路拆解:为什么要绕道“分数”去做生成

1.1 直接建模概率密度,高维下几乎走不通

生成模型的老路子是显式建模 p(x),也就是想办法写出一个带参数的密度函数,让它在训练数据上概率高、在其他地方概率低。这条路在低维还能玩,到了图像这种几千上万维的空间就非常难受:归一化常数 Z = ∫ exp(-E(x)) dx 是一个高维积分,算不出来也近似不好,于是整个密度只能算到一个常数倍。变分自编码器靠引入隐变量和变分下界绕开了这个问题,生成对抗网络干脆不建模密度、只学一个判别信号,扩散模型和 NCSN 则选了第三条路——不去碰那个该死的 Z,只去碰它对 x 的导数。

这个思路的关键观察是:如果你把密度写成 p(x) = exp(-E(x)) / Z 的形式,那么对 x 求梯度时,log Z 是常数,直接消失。也就是说 ∇ₓ log p(x) = -∇ₓ E(x),这个量完全不需要知道 Z 就能定义、也能被估计。NCSN 的全部内容,本质上就是在教一个神经网络去拟合这个梯度场。我给这个量起了个外号叫“地势图上的箭头”——密度高的地方像山谷,箭头永远指向“上坡”方向,也就是概率增大的方向。你手上有了整张地图的箭头,剩下的事就是放一个小球在随机位置,让它顺着箭头走,同时脚下不停抖动,抖着抖着它就滚进山谷里,对应的就是一个像样的样本。

1.2 分数场加朗之万,取代了“从噪声一步步去噪”的叙事

这里有个容易混淆的点。很多人第一次接触扩散模型是从 DDPM 那种“加噪-去噪”的叙事进入的,脑子里默认生成过程是一串离散的时间步,每一步都在预测上一步加进去的噪声。NCSN 的叙事完全不同:它没有时间步的概念,只有一组噪声尺度;它不是逐步去噪,而是在每个噪声尺度上做若干步带随机扰动的梯度上升。这两套语言描述的是同一个东西的两面,但在实现细节上天差地别。如果你拿着 DDPM 的代码习惯去写 NCSN,多半会在采样循环那里把自己绕晕,因为 NCSN 的外层循环是噪声级别,内层循环才是朗之万步数,而且每一步都要重新喂一次当前噪声级别给网络。

我当初理解这套东西花的时间主要在两个地方:一是想明白为什么必须用多个噪声尺度而不能只用一个,二是想明白为什么采样步长必须跟着噪声尺度一起变小。这两个问题后面会各花一节讲透。先说结论:单一噪声尺度下,你要么看到的分布太模糊、要么根本采样不动;而步长如果不跟着缩小,后期的小噪声阶段会直接把样本抖散。这两条就是 NCSN 区别于“朴素分数匹配”的全部工程智慧。

1.3 整条链路长什么样

把流程压缩成一句话:训练时给干净图像加不同强度的噪声,让网络学会“在某个噪声强度下,从带噪图像指回干净数据分布”的方向;采样时从纯噪声出发,先在强噪声下走大步找到大致的位置,再逐步降低噪声、缩小步长,做精修。为了让你有个全局印象,我把各环节的作用和常见参数列在下面这张表里,后面每一节都会展开。

环节干什么关键参数常见取值参考
噪声级别设置定义一组从大到小的噪声强度最大噪声 σ₁、最小噪声 σ_L、级别数 Lσ₁≈1、σ_L≈0.01、L 取 10 左右
数据加噪给干净样本叠高斯噪声得到训练输入高斯噪声标准差与当前级别 σ 一致
网络前向输入带噪图像和噪声级别,输出分数估计网络结构、条件注入方式U 型编解码结构 + 条件归一化
损失计算让输出对齐“负噪声除以 σ”加权系数 λ(σ)λ(σ) = σ²
采样外层按噪声级别从大到小遍历级别顺序严格降序
采样内层每个级别做若干步朗之万更新步数 T、基准步长 εT 取 100 上下,ε 取 1e-4 量级

2. 分数函数到底是什么:从几何直觉到可算的目标

2.1 一个能画出来的例子

假设数据是一维的,分布是两个高斯山峰,左边高一点、右边矮一点。分数函数 s(x) = d/dx log p(x) 就是一条曲线:在左峰左侧为正、指向峰;在左峰右侧变负、指回峰;两峰之间的鞍点附近,左半边指向左峰、右半边指向右峰。你如果沿着这条曲线指的方向走,所有点最终都会流向两个峰之一,谁的分越高,吸引的“流域”越大。这个东西在二维里可以画成矢量场,非常直观:它就是一张“所有点该往哪走”的地图。多维情况下 s(x) 是一个和 x 同维的向量,每个分量是 log p 对对应坐标的偏导,含义完全一样。

这就解释了为什么生成模型只需要分数就够了:采样要的从来不是密度的绝对值,而是“往哪走”的方向信息。绝对高度只影响每个模式被选中的概率,而这个信息其实藏在分数场的散度里——不过那是另一个话题了。NCSN 选择完全放弃归一化常数,把建模目标整个搬到分数场上,是一个相当漂亮的问题重写。

2.2 分数匹配:把不可算的目标变成可算的

问题来了:我们知道 s(x) 的定义,但训练数据只给了我们一堆样本点,并没有给 p(x) 的解析式,那怎么监督?直接的想法是最小化期望平方误差:

目标 = 1/2 · E_{p_data(x)} [ || s_θ(x) - ∇ₓ log p_data(x) ||² ]

但右边那个 ∇ₓ log p_data(x) 正是我们想求的东西,用未知去监督未知,循环了。原始的分数匹配给出了一个漂亮的解法:对上面这个期望做分部积分,把涉及数据分布导数的项转移成只涉及 s_θ 自身导数的项,得到等价目标:

目标 = E_{p_data(x)} [ tr(∇ₓ s_θ(x)) + 1/2 · || s_θ(x) ||² ]

这个式子的好处是完全不需要知道数据的真实分数,只依赖 s_θ 和它的雅可比矩阵迹。坏处也很明显:tr(∇ₓ s_θ) 要求对网络输出的每个分量求所有输入的偏导,参数量一大就是灾难。图像任务里网络输入是几万个像素,这个迹几乎不可能高效算出来。于是有了后续的改良路线,比如用随机投影去近似这个迹——切片式分数匹配就是这么干的,思路是把雅可比向量积算出来再投影,代价从 O(维度) 降到 O(1),但方差偏大,实际训练不太稳定。

2.3 三条路线的取舍

到这里其实有三条技术路线摆在面前,我把它们的优劣摊在表里,你就能明白为什么最终 NCSN 选了第三条。

路线核心思路优点致命短板
原始分数匹配分部积分消掉未知的数据分数目标精确,无偏需要雅可比迹,高维算不动
切片式分数匹配随机投影近似雅可比迹计算量可控,理论无偏方差大,训练初期抖得厉害
去噪分数匹配用加噪核的条件分数替代真实分数只需一次反向传播,稳定高效引入噪声,需要多尺度来兜底

第三条路就是 NCSN 采用的方案,也是它真正能跑起来的原因。注意它“引入噪声”这一点——很多人觉得加噪是为了数据增广,其实不是,加噪是数学上必需的,它是把不可算的目标变成可算目标的代价。而“必须用多个噪声尺度”这个设计,恰好就是为了把加噪带来的偏差补回来。这两件事是绑定在一起的,缺一不可。

3. 去噪分数匹配:一个高斯核带来的化简

3.1 从加噪分布推条件分数

去噪分数匹配的出发点很朴素:既然直接估 p_data 的分数很难,那我先把数据弄“糊”一点,糊到一定程度,分布的分数就好估了。具体做法是给每个干净样本 x 叠一个标准差为 σ 的高斯噪声,得到 x̃ = x + σ·ε,ε 是标准正态。此时带噪样本的分布 q_σ(x̃) 是原分布和一个高斯核的卷积。高斯核有个极好的性质:它的对数梯度是解析的。对于条件分布 q_σ(x̃ | x) = N(x̃; x, σ²I),有:

∇_{x̃} log q_σ(x̃ | x) = (x - x̃) / σ² = -ε / σ

也就是说,“从带噪点指回干净点”的方向,正好等于负的噪声除以噪声标准差。这一步是整个方法的地基,因为它是闭式解,不需要任何近似。

接下来的关键定理是:最小化“网络输出与加噪分布真实分数之间的误差”,在数学上等价于最小化“网络输出与条件分数之间的误差”。这个等价关系是去噪分数匹配的核心结论,证明过程涉及对 x 求期望后配方,形式上和去噪自编码器的损失推导很像。它的实际意义极其重大——我们本来要拟合的 q_σ(x̃) 的分数依然不知道,但条件分数是已知的,于是监督信号有了着落。

3.2 目标函数最终长成什么样

把上面的条件分数代进平方误差,得到每个噪声级别下的损失:

L(σ) = 1/2 · E_{x, ε} [ || s_θ(x + σε, σ) - (-ε/σ) ||² ]

稍微整理一下,把 -ε/σ 移项,可以写成两种等价形式。第一种是直接拟合目标 -ε/σ;第二种是让网络输出乘上 σ 之后再去拟合 -ε,也就是:

L(σ) = 1/2 · E [ || σ · s_θ(x + σε, σ) + ε ||² ]

这两种写法在梯度上是等价的,但数值表现差别不小。第二种把尺度归一化了,让不同 σ 下的损失量级大致可比,训练更稳。实际写代码我更推荐第二种,或者干脆让网络内部就输出一个和 ε 同量级的量。这个细节在论文原文里也提到过,因为 σ 从 1 变到 0.01,两种写法的梯度范数相差两个数量级,混在一起训练很容易把小的那部分淹没掉。

还有一个常被忽略的点:这里的 ε 是逐像素独立采样的标准正态,所以 || ε ||² 的期望正好等于像素数。这意味着损失的数量级会随图像尺寸线性增长,做不同分辨率实验时别拿绝对损失值横向比较,没有意义。

3.3 噪声尺度为什么必须是一组而不是一个

假设只用单一噪声 σ。如果 σ 偏大,带噪分布被抹得太平,几乎接近各向同性高斯,网络能轻松学会分数,但这个分数对应的是“糊掉之后”的分布,采样出来的图也就糊成一团,细节全丢。如果 σ 偏小,带噪分布已经非常接近原始分布,接近到几乎处处是零密度区,样本点极其稀疏,网络根本没有足够的信息去泛化学出分数场,采出来的东西会是碎片。这就是两难:小的 σ 保真但学不动,大的 σ 好学但失真。

解决方式是把两者串起来:用一组从大到小的 σ,让网络在每个尺度上都学会对应的分数,采样时从最大的 σ 开始往下走。大尺度负责“定大方向”,把随机初始化的点拉到数据大致所在的区域;小尺度负责“修细节”,把粗糙的样本打磨出纹理。这就像雕塑,先用大锤定大形,再换小凿子修细节——你不可能拿着雕刻刀去凿一块原石,也不能指望大锤修出眼睫毛。这个类比我觉得比任何公式都更能说明为什么要退火。

4. 网络设计与训练:让模型知道当前在哪个尺度

4.1 噪声级别的注入方式

网络要同时应付十个不同的噪声级别,就必须知道当前处理的是哪一级。最直接的做法是把 σ 作为额外的输入通道拼到图像上,但实验下来效果一般,因为归一化层会把这个常数信息洗掉。NCSN 采用的是条件归一化:U 型结构的每一个归一化层里,缩放系数 γ 和偏置 β 不再是可学习常数,而是 σ 的函数。具体实现上,把 σ 过一个小的嵌入网络,得到一组向量,再线性映射成每层的 γ 和 β。这和后来条件生成里常见的特征调制是同一个思路。

为什么这个方式更有效?我的理解是,σ 影响的是整个网络的“工作点”——同一个卷积核在大噪声下应该表现得像平滑滤波器,在小噪声下应该表现得像边缘增强器。把 σ 注入到归一化层,等于在每个特征层重新校准了动态范围,比从输入层拼一个常数要强力得多。这一点我在复现时做过对比:注入到输入层时,用同一个采样流程,小噪声级别下的样本会出现明显的高频伪影;改成条件归一化之后,伪影基本消失。

还有一个实操细节:归一化层不要用批归一化。批归一化在训练时依赖当前批次的统计量,采样时要么用滑动平均要么用批内统计,两种都会带来不一致,尤其在单批样本很少的时候更明显。实例归一化或者组归一化更合适,它们的统计是逐样本算的,训练和推理行为一致。

4.2 多个尺度的损失怎么加权

十个噪声级别的损失直接平均,会出问题。因为不同 σ 下损失的量级差很多,小 σ 那一项的梯度往往比大 σ 小一两个数量级,训练早期基本被忽略。NCSN 给出的做法是按 σ² 加权:

L_total = (1/L) · Σ_i λ(σ_i) · L(σ_i),其中 λ(σ_i) = σ_i²

这个权重的来由很直观:理想情况下网络输出的分数模长大约在 1/σ 量级,所以平方误差本身的量级是 1/σ²,乘上 σ² 正好把它拉回同一水平。这是一次“按量级归一化”,不是什么玄学调参。我在实现时试过 λ=1 和 λ=σ²,前者训出来的模型在大噪声级别上明显欠拟合,采样初期的落点经常跑到数据流形外面,后期怎么修都修不回来。

关于 σ 的取值,常用的是几何序列:σ_i = σ₁ · (σ_L/σ₁)^((i-1)/(L-1))。σ₁ 的选取原则是“大到足以覆盖整个数据分布的尺度”,数据归一化到 [-1,1] 之后取 1 是合理的,因为任意两个样本之间的距离量级就是这个数。σ_L 的选取原则是“小到接近数据分辨率的下限”,对 32×32 的图像取 0.01 差不多,对更高分辨率的图可以再小一点。级别数 L 取 10 是个平衡点,再多收益递减,再少覆盖不够连续。

4.3 训练循环与几个必须注意的地方

下面是我自己用的一套训练骨架,写在 PyTorch 风格里,注释里标了几个容易踩的坑。

sigmas = geometric_sequence(sigma_1=1.0, sigma_L=0.01, L=10) for x in dataloader: # x 已归一化到 [-1, 1] sigma = sigmas[random.randint(0, L - 1)] # 每个 batch 采一个级别 eps = torch.randn_like(x) x_tilde = x + sigma * eps out = model(x_tilde, sigma) # 输出与 x 同形状 # 写法一:加权 + 归一化目标 loss = (sigma ** 2) * ((out * sigma + eps) ** 2).mean() # 写法二(等价、更直观):直接拟合 -eps/sigma # loss = (sigma ** 2) * ((out + eps / sigma) ** 2).mean() loss.backward() optimizer.step() ema.update(model) # 强烈建议开 EMA

几个必须说的点。第一,每个 batch 采样一个 σ 还是每个样本采一个 σ 都行,前者实现简单,后者梯度方差更小,我倾向后者,把 σ 扩成形状可广播的张量就行。第二,EMA 几乎是必开的,分数匹配的损失函数本身噪声大,模型权重在收敛后会持续抖动,用指数滑动平均的权重去采样,画质提升肉眼可见。第三,数据归一化的尺度必须和 σ₁ 匹配,如果你把数据缩到 [-0.5, 0.5],那 σ₁ 也要相应减半,否则大噪声级别会把信息彻底淹没。第四,训练过程中不要忘了对 σ 本身做嵌入时用对数尺度,因为 σ 跨越两个数量级,直接线性嵌入会让小 σ 区域的分辨率不够。

5. 退火朗之万采样:把分数场走成一张图

5.1 朗之万动力学在做什么

朗之万动力学的更新式是这样的:

x_{t+1} = x_t + (α/2) · ∇ₓ log p(x_t) + √α · z_t,z_t ~ N(0, I)

拆开看,第一项是按分数场(也就是梯度)走一小步,第二项是加一点高斯扰动,α 是步长。这个式子有个非常好的理论性质:当 α 足够小、迭代足够多时,x_t 的分布会收敛到 p(x)。也就是说,它既不是纯粹的梯度上升(那会掉进局部极值),也不是纯粹的随机游走(那永远在乱撞),而是两者的混合,混合比例由 α 控制。温度高的时候到处乱跑,温度低的时候稳稳落谷。

直觉上,你可以把它想象成一个醉汉下山:他看得见坡度(分数项),但每一步都喝多了会走歪(噪声项)。只要歪的程度合适,他最终能走到山谷里,而且不会卡在某块石头后面出不来。这个“不会卡住”的特性正是它比纯梯度下降强的地方,也是它能覆盖多个模式的原因。

5.2 步长为什么必须跟着 σ 缩小

直接在原始数据分布上跑朗之万有两个问题:一是我们只有各个噪声级别下的分数估计,二是不同级别下分数的量级差别巨大。第二个问题决定了步长的设计。

先看量级。网络输出的分数模长大约和 1/σ 成正比——噪声越小,分数场越陡峭。如果步长 α 固定,那么在 σ 很小的级别,单步更新量会大到把样本直接甩出去,采样过程发散。反过来,如果在 σ 很大的级别用很小的步长,那点更新量相对于噪声扰动可以忽略,等于在原地随机游走,浪费算力。

NCSN 的处理方式是让步长与 σ² 成正比:

α_i = ε · σ_i² / σ_L²

其中 ε 是一个全局基准步长,σ_L 是最小噪声级别,起归一化作用。这样设计的妙处在于,加噪项的标准差 √α_i = σ_i · √ε / σ_L,它和分数项的量级正好同比例缩放,两者的比值(也就是信噪比)在所有噪声级别上保持恒定。换句话说,不管在哪个尺度,醉汉的“醉度”是一样的,只是地图的缩放比例在变。这一点是我认为 NCSN 里最优雅的设计,理解了它,采样部分就没有别的难点了。

基准步长 ε 一般取 1e-4 量级。这个值不是随便定的:ε 太大会让采样在最后阶段不停地抖,图像出现彩色噪点和网格伪影;ε 太小则收敛慢,每个级别要跑很多步才有效果。经验上,如果每个级别跑 100 步,ε 取 1e-4 到 2e-5 之间都能出图,具体看数据集。

5.3 采样循环与几个调参直觉

采样代码比训练还短,但顺序和细节处处是坑:

x = torch.randn(B, C, H, W) * sigma_1 # 从最大尺度初始化 for i, sigma in enumerate(sigmas): # 从大到小遍历 alpha = eps_step * sigma ** 2 / sigmas[-1] ** 2 for t in range(T): z = torch.randn_like(x) s = model(x, sigma) x = x + 0.5 * alpha * s + math.sqrt(alpha) * z

第一个坑是初始化。一定要用 σ₁ 去缩放初始噪声,如果直接用标准正态,在大噪声级别下也就罢了,一旦 σ₁ 小于 1,起点会离数据流形太近,反而更容易陷进局部区域。第二个坑是循环顺序,外层必须是噪声级别、内层才是步数,写反了就等于在同一个尺度上反复横跳,白白浪费算力还得不到效果。第三个坑是最后一级的处理,有些实现会在最后一个 σ 上跳过加噪项,让结果更干净;我试过加和不加,差别不大,但如果你的 σ_L 偏大,跳过加噪能明显减少最后的颗粒感。

关于每个级别跑多少步,我在 32×32 这个尺度上试过 30、100、300 三档。30 步的时候,早期大噪声级别还没走稳就降级了,样本会出现结构错位;300 步的画质确实更好,但耗时翻三倍,收益不成比例。100 步是比较甜的点。另外如果你显存紧张,把批量调小、步数保住,比反过来更划算,因为朗之万是需要迭代收敛的,步数不够是真的出不来东西。

6. 这套东西和后来者是什么关系

很多人学完 NCSN 再去看 DDPM,会觉得两边像是两套独立的理论,其实把它们并排放着看会豁然开朗。DDPM 里网络预测的噪声 ε_θ(x_t, t),本质上就是 NCSN 里那个 s_θ 乘上负的噪声标准差:因为去噪分数匹配的最优解满足 s(x̃) = -ε/σ,所以预测噪声和预测分数是同一件事的两种参数化,只差一个系数。理解了这一层,你在读两种代码时就不会觉得是两套东西。

差别主要在两处。第一处是噪声调度:NCSN 用的是离散的一组 σ,各尺度之间没有马尔可夫链的严格推导,靠的是退火的经验做法;后来的工作把它改成了连续的时间变量,用随机微分方程的语言统一描述,前向是漂移扩散、反向是概率流,形式上更漂亮。第二处是采样器:NCSN 用朗之万,后来的工作用各种高阶求解器、预测-校正组合,本质都是在用更少的步数逼近同一条轨迹。但你要问哪个更适合入门理解,我还是推荐 NCSN——它没有随机微分方程的门槛,所有东西都能用“加噪”“梯度”“步长”三个词讲完。

值得一提的是,NCSN 那套“预测噪声再缩放回分数”的参数化,后来被证明在数值上更稳定,因为网络输出的量级被限制在标准正态附近,不会随 σ 剧烈变化。这个工程上的小选择,后来几乎成了这个方向的默认做法。所以哪怕你最终要做的是更新的模型,回头把 NCSN 的实现写一遍,对理解那些默认设置为什么存在,帮助非常大。

7. 踩坑记录与排查速查表

7.1 损失正常但样本是噪点

这是最常见的问题,八成出在采样阶段。先检查步长和 σ 是否匹配:如果 α 用的是固定值而不是按 σ² 缩放,那么在小噪声级别一定会发散,表现就是最后几张图全是高频彩噪。再检查采样循环的顺序,外层级别、内层步数,这个写反了症状一模一样。还有一种可能是网络在训练时没真正用到 σ 条件——比如你在 forward 里接收了 σ 但没传进归一化层,网络实际上只学了一个平均的分数场,采样时不管喂什么级别,输出都差不多,最后也是一团糊。

7.2 样本重复、多样性差

如果采样出来的图看着还行,但翻来覆去就那几个样子,先怀疑大噪声级别的覆盖不够。σ₁ 太小的话,初始化的点根本没有机会跳到远处的模式,只能就近收敛。可以把 σ₁ 调大一档试试。另一个常见原因是级别数 L 太少,相邻级别之间的间隔太大,退火过程出现“断层”,中间那段没有被正确引导。还有一点容易被忘掉:如果训练时每个 batch 只采一个 σ,而 batch 又很小,那每个级别实际见到的样本数差别可能很大,训练不均衡,也会导致某些级别欠拟合。改成逐样本采 σ 能缓解。

7.3 训练损失震荡剧烈

分数匹配的损失函数方差本来就大,因为监督目标里带着随机噪声。轻度震荡是正常的,如果剧烈到不收敛,先看学习率是不是按常规分类任务的量级设的——这类模型的常用学习率是 1e-4 到 2e-4,太大直接发散。其次看权重 λ 有没有开,不开的话小 σ 项几乎不贡献梯度,训练后期会突然崩。最后检查一下归一化层的选择,批归一化在小 batch 下的统计量抖动会直接放大到分数输出上,换成实例归一化通常立刻见效。

症状最可能的原因快速验证方式处理办法
采样结果全是彩色噪点步长未随 σ² 缩放打印各阶段 α 值看是否递减改为 α = ε·σ²/σ_L²
采样不动、图像像模糊色块σ_L 过大或级别数过少观察最小 σ 下的损失降低 σ_L、增加 L
样本高度重复σ₁ 偏小检查初始噪声尺度调大 σ₁,重训
训练损失突然爆掉学习率过大或未加 λ 权重单独看各级别损失曲线降学习率、加 λ=σ²
小噪声级别出现网格伪影最后几步步长仍偏大尝试跳过最后一级加噪减小 ε 或收尾去噪
训练和采样表现不一致用了批归一化切到推理模式对比输出换实例或组归一化
收敛后画质仍不稳定未使用 EMA对比 EMA 与原始权重加速率 0.999 的滑动平均

7.4 几个不太容易想到的经验

第一,训练数据增强要慎用。随机裁剪、翻转对图像这类任务问题不大,但如果用了改变像素值分布的色彩抖动,你实际上改变了 p_data,而分数场是对这个分布定义的,两者会打架。我吃过这个亏,加了强色彩抖动之后,采样结果的色调始终偏,去掉就正常了。第二,σ 的嵌入用对数比线性好,这一点前面提过,但很多人实现时会顺手写成线性,症状是大噪声级别正常、小噪声级别细节全无,很难往这上面想。第三,评估时别只盯着损失,分数匹配的损失和样本质量的相关性并不强,最靠谱的做法是固定随机种子,每隔一段时间采样一小批存下来肉眼对比,这比任何指标都直观。

这套模型我前后实现过三遍,每一遍都会在某个意想不到的地方卡住,但每次卡完再回看公式,都会觉得“原来这里早就写清楚了”。理论篇写到这里,NCSN 的主要环节基本都覆盖了。如果你要动手,我建议先在一维双峰分布上把整条链路跑通——十行代码就能把分数场和采样轨迹画出来,那个视觉效果比任何公式都有说服力。一维调通了,再上图像,能省掉大量盲目的排查时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 11:23:45

信息学竞赛核心能力与高效备赛指南

1. 信息学竞赛的底层逻辑与能力要求 信息学竞赛本质上是一场在有限时间内解决复杂计算问题的脑力马拉松。与日常编程不同,它考察的是选手在高压环境下快速分析问题、设计算法并实现代码的综合能力。根据我多年带队参赛的经验,优秀选手通常具备三个核心特…

作者头像 李华
网站建设 2026/10/27 11:23:36

解决IntelliJ IDEA 2025.3 Maven依赖爆红问题

1. 问题现象与背景分析 最近升级到IntelliJ IDEA 2025.3版本后,不少开发者遇到了一个恼人的问题:每次打开项目时,Maven依赖都会出现大面积爆红(红色波浪线提示),尽管这些依赖实际上是可以正常下载和使用的。…

作者头像 李华
网站建设 2026/10/27 11:23:23

Linux内核rootkit原理:系统调用劫持与内核态权限控制

简介:这是一份面向Linux内核安全与逆向学习者的轻量级Rootkit开发实践资源,适合具备C语言基础和Linux系统编程经验的中初级开发者深入理解内核模块机制、进程管理与隐藏技术。资源包含46个文件,以9个可编译运行的sample示例为核心&#xff0c…

作者头像 李华
网站建设 2026/10/27 11:23:21

从 git 到 co-:英语词源拆解与软件命名自嘲

前阵子有人拿着一个单词来问我,说他在英式剧里看到一句 “You silly git”,转头打开终端又看见 git status ,实在接受不了一个词同时承担这两种身份。顺带他还问了另一串词:grill、practice、drill、evacuate、premises&#xf…

作者头像 李华
网站建设 2026/10/27 11:22:44

AI Agent核心原理与开发实战指南

1. AI Agent基础概念解析:从零理解智能体核心逻辑 第一次听说AI Agent这个概念时,我正为一个客户设计智能客服系统。当时市面上大多数方案还停留在关键词匹配阶段,直到接触到基于Agent架构的解决方案,才发现传统规则引擎和现代智能…

作者头像 李华
网站建设 2026/10/27 11:20:40

Debian 11 手动部署 Kubernetes 完整实践指南

1. 为什么在 Debian 11 上手动部署 K8s 不再是“硬核玩家专属”? 你点开这篇,大概率不是为了看一句“已成功”就关掉——而是正卡在某个环节: kubeadm init 报错 cgroup driver mismatch , containerd 启动后 crictl ps 一…

作者头像 李华