引言:扩散模型要解决什么问题?
在深入扩散模型之前,我们需要先明确:它是用来做什么的?为什么会出现?
生成模型的演进
在扩散模型出现之前,生成模型领域主要由以下方法主导:
GAN的辉煌与困境
优势:
- 生成质量高,图像清晰逼真
- 推理速度快,单步前向传播即可生成
痛点:
- 训练极度不稳定,需要精心调参
- 模式崩溃(Mode Collapse):生成器只生成少数几种样本
- 生成器和判别器的动态平衡难以维持
VAE的稳定与模糊
优势:
- 训练稳定,目标函数明确
- 隐空间连续,支持插值
痛点:
- 生成图像模糊,缺乏细节
- 像素级重建损失导致过度平滑
扩散模型的突破
扩散模型提出了一个全新的思路:不直接生成图像,而是逐步去噪。
核心思想
前向过程(加噪):
- 从真实图像出发,逐步添加高斯噪声
- 经过足够多步后,图像变成纯噪声
反向过程(去噪):
- 从纯噪声出发,逐步去除噪声
- 经过足够多步后,噪声变成清晰图像
为什么这样做有效?
关键洞察:去噪是一个比直接生成更简单的任务。
- 直接生成:从随机噪声一步到位生成图像(GAN的做法)
- 任务难度极高,需要一步跨越巨大的分布差异
- 就像让你一笔画出蒙娜丽莎
- 逐步去噪:每次只去除一点点噪声
- 每一步的任务都很简单,只需要预测”下一步应该往哪个方向去噪”
- 就像用橡皮擦逐渐擦掉草稿上的辅助线,最终显现出清晰的画作
扩散模型的优势
相比GAN和VAE:
| 特性 | GAN | VAE | 扩散模型 |
|---|---|---|---|
| 训练稳定性 | 差 | 好 | 好 |
| 生成质量 | 高 | 中 | 高 |
| 生成多样性 | 易模式崩溃 | 好 | 好 |
| 推理速度 | 快(单步) | 快(单步) | 慢(多步) |
| 可控性 | 需要Conditional GAN | 支持条件注入 | 易于扩展(CFG等) |
金句:扩散模型用”时间换质量”——通过多步迭代去噪,换取更稳定的训练和更高质量的生成。
一、扩散模型的基本原理
整体框架
扩散模型包含两个核心过程:
1. 前向扩散过程(Forward Diffusion Process)
目标:将真实数据逐步转化为纯噪声
从真实图像 出发,逐步添加高斯噪声,得到一系列越来越模糊的图像:
- :原始清晰图像
- :第 步加噪后的图像
- :纯高斯噪声( 通常为1000)
关键特性:
- 这是一个固定的马尔可夫过程,不需要学习
- 每一步只依赖上一步的结果
- 最终分布接近标准高斯分布
2. 反向去噪过程(Reverse Denoising Process)
目标:从纯噪声逐步恢复出真实图像
从纯噪声 出发,逐步去除噪声,得到越来越清晰的图像:
关键特性:
- 这是一个需要学习的过程
- 使用神经网络 预测每一步应该去除的噪声
- 训练目标:让反向过程尽可能逆转前向过程
形象比喻
前向过程:
- 就像在一张清晰的照片上逐渐撒盐
- 撒得越多,照片越模糊
- 最终完全看不出原图
反向过程:
- 就像用魔法逐渐去除盐粒
- 每次去除一点点,照片逐渐清晰
- 最终恢复出原图(或生成新图)
数学定义
前向过程
在第 步,给 添加高斯噪声:
- :第 步的噪声强度(噪声方差)
- :保留原图像的比例
- 通常 (噪声逐渐增强)
重参数化形式:
反向过程
在第 步,从 预测 :
- :神经网络预测的均值
- :预测的方差(通常固定为 )
关键性质:一步到位的前向过程
前向过程有一个非常重要的性质:可以直接从 跳到任意 ,不需要逐步计算。
定义累积噪声系数:
则有:
重参数化形式:
物理意义:
- :保留原图像的比例(随 递减)
- :噪声的比例(随 递增)
- 当 时,, 接近纯噪声
为什么这个性质重要?
- 训练时可以随机采样任意时间步 ,直接生成
- 不需要从 逐步加噪到
- 大大提高了训练效率
二、DDPM:去噪扩散概率模型
DDPM(Denoising Diffusion Probabilistic Models)是扩散模型的经典实现,由Ho et al. 2020年提出。
训练目标
扩散模型的训练目标是最大化数据的对数似然:
但直接优化这个目标很困难。通过变分推断,可以推导出一个更易优化的变分下界(ELBO)。
变分下界推导(简化版)
完整推导较复杂,这里给出核心思路:
- 引入前向过程 作为变分分布
- 应用Jensen不等式得到ELBO
- 将ELBO分解为多个KL散度项
- 简化后得到最终的训练目标
简化的训练目标
经过推导和简化,DDPM的训练目标可以写成:
物理意义:
- :真实添加的噪声
- :神经网络预测的噪声
- 训练目标:让网络学会预测每一步添加的噪声
为什么预测噪声而不是预测图像?
- 预测噪声更稳定:噪声的分布相对简单(高斯分布)
- 预测图像更困难:图像的分布极其复杂
- 类似于残差学习:预测”差异”比预测”目标”更容易
参数化方法的选择
扩散模型的训练目标可以有多种等价的参数化形式,每种都有其优缺点:
1. ε-prediction(噪声预测)
- 优势:DDPM原论文使用,训练稳定,实现简单
- 劣势:在低噪声时间步(t接近0)时,信噪比(SNR)很高,预测误差影响小
- 适用:通用场景,是最常用的选择
2. x₀-prediction(原图预测)
- 优势:直接预测目标,在低噪声时间步表现更好
- 劣势:在高噪声时间步(t接近T)时,预测难度大
- 适用:需要精确重建细节的场景
3. v-prediction(速度预测)
- 优势:平衡了ε和x₀的优缺点,在所有时间步上表现均衡
- 劣势:实现稍复杂,需要额外转换
- 适用:Stable Diffusion等现代模型的选择
SNR加权的作用
不同时间步的学习难度不同,可以通过信噪比(SNR)加权来平衡:
- 高SNR(低噪声):图像清晰,预测容易,可以降低权重
- 低SNR(高噪声):图像模糊,预测困难,可以提高权重
- Min-SNR加权:限制权重的最大值,避免高噪声时间步主导训练
实践建议:初学者使用ε-prediction即可,进阶应用可尝试v-prediction + Min-SNR加权。
训练算法
DDPM的训练过程非常简洁:
算法:DDPM训练输入:数据集 {x_0}, 噪声调度 {β_t}, 总步数 T输出:去噪网络 ε_θ
注:时间步索引 t ∈ {0, 1, ..., T-1}
重复: 1. 从数据集采样 x_0 ~ q(x_0) 2. 随机采样时间步 t ~ Uniform({0, 1, ..., T-1}) 3. 采样噪声 ε ~ N(0, I) 4. 计算 x_t = √(ᾱ_t) x_0 + √(1-ᾱ_t) ε 5. 计算损失 L = ||ε - ε_θ(x_t, t)||² 6. 梯度下降更新 θ直到收敛关键点:
- 每次迭代只需要一个时间步
- 可以并行训练,不需要顺序处理
- 训练目标简单,就是一个均方误差(MSE)
\
采样算法
训练完成后,我们从纯噪声出发生成图像。为了避免“符号 vs 代码索引”混乱,本文采用如下约定:
- 时间步索引:(t \in {0,1,\dots,T-1})
- 记号 (x_T) 表示“最终纯噪声状态”,在代码实现中对应索引 (T-1) 的状态(即最后一步)
采样(DDPM,后验方差版本)
算法:DDPM采样(推荐写法:后验方差)输入:去噪网络 ε_θ, 噪声调度 {β_t}, 总步数 T输出:生成的图像 x_0
1. 采样初始噪声 x_T ~ N(0, I)2. 令 x = x_T3. 对于 t = T-1, T-2, ..., 0: a. 若 t > 0,采样 z ~ N(0, I),否则 z = 0 b. 预测噪声 ε_pred = ε_θ(x, t) c. 计算均值: μ_t = (1/√α_t) * ( x - (β_t/√(1-ᾱ_t)) * ε_pred ) d. 计算后验方差(由前向过程推导得到): β̃_t = (1-ᾱ_{t-1})/(1-ᾱ_t) * β_t σ_t = √β̃_t e. 更新:x ← μ_t + σ_t * z4. 返回 x (即 x_0)物理意义:
- 从纯噪声 (x_T) 开始,每一步做一个“很小的去噪修正”
- (μ_t) 给出“去噪后的中心位置”,(σ_t z) 表示该步的随机性(多样性来源)
方差设定:为什么有多种写法?
在实际实现中,采样方差 (\sigma_t^2) 常见三类设定:
- 后验方差 (\sigma_t^2=\tilde\beta_t)(更“忠实于 DDPM 推导”)
- 由前向噪声调度推导得到,对应一步后验 (q(x_{t-1}|x_t,x_0)) 的方差
- 更推荐作为“论文复现/严谨实现”的默认
- 工程简化 (\sigma_t^2=\beta_t)(更短更快的近似写法)
- 实现简单、成本低,很多教学/简化代码会这样写
- 但严格来说它不是一步后验的真实方差,质量/稳定性可能与后验方差版本略有差异
- 可学习方差(Improved DDPM 等)
- 将方差作为网络输出的一部分,自适应调整每一步不确定性
- 训练更复杂,但可能进一步提升质量
实践建议:
- 学习/快速原型:可以先用 (\sigma_t^2=\beta_t) 理解流程
- 认真复现/追求质量:优先用 (\sigma_t^2=\tilde\beta_t)
噪声调度(Noise Schedule)## 噪声调度(Noise Schedule)
噪声调度 决定了每一步添加多少噪声,是扩散模型的重要超参数。
线性调度(Linear Schedule)
DDPM原论文使用的方案:
- ,
- 噪声强度线性增长
余弦调度(Cosine Schedule)
Improved DDPM提出的改进方案:
- 噪声增长更平滑
- 在训练初期和末期保持更多信息
- 通常效果更好
关键洞察:噪声调度的选择会显著影响生成质量,需要根据任务调整。
噪声调度选择指南
不同的噪声调度适用于不同的场景:
线性调度(Linear Schedule):
- 适用:DDPM原始实现,教学示例
- 优势:简单直观,易于理解
- 劣势:在训练初期和末期噪声变化过快
- 推荐:学习和复现DDPM论文时使用
余弦调度(Cosine Schedule):
- 适用:图像生成的通用选择
- 优势:噪声增长平滑,保留更多信息
- 劣势:需要调整offset参数s
- 推荐:大多数图像生成任务的默认选择
Karras调度(Sigma-based):
- 适用:高分辨率图像生成(Stable Diffusion XL)
- 优势:基于SNR设计,理论更优
- 劣势:需要配合特定采样器(如DPM-Solver)
- 推荐:追求最高质量时使用
实践建议:
- 初学者:从线性调度开始理解原理
- 实际应用:使用余弦调度作为baseline
- 高级优化:尝试Karras调度 + DPM-Solver组合
三、网络架构:U-Net
扩散模型的核心是去噪网络 ,通常使用 U-Net 架构。
为什么选择U-Net?
U-Net最初为医学图像分割设计,但非常适合扩散模型:
1. 编码器-解码器结构
- 编码器(Encoder):逐步下采样,提取高层语义特征
- 解码器(Decoder):逐步上采样,恢复空间分辨率
- 跳跃连接(Skip Connections):将编码器的特征直接传递给解码器
优势:
- 保留细节信息:跳跃连接让解码器能访问低层特征
- 多尺度处理:在不同分辨率上处理图像
2. 时间步嵌入(Time Embedding)
扩散模型需要知道当前是第几步 ,因为不同时间步的噪声强度不同。
实现方式:
- 使用正弦位置编码(类似Transformer)将 编码为向量
- 将时间嵌入注入到U-Net的每一层
3. 注意力机制
在U-Net的瓶颈层(最低分辨率)加入Self-Attention:
- 捕捉全局依赖关系
- 提高生成质量
U-Net的完整结构
输入: x_t (噪声图像) + t (时间步)
编码器: Conv Block 1 (64×64) + Time Embed → 特征1 ↓ 下采样 Conv Block 2 (32×32) + Time Embed → 特征2 ↓ 下采样 Conv Block 3 (16×16) + Time Embed → 特征3 ↓ 下采样
瓶颈层: Conv Block 4 (8×8) + Self-Attention + Time Embed
解码器: ↑ 上采样 + 拼接特征3 Conv Block 5 (16×16) + Time Embed ↑ 上采样 + 拼接特征2 Conv Block 6 (32×32) + Time Embed ↑ 上采样 + 拼接特征1 Conv Block 7 (64×64) + Time Embed
输出: ε_pred (预测的噪声)关键设计:
- 每个Conv Block包含:卷积 → GroupNorm → 激活函数
- 时间嵌入通过线性层投影后加到特征图上
- 跳跃连接通过拼接(Concatenate)实现
四、条件生成:引导扩散模型
纯粹的扩散模型只能随机生成图像,无法控制生成内容。条件生成让我们能够指定生成什么。
Classifier-Free Guidance(无分类器引导)
Stable Diffusion等模型使用的核心技术。
训练
训练时随机丢弃条件(以概率 设置条件为空):
- :条件(如文本、类别)
- :空条件
采样
采样时结合条件和无条件预测:
- :引导强度(guidance scale)
- :无条件生成
- :标准条件生成
- :强化条件,生成更符合条件但可能失真
引导强度的权衡:
guidance scale 的选择需要在条件符合度和生成质量之间权衡:
- w过小(0.5-1.0):
- 生成结果可能与条件不符
- 图像自然,但缺乏细节
- 适合需要多样性的场景
- w适中(7.0-9.0):
- Stable Diffusion的常用范围
- 平衡条件符合度和生成质量
- 大多数应用的最佳选择
- w过大(15.0+):
- 过度强化条件,导致过饱和
- 颜色失真、细节崩坏
- 图像可能出现伪影和不自然的对比度
- 类似于”过度曝光”的效果
实践建议:从w=7.5开始尝试,根据具体任务调整。艺术创作可以尝试更高的w,写实场景建议使用较低的w。
优势:
- 不需要额外的分类器
- 可以灵活调节引导强度
- Stable Diffusion、DALL-E等模型都使用这种方法
文本到图像生成
将文本编码为条件向量,注入到U-Net中:
- 文本编码器:使用CLIP或T5将文本编码为向量
- 交叉注意力(Cross-Attention):在U-Net中加入交叉注意力层
- Query来自图像特征
- Key和Value来自文本特征
- 条件注入:让图像特征能够”查询”文本信息
代表模型:
- Stable Diffusion:使用CLIP文本编码器
- Imagen:使用T5文本编码器
- DALL-E 2:使用CLIP图像-文本对齐
五、扩散模型 vs 其他生成模型
vs GAN
| 维度 | GAN | 扩散模型 |
|---|---|---|
| 训练稳定性 | 差,需要平衡G和D | 好,单一目标函数 |
| 模式崩溃 | 常见 | 罕见 |
| 生成质量 | 高 | 高 |
| 生成多样性 | 易受限 | 好 |
| 推理速度 | 快(1步) | 慢(50-1000步) |
| 训练成本 | 中 | 高 |
| 可控性 | 需要Conditional GAN | 易于扩展(CFG等) |
核心区别:
- GAN:对抗训练,一步生成
- 扩散模型:去噪训练,多步生成
vs VAE
| 维度 | VAE | 扩散模型 |
|---|---|---|
| 生成质量 | 模糊 | 清晰 |
| 隐空间 | 连续、可解释 | 无显式隐空间 |
| 训练稳定性 | 好 | 好 |
| 推理速度 | 快 | 慢 |
| 理论基础 | 变分推断 | 扩散过程 |
核心区别:
- VAE:学习隐空间表示
- 扩散模型:学习去噪过程
六、PyTorch实现示例
\
简化的DDPM实现
下面给出一个更一致、可直接跑通且更接近标准 DDPM 推导的最小实现(仍然刻意保持简洁)。要点:
- 训练使用 (x_t=\sqrt{\bar\alpha_t}x_0+\sqrt{1-\bar\alpha_t}\epsilon)
- 采样使用后验方差 (\tilde\beta_t)(更推荐)
- 全程 batch-safe(支持每个样本不同的 (t))
import torchimport torch.nn as nnimport torch.nn.functional as F
class SimpleDiffusion: def __init__(self, num_steps=1000, beta_start=1e-4, beta_end=2e-2, device="cuda"): self.num_steps = int(num_steps) self.device = device
# 线性噪声调度(教学默认) betas = torch.linspace(beta_start, beta_end, self.num_steps, device=device) alphas = 1.0 - betas alpha_bars = torch.cumprod(alphas, dim=0)
# 保存为成员(在真实工程里可注册为 buffer) self.betas = betas self.alphas = alphas self.alpha_bars = alpha_bars
def _extract(self, a, t, x_shape): # 从长度为 T 的向量 a 中按 batch 的 t 抽取对应元素,并 reshape 成可广播形状 b = t.shape[0] out = a.gather(0, t).view(b, *([1] * (len(x_shape) - 1))) return out
def forward_diffusion(self, x0, t): # 前向扩散:从 x0 直接得到 xt(一步到位) # 注意:x0 建议预处理到 [-1, 1] noise = torch.randn_like(x0) alpha_bar_t = self._extract(self.alpha_bars, t, x0.shape) xt = torch.sqrt(alpha_bar_t) * x0 + torch.sqrt(1.0 - alpha_bar_t) * noise return xt, noise
@torch.no_grad() def reverse_step(self, model, xt, t): # 反向一步:xt -> x_{t-1} # ε-prediction 参数化 + 后验方差 beta_tilde eps_pred = model(xt, t)
alpha_t = self._extract(self.alphas, t, xt.shape) alpha_bar_t = self._extract(self.alpha_bars, t, xt.shape) beta_t = self._extract(self.betas, t, xt.shape)
# DDPM posterior mean (ε-parameterization) mean = (1.0 / torch.sqrt(alpha_t)) * (xt - (beta_t / torch.sqrt(1.0 - alpha_bar_t)) * eps_pred)
# posterior variance beta_tilde t_prev = (t - 1).clamp(min=0) alpha_bar_prev = self._extract(self.alpha_bars, t_prev, xt.shape) beta_tilde = ((1.0 - alpha_bar_prev) / (1.0 - alpha_bar_t)) * beta_t sigma = torch.sqrt(beta_tilde)
# 当 t==0 时不再加噪声 noise = torch.randn_like(xt) mask = (t > 0).float().view(t.shape[0], *([1] * (len(xt.shape) - 1))) return mean + mask * sigma * noise
def train_step(model, diffusion: SimpleDiffusion, x0, optimizer): # 注意:x0 建议预处理到 [-1, 1] b = x0.shape[0] device = x0.device t = torch.randint(0, diffusion.num_steps, (b,), device=device)
xt, noise = diffusion.forward_diffusion(x0, t) noise_pred = model(xt, t)
loss = F.mse_loss(noise_pred, noise) optimizer.zero_grad() loss.backward() optimizer.step() return float(loss.item())
@torch.no_grad()def sample(model, diffusion: SimpleDiffusion, shape, device): # 从纯噪声生成图像,返回值在 [-1, 1],后处理:x = (x + 1) * 127.5 x = torch.randn(shape, device=device) for t in reversed(range(diffusion.num_steps)): t_batch = torch.full((shape[0],), t, dtype=torch.long, device=device) x = diffusion.reverse_step(model, x, t_batch) return x简化的U-Net实现## 简化的U-Net实现
\
import torchimport torch.nn as nnimport torch.nn.functional as F
class SimpleUNet(nn.Module): # 教学版 U-Net:预测噪声 ε # 显式注入 time embedding,并使用 GroupNorm + SiLU(更贴近真实扩散 U-Net) def __init__(self, in_channels=3, base_channels=64, time_dim=256): super().__init__() self.time_dim = time_dim
# time embedding: sinusoidal -> MLP self.time_mlp = nn.Sequential( nn.Linear(time_dim, time_dim * 4), nn.SiLU(), nn.Linear(time_dim * 4, time_dim), )
# encoder self.conv1 = nn.Conv2d(in_channels, base_channels, 3, padding=1) self.gn1 = nn.GroupNorm(8, base_channels) self.tproj1 = nn.Linear(time_dim, base_channels)
self.conv2 = nn.Conv2d(base_channels, base_channels * 2, 3, padding=1) self.gn2 = nn.GroupNorm(8, base_channels * 2) self.tproj2 = nn.Linear(time_dim, base_channels * 2)
self.conv3 = nn.Conv2d(base_channels * 2, base_channels * 4, 3, padding=1) self.gn3 = nn.GroupNorm(8, base_channels * 4) self.tproj3 = nn.Linear(time_dim, base_channels * 4)
# bottleneck self.conv4 = nn.Conv2d(base_channels * 4, base_channels * 8, 3, padding=1) self.gn4 = nn.GroupNorm(8, base_channels * 8) self.tproj4 = nn.Linear(time_dim, base_channels * 8)
# decoder self.dconv3 = nn.Conv2d(base_channels * 8 + base_channels * 4, base_channels * 4, 3, padding=1) self.dgn3 = nn.GroupNorm(8, base_channels * 4) self.dtproj3 = nn.Linear(time_dim, base_channels * 4)
self.dconv2 = nn.Conv2d(base_channels * 4 + base_channels * 2, base_channels * 2, 3, padding=1) self.dgn2 = nn.GroupNorm(8, base_channels * 2) self.dtproj2 = nn.Linear(time_dim, base_channels * 2)
self.dconv1 = nn.Conv2d(base_channels * 2 + base_channels, base_channels, 3, padding=1) self.dgn1 = nn.GroupNorm(8, base_channels) self.dtproj1 = nn.Linear(time_dim, base_channels)
self.out = nn.Conv2d(base_channels, in_channels, 1)
def forward(self, x, t): # t: (B,) temb = self.get_time_embedding(t, self.time_dim) temb = self.time_mlp(temb)
def add_time(h, proj): return h + proj(temb).view(h.shape[0], -1, 1, 1)
# encoder e1 = F.silu(add_time(self.gn1(self.conv1(x)), self.tproj1)) x2 = F.avg_pool2d(e1, 2)
e2 = F.silu(add_time(self.gn2(self.conv2(x2)), self.tproj2)) x3 = F.avg_pool2d(e2, 2)
e3 = F.silu(add_time(self.gn3(self.conv3(x3)), self.tproj3)) x4 = F.avg_pool2d(e3, 2)
# bottleneck b = F.silu(add_time(self.gn4(self.conv4(x4)), self.tproj4))
# decoder d3 = F.interpolate(b, scale_factor=2, mode="nearest") d3 = torch.cat([d3, e3], dim=1) d3 = F.silu(add_time(self.dgn3(self.dconv3(d3)), self.dtproj3))
d2 = F.interpolate(d3, scale_factor=2, mode="nearest") d2 = torch.cat([d2, e2], dim=1) d2 = F.silu(add_time(self.dgn2(self.dconv2(d2)), self.dtproj2))
d1 = F.interpolate(d2, scale_factor=2, mode="nearest") d1 = torch.cat([d1, e1], dim=1) d1 = F.silu(add_time(self.dgn1(self.dconv1(d1)), self.dtproj1))
return self.out(d1)
@staticmethod def get_time_embedding(t, dim=256): # sinusoidal time embedding: (B, dim) half = dim // 2 freqs = torch.exp( -torch.log(torch.tensor(10000.0, device=t.device)) * torch.arange(half, device=t.device) / (half - 1) ) args = t.float()[:, None] * freqs[None, :] emb = torch.cat([torch.sin(args), torch.cos(args)], dim=-1) return emb七、加速技术与变体
扩散模型的主要缺点是推理慢,研究者提出了多种加速方法。
DDIM:确定性采样
DDPM的采样过程是随机的,DDIM提出了确定性采样:
核心思想:
- 跳过部分时间步,只采样子集(如每10步采样一次)
- 使用确定性更新规则,不添加随机噪声
- 可以将1000步压缩到50步,速度提升20倍
优势:
- 大幅加速推理
- 生成结果确定(相同噪声输入得到相同输出)
- 支持图像编辑(可以在隐空间插值)
Latent Diffusion Models(潜在扩散模型)
Stable Diffusion使用的核心技术。
核心思想:
- 不在像素空间做扩散,而在**潜在空间(Latent Space)**做扩散
- 使用VAE将图像压缩到低维潜在空间
- 在潜在空间训练扩散模型
- 生成后用VAE解码器还原到像素空间
优势:
- 计算量大幅降低(潜在空间维度远小于像素空间)
- 训练和推理都更快
- 生成质量不降低
Stable Diffusion的架构:
文本 → CLIP编码器 → 文本嵌入图像 → VAE编码器 → 潜在表示 → U-Net扩散 → 潜在表示 → VAE解码器 → 图像现代采样器与调度
实际应用中,现代扩散模型已经远超DDPM/DDIM的基础实现。
高效采样器
DPM-Solver系列:
- 基于扩散ODE的高阶求解器
- 15-30步即可达到DDPM 1000步的质量
- Stable Diffusion默认使用DPM-Solver++
- 相比DDIM更快更准确
Euler/Heun采样器:
- 基于数值积分的采样方法
- Euler:一阶方法,速度快
- Heun:二阶方法,质量更高但稍慢
- 适合快速原型和实时应用
实际步数对比:
- DDPM原论文:1000步(研究用)
- DDIM:50-100步(早期加速)
- DPM-Solver:20-30步(现代标准)
- LCM/Turbo:4-8步(极速生成)
现代噪声调度
Karras调度(EDM):
- 基于信噪比(SNR)的调度设计
- 在对数空间均匀分布时间步
- 相比线性/余弦调度更稳定
- Stable Diffusion XL采用
实践建议:
- 日常使用:DPM-Solver++ with 25步
- 高质量生成:Heun with 50步
- 快速预览:Euler with 15步
- 使用Karras调度可进一步提升质量
八、应用场景
扩散模型在多个领域取得了突破性成果。
1. 文本到图像生成
代表模型:
- Stable Diffusion:开源,可本地运行
- Midjourney:艺术风格强,商业化成功
- DALL-E 2/3:OpenAI出品,生成质量高
- Imagen:Google出品,使用T5文本编码器
应用:
- 艺术创作、插画设计
- 广告素材生成
- 游戏资产制作
2. 图像编辑
技术:
- Inpainting:修复图像缺失部分
- Outpainting:扩展图像边界
- Image-to-Image:风格迁移、图像变换
代表工具:
- Photoshop的生成式填充
- Stable Diffusion的ControlNet
3. 视频生成
代表模型:
- Sora:OpenAI的视频生成模型,可生成1分钟高质量视频
- Runway Gen-2:商业化视频生成工具
- Pika:文本到视频生成
挑战:
- 时间一致性:保持视频帧之间的连贯性
- 计算成本:视频生成需要更多计算资源
4. 3D生成
技术:
- DreamFusion:文本到3D模型
- Point-E:点云生成
- Shap-E:3D形状生成
应用:
- 游戏建模
- 虚拟现实
- 工业设计
5. 音频生成
代表模型:
- AudioLDM:文本到音频
- Riffusion:音乐生成
应用:
- 音效制作
- 音乐创作
九、总结
核心要点
扩散模型的本质:
- 前向过程:逐步添加噪声,将数据转化为纯噪声
- 反向过程:逐步去除噪声,从纯噪声恢复数据
- 训练目标:学习预测每一步添加的噪声
- 采样过程:从纯噪声开始,逐步去噪生成图像
关键优势:
- 训练稳定:单一目标函数,不需要对抗训练
- 生成质量高:逐步去噪保证细节
- 生成多样性好:不易模式崩溃
- 易于扩展:支持多种条件生成方式(CFG、交叉注意力等)
主要挑战:
- 推理慢:需要数百步迭代
- 计算成本高:训练需要大量资源
- 内存占用大:需要存储多个时间步的中间结果
未来方向:
- 加速推理:DDIM、Consistency Models等
- 降低计算成本:Latent Diffusion、蒸馏等
- 提高可控性:ControlNet、Adapter等
- 扩展应用:视频、3D、音频等
扩散模型的时代意义
扩散模型的出现标志着生成模型进入新时代:
- 从对抗到去噪:训练范式的根本转变
- 从单步到多步:用时间换质量的新思路
- 从研究到应用:Stable Diffusion、Midjourney等商业化成功
扩散模型不仅在图像生成领域超越了GAN,还在视频、3D、音频等多个领域展现出巨大潜力。理解扩散模型的原理,是掌握现代生成式AI的关键。
致谢
本笔记整理了扩散模型的核心原理和实现细节。感谢以下论文和资源:
相关论文:
- Denoising Diffusion Probabilistic Models (DDPM) - Ho et al., 2020
- Denoising Diffusion Implicit Models (DDIM) - Song et al., 2020
- High-Resolution Image Synthesis with Latent Diffusion Models - Rombach et al., 2022 (Stable Diffusion)
- Classifier-Free Diffusion Guidance - Ho & Salimans, 2022
学习资源:
部分信息可能已经过时









