mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
5492 字
15 分钟
扩散模型(Diffusion Models)

引言:扩散模型要解决什么问题?#

在深入扩散模型之前,我们需要先明确:它是用来做什么的?为什么会出现?

生成模型的演进#

在扩散模型出现之前,生成模型领域主要由以下方法主导:

GAN的辉煌与困境#

优势

  • 生成质量高,图像清晰逼真
  • 推理速度快,单步前向传播即可生成

痛点

  • 训练极度不稳定,需要精心调参
  • 模式崩溃(Mode Collapse):生成器只生成少数几种样本
  • 生成器和判别器的动态平衡难以维持

VAE的稳定与模糊#

优势

  • 训练稳定,目标函数明确
  • 隐空间连续,支持插值

痛点

  • 生成图像模糊,缺乏细节
  • 像素级重建损失导致过度平滑

扩散模型的突破#

扩散模型提出了一个全新的思路:不直接生成图像,而是逐步去噪

核心思想#

前向过程(加噪)

  • 从真实图像出发,逐步添加高斯噪声
  • 经过足够多步后,图像变成纯噪声

反向过程(去噪)

  • 从纯噪声出发,逐步去除噪声
  • 经过足够多步后,噪声变成清晰图像

为什么这样做有效?#

关键洞察:去噪是一个比直接生成更简单的任务。

  • 直接生成:从随机噪声一步到位生成图像(GAN的做法)
    • 任务难度极高,需要一步跨越巨大的分布差异
    • 就像让你一笔画出蒙娜丽莎
  • 逐步去噪:每次只去除一点点噪声
    • 每一步的任务都很简单,只需要预测”下一步应该往哪个方向去噪”
    • 就像用橡皮擦逐渐擦掉草稿上的辅助线,最终显现出清晰的画作

扩散模型的优势#

相比GAN和VAE:

特性GANVAE扩散模型
训练稳定性
生成质量
生成多样性易模式崩溃
推理速度快(单步)快(单步)慢(多步)
可控性需要Conditional GAN支持条件注入易于扩展(CFG等)

金句:扩散模型用”时间换质量”——通过多步迭代去噪,换取更稳定的训练和更高质量的生成。


一、扩散模型的基本原理#

整体框架#

扩散模型包含两个核心过程:

1. 前向扩散过程(Forward Diffusion Process)#

目标:将真实数据逐步转化为纯噪声

从真实图像 x0\pmb{x}_0 出发,逐步添加高斯噪声,得到一系列越来越模糊的图像:

x0x1x2xT\pmb{x}_0 \to \pmb{x}_1 \to \pmb{x}_2 \to \cdots \to \pmb{x}_T
  • x0\pmb{x}_0:原始清晰图像
  • xt\pmb{x}_t:第 tt 步加噪后的图像
  • xT\pmb{x}_T:纯高斯噪声(TT 通常为1000)

关键特性

  • 这是一个固定的马尔可夫过程,不需要学习
  • 每一步只依赖上一步的结果
  • 最终分布接近标准高斯分布 N(0,I)\mathcal{N}(0, \pmb{I})

2. 反向去噪过程(Reverse Denoising Process)#

目标:从纯噪声逐步恢复出真实图像

从纯噪声 xT\pmb{x}_T 出发,逐步去除噪声,得到越来越清晰的图像:

xTxT1xT2x0\pmb{x}_T \to \pmb{x}_{T-1} \to \pmb{x}_{T-2} \to \cdots \to \pmb{x}_0

关键特性

  • 这是一个需要学习的过程
  • 使用神经网络 ϵθ\epsilon_\theta 预测每一步应该去除的噪声
  • 训练目标:让反向过程尽可能逆转前向过程

形象比喻#

前向过程

  • 就像在一张清晰的照片上逐渐撒盐
  • 撒得越多,照片越模糊
  • 最终完全看不出原图

反向过程

  • 就像用魔法逐渐去除盐粒
  • 每次去除一点点,照片逐渐清晰
  • 最终恢复出原图(或生成新图)

数学定义#

前向过程#

在第 tt 步,给 xt1\pmb{x}_{t-1} 添加高斯噪声:

q(xtxt1)=N(xt;1βtxt1,βtI)q(\pmb{x}_t | \pmb{x}_{t-1}) = \mathcal{N}(\pmb{x}_t; \sqrt{1-\beta_t} \pmb{x}_{t-1}, \beta_t \pmb{I})
  • βt\beta_t:第 tt 步的噪声强度(噪声方差)
  • 1βt\sqrt{1-\beta_t}:保留原图像的比例
  • 通常 β1<β2<<βT\beta_1 < \beta_2 < \cdots < \beta_T(噪声逐渐增强)

重参数化形式

xt=1βtxt1+βtϵt1,ϵt1N(0,I)\pmb{x}_t = \sqrt{1-\beta_t} \pmb{x}_{t-1} + \sqrt{\beta_t} \pmb{\epsilon}_{t-1}, \quad \pmb{\epsilon}_{t-1} \sim \mathcal{N}(0, \pmb{I})

反向过程#

在第 tt 步,从 xt\pmb{x}_t 预测 xt1\pmb{x}_{t-1}

pθ(xt1xt)=N(xt1;μθ(xt,t),Σθ(xt,t))p_\theta(\pmb{x}_{t-1} | \pmb{x}_t) = \mathcal{N}(\pmb{x}_{t-1}; \pmb{\mu}_\theta(\pmb{x}_t, t), \pmb{\Sigma}_\theta(\pmb{x}_t, t))
  • μθ\pmb{\mu}_\theta:神经网络预测的均值
  • Σθ\pmb{\Sigma}_\theta:预测的方差(通常固定为 σt2I\sigma_t^2 \pmb{I}

关键性质:一步到位的前向过程#

前向过程有一个非常重要的性质:可以直接从 x0\pmb{x}_0 跳到任意 xt\pmb{x}_t,不需要逐步计算

定义累积噪声系数:

αt=1βt,αˉt=s=1tαs\alpha_t = 1 - \beta_t, \quad \bar{\alpha}_t = \prod_{s=1}^{t} \alpha_s

则有:

q(xtx0)=N(xt;αˉtx0,(1αˉt)I)q(\pmb{x}_t | \pmb{x}_0) = \mathcal{N}(\pmb{x}_t; \sqrt{\bar{\alpha}_t} \pmb{x}_0, (1-\bar{\alpha}_t) \pmb{I})

重参数化形式

xt=αˉtx0+1αˉtϵ,ϵN(0,I)\pmb{x}_t = \sqrt{\bar{\alpha}_t} \pmb{x}_0 + \sqrt{1-\bar{\alpha}_t} \pmb{\epsilon}, \quad \pmb{\epsilon} \sim \mathcal{N}(0, \pmb{I})

物理意义

  • αˉt\sqrt{\bar{\alpha}_t}:保留原图像的比例(随 tt 递减)
  • 1αˉt\sqrt{1-\bar{\alpha}_t}:噪声的比例(随 tt 递增)
  • t=Tt=T 时,αˉT0\bar{\alpha}_T \approx 0xT\pmb{x}_T 接近纯噪声

为什么这个性质重要?

  • 训练时可以随机采样任意时间步 tt,直接生成 xt\pmb{x}_t
  • 不需要从 x0\pmb{x}_0 逐步加噪到 xt\pmb{x}_t
  • 大大提高了训练效率

二、DDPM:去噪扩散概率模型#

DDPM(Denoising Diffusion Probabilistic Models)是扩散模型的经典实现,由Ho et al. 2020年提出。

训练目标#

扩散模型的训练目标是最大化数据的对数似然

maxθEx0q(x0)[logpθ(x0)]\max_\theta \mathbb{E}_{\pmb{x}_0 \sim q(\pmb{x}_0)}[\log p_\theta(\pmb{x}_0)]

但直接优化这个目标很困难。通过变分推断,可以推导出一个更易优化的变分下界(ELBO)

变分下界推导(简化版)#

完整推导较复杂,这里给出核心思路:

  1. 引入前向过程 q(x1:Tx0)q(\pmb{x}_{1:T} | \pmb{x}_0) 作为变分分布
  2. 应用Jensen不等式得到ELBO
  3. 将ELBO分解为多个KL散度项
  4. 简化后得到最终的训练目标

简化的训练目标#

经过推导和简化,DDPM的训练目标可以写成:

Lsimple=Et,x0,ϵ[ϵϵθ(xt,t)2]\mathcal{L}_{\text{simple}} = \mathbb{E}_{t, \pmb{x}_0, \pmb{\epsilon}} \left[ \| \pmb{\epsilon} - \pmb{\epsilon}_\theta(\pmb{x}_t, t) \|^2 \right]

物理意义

  • ϵ\pmb{\epsilon}:真实添加的噪声
  • ϵθ(xt,t)\pmb{\epsilon}_\theta(\pmb{x}_t, t):神经网络预测的噪声
  • 训练目标:让网络学会预测每一步添加的噪声

为什么预测噪声而不是预测图像?

  • 预测噪声更稳定:噪声的分布相对简单(高斯分布)
  • 预测图像更困难:图像的分布极其复杂
  • 类似于残差学习:预测”差异”比预测”目标”更容易

参数化方法的选择#

扩散模型的训练目标可以有多种等价的参数化形式,每种都有其优缺点:

1. ε-prediction(噪声预测)#

Lϵ=Et,x0,ϵ[ϵϵθ(xt,t)2]\mathcal{L}_\epsilon = \mathbb{E}_{t, \pmb{x}_0, \pmb{\epsilon}} \left[ \| \pmb{\epsilon} - \pmb{\epsilon}_\theta(\pmb{x}_t, t) \|^2 \right]
  • 优势:DDPM原论文使用,训练稳定,实现简单
  • 劣势:在低噪声时间步(t接近0)时,信噪比(SNR)很高,预测误差影响小
  • 适用:通用场景,是最常用的选择

2. x₀-prediction(原图预测)#

Lx0=Et,x0,ϵ[x0x^θ(xt,t)2]\mathcal{L}_{x_0} = \mathbb{E}_{t, \pmb{x}_0, \pmb{\epsilon}} \left[ \| \pmb{x}_0 - \hat{\pmb{x}}_\theta(\pmb{x}_t, t) \|^2 \right]
  • 优势:直接预测目标,在低噪声时间步表现更好
  • 劣势:在高噪声时间步(t接近T)时,预测难度大
  • 适用:需要精确重建细节的场景

3. v-prediction(速度预测)#

v=αˉtϵ1αˉtx0\pmb{v} = \sqrt{\bar{\alpha}_t} \pmb{\epsilon} - \sqrt{1-\bar{\alpha}_t} \pmb{x}_0
  • 优势:平衡了ε和x₀的优缺点,在所有时间步上表现均衡
  • 劣势:实现稍复杂,需要额外转换
  • 适用:Stable Diffusion等现代模型的选择

SNR加权的作用#

不同时间步的学习难度不同,可以通过信噪比(SNR)加权来平衡:

SNR(t)=αˉt1αˉt\text{SNR}(t) = \frac{\bar{\alpha}_t}{1-\bar{\alpha}_t}
  • 高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. 梯度下降更新 θ
直到收敛

关键点

  • 每次迭代只需要一个时间步 tt
  • 可以并行训练,不需要顺序处理
  • 训练目标简单,就是一个均方误差(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_T
3. 对于 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 * z
4. 返回 x (即 x_0)

物理意义

  • 从纯噪声 (x_T) 开始,每一步做一个“很小的去噪修正”
  • (μ_t) 给出“去噪后的中心位置”,(σ_t z) 表示该步的随机性(多样性来源)

方差设定:为什么有多种写法?#

在实际实现中,采样方差 (\sigma_t^2) 常见三类设定:

  1. 后验方差 (\sigma_t^2=\tilde\beta_t)(更“忠实于 DDPM 推导”)
  • 由前向噪声调度推导得到,对应一步后验 (q(x_{t-1}|x_t,x_0)) 的方差
  • 更推荐作为“论文复现/严谨实现”的默认
  1. 工程简化 (\sigma_t^2=\beta_t)(更短更快的近似写法)
  • 实现简单、成本低,很多教学/简化代码会这样写
  • 但严格来说它不是一步后验的真实方差,质量/稳定性可能与后验方差版本略有差异
  1. 可学习方差(Improved DDPM 等)
  • 将方差作为网络输出的一部分,自适应调整每一步不确定性
  • 训练更复杂,但可能进一步提升质量

实践建议

  • 学习/快速原型:可以先用 (\sigma_t^2=\beta_t) 理解流程
  • 认真复现/追求质量:优先用 (\sigma_t^2=\tilde\beta_t)

噪声调度(Noise Schedule)## 噪声调度(Noise Schedule)#

噪声调度 {βt}\{\beta_t\} 决定了每一步添加多少噪声,是扩散模型的重要超参数。

线性调度(Linear Schedule)#

DDPM原论文使用的方案:

βt=βmin+t1T1(βmaxβmin)\beta_t = \beta_{\min} + \frac{t-1}{T-1} (\beta_{\max} - \beta_{\min})
  • βmin=0.0001\beta_{\min} = 0.0001βmax=0.02\beta_{\max} = 0.02
  • 噪声强度线性增长

余弦调度(Cosine Schedule)#

Improved DDPM提出的改进方案:

αˉt=f(t)f(0),f(t)=cos(t/T+s1+sπ2)2\bar{\alpha}_t = \frac{f(t)}{f(0)}, \quad f(t) = \cos\left(\frac{t/T + s}{1+s} \cdot \frac{\pi}{2}\right)^2
  • 噪声增长更平滑
  • 在训练初期和末期保持更多信息
  • 通常效果更好

关键洞察:噪声调度的选择会显著影响生成质量,需要根据任务调整。

噪声调度选择指南#

不同的噪声调度适用于不同的场景:

线性调度(Linear Schedule)

  • 适用:DDPM原始实现,教学示例
  • 优势:简单直观,易于理解
  • 劣势:在训练初期和末期噪声变化过快
  • 推荐:学习和复现DDPM论文时使用

余弦调度(Cosine Schedule)

  • 适用:图像生成的通用选择
  • 优势:噪声增长平滑,保留更多信息
  • 劣势:需要调整offset参数s
  • 推荐:大多数图像生成任务的默认选择

Karras调度(Sigma-based)

  • 适用:高分辨率图像生成(Stable Diffusion XL)
  • 优势:基于SNR设计,理论更优
  • 劣势:需要配合特定采样器(如DPM-Solver)
  • 推荐:追求最高质量时使用

实践建议

  • 初学者:从线性调度开始理解原理
  • 实际应用:使用余弦调度作为baseline
  • 高级优化:尝试Karras调度 + DPM-Solver组合

三、网络架构:U-Net#

扩散模型的核心是去噪网络 ϵθ(xt,t)\epsilon_\theta(\pmb{x}_t, t),通常使用 U-Net 架构。

为什么选择U-Net?#

U-Net最初为医学图像分割设计,但非常适合扩散模型:

1. 编码器-解码器结构#

  • 编码器(Encoder):逐步下采样,提取高层语义特征
  • 解码器(Decoder):逐步上采样,恢复空间分辨率
  • 跳跃连接(Skip Connections):将编码器的特征直接传递给解码器

优势

  • 保留细节信息:跳跃连接让解码器能访问低层特征
  • 多尺度处理:在不同分辨率上处理图像

2. 时间步嵌入(Time Embedding)#

扩散模型需要知道当前是第几步 tt,因为不同时间步的噪声强度不同。

实现方式

  • 使用正弦位置编码(类似Transformer)将 tt 编码为向量
  • 将时间嵌入注入到U-Net的每一层
TimeEmbed(t)=[sin(ω1t),cos(ω1t),sin(ω2t),cos(ω2t),...]\text{TimeEmbed}(t) = [\sin(\omega_1 t), \cos(\omega_1 t), \sin(\omega_2 t), \cos(\omega_2 t), ...]

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等模型使用的核心技术。

训练#

训练时随机丢弃条件(以概率 pp 设置条件为空):

ϵθ(xt,t,c)ϵθ(xt,t,)\epsilon_\theta(\pmb{x}_t, t, c) \quad \text{和} \quad \epsilon_\theta(\pmb{x}_t, t, \emptyset)
  • cc:条件(如文本、类别)
  • \emptyset:空条件

采样#

采样时结合条件和无条件预测:

ϵ~θ=ϵθ(xt,t,)+w(ϵθ(xt,t,c)ϵθ(xt,t,))\tilde{\pmb{\epsilon}}_\theta = \epsilon_\theta(\pmb{x}_t, t, \emptyset) + w \cdot (\epsilon_\theta(\pmb{x}_t, t, c) - \epsilon_\theta(\pmb{x}_t, t, \emptyset))
  • ww:引导强度(guidance scale)
  • w=0w=0:无条件生成
  • w=1w=1:标准条件生成
  • w>1w>1:强化条件,生成更符合条件但可能失真

引导强度的权衡

guidance scale ww 的选择需要在条件符合度和生成质量之间权衡:

  • 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中:

  1. 文本编码器:使用CLIP或T5将文本编码为向量
  2. 交叉注意力(Cross-Attention):在U-Net中加入交叉注意力层
    • Query来自图像特征
    • Key和Value来自文本特征
  3. 条件注入:让图像特征能够”查询”文本信息

代表模型

  • 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 torch
import torch.nn as nn
import 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 torch
import torch.nn as nn
import 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:音乐生成

应用

  • 音效制作
  • 音乐创作

九、总结#

核心要点#

扩散模型的本质

  1. 前向过程:逐步添加噪声,将数据转化为纯噪声
  2. 反向过程:逐步去除噪声,从纯噪声恢复数据
  3. 训练目标:学习预测每一步添加的噪声
  4. 采样过程:从纯噪声开始,逐步去噪生成图像

关键优势

  • 训练稳定:单一目标函数,不需要对抗训练
  • 生成质量高:逐步去噪保证细节
  • 生成多样性好:不易模式崩溃
  • 易于扩展:支持多种条件生成方式(CFG、交叉注意力等)

主要挑战

  • 推理慢:需要数百步迭代
  • 计算成本高:训练需要大量资源
  • 内存占用大:需要存储多个时间步的中间结果

未来方向

  • 加速推理:DDIM、Consistency Models等
  • 降低计算成本:Latent Diffusion、蒸馏等
  • 提高可控性:ControlNet、Adapter等
  • 扩展应用:视频、3D、音频等

扩散模型的时代意义#

扩散模型的出现标志着生成模型进入新时代:

  • 从对抗到去噪:训练范式的根本转变
  • 从单步到多步:用时间换质量的新思路
  • 从研究到应用:Stable Diffusion、Midjourney等商业化成功

扩散模型不仅在图像生成领域超越了GAN,还在视频、3D、音频等多个领域展现出巨大潜力。理解扩散模型的原理,是掌握现代生成式AI的关键。


致谢#

本笔记整理了扩散模型的核心原理和实现细节。感谢以下论文和资源:

相关论文:

学习资源:

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

扩散模型(Diffusion Models)
https://castorice.xin/posts/扩散模型/
作者
castorice
发布于
2026-03-04
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时