深入理解扩散模型 (Diffusion Model):从原理到实践
1. 扩散模型的核心思想
1.1 什么是扩散?
扩散模型的核心思想可以用一个直观的比喻来理解:
想象一下:
- 前向扩散:将一杯干净的水中滴入墨水,墨水分子逐渐扩散,最终整杯水变成均匀的黑色
- 逆向去噪:如果你能将这个过程倒放——从黑色的水逐步去除墨水分子——你就能恢复出干净的水
扩散模型正是基于这个原理:学习从”噪声”逆向恢复到”数据”的过程。
1.2 扩散过程的两个阶段
扩散模型包含两个核心过程:
- 前向过程 (Forward Process):逐步向数据添加噪声,直到变成纯噪声
- 逆向过程 (Reverse Process):学习从噪声中逐步恢复出原始数据
1.3 扩散过程示意
以下展示了扩散模型在不同时间步的状态变化:
| 阶段 | 时间步 | 描述 | 状态 |
|---|---|---|---|
| 原始 | T=0 | 干净的数据 | 清晰的图像/信号 |
| 早期 | T=250 | 轻度噪声 | 细节开始模糊 |
| 中期 | T=500 | 中度噪声 | 主要结构仍可见 |
| 后期 | T=750 | 重度噪声 | 仅剩轮廓 |
| 最终 | T=1000 | 纯噪声 | 完全随机 |
前向过程(训练时):从 T=0 到 T=T,逐渐添加噪声 逆向过程(生成时):从 T=T 到 T=0,逐步去除噪声生成图像
2. 前向扩散过程 (Forward Process)
2.1 数学定义
前向过程是一个预设的马尔可夫链,它逐步向数据添加高斯噪声。假设原始数据为 ,前向过程定义为:
其中 是第 步的噪声调度参数,。
2.2 关键性质:可直接采样任意时刻
得益于高斯噪声的重参数化性质,我们可以直接计算出任意时刻 的噪声图像 :
其中:
- 是标准高斯噪声
这个性质非常关键:我们不需要逐步迭代就能直接得到任意时刻的噪声图像,这大大简化了训练过程。
2.3 前向过程代码实现
import torchimport numpy as np
def forward_diffusion(x0, t, betas): """ 前向扩散过程:直接从 x0 计算 xt x0: 原始图像 tensor t: 时间步 betas: 噪声调度序列 """ alphas = 1 - betas alpha_bars = torch.cumprod(alphas, dim=0)
# 确保 t 是一维张量 t = t.unsqueeze(-1) if t.dim() > 0 else t
# 采样噪声 epsilon = torch.randn_like(x0)
# 重参数化采样 sqrt_alpha_bar = torch.sqrt(alpha_bars[t]) sqrt_one_minus_alpha_bar = torch.sqrt(1 - alpha_bars[t])
xt = sqrt_alpha_bar * x0 + sqrt_one_minus_alpha_bar * epsilon
return xt, epsilon3. 逆向去噪过程 (Reverse Process)
3.1 学习逆向分布
逆向过程是扩散模型的核心——我们需要一个神经网络来学习 。
理论上,逆向分布也是高斯分布:
其中 和 是由神经网络预测的参数。
3.2 简化预测目标
为了简化训练,DDPM 论文提出了一个关键技巧:让网络预测噪声 ,而不是直接预测均值。
由前向过程的公式:
我们可以推导出:
因此,预测噪声就等价于预测原始图像,简化了学习目标。
3.3 逆向过程代码实现
import torchimport torch.nn.functional as F
def p_sample(model, xt, t, betas): """ 从 xt 采样 xt-1 """ t = torch.tensor([t])
# 模型预测噪声 epsilon_theta = model(xt, t)
# 提取参数 beta_t = betas[t] alpha_t = 1 - beta_t alpha_bar_t = alpha_t.prod() # 简化的 cumprod
# 计算均值 mean = (1 / torch.sqrt(alpha_t)) * ( xt - (beta_t / torch.sqrt(1 - alpha_bar_t)) * epsilon_theta )
# 如果是最后一步,直接返回均值 if t[-1] == len(betas) - 1: return mean
# 计算方差 variance = beta_t
# 采样 noise = torch.randn_like(xt) return mean + torch.sqrt(variance) * noise
def reverse_process(model, shape, betas): """ 从纯噪声生成图像 """ # 从纯噪声开始 xt = torch.randn(shape)
# 逐步去噪 for t in reversed(range(len(betas))): xt = p_sample(model, xt, t, betas)
return xt4. 噪声调度 (Noise Scheduling)
4.1 为什么需要噪声调度?
噪声调度(Noise Scheduling)决定了噪声如何随时间步增加——它直接影响模型的训练稳定性和生成质量。
4.2 常见的调度策略
| 调度类型 | 特点 | 应用 |
|---|---|---|
| 线性 (Linear) | 均匀递增 | DDPM |
| 余弦 (Cosine) | 平滑衰减 | Stable Diffusion |
| 二次 (Quadratic) | 前期慢后期快 | early β scheduling |
| Scaled Linear | 缩放线性 | OpenAI guided diffusion |
4.3 噪声调度可视化
4.3 噪声调度对比
不同调度策略下噪声水平的变化对比:
噪声水平 1.0 │ ▓▓▓▓▓▓▓▓▓▓ │ ▓▓▓▓▓▓▓▓ 0.8 │ ▓▓▓▓▓▓▓▓ │ ▓▓▓▓▓▓▓▓ ← 二次 (Quadratic) 0.6 │ ▓▓▓▓▓▓▓▓ ●───●───●───●───●───●───●───●───● │ ▓▓▓▓▓▓▓▓ ← 余弦 (Cosine) ───────────────────────── 0.4 │ ▓▓▓▓▓▓▓▓ ●───●───●───●───●───●───●───●───●───●───●───●───●───●───● │ ← 线性 (Linear) ──────────────────────────────── 0.2 │ │ 0.0 │●─────────────────────────────────────────────────────────────── └──────────────────────────────────────────────────────────────── T=0 T=1000| 调度类型 | 特点 | 前期噪声增长 | 后期噪声增长 | 适用场景 |
|---|---|---|---|---|
| 线性 (Linear) | 均匀递增 | 慢 | 快 | DDPM 原始论文 |
| 余弦 (Cosine) | 平滑衰减 | 很慢 | 很快 | Stable Diffusion |
| 二次 (Quadratic) | 前期慢后期快 | 很慢 | 很快 | 早期实验 |
推荐:现代扩散模型(如 Stable Diffusion)普遍采用余弦调度,因为它在训练前期保持较高的信号水平,有助于模型学习有意义的特征。
4.4 调度策略代码对比
def get_betas_schedule(schedule_type, T, beta_start=1e-4, beta_end=0.02): """生成不同类型的噪声调度序列"""
if schedule_type == "linear": # 线性调度:从 beta_start 线性增加到 beta_end return torch.linspace(beta_start, beta_end, T)
elif schedule_type == "cosine": # 余弦调度:更平滑的过渡 steps = T + 1 x = torch.linspace(0, T, steps) alphas_cumprod = torch.cos(((x / T) + 0.008) * torch.pi / 2) ** 2 alphas_cumprod = alphas_cumprod / alphas_cumprod[0] betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1]) return torch.clip(betas, beta_start, beta_end)
elif schedule_type == "quadratic": # 二次调度:前期慢后期快 return beta_start + (beta_end - beta_start) * (torch.linspace(0, 1, T) ** 2)5. U-Net 骨干网络
5.1 为什么选择 U-Net?
大多数扩散模型使用 U-Net 作为噪声预测网络,原因在于:
- 编码器-解码器结构:能捕捉多尺度特征
- 跳跃连接:保留低级细节信息,对重建图像至关重要
- 对称性:编码器和解码器镜像对称,简化设计
5.2 U-Net 在扩散模型中的改进
class DiffusionUNet(nn.Module): def __init__(self, in_channels=3, out_channels=3, base_channels=128): super().__init__()
# 编码器(下采样) self.enc1 = ResidualBlock(in_channels, base_channels) self.enc2 = ResidualBlock(base_channels, base_channels * 2) self.enc3 = ResidualBlock(base_channels * 2, base_channels * 4)
# 瓶颈 self.bottleneck = ResidualBlock(base_channels * 4, base_channels * 4)
# 解码器(上采样) self.dec1 = ResidualBlock(base_channels * 8, base_channels * 2) # 拼接跳跃连接 self.dec2 = ResidualBlock(base_channels * 4, base_channels) self.dec3 = ResidualBlock(base_channels * 2, out_channels)
# 时间步嵌入 self.time_mlp = nn.Sequential( SinusoidalPosEmb(base_channels), nn.Linear(base_channels, base_channels * 4), nn.SiLU(), nn.Linear(base_channels * 4, base_channels) )
def forward(self, x, t): # 编码 e1 = self.enc1(x) e2 = self.enc2(F.max_pool2d(e1, 2)) e3 = self.enc3(F.max_pool2d(e2, 2))
# 瓶颈 + 时间嵌入 b = self.bottleneck(e3) t_emb = self.time_mlp(t)
# 解码(带跳跃连接) d1 = self.dec1(torch.cat([F.interpolate(b, e3.shape[2:], mode='nearest'), e3], dim=1)) d2 = self.dec2(torch.cat([F.interpolate(d1, e2.shape[2:], mode='nearest'), e2], dim=1)) d3 = self.dec3(torch.cat([F.interpolate(d2, e1.shape[2:], mode='nearest'), e1], dim=1))
return d35.3 关键组件
- 残差块 (Residual Block):包含 Group Normalization 和 SiLU 激活
- 时间步嵌入 (Time Embedding):使用正弦位置编码让网络感知当前时间步
- 注意力机制:在高级特征上加入自注意力,增强全局建模能力
6. 条件扩散 (Conditional Diffusion)
6.1 分类器引导 (Classifier Guidance)
为了让扩散模型根据条件生成(如文本描述),早期方法使用分类器引导:
其中 是引导强度, 是分类器梯度。
6.2 无分类器引导 (Classifier-Free Guidance)
无分类器引导是更优雅的方案,不需要额外的分类器:
其中:
- 是条件(如文本嵌入)
- 是空条件(无条件)
- 是引导强度(通常 5-15)
- :无引导
- :轻度引导,保留多样性
- :中度引导,平衡质量与多样性
- :强度引导,高质量但多样性降低
6.3 交叉注意力机制
在 Stable Diffusion 等模型中,文本条件通过交叉注意力机制注入:
文本嵌入 → Cross-Attention → U-Net 特征这允许模型在生成过程中”关注”文本的不同部分。
7. 扩散模型 vs GAN
| 特性 | 扩散模型 | GAN |
|---|---|---|
| 训练稳定性 | 稳定,使用简单的 MSE 损失 | 不稳定,需要精心平衡对抗训练 |
| 生成多样性 | 高 | 中等,容易发生模式坍缩 |
| 推理速度 | 慢(需要多步迭代) | 快(单步生成) |
| 损失函数 | 简单的重构/MSE 损失 | 对抗损失 |
| 理论基础 | 渐近理论 | 博弈论 |
| 超参数敏感性 | 中等 | 高 |
虽然扩散模型推理慢,但有多种加速方法:
- DDIM:使用非马尔可夫噪声调度,减少步数到 20-50 步
- LCM (Latent Consistency Models):直接在潜在空间生成,4-8 步即可
- SDXL-Turbo:单步或极少步生成
8. 现代扩散模型应用
8.1 图像生成
| 模型 | 特点 | 开源 |
|---|---|---|
| Stable Diffusion | Latent Diffusion,高效 | ✅ |
| DALL-E 3 | GPT-4V 驱动,高保真 | ❌ |
| Imagen | 超分辨率级联,文字渲染强 | ❌ |
| Midjourney | 艺术风格优化 | ❌ |
| SDXL | 1024px 大图支持 | ✅ |
8.2 视频生成
- Sora:OpenAI 的视频生成模型,基于扩散变换器
- Stable Video Diffusion:Stability AI 的开源视频扩散
- Runway Gen-2/3:创意视频生成
8.3 3D 生成
- Point-E:OpenAI 的 3D 点云生成
- DreamFusion:Google 的 3D 物体生成
- Zero-1-to-3:从单图重建 3D
8.4 音频合成
- AudioLDM:基于扩散的音频生成
- Riffusion:音乐生成
- SPEAR-TTS:文本转语音
9. 核心公式总结
- 前向过程(第 t 步):
- 逆向过程均值:
- 无分类器引导:
10. 总结与展望
扩散模型的核心创新在于:
- 渐进式生成:通过多步迭代逐步去除噪声,生成高质量样本
- 稳定训练:简单的 MSE 损失替代对抗训练
- 条件生成:通过引导机制实现灵活的条件控制
- 潜在空间:Latent Diffusion 大幅提升效率
当前挑战:
- 推理速度仍有提升空间
- 长视频生成的一致性问题
- 3D 内容生成的质量和效率
未来方向:
- 端到端的单步或少步模型
- 扩散模型与语言模型的深度融合
- 多模态统一生成框架
参考资料
- Ho, J., et al. (2020). “Denoising Diffusion Probabilistic Models.” NeurIPS.
- Rombach, R., et al. (2022). “High-Resolution Image Synthesis with Latent Diffusion Models.” CVPR.
- Ho, J., et al. (2022). “Cascaded Diffusion Models for High Fidelity Image Generation.” JMLR.
- Ho, J., & Salimans, T. (2022). “Classifier-Free Diffusion Guidance.” NeurIPS Workshop.
- Peebles, W., & Xie, Y. (2023). “Scalable Diffusion Models with Transformers.” ICCV.
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!

