本教程从 VAE 的局限出发,解释 DDPM 为什么要把“一次生成”改成“多步去噪”,再从前向扩散、反向生成、ELBO、真实后验、噪声预测目标一路推导到最终训练和采样公式。
DDPM 的主线可以压缩成五步:
VAE 通过潜变量 $z$ 一步生成图像,但压缩和重建假设容易带来细节损失。
DDPM 不急着一步生成完整图像,而是先定义一个固定的加噪过程,把真实图像逐步变成高斯噪声。
生成时从高斯噪声开始,训练神经网络学习每一步如何去掉一点噪声。
严格推导来自最大似然和 ELBO,但最终可以简化成一个很直观的监督学习任务:预测加到图像里的噪声。
实现时的核心不是复杂 loss,而是正确处理噪声日程、时间步、U-Net 输入输出、采样公式和数值范围。
DDPM 先把图像逐步加噪到接近纯噪声,再训练模型反过来逐步去噪;它把高难度的“一步生成图像”拆成很多个简单的“预测并去掉噪声”问题。
DDPM 最常用的训练目标是:
$$
\mathcal{L}_{\mathrm{simple}}(\theta)
{=}
\mathbb{E}_{t,x_0,\epsilon}
\left[
\left|
\epsilon-\epsilon_\theta(x_t,t)
\right|^2
\right]
$$
其中:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon,
\quad
\epsilon\sim\mathcal{N}(0,I)
$$
也就是说,训练时我们知道真实噪声 $\epsilon$ ,网络只需要根据带噪图像 $x_t$ 和时间步 $t$ 把它预测出来。
VAE 很重要,但它也有几个常见问题。
第一,VAE 通常把图像压缩到一个低维潜变量:
$$
x\rightarrow z\rightarrow \hat{x}
$$
如果 $z$ 的容量不足,细节会丢失,生成图像容易偏模糊。
第二,VAE 的重建项常对应像素级似然。以高斯似然为例,最大化 $\log p_\theta(x|z)$ 常等价于最小化均方误差。均方误差会鼓励模型输出“平均答案”,而平均图像往往看起来不锐利。
第三,VAE 一步从 $z$ 生成 $x$ :
$$
z\rightarrow x
$$
这要求 Decoder 在一次前向传播中完成从抽象语义到全部像素细节的生成。对复杂图像来说,这个映射压力很大。
所以 VAE 的核心矛盾可以理解为:
它想用一个规整、可采样的潜空间生成图像,但这个压缩和一步解码过程可能损失高频细节。
DDPM 不再把生成看成:
$$
z\rightarrow x
$$
而是看成:
$$
x_T\rightarrow x_{T-1}\rightarrow\cdots\rightarrow x_1\rightarrow x_0
$$
其中:
$x_0$ 是真实干净图像;
$x_1,\dots,x_T$ 是逐步加噪后的图像;
$x_T$ 接近标准高斯噪声。
生成时先采样:
$$
x_T\sim\mathcal{N}(0,I)
$$
然后每一步只做一件小事:
根据当前带噪图像,预测噪声并去掉一点噪声。
DDPM 的好处是:
训练目标稳定,不需要 GAN 那样的对抗训练。
每一步任务简单,只需要学习局部去噪。
图像空间中逐步生成,不需要把所有细节压进一个很小的潜变量。
可以使用 U-Net 保留多尺度空间细节。
数学上可以从 ELBO 推导到简单的噪声预测 MSE。
DDPM 有两个过程:
前向扩散过程:固定规则,不需要学习,把真实图像逐步加噪。
反向生成过程:需要学习,从噪声逐步去噪生成图像。
前向过程从真实图像 $x_0$ 出发:
$$
x_0\rightarrow x_1\rightarrow x_2\rightarrow\cdots\rightarrow x_T
$$
DDPM 把它定义为 Markov chain:
$$
q(x_{1:T}|x_0)
{=}
\prod_{t=1}^{T}
q(x_t|x_{t-1})
$$
Markov chain 的意思是:如果已经知道 $x_{t-1}$ ,那么生成 $x_t$ 时不需要再直接看更早的状态。
每一步加噪定义为:
$$
q(x_t|x_{t-1})
{=}
\mathcal{N}
\left(
x_t;
\sqrt{1-\beta_t}x_{t-1},
\beta_t I
\right)
$$
这里:
$\beta_t$ 是第 $t$ 步的噪声强度;
$\beta_t I$ 表示每个维度加入方差为 $\beta_t$ 的独立高斯噪声;
$\sqrt{1-\beta_t}x_{t-1}$ 表示保留一部分上一时刻的图像信号。
通常定义:
$$
\alpha_t=1-\beta_t
$$
于是:
$$
\boxed{
q(x_t|x_{t-1})
{=}
\mathcal{N}
\left(
x_t;
\sqrt{\alpha_t}x_{t-1},
(1-\alpha_t)I
\right)
}
$$
如果:
$$
x\sim\mathcal{N}(\mu,\sigma^2I)
$$
就可以写成:
$$
x=\mu+\sigma\epsilon,
\quad
\epsilon\sim\mathcal{N}(0,I)
$$
把这个规则用到 DDPM 的一步前向分布:
$$
x_t|x_{t-1}
\sim
\mathcal{N}
\left(
\sqrt{\alpha_t}x_{t-1},
(1-\alpha_t)I
\right)
$$
得到:
$$
\boxed{
x_t
{=}
\sqrt{\alpha_t}x_{t-1}
+
\sqrt{1-\alpha_t}\epsilon_t
}
$$
其中:
$$
\epsilon_t\sim\mathcal{N}(0,I)
$$
这就是一步加噪公式。
很多初学者会问:为什么是 $\sqrt{1-\beta_t}$ ,而不是 $1-\beta_t$ ?
假设 $x_{t-1}$ 的方差大约是 $I$ ,噪声 $\epsilon_t$ 的方差也是 $I$ 。如果:
$$
x_t
{=}
\sqrt{1-\beta_t}x_{t-1}
+
\sqrt{\beta_t}\epsilon_t
$$
那么方差大约是:
$$
\mathrm{Var}(x_t)
{=}
(1-\beta_t)I+\beta_t I
{=}
I
$$
也就是说,每一步加噪后,整体尺度不会突然变大或变小。
如果改成:
$$
x_t=(1-\beta_t)x_{t-1}+\sqrt{\beta_t}\epsilon_t
$$
那么方差会变成:
$$
(1-\beta_t)^2I+\beta_t I
$$
它不再正好等于 $I$ 。所以 DDPM 使用平方根系数,是为了让信号和噪声的方差配比稳定。
训练 DDPM 时,如果每次都从 $x_0$ 一步步加噪到 $x_t$ ,会很慢。幸运的是,前向过程有闭式公式,可以直接从 $x_0$ 得到任意时间步的 $x_t$ 。
最终公式是:
$$
\boxed{
q(x_t|x_0)
{=}
\mathcal{N}
\left(
x_t;
\sqrt{\bar{\alpha}_t}x_0,
(1-\bar{\alpha}_t)I
\right)
}
$$
其中:
$$
\bar{\alpha}_t
{=}
\prod_{s=1}^{t}\alpha_s
$$
对应采样公式是:
$$
\boxed{
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
}
$$
其中:
$$
\epsilon\sim\mathcal{N}(0,I)
$$
这个公式非常重要。它说明训练时可以随机选择一个时间步 $t$ ,然后一次性构造对应的带噪图像 $x_t$ 。
一步加噪是:
$$
x_t
{=}
\sqrt{\alpha_t}x_{t-1}
+
\sqrt{1-\alpha_t}\epsilon_t
$$
再写出上一时刻:
$$
x_{t-1}
{=}
\sqrt{\alpha_{t-1}}x_{t-2}
+
\sqrt{1-\alpha_{t-1}}\epsilon_{t-1}
$$
代入第一式:
$$
x_t
{=}
\sqrt{\alpha_t}
\left(
\sqrt{\alpha_{t-1}}x_{t-2}
+
\sqrt{1-\alpha_{t-1}}\epsilon_{t-1}
\right)
+
\sqrt{1-\alpha_t}\epsilon_t
$$
展开:
$$
x_t
{=}
\sqrt{\alpha_t\alpha_{t-1}}x_{t-2}
+
\sqrt{\alpha_t(1-\alpha_{t-1})}\epsilon_{t-1}
+
\sqrt{1-\alpha_t}\epsilon_t
$$
后两项都是独立高斯噪声的线性组合,所以仍然是高斯噪声。它的方差是:
$$
\alpha_t(1-\alpha_{t-1})+(1-\alpha_t)
{=}
1-\alpha_t\alpha_{t-1}
$$
因此可以合并成一个新的标准高斯噪声 $\epsilon$ :
$$
x_t
{=}
\sqrt{\alpha_t\alpha_{t-1}}x_{t-2}
+
\sqrt{1-\alpha_t\alpha_{t-1}}\epsilon
$$
继续递推到 $x_0$ ,就得到:
$$
x_t
{=}
\sqrt{\alpha_t\alpha_{t-1}\cdots\alpha_1}x_0
+
\sqrt{1-\alpha_t\alpha_{t-1}\cdots\alpha_1}\epsilon
$$
定义:
$$
\bar{\alpha}_t
{=}
\alpha_t\alpha_{t-1}\cdots\alpha_1
$$
就得到任意时刻公式:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
因为:
$$
\bar{\alpha}_t
{=}
\prod_{s=1}^{t}\alpha_s
$$
而:
$$
\alpha_s=1-\beta_s<1
$$
当 $t$ 足够大时:
$$
\bar{\alpha}_t\rightarrow 0
$$
代入:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
第一项会消失:
$$
\sqrt{\bar{\alpha}_t}x_0\rightarrow 0
$$
第二项会变成:
$$
\sqrt{1-\bar{\alpha}_t}\epsilon\rightarrow\epsilon
$$
所以:
$$
x_T\approx\epsilon\sim\mathcal{N}(0,I)
$$
这解释了为什么生成时可以从纯高斯噪声开始。
前向过程是人为固定的,不需要学习。真正需要学习的是反向过程:
$$
x_T\rightarrow x_{T-1}\rightarrow\cdots\rightarrow x_0
$$
模型联合分布写成:
$$
p_\theta(x_{0:T})
{=}
p(x_T)
\prod_{t=1}^{T}
p_\theta(x_{t-1}|x_t)
$$
其中:
$$
p(x_T)=\mathcal{N}(0,I)
$$
反向一步通常也设为高斯分布:
$$
\boxed{
p_\theta(x_{t-1}|x_t)
{=}
\mathcal{N}
\left(
x_{t-1};
\mu_\theta(x_t,t),
\sigma_t^2I
\right)
}
$$
这里:
$\mu_\theta(x_t,t)$ 是神经网络预测出的反向均值;
$\sigma_t^2I$ 是反向方差,很多基础实现中先固定不学;
网络输入是带噪图像 $x_t$ 和时间步 $t$ ;
网络输出可以设计成预测均值、预测原图 $x_0$ 或预测噪声 $\epsilon$ 。
直觉上,反向一步在做:
给定当前噪声图 $x_t$ ,猜测上一步稍微干净一点的图像 $x_{t-1}$ 应该在哪里。
DDPM 作为生成模型,本来希望最大化真实图像的似然:
$$
\log p_\theta(x_0)
$$
但:
$$
p_\theta(x_0)
{=}
\int p_\theta(x_{0:T})dx_{1:T}
$$
这个积分需要把所有中间变量 $x_1,\dots,x_T$ 都积分掉,直接计算很困难。
因此 DDPM 使用变分下界。训练时最小化负 ELBO:
$$
\mathcal{L}
{=}
\mathbb{E}_{q(x_{1:T}|x_0)}
\left[
{-}
\log
\frac{
p_\theta(x_{0:T})
}{
q(x_{1:T}|x_0)
}
\right]
$$
其中:
$$
p_\theta(x_{0:T})
{=}
p(x_T)
\prod_{t=1}^{T}
p_\theta(x_{t-1}|x_t)
$$
$$
q(x_{1:T}|x_0)
{=}
\prod_{t=1}^{T}
q(x_t|x_{t-1})
$$
详细代数推导放在附录 A。主文只保留结论:
$$
\boxed{
\mathcal{L}
{=}
\mathbb{E}_q
\left[
D_{\mathrm{KL}}
\left(
q(x_T|x_0)|p(x_T)
\right)
+
\sum_{t>1}
D_{\mathrm{KL}}
\left(
q(x_{t-1}|x_t,x_0)
|
p_\theta(x_{t-1}|x_t)
\right)
{-}
\log p_\theta(x_0|x_1)
\right]
}
$$
它有三类项。
第一项:
$$
D_{\mathrm{KL}}
\left(
q(x_T|x_0)|p(x_T)
\right)
$$
表示最终噪声分布要接近标准高斯。因为 $T$ 足够大时 $q(x_T|x_0)\approx\mathcal{N}(0,I)$ ,这一项通常接近常数。
中间项:
$$
D_{\mathrm{KL}}
\left(
q(x_{t-1}|x_t,x_0)
|
p_\theta(x_{t-1}|x_t)
\right)
$$
是训练的核心。它要求模型学到的反向一步分布 $p_\theta(x_{t-1}|x_t)$ 接近真实后验 $q(x_{t-1}|x_t,x_0)$ 。
最后一项:
$$
-\log p_\theta(x_0|x_1)
$$
表示从 $x_1$ 恢复 $x_0$ 的重建误差。
所以 DDPM 的关键问题变成:
如何让神经网络学会逼近真实后验 $q(x_{t-1}|x_t,x_0)$ ?
真实后验是:
$$
q(x_{t-1}|x_t,x_0)
$$
它表示:如果已知原图 $x_0$ 和当前带噪图 $x_t$ ,那么上一时刻 $x_{t-1}$ 应该是什么分布。
根据贝叶斯公式:
$$
q(x_{t-1}|x_t,x_0)
{=}
\frac{
q(x_t|x_{t-1},x_0)
q(x_{t-1}|x_0)
}{
q(x_t|x_0)
}
$$
由于前向过程是马尔科夫链:
$$
q(x_t|x_{t-1},x_0)
{=}
q(x_t|x_{t-1})
$$
所以:
$$
q(x_{t-1}|x_t,x_0)
{=}
\frac{
q(x_t|x_{t-1})
q(x_{t-1}|x_0)
}{
q(x_t|x_0)
}
$$
右边三个分布都是高斯,所以结果仍然是高斯:
$$
\boxed{
q(x_{t-1}|x_t,x_0)
{=}
\mathcal{N}
\left(
x_{t-1};
\tilde{\mu}_t(x_t,x_0),
\tilde{\beta}_tI
\right)
}
$$
其中:
$$
\boxed{
\tilde{\mu}_t(x_t,x_0)
{=}
\frac{
\sqrt{\bar{\alpha}_{t-1}}\beta_t
}{
1-\bar{\alpha}_t
}
x_0
+
\frac{
\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
x_t
}
$$
以及:
$$
\boxed{
\tilde{\beta}_t
{=}
\frac{
1-\bar{\alpha}_{t-1}
}{
1-\bar{\alpha}_t
}
\beta_t
}
$$
详细配方推导放在附录 B。主文先理解它的含义:
真实后验均值 $\tilde{\mu}_t(x_t,x_0)$ 是 $x_0$ 和 $x_t$ 的线性组合;
它告诉我们从 $x_t$ 往回走一步时,最合理的中心位置;
训练模型时,可以让模型预测的均值 $\mu_\theta(x_t,t)$ 去接近这个真实均值。
模型反向分布是:
$$
p_\theta(x_{t-1}|x_t)
{=}
\mathcal{N}
\left(
x_{t-1};
\mu_\theta(x_t,t),
\sigma_t^2I
\right)
$$
如果固定方差,那么两个高斯分布之间的 KL 与模型有关的部分就是均值 MSE:
$$
L_{t-1}
{=}
\mathbb{E}_q
\left[
\frac{1}{2\sigma_t^2}
\left|
\tilde{\mu}_t(x_t,x_0)
{-}
\mu_\theta(x_t,t)
\right|^2
\right]
+
C
$$
其中 $C$ 是与模型参数 $\theta$ 无关的常数。
这一步把复杂的概率匹配问题变成了:
让模型预测的反向均值接近真实后验均值。
理论上,网络可以直接预测:
$$
\mu_\theta(x_t,t)
$$
但 DDPM 实践中更常预测噪声:
$$
\epsilon_\theta(x_t,t)
$$
这一步看起来突然,其实来自公式重写。
真实后验均值可以写成:
$$
\tilde{\mu}_t(x_t,x_0)
{=}
A_tx_0+B_tx_t
$$
其中:
$$
A_t
{=}
\frac{
\sqrt{\bar{\alpha}_{t-1}}\beta_t
}{
1-\bar{\alpha}_t
}
$$
$$
B_t
{=}
\frac{
\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
$$
生成时不知道真实 $x_0$ ,所以可以让网络预测原图:
$$
x_{0,\theta}(x_t,t)
$$
再构造:
$$
\mu_\theta(x_t,t)
{=}
\tilde{\mu}_t
\left(
x_t,
x_{0,\theta}(x_t,t)
\right)
$$
这说明预测原图 $x_0$ 和预测均值 $\mu_\theta$ 是可以互相转换的。
前向加噪公式是:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
把它对 $x_0$ 求解:
$$
x_0
{=}
\frac{
x_t-\sqrt{1-\bar{\alpha}_t}\epsilon
}{
\sqrt{\bar{\alpha}_t}
}
$$
如果网络预测噪声:
$$
\epsilon_\theta(x_t,t)
$$
就可以构造预测原图:
$$
\boxed{
x_{0,\theta}(x_t,t)
{=}
\frac{
x_t-\sqrt{1-\bar{\alpha}_t}\epsilon_\theta(x_t,t)
}{
\sqrt{\bar{\alpha}_t}
}
}
$$
再把 $x_{0,\theta}$ 代入上一节的 $\mu_\theta$ ,得到 DDPM 最常见的反向均值参数化:
$$
\boxed{
\mu_\theta(x_t,t)
{=}
\frac{1}{\sqrt{\alpha_t}}
\left(
x_t
{-}
\frac{
\beta_t
}{
\sqrt{1-\bar{\alpha}_t}
}
\epsilon_\theta(x_t,t)
\right)
}
$$
这个公式的完整代数化简放在附录 C。
预测噪声有三个直观优势。
第一,监督信号天然可得。训练时我们自己采样:
$$
\epsilon\sim\mathcal{N}(0,I)
$$
然后构造:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
所以真实噪声 $\epsilon$ 是已知标签。
第二,目标形式统一。无论图像内容多复杂,噪声始终来自标准高斯:
$$
\epsilon\sim\mathcal{N}(0,I)
$$
第三,采样公式直观。模型预测当前图像中的噪声,然后把它从 $x_t$ 中减掉。
真实后验均值可以写成噪声形式:
$$
\tilde{\mu}_t(x_t,x_0)
{=}
\frac{1}{\sqrt{\alpha_t}}
\left(
x_t
{-}
\frac{
\beta_t
}{
\sqrt{1-\bar{\alpha}_t}
}
\epsilon
\right)
$$
模型均值写成:
$$
\mu_\theta(x_t,t)
{=}
\frac{1}{\sqrt{\alpha_t}}
\left(
x_t
{-}
\frac{
\beta_t
}{
\sqrt{1-\bar{\alpha}_t}
}
\epsilon_\theta(x_t,t)
\right)
$$
两者相减:
$$
\tilde{\mu}_t(x_t,x_0)
{-}
\mu_\theta(x_t,t)
{=}
\frac{
\beta_t
}{
\sqrt{\alpha_t}\sqrt{1-\bar{\alpha}_t}
}
\left(
\epsilon_\theta(x_t,t)-\epsilon
\right)
$$
因此:
$$
\left|
\tilde{\mu}_t(x_t,x_0)
{-}
\mu_\theta(x_t,t)
\right|^2
{=}
\frac{
\beta_t^2
}{
\alpha_t(1-\bar{\alpha}_t)
}
\left|
\epsilon-\epsilon_\theta(x_t,t)
\right|^2
$$
代入 KL 对应的均值匹配项,会得到一个加权的噪声预测 MSE:
$$
L_{t-1}
{=}
\mathbb{E}
\left[
\frac{
\beta_t^2
}{
2\sigma_t^2\alpha_t(1-\bar{\alpha}_t)
}
\left|
\epsilon-\epsilon_\theta(x_t,t)
\right|^2
\right]
+
C
$$
DDPM 实践中常使用简化目标,去掉复杂权重:
$$
\boxed{
\mathcal{L}_{\mathrm{simple}}(\theta)
{=}
\mathbb{E}_{t,x_0,\epsilon}
\left[
\left|
\epsilon-\epsilon_\theta(x_t,t)
\right|^2
\right]
}
$$
其中:
$$
\boxed{
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
}
$$
这就是 DDPM 最常见的训练目标。
一次训练迭代可以理解为:
从数据集中采样真实图像:
$$
x_0\sim q_{\mathrm{data}}(x_0)
$$
随机采样时间步:
$$
t\sim\mathrm{Uniform}({1,\dots,T})
$$
采样高斯噪声:
$$
\epsilon\sim\mathcal{N}(0,I)
$$
构造带噪图像:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
让网络预测噪声:
$$
\epsilon_\theta(x_t,t)
$$
最小化噪声预测误差:
$$
\left|
\epsilon-\epsilon_\theta(x_t,t)
\right|^2
$$
训练完成后,从纯噪声开始:
$$
x_T\sim\mathcal{N}(0,I)
$$
然后从 $t=T$ 到 $t=1$ 逐步执行:
$$
x_{t-1}
{=}
\mu_\theta(x_t,t)
+
\sigma_tz
$$
其中:
$$
z\sim\mathcal{N}(0,I)
$$
代入噪声预测形式的均值:
$$
\boxed{
x_{t-1}
{=}
\frac{1}{\sqrt{\alpha_t}}
\left(
x_t
{-}
\frac{
\beta_t
}{
\sqrt{1-\bar{\alpha}_t}
}
\epsilon_\theta(x_t,t)
\right)
+
\sigma_tz
}
$$
通常在最后一步 $t=1$ 时不再加入随机噪声,即令:
$$
z=0
$$
这样可以避免最后输出又被额外扰动。
本节结合 STL10 代码说明 DDPM 如何从公式落到 PyTorch 实现。完整代码位于:
code/train_stl10_ddpm.py
code/generate_stl10_ddpm.py
数据集采用 STL10,代码中使用 split="train",图像默认调整为 $96\times96$ 。第一次运行时会自动下载数据集,需要提前安装好 torch 和 torchvision。
这份实现采用最基础的 DDPM 配置:线性 beta schedule、噪声预测目标、MSE loss、小型 U-Net 噪声预测器。它的目的不是一次写出最强扩散模型,而是先跑通“训练时加噪、模型预测噪声、采样时逐步去噪”这条闭环。
STL10 是 RGB 图像,因此模型输入形状为:
$$
x_0\in\mathbb{R}^{3\times96\times96}
$$
训练脚本中先把图像 resize 到指定大小,再把像素归一化到 $[-1,1]$ :
transform = transforms .Compose (
[
transforms .Resize ((args .image_size , args .image_size )),
transforms .ToTensor (),
transforms .Normalize ((0.5 , 0.5 , 0.5 ), (0.5 , 0.5 , 0.5 )),
]
)
train_set = datasets .STL10 (
root = args .data_dir ,
split = "train" ,
download = True ,
transform = transform ,
)
这里的 Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)) 会把原来的 $[0,1]$ 像素变成 $[-1,1]$ :
$$
x_{\text{norm}}=2x-1
$$
这样做的原因是,DDPM 最后会把图像逐渐加到标准高斯噪声附近。图像范围接近 $[-1,1]$ 时,数据尺度和噪声尺度更匹配。生成保存图片时,再把结果映射回 $[0,1]$ :
utils .save_image ((images + 1 ) / 2 , out_path , nrow = int (num_samples ** 0.5 ))
DDPM 的网络不能只看带噪图像 $x_t$ ,还必须知道当前时间步 $t$ 。同一张图在 $t=50$ 和 $t=900$ 时噪声强度完全不同,如果不给时间信息,网络不知道应该去掉多少噪声。
代码中使用正弦时间嵌入,把整数时间步转成连续向量:
class SinusoidalTimeEmbedding (nn .Module ):
def __init__ (self , dim : int ) -> None :
super ().__init__ ()
self .dim = dim
def forward (self , t : torch .Tensor ) -> torch .Tensor :
half = self .dim // 2
freq = torch .exp (
- math .log (10000 )
* torch .arange (half , device = t .device , dtype = torch .float32 )
/ max (half - 1 , 1 )
)
angles = t .float ()[:, None ] * freq [None , :]
emb = torch .cat ([angles .sin (), angles .cos ()], dim = - 1 )
return F .pad (emb , (0 , self .dim % 2 ))
得到的时间向量再经过 MLP:
self .time_mlp = nn .Sequential (
SinusoidalTimeEmbedding (time_dim ),
nn .Linear (time_dim , time_dim ),
nn .SiLU (),
nn .Linear (time_dim , time_dim ),
)
这样每个时间步都有一个可供网络使用的条件向量。
U-Net 中的每个残差块都接收两个输入:图像特征 x 和时间特征 time_emb。时间特征先经过线性层投影到当前通道数,再加到卷积特征图上:
class ResBlock (nn .Module ):
def __init__ (self , in_ch : int , out_ch : int , time_dim : int ) -> None :
super ().__init__ ()
self .conv1 = nn .Conv2d (in_ch , out_ch , 3 , padding = 1 )
self .norm1 = nn .GroupNorm (norm_groups (out_ch ), out_ch )
self .conv2 = nn .Conv2d (out_ch , out_ch , 3 , padding = 1 )
self .norm2 = nn .GroupNorm (norm_groups (out_ch ), out_ch )
self .time_proj = nn .Sequential (nn .SiLU (), nn .Linear (time_dim , out_ch ))
self .skip = nn .Conv2d (in_ch , out_ch , 1 ) if in_ch != out_ch else nn .Identity ()
def forward (self , x : torch .Tensor , time_emb : torch .Tensor ) -> torch .Tensor :
h = F .silu (self .norm1 (self .conv1 (x )))
h = h + self .time_proj (time_emb )[:, :, None , None ]
h = F .silu (self .norm2 (self .conv2 (h )))
return h + self .skip (x )
其中:
time_proj(time_emb) 的形状是 [B, C];
[:, :, None, None] 把它变成 [B, C, 1, 1];
加到 h 上时会自动 broadcast 到整张特征图。
这一步对应公式中的条件网络:
$$
\epsilon_\theta(x_t,t)
$$
也就是说,模型预测噪声时同时依赖图像状态 $x_t$ 和时间步 $t$ 。
训练脚本中的核心模型是 UNet96。它输入一张 $96\times96$ 的带噪 RGB 图像,输出同样大小的噪声预测:
$$
x_t\in\mathbb{R}^{3\times96\times96}
$$
$$
\epsilon_\theta(x_t,t)\in\mathbb{R}^{3\times96\times96}
$$
模型先下采样提取语义特征,再上采样恢复空间分辨率:
self .in_conv = nn .Conv2d (in_ch , base , 3 , padding = 1 )
self .down1 = ResBlock (base , base , time_dim )
self .down2 = ResBlock (base , base * 2 , time_dim )
self .down3 = ResBlock (base * 2 , base * 4 , time_dim )
self .down4 = ResBlock (base * 4 , base * 8 , time_dim )
self .mid = ResBlock (base * 8 , base * 8 , time_dim )
self .up3 = ResBlock (base * 8 + base * 4 , base * 4 , time_dim )
self .up2 = ResBlock (base * 4 + base * 2 , base * 2 , time_dim )
self .up1 = ResBlock (base * 2 + base , base , time_dim )
self .out = nn .Sequential (
nn .GroupNorm (norm_groups (base ), base ),
nn .SiLU (),
nn .Conv2d (base , in_ch , 3 , padding = 1 ),
)
前向传播时,上采样阶段会把深层特征和下采样阶段保存的特征拼接起来:
x1 = self .down1 (self .in_conv (x ), time_emb )
x2 = self .down2 (self .pool (x1 ), time_emb )
x3 = self .down3 (self .pool (x2 ), time_emb )
x4 = self .down4 (self .pool (x3 ), time_emb )
h = self .mid (x4 , time_emb )
h = F .interpolate (h , scale_factor = 2 , mode = "nearest" )
h = self .up3 (torch .cat ([h , x3 ], dim = 1 ), time_emb )
h = F .interpolate (h , scale_factor = 2 , mode = "nearest" )
h = self .up2 (torch .cat ([h , x2 ], dim = 1 ), time_emb )
h = F .interpolate (h , scale_factor = 2 , mode = "nearest" )
h = self .up1 (torch .cat ([h , x1 ], dim = 1 ), time_emb )
return self .out (h )
这里的 skip connection 很重要。DDPM 要做的是逐像素噪声预测,既需要全局语义,也需要局部纹理位置。下采样路径负责扩大感受野,上采样路径负责恢复分辨率,skip connection 负责把浅层细节送回去。
Diffusion 类负责两件事:
预计算前向扩散和反向采样需要的系数;
提供 q_sample 和 sample 两个核心函数。
初始化时先定义线性 beta schedule:
self .betas = torch .linspace (beta_start , beta_end , timesteps , device = self .device )
self .alphas = 1.0 - self .betas
self .alpha_bars = torch .cumprod (self .alphas , dim = 0 )
self .sqrt_alpha_bars = torch .sqrt (self .alpha_bars )
self .sqrt_one_minus_alpha_bars = torch .sqrt (1.0 - self .alpha_bars )
这对应前面推导过的:
$$
\alpha_t=1-\beta_t
$$
$$
\bar{\alpha}_t=\prod_{s=1}^{t}\alpha_s
$$
因为这些量只和时间步有关,所以应该在初始化时一次算好,而不是每个 batch 重新计算。
_extract 的作用是从长度为 timesteps 的系数表中取出当前 batch 对应的时间步系数:
def _extract (self , values : torch .Tensor , t : torch .Tensor , x : torch .Tensor ) -> torch .Tensor :
return values .gather (0 , t ).view (t .size (0 ), * ((1 ,) * (x .ndim - 1 )))
例如一个 batch 有 32 张图,每张图随机到不同的时间步,_extract 就能取出 32 个对应的 $\sqrt{\bar{\alpha}_t}$ ,并 reshape 成 [B,1,1,1],方便和图像张量相乘。
训练 DDPM 不需要从 $x_0$ 一步步加噪到 $x_t$ ,而是直接使用闭式公式:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
代码中的 q_sample 正是在实现这个公式:
def q_sample (
self ,
x0 : torch .Tensor ,
t : torch .Tensor ,
noise : torch .Tensor | None = None ,
) -> torch .Tensor :
if noise is None :
noise = torch .randn_like (x0 )
return (
self ._extract (self .sqrt_alpha_bars , t , x0 ) * x0
+ self ._extract (self .sqrt_one_minus_alpha_bars , t , x0 ) * noise
)
这里的 noise 就是训练监督信号 $\epsilon$ 。因为噪声是我们自己采样出来的,所以训练标签天然已知,不需要人工标注。
一次训练迭代可以写成五步:
从数据集中取出真实图像 $x_0$ ;
随机采样时间步 $t$ ;
随机采样噪声 $\epsilon$ ;
用闭式公式得到 $x_t$ ;
让模型根据 $(x_t,t)$ 预测 $\epsilon$ 。
训练脚本中的核心代码是:
for x0 , _ in train_loader :
x0 = x0 .to (device )
t = torch .randint (0 , args .timesteps , (x0 .size (0 ),), device = device )
noise = torch .randn_like (x0 )
xt = diffusion .q_sample (x0 , t , noise )
loss = F .mse_loss (model (xt , t ), noise )
optimizer .zero_grad (set_to_none = True )
loss .backward ()
optimizer .step ()
这正对应最终简化目标:
$$
\mathcal{L}_{\mathrm{simple}}(\theta)
{=}
\mathbb{E}_{t,x_0,\epsilon}
\left[
\left|
\epsilon-\epsilon_\theta(x_t,t)
\right|^2
\right]
$$
注意,模型不是直接预测干净图像 $x_0$ ,而是预测本次加进去的噪声 $\epsilon$ 。预测出噪声后,采样阶段就可以根据 DDPM 公式一步步构造反向均值。
train_stl10_ddpm.py 会保存:
checkpoints/stl10_ddpm.pt:模型权重和必要配置;
outputs/stl10_sample_epoch_XXX.png:训练过程中从噪声采样得到的 STL10 风格图像。
checkpoint 中保存了:
{
"model_state" : model .state_dict (),
"base" : args .base ,
"timesteps" : args .timesteps ,
"image_size" : args .image_size ,
}
这些配置会被生成脚本复用。尤其是 image_size,因为 STL10 默认使用 $96\times96$ ,生成阶段必须和训练阶段保持一致。
运行方式示例:
python train_stl10_ddpm.py --epochs 200 --batch-size 32 --image-size 96
运行结果如下所示,可以看出,一开始训练的时候噪声比较大,当训练到200轮的时候,已经能够重建出一些轮廓,但是效果还是一般,教程为了让大家更好的理解,并没有添加很多的改进技巧,例如注意力机制,类别条件生成等,各位学习完课程之后可以进一步的学习~
训练结束后,生成阶段从纯高斯噪声开始:
$$
x_T\sim\mathcal{N}(0,I)
$$
然后按照时间步反向循环:
$$
x_T\rightarrow x_{T-1}\rightarrow\cdots\rightarrow x_0
$$
代码中的 sample 函数实现了这个过程:
@torch .no_grad ()
def sample (self , model : nn .Module , shape : tuple [int , int , int , int ]) -> torch .Tensor :
model .eval ()
x = torch .randn (shape , device = self .device )
for step in reversed (range (self .timesteps )):
t = torch .full ((shape [0 ],), step , device = self .device , dtype = torch .long )
pred_noise = model (x , t )
beta_t = self ._extract (self .betas , t , x )
alpha_t = self ._extract (self .alphas , t , x )
alpha_bar_t = self ._extract (self .alpha_bars , t , x )
mean = (1.0 / torch .sqrt (alpha_t )) * (
x - beta_t / torch .sqrt (1.0 - alpha_bar_t ) * pred_noise
)
x = mean if step == 0 else mean + torch .sqrt (beta_t ) * torch .randn_like (x )
return x .clamp (- 1 , 1 )
其中反向均值为:
$$
\mu_\theta(x_t,t)
{=}
\frac{1}{\sqrt{\alpha_t}}
\left(
x_t
{-}
\frac{\beta_t}{\sqrt{1-\bar{\alpha}_t}}
\epsilon_\theta(x_t,t)
\right)
$$
代码中的这一行:
x = mean if step == 0 else mean + torch .sqrt (beta_t ) * torch .randn_like (x )
表示最后一步不再额外加随机噪声。否则生成出的 $x_0$ 会被最后一次噪声扰动。
generate_stl10_ddpm.py 只做生成,不再读取训练数据。它的流程是:
加载 checkpoints/stl10_ddpm.pt;
根据 checkpoint 里的 base 和 image_size 重建 UNet96;
加载模型权重;
从标准高斯噪声开始反向采样;
保存生成图像。
核心代码为:
checkpoint = torch .load (args .checkpoint , map_location = device )
image_size = int (checkpoint .get ("image_size" , 96 ))
model = UNet96 (base = int (checkpoint ["base" ])).to (device )
model .load_state_dict (checkpoint ["model_state" ])
diffusion = Diffusion (timesteps = int (checkpoint ["timesteps" ]), device = device )
with torch .no_grad ():
images = diffusion .sample (model , (args .num_samples , 3 , image_size , image_size ))
utils .save_image ((images + 1 ) / 2 , args .out , nrow = max (1 , int (args .num_samples ** 0.5 )))
运行方式示例:
python generate_stl10_ddpm.py --checkpoint checkpoints/stl10_ddpm.pt --out outputs/generated_stl10.png --num-samples 1
默认只生成一张 $96\times96$ 图像,方便先检查模型是否真的能从噪声生成可见结构。如果想看多张图,可以调大 --num-samples。
运行结果如下所示:
不是。前向过程:
$$
q(x_t|x_{t-1})
{=}
\mathcal{N}
\left(
x_t;
\sqrt{\alpha_t}x_{t-1},
\beta_tI
\right)
$$
是人为固定的加噪规则。需要学习的是反向过程:
$$
p_\theta(x_{t-1}|x_t)
$$
因为有闭式公式:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
它可以直接从 $x_0$ 得到任意时间步的 $x_t$ 。
因为训练时噪声 $\epsilon$ 是我们自己采样出来的,天然就是监督标签。预测噪声后,可以通过公式恢复 $x_0$ 或构造反向均值:
$$
\mu_\theta(x_t,t)
{=}
\frac{1}{\sqrt{\alpha_t}}
\left(
x_t
{-}
\frac{
\beta_t
}{
\sqrt{1-\bar{\alpha}_t}
}
\epsilon_\theta(x_t,t)
\right)
$$
所以预测噪声并不弱,它等价于学习反向去噪步骤。
VAE 是:
$$
z\sim\mathcal{N}(0,I),
\quad
x\sim p_\theta(x|z)
$$
也就是从潜变量一步生成图像。
DDPM 是:
$$
x_T\sim\mathcal{N}(0,I)
$$
$$
x_T\rightarrow x_{T-1}\rightarrow\cdots\rightarrow x_0
$$
也就是从噪声开始多步去噪生成图像。
主要缺点是采样慢。因为它通常需要从 $T$ 到 $1$ 逐步反推,基础版本可能需要几百到上千步。后续可以用 DDIM、加速采样器或蒸馏方法减少采样步数。
DDPM 的核心逻辑是:
固定前向加噪:
$$
q(x_t|x_{t-1})
{=}
\mathcal{N}
\left(
x_t;
\sqrt{\alpha_t}x_{t-1},
\beta_tI
\right)
$$
推出任意时刻加噪:
$$
x_t
{=}
\sqrt{\bar{\alpha}_t}x_0
+
\sqrt{1-\bar{\alpha}_t}\epsilon
$$
学习反向去噪:
$$
p_\theta(x_{t-1}|x_t)
{=}
\mathcal{N}
\left(
x_{t-1};
\mu_\theta(x_t,t),
\sigma_t^2I
\right)
$$
用 ELBO 得到真实后验匹配:
$$
D_{\mathrm{KL}}
\left(
q(x_{t-1}|x_t,x_0)
|
p_\theta(x_{t-1}|x_t)
\right)
$$
把高斯 KL 转成均值 MSE,再转成噪声预测 MSE:
$$
\boxed{
\mathcal{L}_{\mathrm{simple}}(\theta)
{=}
\mathbb{E}_{t,x_0,\epsilon}
\left[
\left|
\epsilon-\epsilon_\theta(x_t,t)
\right|^2
\right]
}
$$
与 VAE 相比,DDPM 不把全部图像信息压缩进一个低维潜变量,而是在图像空间中逐步去噪。它的训练目标来自概率推导,但最终落地成非常简单的监督学习问题。
VAE 为什么容易生成模糊图像?
DDPM 为什么不直接从潜变量一步生成图像?
前向过程中的 $\beta_t$ 控制什么?
为什么一步加噪公式中要使用平方根系数?
如何从 $x_0$ 直接采样任意时间步的 $x_t$ ?
为什么 $x_T$ 会接近标准高斯噪声?
DDPM 的反向过程为什么要建模为高斯分布?
ELBO 分解中的中间 KL 项在约束什么?
为什么真实后验 $q(x_{t-1}|x_t,x_0)$ 有解析解?
预测噪声 $\epsilon$ 为什么等价于学习反向去噪?
采样时为什么最后一步通常不再加噪声?
如果没有时间嵌入,DDPM 网络会遇到什么问题?
DDPM 最小化负 ELBO:
$$
\mathcal{L}
{=}
\mathbb{E}_{q(x_{1:T}|x_0)}
\left[
{-}
\log
\frac{
p_\theta(x_{0:T})
}{
q(x_{1:T}|x_0)
}
\right]
$$
展开模型联合分布:
$$
p_\theta(x_{0:T})
{=}
p(x_T)
\prod_{t=1}^{T}
p_\theta(x_{t-1}|x_t)
$$
展开前向分布:
$$
q(x_{1:T}|x_0)
{=}
\prod_{t=1}^{T}
q(x_t|x_{t-1})
$$
代入:
$$
\mathcal{L}
{=}
\mathbb{E}_q
\left[
{-}
\log
\frac{
p(x_T)
\prod_{t=1}^{T}
p_\theta(x_{t-1}|x_t)
}{
\prod_{t=1}^{T}
q(x_t|x_{t-1})
}
\right]
$$
拆开对数:
$$
\mathcal{L}
{=}
\mathbb{E}_q
\left[
{-}
\log p(x_T)
{-}
\sum_{t=1}^{T}
\log p_\theta(x_{t-1}|x_t)
+
\sum_{t=1}^{T}
\log q(x_t|x_{t-1})
\right]
$$
对 $t>1$ 使用:
$$
q(x_{t-1}|x_t,x_0)
{=}
\frac{
q(x_t|x_{t-1})q(x_{t-1}|x_0)
}{
q(x_t|x_0)
}
$$
所以:
$$
q(x_t|x_{t-1})
{=}
\frac{
q(x_{t-1}|x_t,x_0)q(x_t|x_0)
}{
q(x_{t-1}|x_0)
}
$$
将这些项代入并整理 telescoping 项,可以得到:
$$
\mathcal{L}
{=}
\mathbb{E}_q
\left[
{-}
\log
\frac{
p(x_T)
}{
q(x_T|x_0)
}
{-}
\sum_{t>1}
\log
\frac{
p_\theta(x_{t-1}|x_t)
}{
q(x_{t-1}|x_t,x_0)
}
{-}
\log p_\theta(x_0|x_1)
\right]
$$
写成 KL 形式:
$$
\boxed{
\mathcal{L}
{=}
\mathbb{E}_q
\left[
D_{\mathrm{KL}}
\left(
q(x_T|x_0)|p(x_T)
\right)
+
\sum_{t>1}
D_{\mathrm{KL}}
\left(
q(x_{t-1}|x_t,x_0)
|
p_\theta(x_{t-1}|x_t)
\right)
{-}
\log p_\theta(x_0|x_1)
\right]
}
$$
这个式子说明:训练 DDPM 的核心,就是让模型的反向一步分布匹配前向过程推出来的真实后验。
附录 B:真实后验 $q(x_{t-1}|x_t,x_0)$ 的详细推导
令:
$$
z=x_{t-1}
$$
我们要求:
$$
q(z|x_t,x_0)
$$
根据贝叶斯公式:
$$
q(z|x_t,x_0)
\propto
q(x_t|z)q(z|x_0)
$$
其中:
$$
q(x_t|z)
{=}
\mathcal{N}
\left(
x_t;
\sqrt{\alpha_t}z,
\beta_tI
\right)
$$
$$
q(z|x_0)
{=}
\mathcal{N}
\left(
z;
\sqrt{\bar{\alpha}_{t-1}}x_0,
(1-\bar{\alpha}_{t-1})I
\right)
$$
只看与 $z$ 有关的指数部分:
$$
q(z|x_t,x_0)
\propto
\exp
\left(
{-}
\frac{1}{2\beta_t}
\left|
x_t-\sqrt{\alpha_t}z
\right|^2
{-}
\frac{1}{2(1-\bar{\alpha}_{t-1})}
\left|
z-\sqrt{\bar{\alpha}_{t-1}}x_0
\right|^2
\right)
$$
展开第一项:
$$
\left|
x_t-\sqrt{\alpha_t}z
\right|^2
{=}
\left|x_t\right|^2
{-}
2\sqrt{\alpha_t}x_t^\top z
+
\alpha_t\left|z\right|^2
$$
与 $z$ 有关的部分是:
$$
{-}
\frac{\alpha_t}{2\beta_t}
\left|z\right|^2
+
\frac{\sqrt{\alpha_t}}{\beta_t}
x_t^\top z
$$
展开第二项:
$$
\left|
z-\sqrt{\bar{\alpha}_{t-1}}x_0
\right|^2
{=}
\left|z\right|^2
{-}
2\sqrt{\bar{\alpha}_{t-1}}x_0^\top z
+
\bar{\alpha}_{t-1}
\left|x_0\right|^2
$$
与 $z$ 有关的部分是:
$$
{-}
\frac{1}{2(1-\bar{\alpha}_{t-1})}
\left|z\right|^2
+
\frac{\sqrt{\bar{\alpha}_{t-1}}}{1-\bar{\alpha}_{t-1}}
x_0^\top z
$$
合并:
$$
\log q(z|x_t,x_0)
{=}
{-}
\frac{1}{2}
\left(
\frac{\alpha_t}{\beta_t}
+
\frac{1}{1-\bar{\alpha}_{t-1}}
\right)
\left|z\right|^2
+
\left(
\frac{\sqrt{\alpha_t}}{\beta_t}x_t
+
\frac{\sqrt{\bar{\alpha}_{t-1}}}{1-\bar{\alpha}_{t-1}}x_0
\right)^\top z
+
C
$$
高斯分布的标准指数形式是:
$$
\log\mathcal{N}(z;\mu,\Sigma)
{=}
{-}
\frac{1}{2}
z^\top\Sigma^{-1}z
+
z^\top\Sigma^{-1}\mu
+
C
$$
对比二次项系数:
$$
\Sigma^{-1}
{=}
\left(
\frac{\alpha_t}{\beta_t}
+
\frac{1}{1-\bar{\alpha}_{t-1}}
\right)I
$$
所以:
$$
\Sigma
{=}
\left(
\frac{\alpha_t}{\beta_t}
+
\frac{1}{1-\bar{\alpha}_{t-1}}
\right)^{-1}I
$$
通分:
$$
\Sigma
{=}
\frac{
\beta_t(1-\bar{\alpha}_{t-1})
}{
\alpha_t(1-\bar{\alpha}_{t-1})+\beta_t
}
I
$$
因为:
$$
\bar{\alpha}_t=\alpha_t\bar{\alpha}_{t-1}
$$
且:
$$
\alpha_t+\beta_t=1
$$
所以:
$$
\alpha_t(1-\bar{\alpha}_{t-1})+\beta_t
{=}
1-\bar{\alpha}_t
$$
因此:
$$
\Sigma
{=}
\frac{
\beta_t(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
I
$$
即:
$$
\boxed{
\tilde{\beta}_t
{=}
\frac{
1-\bar{\alpha}_{t-1}
}{
1-\bar{\alpha}_t
}
\beta_t
}
$$
再求均值。由一次项系数:
$$
\Sigma^{-1}\mu
{=}
\frac{\sqrt{\alpha_t}}{\beta_t}x_t
+
\frac{\sqrt{\bar{\alpha}_{t-1}}}{1-\bar{\alpha}_{t-1}}x_0
$$
所以:
$$
\mu
{=}
\Sigma
\left(
\frac{\sqrt{\alpha_t}}{\beta_t}x_t
+
\frac{\sqrt{\bar{\alpha}_{t-1}}}{1-\bar{\alpha}_{t-1}}x_0
\right)
$$
代入 $\Sigma$ :
$$
\mu
{=}
\frac{
\beta_t(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
\left(
\frac{\sqrt{\alpha_t}}{\beta_t}x_t
+
\frac{\sqrt{\bar{\alpha}_{t-1}}}{1-\bar{\alpha}_{t-1}}x_0
\right)
$$
得到:
$$
\mu
{=}
\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
$$
所以:
$$
\boxed{
\tilde{\mu}_t(x_t,x_0)
{=}
\frac{
\sqrt{\bar{\alpha}_{t-1}}\beta_t
}{
1-\bar{\alpha}_t
}
x_0
+
\frac{
\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
x_t
}
$$
已知:
$$
\mu_\theta(x_t,t)
{=}
\frac{
\sqrt{\bar{\alpha}_{t-1}}\beta_t
}{
1-\bar{\alpha}_t
}
x_{0,\theta}(x_t,t)
+
\frac{
\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
x_t
$$
又有:
$$
x_{0,\theta}(x_t,t)
{=}
\frac{
x_t-\sqrt{1-\bar{\alpha}_t}\epsilon_\theta(x_t,t)
}{
\sqrt{\bar{\alpha}_t}
}
$$
代入:
$$
\mu_\theta(x_t,t)
{=}
\frac{
\sqrt{\bar{\alpha}_{t-1}}\beta_t
}{
1-\bar{\alpha}_t
}
\cdot
\frac{
x_t-\sqrt{1-\bar{\alpha}_t}\epsilon_\theta(x_t,t)
}{
\sqrt{\bar{\alpha}_t}
}
+
\frac{
\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
x_t
$$
因为:
$$
\bar{\alpha}_t=\alpha_t\bar{\alpha}_{t-1}
$$
所以:
$$
\frac{
\sqrt{\bar{\alpha}_{t-1}}
}{
\sqrt{\bar{\alpha}_t}
}
{=}
\frac{1}{\sqrt{\alpha_t}}
$$
于是:
$$
\mu_\theta(x_t,t)
{=}
\frac{
\beta_t
}{
\sqrt{\alpha_t}(1-\bar{\alpha}_t)
}
x_t
{-}
\frac{
\beta_t
}{
\sqrt{\alpha_t}\sqrt{1-\bar{\alpha}_t}
}
\epsilon_\theta(x_t,t)
+
\frac{
\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
x_t
$$
合并 $x_t$ 系数:
$$
\frac{
\beta_t
}{
\sqrt{\alpha_t}(1-\bar{\alpha}_t)
}
+
\frac{
\sqrt{\alpha_t}(1-\bar{\alpha}_{t-1})
}{
1-\bar{\alpha}_t
}
$$
通分:
$$
\frac{
\beta_t+\alpha_t(1-\bar{\alpha}_{t-1})
}{
\sqrt{\alpha_t}(1-\bar{\alpha}_t)
}
$$
因为:
$$
\beta_t+\alpha_t=1
$$
且:
$$
\alpha_t\bar{\alpha}_{t-1}=\bar{\alpha}_t
$$
所以:
$$
\beta_t+\alpha_t(1-\bar{\alpha}_{t-1})
{=}
\beta_t+\alpha_t-\alpha_t\bar{\alpha}_{t-1}
{=}
1-\bar{\alpha}_t
$$
因此 $x_t$ 的系数是:
$$
\frac{1}{\sqrt{\alpha_t}}
$$
最终:
$$
\mu_\theta(x_t,t)
{=}
\frac{1}{\sqrt{\alpha_t}}x_t
{-}
\frac{
\beta_t
}{
\sqrt{\alpha_t}\sqrt{1-\bar{\alpha}_t}
}
\epsilon_\theta(x_t,t)
$$
提取 $\frac{1}{\sqrt{\alpha_t}}$ :
$$
\boxed{
\mu_\theta(x_t,t)
{=}
\frac{1}{\sqrt{\alpha_t}}
\left(
x_t
{-}
\frac{
\beta_t
}{
\sqrt{1-\bar{\alpha}_t}
}
\epsilon_\theta(x_t,t)
\right)
}
$$