前向过程 (Forward Process):加噪
前向过程是将原始数据 x0x_0x0 逐步变为纯噪声 xtx_txt。每一步添加的噪声由预定义的方差调度(Variance Schedule) βt\beta_tβt 决定:
q(xt∣xt−1)=N(xt;1−βtxt−1,βtI)q(x_t | x_{t-1}) = \mathcal{N}(x_t; \sqrt{1 - \beta_t} x_{t-1}, \beta_t \mathbf{I})q(xt∣xt−1)=N(xt;1−βt xt−1,βtI)
重参数化技巧 (The Nice Property)
通过令 αt=1−βt\alpha_t = 1 - \beta_tαt=1−βt 且αˉt=∏i=1tαi\bar{\alpha}t = \prod{i=1}^t \alpha_iαˉt=∏i=1tαi, 我们可以直接从 x0x_0x0 推导出任意时刻 xtx_txt 的分布,而不需要一步步迭代:
αˉt=∏i=1tαi\bar{\alpha}t = \prod{i=1}^t \alpha_iαˉt=∏i=1tαi xt=αˉtx0+1−αˉtϵ,ϵ∼N(0,I)x_t = \sqrt{\bar{\alpha}_t} x_0 + \sqrt{1 - \bar{\alpha}_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I})xt=αˉt x0+1−αˉt ϵ,ϵ∼N(0,I)
这意味着 xtx_txt 可以看作是原始图像的缩放加上一个加权噪声。
具体推导技巧:
1.1 基础步进公式
在前向加噪过程中,每一步 ttt 都是在 t−1t-1t−1 的基础上添加一个高斯噪声:
q(xt∣xt−1)=N(xt;αtxt−1,(1−αt)I)q(x_t | x_{t-1}) = \mathcal{N}(x_t; \sqrt{\alpha_t} x_{t-1}, (1 - \alpha_t) \mathbf{I})q(xt∣xt−1)=N(xt;αt xt−1,(1−αt)I)
利用重参数化,我们可以将 xtx_txt 写成:
xt=αtxt−1+1−αtϵt−1x_t = \sqrt{\alpha_t} x_{t-1} + \sqrt{1 - \alpha_t} \epsilon_{t-1}xt=αt xt−1+1−αt ϵt−1 其中 ϵt−1∼N(0,I)\epsilon_{t-1} \sim \mathcal{N}(0, \mathbf{I})ϵt−1∼N(0,I)
根据高斯分布的性质,如果x=a⋅μ+b⋅ϵx = a \cdot \mu + b \cdot \epsilonx=a⋅μ+b⋅ϵ,且 ϵ∼N(0,σ2)\epsilon \sim \mathcal{N}(0, \sigma^2)ϵ∼N(0,σ2),那么 xxx 服从分布 N(aμ,b2σ2)\mathcal{N}(a\mu, b^2\sigma^2)N(aμ,b2σ2)
1.2 递归展开
为了得到 xtx_txt 与 x0x_0x0 的关系,我们尝试展开这个递归式。
第一步迭代 xt−1x_{t-1}xt−1 到 xt−2x_{t-2}xt−2
已知xt−1=αt−1xt−2+1−αt−1ϵt−2x_{t-1} = \sqrt{\alpha_{t-1}} x_{t-2} + \sqrt{1 - \alpha_{t-1}} \epsilon_{t-2}xt−1=αt−1 xt−2+1−αt−1 ϵt−2 ,代入 xtx_txt 的公式中:
xt=αt(αt−1xt−2+1−αt−1ϵt−2)+1−αtϵt−1x_t = \sqrt{\alpha_t} \left( \sqrt{\alpha_{t-1}} x_{t-2} + \sqrt{1 - \alpha_{t-1}} \epsilon_{t-2} \right) + \sqrt{1 - \alpha_t} \epsilon_{t-1}xt=αt (αt−1 xt−2+1−αt−1 ϵt−2)+1−αt ϵt−1
xt=αtαt−1xt−2+(αt(1−αt−1)ϵt−2+1−αtϵt−1)x_t = \sqrt{\alpha_t \alpha_{t-1}} x_{t-2} + \left( \sqrt{\alpha_t (1 - \alpha_{t-1})} \epsilon_{t-2} + \sqrt{1 - \alpha_t} \epsilon_{t-1} \right)xt=αtαt−1 xt−2+(αt(1−αt−1) ϵt−2+1−αt ϵt−1)
两个独立的高斯分布相加,结果仍然是高斯分布,且方差直接相加。
1.3 高斯分布的叠加性质
叠加后的总方差为:
Total Variance=αt(1−αt−1)+(1−αt)=αt−αtαt−1+1−αt=1−αtαt−1\text{Total Variance} = \alpha_t (1 - \alpha_{t-1}) + (1 - \alpha_t) = \alpha_t - \alpha_t \alpha_{t-1} + 1 - \alpha_t = 1 - \alpha_t \alpha_{t-1}Total Variance=αt(1−αt−1)+(1−αt)=αt−αtαt−1+1−αt=1−αtαt−1
利用重参数化,这两项噪声可以合并为一个新的标准高斯噪声ϵˉt−2\bar{\epsilon}{t-2}ϵˉt−2:
xt=αtαt−1xt−2+1−αtαt−1ϵˉt−2x_t = \sqrt{\alpha_t \alpha{t-1}} x_{t-2} + \sqrt{1 - \alpha_t \alpha_{t-1}} \bar{\epsilon}_{t-2}xt=αtαt−1 xt−2+1−αtαt−1 ϵˉt−2
1.4 推广到xtx_txt
以此类推,如果我们一直迭代到x0x_0x0,系数会变成累乘。我们定义 αˉt=∏i=1tαi\bar{\alpha}t = \prod{i=1}^t \alpha_iαˉt=∏i=1tαi:
xt=αˉtx0+1−αˉtϵ,ϵ∼N(0,I)x_t = \sqrt{\bar{\alpha}_t} x_0 + \sqrt{1 - \bar{\alpha}_t} \epsilon, \quad \epsilon \sim \mathcal{N}(0, \mathbf{I})xt=αˉt x0+1−αˉt ϵ,ϵ∼N(0,I)
反向过程 (Reverse Process):去噪
我们的目标是求 q(xt−1∣xt)q(x_{t-1} | x_t)q(xt−1∣xt),即已知噪声图推断前一时刻的图。由于 q(xt−1∣xt)q(x_{t-1} | x_t)q(xt−1∣xt) 在数学上不可直接计算,我们用一个神经网络 pθ(xt−1∣xt)p_\theta(x_{t-1} | x_t)pθ(xt−1∣xt) 来拟合它:
pθ(xt−1∣xt)=N(xt−1;μθ(xt,t),Σθ(xt,t))p_\theta(x_{t-1} | x_t) = \mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \Sigma_\theta(x_t, t))pθ(xt−1∣xt)=N(xt−1;μθ(xt,t),Σθ(xt,t))
贝叶斯公式展开
根据贝叶斯定理,如果在已知x0x_0x0的条件下,反向过程其实是有闭式解的高斯分布。通过推导可知,模型 μθ\mu_\thetaμθ的核心任务是预测噪声。
根据贝叶斯定理:
q(xt−1∣xt,x0)=q(xt∣xt−1,x0)q(xt−1∣x0)q(xt∣x0)q(x_{t-1} | x_t, x_0) = q(x_t | x_{t-1}, x_0) \frac{q(x_{t-1} | x_0)}{q(x_t | x_0)}q(xt−1∣xt,x0)=q(xt∣xt−1,x0)q(xt∣x0)q(xt−1∣x0)
由于扩散过程是马尔可夫链,xtx_txt 只取决于xt−1x_{t-1}xt−1,所以 q(xt∣xt−1,x0)=q(xt∣xt−1)q(x_t | x_{t-1}, x_0) = q(x_t | x_{t-1})q(xt∣xt−1,x0)=q(xt∣xt−1)。
我们已知这三个项都是高斯分布:
- q(xt∣xt−1)∝exp(−(xt−αtxt−1)22βt)q(x_t | x_{t-1}) \propto \exp \left( -\frac{(x_t - \sqrt{\alpha_t}x_{t-1})^2}{2\beta_t} \right)q(xt∣xt−1)∝exp(−2βt(xt−αt xt−1)2)
- q(xt−1∣x0)∝exp(−(xt−1−αˉt−1x0)22(1−αˉt−1))q(x_{t-1} | x_0) \propto \exp \left( -\frac{(x_{t-1} - \sqrt{\bar{\alpha}{t-1}}x_0)^2}{2(1-\bar{\alpha}{t-1})} \right)q(xt−1∣x0)∝exp(−2(1−αˉt−1)(xt−1−αˉt−1 x0)2)
- q(xt∣x0)∝exp(−(xt−αˉtx0)22(1−αˉt))q(x_t | x_0) \propto \exp \left( -\frac{(x_t - \sqrt{\bar{\alpha}_t}x_0)^2}{2(1-\bar{\alpha}_t)} \right)q(xt∣x0)∝exp(−2(1−αˉt)(xt−αˉt x0)2)
指数项合并
将上述概率密度函数带入贝叶斯公式,我们只关注指数部分(因为常数项最终会归一化)。指数项 EEE 可以写为:
E=−12[(xt−αtxt−1)2βt+(xt−1−αˉt−1x0)21−αˉt−1−(xt−αˉtx0)21−αˉt]E = -\frac{1}{2} \left[ \frac{(x_t - \sqrt{\alpha_t}x_{t-1})^2}{\beta_t} + \frac{(x_{t-1} - \sqrt{\bar{\alpha}{t-1}}x_0)^2}{1-\bar{\alpha}{t-1}} - \frac{(x_t - \sqrt{\bar{\alpha}_t}x_0)^2}{1-\bar{\alpha}_t} \right]E=−21[βt(xt−αt xt−1)2+1−αˉt−1(xt−1−αˉt−1 x0)2−1−αˉt(xt−αˉt x0)2]
注意到我们要解的是关于 xt−1x_{t-1}xt−1 的分布,所以可以将 EEE 整理成关于 xt−1x_{t-1}xt−1 的二次型: axt−12+bxt−1+cax_{t-1}^2 + bx_{t-1} + caxt−12+bxt−1+c
提取 xt−1x_{t-1}xt−1 的系数
展开括号并只保留含 xt−1x_{t-1}xt−1 的项:
E=−12[xt2−2αtxtxt−1+αtxt−12βt+xt−12−2αˉt−1x0xt−1+αˉt−1x021−αˉt−1+C]E = -\frac{1}{2} \left[ \frac{x_t^2 - 2\sqrt{\alpha_t}x_tx_{t-1} + \alpha_tx_{t-1}^2}{\beta_t} + \frac{x_{t-1}^2 - 2\sqrt{\bar{\alpha}{t-1}}x_0x{t-1} + \bar{\alpha}{t-1}x_0^2}{1-\bar{\alpha}{t-1}} + C \right]E=−21[βtxt2−2αt xtxt−1+αtxt−12+1−αˉt−1xt−12−2αˉt−1 x0xt−1+αˉt−1x02+C]
提取 xt−12x_{t-1}^2xt−12 的系数(即方差的倒数 1σ~t2\frac{1}{\tilde{\sigma}_t^2}σ~t21):
1σ~t2=αtβt+11−αˉt−1\frac{1}{\tilde{\sigma}t^2} = \frac{\alpha_t}{\beta_t} + \frac{1}{1-\bar{\alpha}{t-1}}σ~t21=βtαt+1−αˉt−11
提取 xt−1x_{t-1}xt−1 的一次项系数:
coeff(xt−1)=αtβtxt+αˉt−11−αˉt−1x0coeff(x_{t-1}) = \frac{\sqrt{\alpha_t}}{\beta_t}x_t + \frac{\sqrt{\bar{\alpha}{t-1}}}{1-\bar{\alpha}{t-1}}x_0coeff(xt−1)=βtαt xt+1−αˉt−1αˉt−1 x0
求解均值 μ~t\tilde{\mu}_tμ~t
对于高斯分布 exp(−(x−μ)22σ2)\exp(-\frac{(x-\mu)^2}{2\sigma^2})exp(−2σ2(x−μ)2),其一次项系数与二次项系数的关系是 μ=coeff(x)二次项系数\mu = \frac{coeff(x)}{\text{二次项系数}}μ=二次项系数coeff(x)。
因此:
μ~t(xt,x0)=(αtβtxt+αˉt−11−αˉt−1x0)/(αtβt+11−αˉt−1)\tilde{\mu}t(x_t, x_0) = \left( \frac{\sqrt{\alpha_t}}{\beta_t}x_t + \frac{\sqrt{\bar{\alpha}{t-1}}}{1-\bar{\alpha}{t-1}}x_0 \right) / \left( \frac{\alpha_t}{\beta_t} + \frac{1}{1-\bar{\alpha}{t-1}} \right)μ~t(xt,x0)=(βtαt xt+1−αˉt−1αˉt−1 x0)/(βtαt+1−αˉt−11)
经过一系列代数化简(利用 βt=1−αt\beta_t = 1-\alpha_tβt=1−αt 和 αˉt=αtαˉt−1\bar{\alpha}t = \alpha_t \bar{\alpha}{t-1}αˉt=αtαˉt−1),可以简化为:
μ~t(xt,x0)=αt(1−αˉt−1)1−αˉtxt+αˉt−1βt1−αˉtx0\tilde{\mu}t(x_t, x_0) = \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μ~t(xt,x0)=1−αˉtαt (1−αˉt−1)xt+1−αˉtαˉt−1 βtx0
高斯分布的概率密度函数 f(x)=1σ2πexp(−(x−μ)22σ2)f(x) = \frac{1}{\sigma\sqrt{2\pi}} \exp\left( -\frac{(x - \mu)^2}{2\sigma^2} \right)f(x)=σ2π 1exp(−2σ2(x−μ)2)
如果我们把指数部分展开,它其实是一个关于 x 的二次多项式:
−12σ2(x2−2μx+μ2)=−12σ2⏟x2 的系数x2+μσ2⏟x 的系数x+常数项-\frac{1}{2\sigma^2}(x^2 - 2\mu x + \mu^2) = \underbrace{-\frac{1}{2\sigma^2}}{x^2 \text{ 的系数}} x^2 + \underbrace{\frac{\mu}{\sigma^2}}{x \text{ 的系数}} x + \text{常数项}−2σ21(x2−2μx+μ2)=x2 的系数 −2σ21x2+x 的系数 σ2μx+常数项
引入噪声 ϵ\epsilonϵ (最终训练形式)
在训练时我们并不知道x0x_0x0,但通过之前的重参数化公式,我们知道 x0=1αˉt(xt−1−αˉtϵ)x_0 = \frac{1}{\sqrt{\bar{\alpha}_t}}(x_t - \sqrt{1-\bar{\alpha}_t}\epsilon)x0=αˉt 1(xt−1−αˉt ϵ)。
将这个 x0x_0x0 代入上面的均值公式,消去 x0x_0x0,最终得到模型预测的目标:
μ~t=1αt(xt−1−αt1−αˉtϵ)\tilde{\mu}_t = \frac{1}{\sqrt{\alpha_t}} \left( x_t - \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}_t}} \epsilon \right)μ~t=αt 1(xt−1−αˉt 1−αtϵ)
目标函数 (Loss Function)
通过简化变分下界(Variational Lower Bound, VLB),DDPM 最终的损失函数出奇地简洁。它本质上是在最小化预测噪声与真实噪声之间的L2L2L2距离:
Lsimple(θ)=Ex0,ϵ,t[∥ϵ−ϵθ(αˉtx0+1−αˉtϵ,t)∥2]L_{simple}(\theta) = \mathbb{E}{x_0, \epsilon, t} \left[ \| \epsilon - \epsilon\theta(\sqrt{\bar{\alpha}_t} x_0 + \sqrt{1 - \bar{\alpha}_t} \epsilon, t) \|^2 \right]Lsimple(θ)=Ex0,ϵ,t[∥ϵ−ϵθ(αˉt x0+1−αˉt ϵ,t)∥2]
- ϵ\epsilonϵ 是我们在前向过程中注入的真实随机噪声。
- ϵθ\epsilon_\thetaϵθ 是神经网络预测的噪声。
预测与采样 (Sampling)
在推理阶段,我们从 xT∼N(0,I)x_T \sim \mathcal{N}(0, \mathbf{I})xT∼N(0,I) 开始,利用模型预测的噪声 ϵθ\epsilon_\thetaϵθ 不断迭代回退:
xt−1=1αt(xt−1−αt1−αˉtϵθ(xt,t))+σtzx_{t-1} = \frac{1}{\sqrt{\alpha_t}} \left( x_t - \frac{1-\alpha_t}{\sqrt{1-\bar{\alpha}t}} \epsilon\theta(x_t, t) \right) + \sigma_t zxt−1=αt 1(xt−1−αˉt 1−αtϵθ(xt,t))+σtz
其中 zzz 是标准正态噪声,用于保持生成过程的随机性和多样性。
Demo
python
import torch
import torch.nn as nn
import torch.nn.functional as F
class SimpleDiffusion:
def __init__(self, steps=1000, beta_start=1e-4, beta_end=0.02):
self.steps = steps
# 1. 定义线性方差调度 beta
self.betas = torch.linspace(beta_start, beta_end, steps)
# 2. 推导数学系数
self.alphas = 1. - self.betas
self.alphas_cumprod = torch.cumprod(self.alphas, dim=0) # 对应数学中的 \bar{alpha}_t
# 3. 用于前向加噪的系数: x_t = sqrt_alphas_cumprod * x_0 + sqrt_one_minus_alphas_cumprod * epsilon
self.sqrt_alphas_cumprod = torch.sqrt(self.alphas_cumprod)
self.sqrt_one_minus_alphas_cumprod = torch.sqrt(1. - self.alphas_cumprod)
def forward_diffusion(self, x_0, t):
"""前向加噪:对应重参数化技巧推导"""
noise = torch.randn_like(x_0)
# 提取对应时刻 t 的系数
sqrt_alpha_bar = self.sqrt_alphas_cumprod[t].view(-1, 1, 1, 1)
sqrt_one_minus_alpha_bar = self.sqrt_one_minus_alphas_cumprod[t].view(-1, 1, 1, 1)
# 直接计算 x_t
x_t = sqrt_alpha_bar * x_0 + sqrt_one_minus_alpha_bar * noise
return x_t, noise
@torch.no_grad()
def sample(self, model, shape):
"""反向采样:对应利用贝叶斯公式推导出的均值 \tilde{\mu}_t"""
device = next(model.parameters()).device
x_t = torch.randn(shape).to(device) # 从纯高斯噪声开始
for i in reversed(range(self.steps)):
t = torch.full((shape[0],), i, device=device, dtype=torch.long)
# 模型预测噪声 epsilon_theta
predicted_noise = model(x_t, t)
# 获取当前时刻的系数
alpha = self.alphas[i]
alpha_bar = self.alphas_cumprod[i]
beta = self.betas[i]
# 核心推导落地:计算均值 mu
# 公式: x_{t-1} = 1/sqrt(alpha) * (x_t - (1-alpha)/sqrt(1-alpha_bar) * predicted_noise)
mu = (1 / torch.sqrt(alpha)) * (
x_t - ((1 - alpha) / torch.sqrt(1 - alpha_bar)) * predicted_noise
)
if i > 0:
# 加上公式末尾的随机项 sigma * z
noise = torch.randn_like(x_t)
x_t = mu + torch.sqrt(beta) * noise
else:
x_t = mu
return x_t
# 模拟一个极简的预测噪声的 U-Net (仅作结构演示)
class SimpleUNet(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Conv2d(3, 64, 3, padding=1),
nn.ReLU(),
nn.Conv2d(64, 3, 3, padding=1)
)
def forward(self, x, t):
# 实际中这里会把时间 t 转化为 Embedding 注入网络
return self.net(x)
# --- 使用示例 ---
diff = SimpleDiffusion(steps=1000)
model = SimpleUNet()
img_shape = (1, 3, 64, 64)
# 采样生成图片
generated_img = diff.sample(model, img_shape)