论文链接:Denoising Diffusion Probabilistic Models

之前我们讨论过,生成模型的目的是:给定从真实分布 \(P(x)\) 中采样的观测数据 \(x\),训练得到一个由参数 \(\theta\) 控制、能够逼近真实分布的模型 \(p_\theta(x)\),这个任务太难了,所以使用变分推断去逼近,即为从一个标准高斯分布出发,经过某种映射或推导得到真实分布。而这个“某种映射”,并不要求一步到位,可以分多步执行。其中一个影响最大的实现方式,就是扩散模型。

DDPM 核心思想:拆沙堡与建沙堡

核心思考出发点:在生成模型(如 GAN 或 VAE)中,我们总是试图让神经网络“一步到位”地从一堆随机噪声中变出一张高清图片。这就像是让一个没有经验的人,瞬间凭空捏造出一座精美的沙堡,难度极高,模型很容易崩溃(比如 GAN 的模式崩溃)。

能不能换个思路?

  1. 拆沙堡(前向加噪):我们先拿一座建好的精美沙堡(真实图片),每次从上面拿走一小撮沙子,并随机撒上一把散沙(加高斯噪声)。经过成千上万次这样微小的破坏,沙堡最终会变成一堆毫无规律的散沙(纯高斯噪声)。这个过程是非常简单且确定的。
  2. 建沙堡(反向去噪):如果我们能训练一个神经网络,让它学会“如何撤销刚才那一次微小的破坏”(预测并去掉那把散沙),那么只要我们从一堆散沙开始,让神经网络连续执行成千上万次“撤销破坏”的操作,最终就能无中生有地建起一座精美的沙堡!

这就是 DDPM (Denoising Diffusion Probabilistic Models) 的核心魅力:将一个极其困难的“一步生成”问题,拆解成了成千上万个极其简单的“微小去噪”问题。

DDPM 示意图

这个过程可以形象地用上图表示,扩散模型中有两个过程,分别是前向过程(从图像加噪得到噪音)和反向过程(从噪音去噪得到图像)。在上图中,向图像 \(\mathbf{x}_0\) 逐渐添加噪声可以得到一系列的 \(\mathbf{x}_1,\mathbf{x}_2,...,\mathbf{x}_T\),最后的 \(\mathbf{x}_T\) 即接近完全的高斯噪声,这个过程显然是比较容易的。而从 \(\mathbf{x}_T\) 逐渐去噪得到 \(\mathbf{x}_0\) 并不容易,扩散模型学习的就是这个去噪的过程。

前向过程(加噪)

我们从比较简单的前向过程开始。首先回顾一下线性组合的方差公式:对于常数 \(a, b\)独立随机变量 \(X, Y\),有

\[ \mathrm{Var}(aX+bY) = a^2\mathrm{Var}(X) + b^2\mathrm{Var}(Y)。 \]

如果 \(X, Y\) 不独立,还需要加上 \(2ab\,\mathrm{Cov}(X,Y)\) 的交叉协方差项。

另外,一个常用的概念是马尔科夫链:设 \(\{X_t\}_{t=0}^\infty\) 是一个随机过程,如果对于任意时刻 \(t\) 和状态 \(i_0, i_1, \ldots, i_{t+1}\),都有

\[ P(X_{t+1} \mid X_0, X_1, \ldots, X_t) = P(X_{t+1} \mid X_t), \]

那么称 \(\{X_t\}\) 为马尔科夫链,即未来状态只依赖于当前状态,与过去状态无关。

在前向加噪过程中,我们希望对图像逐步添加高斯噪声,并且在设定条件下保持总体方差不变。令初始图像为 \(x_0\),标准高斯噪声为 \(\epsilon_t \sim \mathcal{N}(0,\mathbf{I})\),且假设 \(\epsilon_t\)\(x_{t-1}\) 独立。我们构造递推:

\[ x_t = \sqrt{1-\beta_t}\,x_{t-1} + \sqrt{\beta_t}\,\epsilon_t, \]

此时协方差满足:

\[ \mathrm{Cov}(x_t) = (1-\beta_t)\,\mathrm{Cov}(x_{t-1}) + \beta_t\,\mathbf{I}。 \]

将上式写成条件概率分布的形式,可以得到:

\[ q(x_t \mid x_{t-1}) = \mathcal{N}\!\big(x_t;\ \sqrt{1-\beta_t}\,x_{t-1},\ \beta_t\,\mathbf{I} \big), \]

其中均值是 \(\sqrt{1-\beta_t}\,x_{t-1}\),协方差是 \(\beta_t\),每个维度的标准差为 \(\sqrt{\beta_t}\)

在实际上进行加噪时,起始时使用的方差比较小,随着加噪步骤增加,方差会逐渐增大。例如在 DDPM 的原文中,使用的方差是从 \(\beta_1=10^{-4}\) 随加噪时间步线性增大到 \(\beta_T=0.02\)。这样设置主要是为了方便模型进行学习,如果在最开始就加入很大的噪声,对图像信息的破坏会比较严重,不利于模型学习图像的信息。这个过程也可以从反向进行理解,即去噪时先去掉比较大的噪音得到图像的雏形,再去掉小噪音进行细节的微调。

上边等号的右边表示的就是当前的变量 \(\mathbf{x}_t\) 满足一个 \(\mathcal{N}(\sqrt{1-\beta_t}\mathbf{x}_{t-1},\beta_t\mathbf{I})\) 的概率分布。通过上边的公式我们可以看到,每一个时间步的 \(\mathbf{x}_t\) 都只和 \(\mathbf{x}_{t-1}\) 有关,因此这个扩散过程是一个马尔可夫过程。在前向过程中,每一步的 \(\beta\) 都是固定的,真正的变量只有 \(\mathbf{x}_{t-1}\),那么我们可以将公式中的 \(\mathbf{x}_{t-1}\) 进一步展开: \[ \begin{aligned} \mathbf{x}_t&=\sqrt{1-\beta_t}\mathbf{x}_{t-1}+\sqrt{\beta_t}\epsilon_{t-1}\\ &=\sqrt{1-\beta_t}(\sqrt{1-\beta_{t-1}}\mathbf{x}_{t-2}+\sqrt{\beta_{t-1}}\epsilon_{t-2})+\sqrt{\beta_t}\epsilon_{t-1}\\ &=\sqrt{(1-\beta_t)(1-\beta_{t-1})}\mathbf{x}_{t-2}+\sqrt{(1-\beta_t)\beta_{t-1}}\epsilon_{t-2}+\sqrt{\beta_t}\epsilon_{t-1} \end{aligned} \] 在上边的公式里,实际上 \(\epsilon_{t-2}\)\(\epsilon_{t-1}\) 是同分布的,都是 \(\mathcal{N}(0,1)\),因此可以进行合并(两个高斯分布的线性加权公式):

两个高斯分布线性加权的公式

一般情况:

\(X \sim \mathcal{N}(\mu_X, \sigma_X^2)\)\(Y \sim \mathcal{N}(\mu_Y, \sigma_Y^2)\),且 \(X \perp Y\)(独立),则:

\[aX + bY \ \sim\ \mathcal{N}\big(a\mu_X + b\mu_Y,\ a^2\sigma_X^2 + b^2\sigma_Y^2 \big)\]

零均值同分布的特例:

\(X, Y \stackrel{i.i.d.}{\sim} \mathcal{N}(0, 1)\),则:

\[aX + bY \ \sim\ \mathcal{N}\big(0,\ a^2 + b^2 \big)\]

或者写作:

\[aX + bY \ \stackrel{d}{=}\ \sqrt{a^2 + b^2}\,\epsilon, \quad \epsilon \sim \mathcal{N}(0, 1)\]

因此有 \[ \begin{aligned} \mathbf{x}_t&=\sqrt{(1-\beta_t)(1-\beta_{t-1})}\mathbf{x}_{t-2}+\sqrt{(\sqrt{(1-\beta_t)\beta_{t-1}})^2+(\sqrt{\beta_t})^2}\bar{\epsilon}_{t-2}\\ &=\sqrt{(1-\beta_t)(1-\beta_{t-1})}\mathbf{x}_{t-2}+\sqrt{1-(1-\beta_t)(1-\beta_{t-1})}\bar{\epsilon}_{t-2} \end{aligned} \]

\(\alpha_t=1-\beta_t\)\(\bar{\alpha}_t=\prod_{i=1}^t\alpha_i\),继续推导,可以得到: \[ \begin{aligned} \mathbf{x}_t&=\sqrt{\alpha_t\alpha_{t-1}}\mathbf{x}_{t-2}+\sqrt{1-\alpha_t\alpha_{t-1}}\bar{\epsilon}_{t-2}\\ &=\cdots\\ &=\sqrt{\bar{\alpha}_t}\mathbf{x}_0+\sqrt{1-\bar{\alpha}_t}\epsilon \end{aligned} \] 通过上述的推导,我们发现给定 \(\mathbf{x}_0\) 和加噪的时间步,可以直接用一步就得到 \(\mathbf{x}_t\),而并不需要一步步地重复最开始的加权求和。和上述同理,这个关系也可以写成: \[ q(\mathbf{x}_t|\mathbf{x}_0)=\mathcal{N}(\mathbf{x}_t;\sqrt{\bar{\alpha}_t}\mathbf{x}_0,(1-\bar{\alpha}_t)\mathbf{I}) \] 从这个式子里我们可以看出,加噪过程中的 \(\mathbf{x}_t\) 可以看作原始图像 \(\mathbf{x}_0\) 和高斯噪声 \(\epsilon\) 的线性组合,且两个组合系数的平方和为 1。在实现加噪过程时,加噪的 scheduler 也是根据 \(\bar{\alpha}_t\) 设计的,这样更加直接,且为了保证最后得到的足够接近噪声,可以将 \(\bar\alpha_t\) 直接设置为一个接近 0 的数。

反向过程

正如文章开始所说的,反向过程就是从 \(\mathbf{x}_T\) 逐渐去噪得到 \(\mathbf{x}_0\) 的过程,也就是求 \(q(\mathbf{x}_{t-1}|\mathbf{x}_t)\)。根据贝叶斯公式: \[ q(\mathbf{x}_{t-1}|\mathbf{x}_t)=\frac{q(\mathbf{x}_t|\mathbf{x}_{t-1})q(\mathbf{x}_{t-1})}{q(\mathbf{x}_t)} \] 在上边的公式里,在前文中我们已经给出了 \(q(\mathbf{x}|\mathbf{x}_{t-1})\),但 \(q(\mathbf{x}_{t-1})\)\(q(\mathbf{x}_t)\) 依然是未知的。虽然这两个分布目前未知,但是在上一节的最后,我们已经推导出了 \(q(\mathbf{x}_t|\mathbf{x}_0)\) 这个分布,那么我们可以给上面的贝叶斯公式加上 \(\mathbf{x}_0\) 作为条件,将等号右侧的两个未知分布转化为已知分布: \[ q(\mathbf{x}_{t-1}|\mathbf{x}_t,\mathbf{x}_0)=\frac{q(\mathbf{x}_t|\mathbf{x}_{t-1},\mathbf{x}_0)q(\mathbf{x}_{t-1}|\mathbf{x}_0)}{q(\mathbf{x}_t|\mathbf{x}_0)} \] 而且因为先验分布 \(q(\mathbf{x}_t|\mathbf{x}_{t-1})\) 是马尔可夫过程,\(\mathbf{x}_t\) 只与 \(\mathbf{x}_{t-1}\) 有关,而与 \(\mathbf{x}_0\) 无关,所以上边式子里的 \(q(\mathbf{x}_t|\mathbf{x}_{t-1},\mathbf{x}_0)=q(\mathbf{x}_t|\mathbf{x}_{t-1})\)。但推导到这里还有问题,我们把 \(\mathbf{x}_0\) 加入到了条件概率分布的条件中,但 \(\mathbf{x}_0\) 依然是未知的,因此我们需要继续推导出一个与 \(\mathbf{x}_0\) 无关的式子。

上面的公式右侧的几个条件概率分布全都是高斯分布: \[ \begin{aligned} q(\mathbf{x}_t|\mathbf{x}_{t-1})&=\mathcal{N}(\mathbf{x}_t;\sqrt{\alpha_t}\mathbf{x}_{t-1},1-\alpha_t)\\ q(\mathbf{x}_{t-1}|\mathbf{x}_0)&=\mathcal{N}(\mathbf{x}_{t-1};\sqrt{\bar{\alpha}_{t-1}}\mathbf{x}_0,1-\bar{\alpha}_{t-1})\\ q(\mathbf{x}_t|\mathbf{x}_0)&=\mathcal{N}(\mathbf{x}_t;\sqrt{\bar{\alpha}_t}\mathbf{x}_0,1-\bar\alpha_t) \end{aligned} \] 用概率密度函数把这个公式展开,如果不看前边的常数项,可以得到: \[ \begin{aligned} q(\mathbf{x}_{t-1}|\mathbf{x}_t,\mathbf{x}_0)&\propto\exp\left(-\frac{1}{2}\left[\frac{(\mathbf{x}_t-\sqrt{\alpha_t}\mathbf{x}_{t-1})^2}{\beta_t}+\frac{(\mathbf{x}_{t-1}-\sqrt{\bar\alpha_{t-1}}\mathbf{x}_0)^2}{1-\bar\alpha_{t-1}}+\frac{(\mathbf{x}_t-\sqrt{\bar{\alpha}_t}\mathbf{x}_0)}{1-\bar\alpha_t}\right]\right)\\ \end{aligned} \] 因为我们在这一步去噪的时候想求得的是 \(\mathbf{x}_{t-1}\) 的分布,所以我们把上式展开并整理成一个关于 \(\mathbf{x}_{t-1}\) 的多项式: \[ q(\mathbf{x}_{t-1}|\mathbf{x}_t,\mathbf{x}_0)\propto\exp\left(-\frac{1}{2}\left[\left(\frac{\alpha_t}{\beta_t}+\frac{1}{1-\bar\alpha_{t-1}}\right)\mathbf{x}_{t-1}^2-\left(\frac{2\sqrt{\alpha_t}}{\beta_t}\mathbf{x}_t+\frac{2\sqrt{\bar\alpha_{t-1}}}{1-\bar\alpha_{t-1}}\mathbf{x}_0\right)\mathbf{x}_{t-1}+C(\mathbf{x}_t,\mathbf{x}_0)\right]\right) \] 上边的式子里常数项不重要(因为可以直接变成常数从指数部分挪走),所以可以暂时不管。对比高斯分布(可以证明反向过程的分布也是高斯分布)的指数部分 \(\exp\left(-\frac{1}{2}\left(\frac{1}{\sigma^2}x^2-\frac{2\mu}{\sigma^2}x+\frac{\mu^2}{\sigma^2}\right)\right)\)\[ \begin{cases} \begin{aligned} \frac{1}{\sigma^2}&=\frac{\alpha_t}{\beta_t}+\frac{1}{1-\bar\alpha_{t-1}}\\ \frac{2\mu}{\sigma^2}&=\frac{2\sqrt{\alpha_t}}{\beta_t}\mathbf{x}_t+\frac{2\sqrt{\bar\alpha_{t-1}}}{1-\bar\alpha_{t-1}}\mathbf{x}_0 \end{aligned} \end{cases} \] 可以发现 \(\sigma\) 的表达式里都是我们 scheduler 里的定值,而求解出均值 \(\mu\)\[ \mu=\frac{\sqrt{\alpha_t}(1-\bar\alpha_{t-1})}{1-\bar\alpha_t}\mathbf{x}_t+\frac{\sqrt{\bar\alpha_{t-1}}\beta_t}{1-\bar\alpha_t}\mathbf{x}_0 \] 代入上一章最后的 \(\mathbf{x}_t=\sqrt{\bar{\alpha}_t}\mathbf{x}_0+\sqrt{1-\bar{\alpha}_t}\epsilon\),得到: \[ \mu=\frac{1}{\sqrt{\alpha_t}}\left(\mathbf{x}_t-\frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}}\tilde{\epsilon}\right) \] 注意在反向过程中我们并不知道在前向过程中加入的噪声 \(\epsilon\)\(\mathcal{N}(0,1)\) 中的具体哪一个噪声,而噪声也没有办法继续转换成其他的形式。因此我们使用神经网络在反向过程中估计的目标就是 \(\tilde{\epsilon}\)。在这个网络中,输入除了 \(\mathbf{x}_t\) 之外还需要 \(t\),可以简单理解为:加噪过程中 \(\mathbf{x}_t\) 的噪声含量是由 \(t\) 决定的,因此在预测噪声时也需要知道时间步 \(t\)​ 作为参考,以降低预测噪声的难度。

注:关于反向过程为什么要这样做,Lilian Weng 基于变分推断给出了一个复杂的证明,因为过于难以理解,这里暂且把它跳过。

Loss的来源

DDPM的训练目标来源于最大化数据的对数似然 \(\log p_\theta(x_0)\)。由于直接优化这个目标很困难,所以使用变分推断来构造证据下界。

根据变分推断理论,我们有: \[ \log p_\theta(x_0) \geq \mathbb{E}_{x_{1:T} \sim q(x_{1:T}|x_0)}[\log p_\theta(x_0|x_{1:T}) - KL(q(x_{1:T}|x_0)||p_\theta(x_{1:T}))] \]

这个不等式的右边被称为变分下界(ELBO,Evidence Lower BOund)。

将变分下界展开,我们可以得到: \[ \log p_\theta(x_0) \geq \underbrace{\mathbb{E}_{x_{1:T} \sim q(x_{1:T}|x_0)}[\log p_\theta(x_0|x_1)]}_{\text{reconstruction}} - \underbrace{KL(q(x_{1:T}|x_0)||p_\theta(x_{1:T}))}_{\text{regularization}} - \sum_{t=2}^T \underbrace{\mathbb{E}_{x_{1:T} \sim q(x_{1:T}|x_0)}[KL(q(x_{t-1}|x_t, x_0)||p_\theta(x_{t-1}|x_t))]}_{\text{matching}} \]

现在我们逐项分析这个表达式,对于第三个matching项: \[ KL(q(x_{t-1}|x_t, x_0)||p_\theta(x_{t-1}|x_t)) = KL(\mathcal{N}(x_{t-1}; \mu(x_t, x_0), \sigma_t^2\mathbf{I})||\mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \sigma_t^2\mathbf{I})) \] 对于两个具有相同协方差矩阵的多元高斯分布,KL散度简化为: \[ KL(\mathcal{N}(\mu_1, \Sigma)||\mathcal{N}(\mu_2, \Sigma)) = \frac{1}{2\sigma_t^2}||\mu(x_t, x_0) - \mu_\theta(x_t, t)||^2 \]

\[ ||\mu(x_t, x_0) - \mu_\theta(x_t, t)||^2 = \left|\left|\frac{\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})}{1-\bar{\alpha}_t}x_t + \frac{\sqrt{\bar{\alpha}_{t-1}}\beta_t}{1-\bar{\alpha}_t}x_0 - \mu_\theta(x_t, t)\right|\right|^2 \]

\(x_t = \sqrt{\bar{\alpha}_t}x_0 + \sqrt{1-\bar{\alpha}_t}\epsilon\) 代入上式: \[ = \left|\left|\frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}}\epsilon\right) - \mu_\theta(x_t, t)\right|\right|^2 \]

\(\mu(x_t, x_0)\) 或者说 \(\mu(x_t, \epsilon)\) 里,其实只有 \(\epsilon\) 是逆向过程中未知的,所以我们可以用 \(\mu_\theta(x_t, t)\)\(\mu(x_t, x_0)\) 的格式对齐:

\[ ||\mu(x_t, x_0) - \mu_\theta(x_t, t)||^2 = \left|\left|\frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}}\epsilon\right) - \mu_\theta(x_t, t)\right|\right|^2 \]

\[ = \left|\left|\frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}}\epsilon\right) - \frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}}\epsilon_\theta(x_t, t)\right)\right|\right|^2 \]

\[ = \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}} \cdot \frac{1}{\alpha_t} ||\epsilon - \epsilon_\theta(x_t, t)||^2 \]

因此: \[ KL(q(x_{t-1}|x_t, x_0)||p_\theta(x_{t-1}|x_t)) = KL(\mathcal{N}(x_{t-1}; \mu(x_t, x_0), \sigma_t^2\mathbf{I})||\mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \sigma_t^2\mathbf{I})) \]

\[ = \frac{1}{2\sigma_t^2}||\mu(x_t, x_0) - \mu_\theta(x_t, t)||^2 \]

\[ = \frac{1}{2\sigma_t^2} \cdot \frac{(1-\alpha_t)^2}{(1-\bar{\alpha}_t)\alpha_t}||\epsilon - \epsilon_\theta(x_t, t)||^2 \]

\(\alpha_0 = \bar{\alpha}_0 = 1\),所以当 \(t=1\) 时,重构项和matching项可以合并。

最终loss表达式

\[ \text{LOSS} = -\text{ELBO} = \sum_{t=1}^T \frac{1}{2\sigma_t^2} \cdot \frac{(1-\alpha_t)^2}{(1-\bar{\alpha}_t)\alpha_t} \mathbb{E}_{x_t \sim q(x_t|x_0)}[||\epsilon - \epsilon_\theta(x_t, t)||^2] \]

简化loss,DDPM作者发现直接不要前面的系数模型学习的效果更好

\[ L_{\text{simple}}(\theta) = \mathbb{E}_{x_0, t, \epsilon}[||\epsilon - \epsilon_\theta(\sqrt{\bar{\alpha}_t}x_0 + \sqrt{1-\bar{\alpha}_t}\epsilon, t)||^2] \]

这个简化版本的损失函数具有以下优点:

  • 去除了复杂的权重系数,使训练更加稳定
  • 所有时间步的损失贡献相等,避免了某些时间步主导训练过程
  • 实验表明这种简化版本在实际应用中效果更好

这就是DDPM训练中使用的最终损失函数,它直接优化噪声预测的准确性。

具体的训练过程

我们已经知道了去噪网络的参数和预测目标,下一个问题就是如何去训练这个去噪网络。原始论文中给出了如下的训练过程:

DDPM 训练

该算法的核心步骤如下:

  1. 数据采样:从训练数据分布 \(q(\mathbf{x}_0)\) 中随机采样一个样本 \(\mathbf{x}_0\)
  2. 时间步采样:从均匀分布中随机采样时间步 \(t\)
  3. 噪声采样:从标准高斯分布 \(\mathcal{N}(0, \mathbf{I})\) 中采样噪声 \(\epsilon\)
  4. 前向扩散:利用重参数化技巧,通过公式 \(\mathbf{x}_t=\sqrt{\bar{\alpha}_t}\mathbf{x}_0+\sqrt{1-\bar{\alpha}_t}\epsilon\) 直接计算第 \(t\) 步的噪声图像
  5. 噪声预测:将 \(\mathbf{x}_t\) 和时间步 \(t\) 输入去噪网络 \(\epsilon_\theta(\mathbf{x}_t, t)\),预测添加的噪声
  6. 损失计算:计算预测噪声与真实噪声之间的 L2 损失:\(\mathcal{L} = \|\epsilon - \epsilon_\theta(\mathbf{x}_t, t)\|^2\)

关键理解:这里需要澄清一个重要概念——为什么要预测"特定的"噪声 \(\epsilon\) 而不是任意的高斯噪声?

原因在于:虽然 \(\epsilon\) 确实是从标准高斯分布中采样的,但在给定 \(\mathbf{x}_0\)\(\mathbf{x}_t\) 的情况下,将 \(\mathbf{x}_0\) 变换到 \(\mathbf{x}_t\) 的噪声 \(\epsilon\) 是唯一确定的。网络学习的是这种从噪声图像 \(\mathbf{x}_t\) 到其对应的"噪声成分" \(\epsilon\) 的映射关系。在反向去噪过程中,准确预测这个特定的噪声至关重要,因为:

  • 它决定了反向过程中每一步的去噪方向
  • 任何预测误差都会在多步去噪过程中累积,导致生成质量下降
  • 这种噪声预测实际上隐含地学习了数据分布的梯度信息(score function)

具体的采样过程

论文中同样也给出了采样过程:

DDPM 采样

采样算法通过迭代反向去噪过程生成新样本,具体步骤如下:

算法输入:训练好的噪声预测网络 \(\epsilon_\theta(\mathbf{x}_t, t)\)

算法步骤

  1. 初始化:从标准高斯分布采样初始噪声 \(\mathbf{x}_T \sim \mathcal{N}(0, \mathbf{I})\)

  2. 迭代去噪:对于 \(t = T, T-1, ..., 1\),执行以下步骤:

    1. 噪声预测:使用神经网络预测当前步的噪声 \[\hat{\epsilon} = \epsilon_\theta(\mathbf{x}_t, t)\]

    2. 计算去噪均值:根据后验分布公式计算 \[\mu_\theta(\mathbf{x}_t, t) = \frac{1}{\sqrt{\alpha_t}}\left(\mathbf{x}_t - \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}}\hat{\epsilon}\right)\]

    3. 采样下一步

      • \(t > 1\) 时,添加随机噪声: \[\mathbf{x}_{t-1} = \mu_\theta(\mathbf{x}_t, t) + \sigma_t \mathbf{z}, \quad \text{其中} \quad \mathbf{z} \sim \mathcal{N}(0, \mathbf{I})\]
      • \(t = 1\) 时,直接输出均值: \[\mathbf{x}_0 = \mu_\theta(\mathbf{x}_1, 1)\]

其中,方差 \(\sigma_t^2\) 可以设置为:

  • \(\sigma_t^2 = \beta_t\)(DDPM原论文选择)
  • \(\sigma_t^2 = \tilde{\beta}_t = \frac{1-\bar{\alpha}_{t-1}}{1-\bar{\alpha}_t}\beta_t\)(理论最优后验方差)

数学原理:该算法基于重参数化技巧,将从 \(\mathcal{N}(\mu, \sigma^2)\) 的采样分解为确定性的均值计算和随机的噪声添加两部分。最后一步不添加噪声是为了获得确定性的输出,避免在生成的最终图像中引入不必要的随机性。

DDPM 的代码实现

现有的主流方法使用 UNet 来实现去噪网络,如下图所示。

去噪网络的结构

在此我们不关心网络的架构,感兴趣的同学可以自己去阅读源码。这个网络接收一个噪声图 \(\mathbf{x}_t\) 和一个时间步 \(t\) 作为参数,并输出一个噪声的预测结果 \(\epsilon_\theta(\mathbf{x}_t,t)\)

DDPM 核心算法

首先我们需要先定义 \(\beta\)\(\alpha\),以及 \(\bar\alpha\) 等最基本的常量,这里我们保持 DDPM 原论文的配置,也就是 \(\beta\) 初始为 \(1\times10^{-4}\),最终为 \(0.02\),且共有 \(1000\) 个时间步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import torch  # 导入PyTorch库

class DDPM:
def __init__(
self,
num_train_timesteps: int = 1000, # 训练时的总时间步数T,默认1000步
beta_start: float = 0.0001, # 噪声调度的起始值β_1,控制初始加噪强度
beta_end: float = 0.02, # 噪声调度的结束值β_T,控制最终加噪强度
):
# 生成线性噪声调度序列 β_t,从β_start线性增长到β_end
# 形状: [num_train_timesteps],即 [1000]
# β_t 控制每一步添加噪声的比例,越大则加噪越强
self.betas = torch.linspace(beta_start, beta_end, num_train_timesteps, dtype=torch.float32)

# 计算 α_t = 1 - β_t
# α_t 表示每一步保留原信号的比例
# 形状: [num_train_timesteps]
self.alphas = 1.0 - self.betas

# 计算累积乘积 ᾱ_t = ∏(i=1 to t) α_i
# torch.cumprod 沿着维度0进行累积乘法
# ᾱ_t 表示从x_0到x_t总共保留了多少原始信号
# 例如: ᾱ_3 = α_1 * α_2 * α_3
# 形状: [num_train_timesteps]
self.alphas_cumprod = torch.cumprod(self.alphas, dim=0)

# 生成逆序的时间步序列,用于采样时从T-1到0的逆向扩散过程
# torch.arange(999, -1, -1) 生成 [999, 998, 997, ..., 2, 1, 0]
# 采样时按此顺序逐步去噪:x_T -> x_{T-1} -> ... -> x_1 -> x_0
# 形状: [num_train_timesteps]
self.timesteps = torch.arange(num_train_timesteps - 1, -1, -1)

参数详细说明:

  1. 噪声调度 (Noise Schedule)

    • beta_start = 0.0001:起始时加噪很轻微(保留99.99%的信号)
    • beta_end = 0.02:结束时加噪较强(保留98%的信号)
    • 线性增长确保噪声逐渐增强
  2. 关键变量的数学含义

    • betas (β_t):每一步的噪声强度,从 beta_startbeta_end 线性递增
    • alphas (α_t = 1 - β_t):每步保留原始信号的比例
    • alphas_cumprod (ᾱ_t = ∏α_i):从 \(x_0\)\(x_t\) 累积保留的信号比例。这是前向加噪公式 \(q(x_t|x_0) = \mathcal{N}(\sqrt{\bar\alpha_t}\,x_0,\,(1-\bar\alpha_t)\,\mathbf{I})\) 的核心参数

总结与算法对比

算法对比:DDPM vs GAN vs VAE

  • 与 GAN 对比:GAN 的训练极其不稳定(生成器和判别器相互博弈,容易模式崩溃),而 DDPM 的训练极其稳定(只是在做一个简单的加噪-去噪 MSE 预测),且生成质量甚至超越了 GAN。但代价是,DDPM 采样需要几百步迭代,速度远慢于 GAN 的一步生成。
  • 与 VAE 对比:VAE 也是通过变分推断(ELBO)来学习分布,但 VAE 的隐变量空间是低维的,导致生成的图片往往比较模糊。而 DDPM 保持了与原图相同维度的隐变量,且通过几百步的细微去噪,生成的细节极其逼真。

开源代码参考: 目前最权威的 DDPM 开源实现是 Hugging Face 的 diffusers 库。你可以查看 DDPMScheduler 类的 add_noise(前向加噪)和 step(反向去噪)方法,其核心逻辑与我们上面推导的公式完全一致。

参考资料:

  1. Denoising Diffusion Probabilistic Models
  2. What are Diffusion Models?