跳转至

03-扩散模型基础

⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。

📌 定位说明:本章为扩散模型入门级教学,侧重基础原理。深度进阶请参阅 扩散模型学习(含完整数学推导、 DDPM 从零实现、 SD 深入、条件生成、视频生成等 35+篇专题)


学习时间: 约 6-8 小时 难度级别: ⭐⭐⭐ 中级 前置知识: 概率论基础、 PyTorch 基础、 GAN/VAE 基本概念(参见本目录 01/02 ) 实践环境: Python 3.10+, PyTorch 2.0+


目录


1. 概述:扩散模型的革命性地位

1.1 什么是扩散模型

扩散模型( Diffusion Models )是一类基于迭代去噪的生成模型。其核心思想借鉴了非平衡热力学:

  1. 前向过程:逐步向数据添加高斯噪声,直到数据变成纯噪声
  2. 逆向过程:学习一个神经网络,逐步从噪声中恢复出原始数据
Text Only
原始图像 → [加噪] → [加噪] → ... → [加噪] → 纯高斯噪声
纯高斯噪声 → [去噪] → [去噪] → ... → [去噪] → 生成图像

1.2 为什么扩散模型如此重要

扩散模型在 2020 年后迅速崛起,成为生成式 AI 的主导范式

里程碑 时间 意义
DDPM ( Ho et al.) 2020.06 证明扩散模型可以生成高质量图像
Improved DDPM 2021.02 引入学习的方差调度,提升对数似然
DALL-E 2 2022.04 基于扩散模型的文生图系统
Stable Diffusion 2022.08 开源 Latent Diffusion ,引爆社区
SDXL 2023.07 更高分辨率,更优质量
Sora 2024.02 基于 DiT 的视频生成,震惊学界
SD3/Flux 2024-2025 Flow Matching + DiT ,新一代架构

1.3 扩散模型 vs 其他生成模型

传统生成模型的痛点

  • GAN:训练不稳定(模式崩塌、梯度消失),多样性有限
  • VAE:生成图像模糊(后验近似的信息损失), ELBO 松弛
  • Flow:架构约束严格(可逆网络),表达能力受限

扩散模型的优势

  • ✅ 训练稳定(简单的 MSE 损失)
  • ✅ 生成质量极高(超越 GAN 的 FID 得分)
  • ✅ 覆盖全部模式(不存在模式崩塌)
  • ✅ 似然可计算(有理论保证)
  • ✅ 架构灵活( UNet 、 Transformer 均可)

代价

  • ❌ 采样速度慢(需要多步迭代去噪)
  • ❌ 计算资源需求大

💡 核心洞察:扩散模型本质上将一个复杂的生成任务分解成了许多简单的去噪子任务,每一步只需要去除少量噪声,因此每一步都容易学习。

扩散过程示意图


2. DDPM 核心原理

DDPM ( Denoising Diffusion Probabilistic Models )是扩散模型最经典的形式化框架,由 Ho et al. 2020 提出。

2.1 前向扩散过程

前向过程( Forward Process / Diffusion Process )是一个固定的马尔可夫链,逐步向数据添加高斯噪声:

\[q(\mathbf{x}_t | \mathbf{x}_{t-1}) = \mathcal{N}(\mathbf{x}_t; \sqrt{1 - \beta_t} \, \mathbf{x}_{t-1}, \beta_t \mathbf{I})\]

其中: - \(\mathbf{x}_0\) 是原始数据 - \(\mathbf{x}_t\) 是第 \(t\) 步的含噪数据 - \(\beta_t \in (0, 1)\) 是噪声调度参数,控制每步添加的噪声量 - \(T\) 是总步数(通常取 1000 )

关键性质 — 任意时刻的闭式解

定义 \(\alpha_t = 1 - \beta_t\)\(\bar{\alpha}_t = \prod_{s=1}^{t} \alpha_s\),则:

\[q(\mathbf{x}_t | \mathbf{x}_0) = \mathcal{N}(\mathbf{x}_t; \sqrt{\bar{\alpha}_t} \, \mathbf{x}_0, (1 - \bar{\alpha}_t) \mathbf{I})\]

这意味着可以直接从 \(\mathbf{x}_0\) 跳到任意 \(\mathbf{x}_t\),而不需要逐步执行:

\[\mathbf{x}_t = \sqrt{\bar{\alpha}_t} \, \mathbf{x}_0 + \sqrt{1 - \bar{\alpha}_t} \, \boldsymbol{\epsilon}, \quad \boldsymbol{\epsilon} \sim \mathcal{N}(\mathbf{0}, \mathbf{I})\]

💡 直觉理解\(\sqrt{\bar{\alpha}_t}\) 控制原始信号的保留比例,\(\sqrt{1-\bar{\alpha}_t}\) 控制噪声的混入比例。随着 \(t\) 增大,信号逐渐消失,噪声逐渐主导。

2.2 逆向去噪过程

逆向过程( Reverse Process )是前向过程的"倒放",从噪声中恢复数据:

\[p_\theta(\mathbf{x}_{t-1} | \mathbf{x}_t) = \mathcal{N}(\mathbf{x}_{t-1}; \boldsymbol{\mu}_\theta(\mathbf{x}_t, t), \sigma_t^2 \mathbf{I})\]

为什么需要神经网络?

前向过程的逆向条件分布 \(q(\mathbf{x}_{t-1} | \mathbf{x}_t)\) 依赖于整个数据分布,无法直接计算。但如果同时知道 \(\mathbf{x}_0\),则后验是可解析的:

\[q(\mathbf{x}_{t-1} | \mathbf{x}_t, \mathbf{x}_0) = \mathcal{N}(\mathbf{x}_{t-1}; \tilde{\boldsymbol{\mu}}_t(\mathbf{x}_t, \mathbf{x}_0), \tilde{\beta}_t \mathbf{I})\]

其中:

\[\tilde{\boldsymbol{\mu}}_t = \frac{\sqrt{\bar{\alpha}_{t-1}} \beta_t}{1 - \bar{\alpha}_t} \mathbf{x}_0 + \frac{\sqrt{\alpha_t}(1 - \bar{\alpha}_{t-1})}{1 - \bar{\alpha}_t} \mathbf{x}_t\]
\[\tilde{\beta}_t = \frac{1 - \bar{\alpha}_{t-1}}{1 - \bar{\alpha}_t} \beta_t\]

三种等价的参数化方式

参数化 网络预测目标 说明
预测噪声 \(\boldsymbol{\epsilon}_\theta(\mathbf{x}_t, t)\) 添加的噪声 \(\boldsymbol{\epsilon}\) DDPM 原始方式,最常用
预测 \(\mathbf{x}_0\) 原始数据 \(\mathbf{x}_0\) 直接预测去噪结果
预测 \(\mathbf{v}\) \(v = \sqrt{\bar\alpha_t}\boldsymbol{\epsilon} - \sqrt{1-\bar\alpha_t}\mathbf{x}_0\) "v-prediction",数值更稳定

DDPM 选择预测噪声,将 \(\boldsymbol{\mu}_\theta\) 表示为:

\[\boldsymbol{\mu}_\theta(\mathbf{x}_t, t) = \frac{1}{\sqrt{\alpha_t}} \left( \mathbf{x}_t - \frac{\beta_t}{\sqrt{1 - \bar{\alpha}_t}} \boldsymbol{\epsilon}_\theta(\mathbf{x}_t, t) \right)\]

2.3 噪声调度( Noise Schedule )

噪声调度定义了 \(\{\beta_t\}_{t=1}^{T}\) 的变化规律:

线性调度( DDPM 原始)

\[\beta_t = \beta_{\min} + \frac{t-1}{T-1}(\beta_{\max} - \beta_{\min})\]

典型值:\(\beta_{\min}=10^{-4}\)\(\beta_{\max}=0.02\)

余弦调度( Improved DDPM )

\[\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\]

💡 为什么余弦调度更好? 线性调度在靠近 \(T\) 时信号衰减过快,导致大量步骤浪费在纯噪声上。余弦调度使 \(\bar{\alpha}_t\) 更平滑地下降,让每一步都更有效。

Python
import torch
import math

def linear_beta_schedule(T, beta_min=1e-4, beta_max=0.02):
    """线性噪声调度"""
    return torch.linspace(beta_min, beta_max, T)

def cosine_beta_schedule(T, s=0.008):
    """余弦噪声调度(Improved DDPM)"""
    steps = torch.arange(T + 1, dtype=torch.float64)
    alphas_cumprod = torch.cos(((steps / T) + s) / (1 + s) * math.pi * 0.5) ** 2
    alphas_cumprod = alphas_cumprod / alphas_cumprod[0]
    betas = 1 - (alphas_cumprod[1:] / alphas_cumprod[:-1])  # 切片操作取子序列
    return torch.clip(betas, 0.0001, 0.9999).float()  # 链式调用,连续执行多个方法

2.4 训练目标与损失函数

DDPM 的训练目标通过变分推断推导而来。最终简化为一个极其简洁的损失:

\[L_{\text{simple}} = \mathbb{E}_{t, \mathbf{x}_0, \boldsymbol{\epsilon}} \left[ \| \boldsymbol{\epsilon} - \boldsymbol{\epsilon}_\theta(\mathbf{x}_t, t) \|^2 \right]\]

训练流程伪代码

Text Only
repeat:
    1. 采样 x_0 ~ q(x_0)           # 从训练集采一张图
    2. 采样 t ~ Uniform(1, T)       # 随机选一个时间步
    3. 采样 ε ~ N(0, I)             # 采样随机噪声
    4. 计算 x_t = √ᾱₜ · x_0 + √(1-ᾱₜ) · ε   # 加噪
    5. 计算损失 L = ||ε - ε_θ(x_t, t)||²       # 预测噪声的MSE
    6. 梯度下降更新 θ
until converged

推理/采样流程

Text Only
1. 采样 x_T ~ N(0, I)              # 从纯噪声开始
2. for t = T, T-1, ..., 1:
     z ~ N(0, I) if t > 1 else z = 0
     x_{t-1} = (1/√αₜ)(x_t - βₜ/√(1-ᾱₜ) · ε_θ(x_t, t)) + σₜ · z
3. return x_0

💡 为什么这个损失如此有效? 这实际上是去噪得分匹配( Denoising Score Matching )的一种形式。网络不需要学习整个数据分布,只需要学习"每个时间步的噪声长什么样"——这是一个远比直接生成图像简单得多的任务。


3. Score-Based 模型与 SDE 统一框架

3.1 得分函数( Score Function )

得分函数定义为对数概率密度的梯度:

\[\mathbf{s}(\mathbf{x}) = \nabla_{\mathbf{x}} \log p(\mathbf{x})\]

得分函数指向数据密度增大的方向。一旦知道得分函数,就可以通过朗之万动力学( Langevin Dynamics )采样:

\[\mathbf{x}_{i+1} = \mathbf{x}_i + \frac{\delta}{2} \nabla_{\mathbf{x}} \log p(\mathbf{x}_i) + \sqrt{\delta} \, \mathbf{z}_i, \quad \mathbf{z}_i \sim \mathcal{N}(\mathbf{0}, \mathbf{I})\]

3.2 Score Matching 与 NCSN

直接估计得分函数需要知道 \(p(\mathbf{x})\)(归一化常数),这不可行。得分匹配( Score Matching )提供了不需要归一化常数的训练方法。

NCSN( Noise Conditional Score Network , Song & Ermon 2019 ): - 在多个噪声尺度下估计得分函数 - 使用退火朗之万动力学采样

3.3 SDE 统一框架

Song et al. (2021) 提出了统一视角:扩散过程可以用随机微分方程( SDE )描述:

前向 SDE: $\(d\mathbf{x} = \mathbf{f}(\mathbf{x}, t) \, dt + g(t) \, d\mathbf{w}\)$

逆向 SDE: $\(d\mathbf{x} = [\mathbf{f}(\mathbf{x}, t) - g(t)^2 \nabla_{\mathbf{x}} \log p_t(\mathbf{x})] \, dt + g(t) \, d\bar{\mathbf{w}}\)$

离散模型 对应的 SDE
DDPM VP-SDE ( Variance Preserving )
NCSN VE-SDE ( Variance Exploding )

💡 SDE 框架还引出了ODE 采样器( Probability Flow ODE ),可以用确定性的 ODE 求解器加速采样,这是 DDIM 等加速方法的理论基础。


4. 条件生成: Classifier-Free Guidance

4.1 条件扩散模型

实际应用中,我们通常希望有条件地生成内容(如根据文本描述生成图像)。条件扩散模型建模:

\[p_\theta(\mathbf{x}_{0:T} | c) \quad \text{其中 } c \text{ 为条件(文本,类别等)}\]

4.2 Classifier Guidance

Dhariwal & Nichol (2021) 提出使用预训练分类器引导生成:

\[\hat{\boldsymbol{\epsilon}}_\theta(\mathbf{x}_t, t, c) = \boldsymbol{\epsilon}_\theta(\mathbf{x}_t, t) - \sqrt{1-\bar{\alpha}_t} \cdot w \cdot \nabla_{\mathbf{x}_t} \log p_\phi(c | \mathbf{x}_t)\]

缺点:需要额外训练一个在含噪图像上工作的分类器。

4.3 Classifier-Free Guidance ( CFG )

Ho & Salimans (2022) 提出了更优雅的方案——无分类器引导

核心思想:同一个网络同时学习有条件和无条件生成,推理时用两者的线性外推:

\[\hat{\boldsymbol{\epsilon}}_\theta(\mathbf{x}_t, t, c) = (1 + w) \cdot \boldsymbol{\epsilon}_\theta(\mathbf{x}_t, t, c) - w \cdot \boldsymbol{\epsilon}_\theta(\mathbf{x}_t, t, \varnothing)\]

其中: - \(w\) 是引导强度( guidance scale ),典型值 5-15 - \(\varnothing\) 表示空条件(训练时随机丢弃条件实现) - \(w=0\) 等价于不使用引导 - \(w\) 越大,生成结果越忠于条件,但多样性降低

训练技巧:训练时以一定概率(如 10%-20%)将条件 \(c\) 替换为 \(\varnothing\)(空字符串 embedding ),使模型同时学会有条件和无条件生成。

Python
def classifier_free_guidance(model, x_t, t, cond, w=7.5, uncond=None):
    """Classifier-Free Guidance采样"""
    # 有条件预测
    eps_cond = model(x_t, t, cond)
    # 无条件预测
    eps_uncond = model(x_t, t, uncond)
    # CFG组合
    eps_guided = (1 + w) * eps_cond - w * eps_uncond
    return eps_guided

💡 为什么 CFG 如此重要? 几乎所有现代文生图模型( DALL-E ⅔ 、 Stable Diffusion 、 Midjourney 、 Imagen )都使用 CFG 。它是连接"文本理解"和"图像生成"的关键桥梁。

Classifier-Free Guidance 示意图


5. Latent Diffusion Models 与 Stable Diffusion

5.1 像素空间的困境

直接在像素空间运行扩散模型面临严重的计算瓶颈:

  • 一张 512×512×3 的图像有 786,432 维
  • 每步去噪都需要完整的 UNet 前向传播
  • 训练和推理成本极高

5.2 LDM 核心架构

Rombach et al. (2022) 提出Latent Diffusion Models ( LDM ):在压缩的潜在空间( Latent Space )中运行扩散过程。

Text Only
┌─────────────────────────────────────────────────┐
│                LDM 架构                          │
│                                                  │
│  像素空间          潜在空间          像素空间      │
│  ┌──────┐    ┌─────────────────┐    ┌──────┐    │
│  │ 图像 │───→│  编码器(E)       │───→│ z_0  │    │
│  │512x512│    │  下采样 8x       │    │64x64 │    │
│  └──────┘    └─────────────────┘    └──┬───┘    │
│                                        │        │
│              ┌─────────────────┐       ↓        │
│              │   扩散过程       │    加噪/去噪   │
│              │   (在z空间)      │    (UNet)      │
│              └─────────────────┘       │        │
│                                        ↓        │
│  ┌──────┐    ┌─────────────────┐    ┌──────┐    │
│  │ 图像 │←──│  解码器(D)       │←──│ z_0' │    │
│  │512x512│    │  上采样 8x       │    │64x64 │    │
│  └──────┘    └─────────────────┘    └──────┘    │
│                                                  │
│  条件输入──→ CLIP Text Encoder ──→ Cross-Attention│
└─────────────────────────────────────────────────┘

Stable Diffusion 架构图

组件 作用 关键细节
AutoEncoder (VAE) 图像↔潜在空间 转换 压缩比 8x ,潜在维度 4
UNet 逆向去噪网络 含 ResNet 块 + Self-Attention + Cross-Attention
Text Encoder 文本条件编码 CLIP ViT-L/14 ( SD1.x )/ OpenCLIP ViT-bigG ( SDXL )
Cross-Attention 文本-图像交互 \(\text{Attention}(Q_{img}, K_{text}, V_{text})\)

5.3 Stable Diffusion 架构详解

Stable Diffusion 是 LDM 的具体实现,由 Stability AI 开源:

SD 1.x / 2.x: - VAE :将 512×512×3 压缩到 64×64×4 - UNet :约 860M 参数,含 ResBlock + SpatialTransformer - 文本编码器: CLIP ( SD1.x )/ OpenCLIP ( SD2.x ) - 采样器: DDIM 、 Euler 、 DPM-Solver 等

SDXL: - 更大的 UNet ( 2.6B 参数),双文本编码器 - 新增尺寸/裁剪条件 embedding - 可选的 Refiner 模型

SD3 / Flux: - 从 UNet 转向Transformer架构( MM-DiT ) - 采用Flow Matching替代传统 DDPM 噪声调度 - 支持更高分辨率和更好的文本渲染 - MMDiT ( Multimodal DiT ):将文本和图像视为等价序列,使用联合注意力机制 - Rectified Flow:改进的 Flow Matching 训练目标,生成质量更高

5.4 为什么在潜在空间工作

比较维度 像素空间扩散 潜在空间扩散
数据维度 512×512×3 = 786K 64×64×4 = 16K
计算量 极大 降低约 50x
训练时间 数周(大集群) 数天
生成质量 高( VAE 重建损失可忽略)
灵活性 高( VAE 可复用)

6. DiT : Diffusion Transformer 架构

6.1 从 UNet 到 Transformer

传统扩散模型使用UNet作为去噪网络。 Peebles & Xie (2023) 提出的DiT ( Diffusion Transformer )证明 Transformer 架构在扩散模型中同样有效甚至更优。

6.2 DiT 架构

Text Only
┌─────────────────────────────────┐
│         DiT Architecture         │
│                                  │
│  输入: z_t (含噪潜在表示)          │
│    ↓                             │
│  Patchify (切分为patch tokens)    │
│    ↓                             │
│  + Positional Embedding          │
│    ↓                             │
│  ┌───────────────────────────┐   │
│  │   DiT Block × N           │   │
│  │  ┌─────────────────────┐  │   │
│  │  │ LayerNorm (adaLN)   │  │   │
│  │  │ Self-Attention       │  │   │
│  │  │ LayerNorm (adaLN)   │  │   │
│  │  │ Feed-Forward         │  │   │
│  │  └─────────────────────┘  │   │
│  └───────────────────────────┘   │
│    ↓                             │
│  Linear + Reshape                │
│    ↓                             │
│  输出: 预测的噪声 ε               │
└─────────────────────────────────┘
│                                  │
│  条件注入: t + class label       │
│  → adaLN-Zero (自适应LayerNorm)  │
└─────────────────────────────────┘

DiT 架构图

DiT 的关键创新

  • Patchify:将潜在表示切分为 non-overlapping patches ,类似 ViT
  • adaLN-Zero:使用自适应 Layer Normalization 注入条件(时间步、类别)
  • Scaling 效果好:模型规模增大时性能持续提升

6.3 DiT 的影响

DiT 开启了扩散模型的"Scaling Law"时代:

模型 基于 DiT 应用 关键特性
Sora ( OpenAI ) ✅ Spatial-Temporal DiT 视频生成 长视频生成,时空一致性
SD3 ( Stability AI ) ✅ MM-DiT 文生图 MMDiT+Rectified Flow
Flux ( Black Forest Labs ) ✅ 改进 DiT 文生图 优秀文本渲染
HunyuanVideo (腾讯) 视频生成 中文视频生成
CogVideoX (智谱) 视频生成 高质量视频
Qwen-Image (阿里) 文生图 Flow Matching

💡 趋势DiT + Flow Matching 正在替代 UNet + DDPM 成为新一代生成模型的标准配置。

6.4 MMDiT 架构详解

MMDiT ( Multimodal Diffusion Transformer )是 SD3 和 Flux 的核心创新:

核心思想:将文本和图像作为等价的 token 序列,在同一个 Transformer 中联合处理

Python
class JointAttention(nn.Module):  # 继承nn.Module定义神经网络层
    """MMDiT的联合注意力机制"""
    def __init__(self, d_model, num_heads):  # __init__构造方法,创建对象时自动调用
        super().__init__()  # super()调用父类方法
        self.d_model = d_model
        self.num_heads = num_heads
        self.head_dim = d_model // num_heads
        self.to_q = nn.Linear(d_model, d_model)
        self.to_k = nn.Linear(d_model, d_model)
        self.to_v = nn.Linear(d_model, d_model)
        self.scale = self.head_dim ** -0.5

    def forward(self, img, txt):
        """
        img: (batch, img_tokens, d_model) - 图像tokens
        txt: (batch, txt_tokens, d_model) - 文本tokens
        """
        # 拼接图像和文本tokens
        x = torch.cat([img, txt], dim=1)  # (batch, img_tokens + txt_tokens, d_model)

        # 计算Q, K, V
        q = self.to_q(x)
        k = self.to_k(x)
        v = self.to_v(x)

        # 分割为多头
        q = q.view(q.size(0), -1, self.num_heads, self.head_dim).transpose(1, 2)
        k = k.view(k.size(0), -1, self.num_heads, self.head_dim).transpose(1, 2)
        v = v.view(v.size(0), -1, self.num_heads, self.head_dim).transpose(1, 2)

        # 自注意力(所有tokens互相attend)
        attn = (q @ k.transpose(-2, -1)) * self.scale
        attn = attn.softmax(dim=-1)
        out = attn @ v

        # 拆分回图像和文本
        out = out.transpose(1, 2).contiguous().view(out.size(0), -1, self.d_model)
        img_out, txt_out = out.split([img.size(1), txt.size(1)], dim=1)

        return img_out, txt_out

MMDiT 的优势: 1. 文本-图像对齐更好:双向注意力机制让文本和图像充分交互 2. 架构更简洁:无需 Cross-Attention 模块 3. 训练更稳定:统一的注意力机制更易优化

6.5 Flow Matching 与 Rectified Flow

Flow Matching是新一代扩散模型的训练范式:

传统 DDPM 的问题: - 噪声调度复杂(线性/余弦调度) - 采样步数多(通常 50-1000 步) - 训练目标不够直观

Flow Matching 的改进: - Rectified Flow:使用"修正流"替代噪声预测 - 更快的采样:可降至 10-25 步甚至更少 - 更好的质量:生成图像更清晰,文本渲染更准确

Python
# Flow Matching的核心思想(简化版)
class FlowMatchingLoss(nn.Module):
    """Flow Matching损失函数"""
    def forward(self, model, x_0, t):
        """
        x_0: 原始数据
        t: 时间步
        """
        # 1. 采样噪声和中间状态
        eps = torch.randn_like(x_0)
        x_t = (1 - t) * x_0 + t * eps  # 简单线性插值

        # 2. 预测速度场(velocity field)
        v_pred = model(x_t, t)

        # 3. 计算目标速度场
        v_target = eps - x_0  # 从x_0到噪声的方向

        # 4. MSE损失
        loss = F.mse_loss(v_pred, v_target)
        return loss

Rectified Flow 的关键改进: - 使用更复杂的插值策略(非简单线性) - 引入"修正"项,改善长距离流动 - 结合 CFG ( Classifier-Free Guidance ),生成质量更高


7. 主要应用领域

7.1 图像生成

应用 代表模型 说明
文生图( T2I ) Stable Diffusion, DALL-E 3, Midjourney 最成熟的应用
图像编辑 InstructPix2Pix, ControlNet 根据指令编辑图像
图像修复( Inpainting ) SD Inpainting 补全图像缺失部分
超分辨率 SR3, StableSR 低分辨率→高分辨率
风格迁移 LoRA, DreamBooth 少样本个性化生成

7.2 视频生成

  • Sora( OpenAI ):可生成长达 60 秒的高质量视频
  • Runway Gen-3:商用视频生成平台
  • Kling/可灵(快手):国产视频生成模型
  • HunyuanVideo(腾讯):开源视频生成

核心挑战:时间一致性、长视频生成、计算成本

7.3 3D 生成

  • DreamFusion: Text-to-3D ,使用 SDS ( Score Distillation Sampling )
  • Zero-1-to-3:单张图像生成 3D
  • Point-E / Shap-E( OpenAI ):点云/隐式场 3D 生成
  • Instant3D:快速 3D 生成

7.4 音频生成

  • AudioLDM:文本到音频
  • MusicLDM:音乐生成
  • Bark:语音合成
  • Stable Audio: Stability AI 的音频生成

7.5 其他应用

  • 分子设计:药物分子生成( Diffusion for molecule generation )
  • 蛋白质设计: RFDiffusion (蛋白质结构生成)
  • 机器人控制: Diffusion Policy (扩散策略,用于机器人动作生成)
  • 时间序列: TimeGrad (概率时间序列预测)

8. GAN / VAE / Diffusion 对比

8.1 综合对比表

维度 GAN VAE Diffusion Models
生成质量 高(锐利) 中(偏模糊) 极高
训练稳定性 差(模式崩塌)
模式覆盖 差(模式遗漏) 极好
采样速度 极快(一步) 极快(一步) 慢(多步迭代)
似然评估 不可 可( ELBO )
训练目标 对抗博弈 ELBO 最大化 去噪得分匹配
核心网络 生成器+判别器 编码器+解码器 去噪网络
潜在空间 无结构 有结构(连续) 隐式
条件生成 cGAN CVAE CFG
主要缺点 训练不稳定 图像模糊 速度慢
当前地位 特定领域适用 作为组件使用 主导范式

8.2 采样速度优化

扩散模型的主要缺点是采样慢。常见加速方法:

方法 步数 核心思想
DDIM 10-50 确定性采样,跳步
DPM-Solver 10-25 高阶 ODE 求解器
Consistency Models 1-2 直接映射噪声到数据
Latent Consistency Model (LCM) 1-4 潜在空间一致性蒸馏
蒸馏( Distillation ) 1-8 教师-学生蒸馏
SDXL Turbo / Lightning 1-4 对抗蒸馏

9. PyTorch 实战:简单 DDPM 训练循环

以下是一个完整可运行的简单 DDPM 实现,在 MNIST 数据集上训练:

Python
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import math

# ========================================
# 1. 噪声调度器
# ========================================
class NoiseScheduler:
    """管理DDPM的噪声调度参数"""

    def __init__(self, T=1000, beta_min=1e-4, beta_max=0.02, device='cpu'):
        self.T = T
        self.device = device

        # 线性beta调度
        self.betas = torch.linspace(beta_min, beta_max, T).to(device)
        self.alphas = 1.0 - self.betas
        self.alphas_cumprod = torch.cumprod(self.alphas, dim=0)
        self.alphas_cumprod_prev = F.pad(self.alphas_cumprod[:-1], (1, 0), value=1.0)

        # 预计算常用系数
        self.sqrt_alphas_cumprod = torch.sqrt(self.alphas_cumprod)
        self.sqrt_one_minus_alphas_cumprod = torch.sqrt(1.0 - self.alphas_cumprod)
        self.sqrt_recip_alphas = torch.sqrt(1.0 / self.alphas)

        # 逆向过程的后验方差
        self.posterior_variance = (
            self.betas * (1.0 - self.alphas_cumprod_prev) / (1.0 - self.alphas_cumprod)
        )

    def add_noise(self, x_0, t, noise=None):
        """前向过程:给x_0添加噪声得到x_t"""
        if noise is None:
            noise = torch.randn_like(x_0)

        sqrt_alpha = self.sqrt_alphas_cumprod[t].view(-1, 1, 1, 1)  # view重塑张量形状(要求内存连续)
        sqrt_one_minus_alpha = self.sqrt_one_minus_alphas_cumprod[t].view(-1, 1, 1, 1)

        x_t = sqrt_alpha * x_0 + sqrt_one_minus_alpha * noise
        return x_t, noise

    @torch.no_grad()  # 禁用梯度计算,节省内存
    def sample_step(self, model, x_t, t):
        """逆向过程:单步去噪 x_t -> x_{t-1}"""
        t_tensor = torch.full((x_t.shape[0],), t, device=self.device, dtype=torch.long)

        # 预测噪声
        predicted_noise = model(x_t, t_tensor)

        # 计算均值
        beta_t = self.betas[t]
        sqrt_recip_alpha_t = self.sqrt_recip_alphas[t]
        sqrt_one_minus_alpha_cumprod_t = self.sqrt_one_minus_alphas_cumprod[t]

        mean = sqrt_recip_alpha_t * (
            x_t - beta_t / sqrt_one_minus_alpha_cumprod_t * predicted_noise
        )

        if t > 0:
            noise = torch.randn_like(x_t)
            variance = torch.sqrt(self.posterior_variance[t])
            return mean + variance * noise
        else:
            return mean

# ========================================
# 2. 时间步嵌入
# ========================================
class SinusoidalPositionEmbedding(nn.Module):
    """正弦位置编码,将时间步t编码为向量"""

    def __init__(self, dim):
        super().__init__()
        self.dim = dim

    def forward(self, t):
        device = t.device
        half_dim = self.dim // 2
        emb = math.log(10000) / (half_dim - 1)
        emb = torch.exp(torch.arange(half_dim, device=device) * -emb)
        emb = t[:, None].float() * emb[None, :]
        emb = torch.cat([emb.sin(), emb.cos()], dim=-1)
        return emb

# ========================================
# 3. 简单UNet去噪网络
# ========================================
class SimpleUNet(nn.Module):
    """简化版UNet,用于MNIST去噪"""

    def __init__(self, in_channels=1, time_dim=256):
        super().__init__()

        # 时间步嵌入
        self.time_mlp = nn.Sequential(
            SinusoidalPositionEmbedding(time_dim),
            nn.Linear(time_dim, time_dim),
            nn.GELU(),
            nn.Linear(time_dim, time_dim),
        )

        # 编码器(下采样)
        self.enc1 = self._make_block(in_channels, 64)
        self.enc2 = self._make_block(64, 128)
        self.pool = nn.MaxPool2d(2)

        # 瓶颈层
        self.bottleneck = self._make_block(128, 256)
        self.time_proj = nn.Linear(time_dim, 256)

        # 解码器(上采样)
        self.up2 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2)
        self.dec2 = self._make_block(256, 128)  # 256 = 128(up) + 128(skip)
        self.up1 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)
        self.dec1 = self._make_block(128, 64)   # 128 = 64(up) + 64(skip)

        # 输出层
        self.out = nn.Conv2d(64, in_channels, kernel_size=1)

    def _make_block(self, in_ch, out_ch):
        return nn.Sequential(
            nn.Conv2d(in_ch, out_ch, 3, padding=1),
            nn.GroupNorm(8, out_ch),
            nn.GELU(),
            nn.Conv2d(out_ch, out_ch, 3, padding=1),
            nn.GroupNorm(8, out_ch),
            nn.GELU(),
        )

    def forward(self, x, t):
        # 时间步嵌入
        t_emb = self.time_mlp(t)  # (B, time_dim)

        # 编码器
        e1 = self.enc1(x)                    # (B, 64, 28, 28)
        e2 = self.enc2(self.pool(e1))        # (B, 128, 14, 14)

        # 瓶颈层 + 时间条件注入
        b = self.bottleneck(self.pool(e2))   # (B, 256, 7, 7)
        t_proj = self.time_proj(t_emb)[:, :, None, None]
        b = b + t_proj                        # 时间条件加入

        # 解码器 + 跳跃连接
        d2 = self.up2(b)                      # (B, 128, 14, 14)
        d2 = self.dec2(torch.cat([d2, e2], dim=1))
        d1 = self.up1(d2)                     # (B, 64, 28, 28)
        d1 = self.dec1(torch.cat([d1, e1], dim=1))

        return self.out(d1)

# ========================================
# 4. 训练循环
# ========================================
def train_ddpm(
    epochs=10,
    batch_size=128,
    lr=2e-4,
    T=1000,
    device='cuda' if torch.cuda.is_available() else 'cpu',
):
    """训练DDPM模型"""
    print(f"Using device: {device}")

    # 数据集
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize([0.5], [0.5]),  # 归一化到 [-1, 1]
    ])
    dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=2)  # DataLoader批量加载数据,支持shuffle和多进程

    # 模型和优化器
    model = SimpleUNet(in_channels=1).to(device)
    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    scheduler = NoiseScheduler(T=T, device=device)

    print(f"Model parameters: {sum(p.numel() for p in model.parameters()):,}")

    # 训练
    for epoch in range(epochs):
        total_loss = 0
        for batch_idx, (images, _) in enumerate(dataloader):  # enumerate同时获取索引和元素
            images = images.to(device)

            # 随机采样时间步
            t = torch.randint(0, T, (images.shape[0],), device=device)

            # 前向加噪
            x_t, noise = scheduler.add_noise(images, t)

            # 预测噪声
            predicted_noise = model(x_t, t)

            # MSE损失
            loss = F.mse_loss(predicted_noise, noise)

            # 反向传播
            optimizer.zero_grad()
            loss.backward()  # 反向传播计算梯度
            # 梯度裁剪(稳定训练)
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
            optimizer.step()  # 根据梯度更新模型参数

            total_loss += loss.item()  # .item()将单元素张量转为Python数值

        avg_loss = total_loss / len(dataloader)
        print(f"Epoch [{epoch+1}/{epochs}], Loss: {avg_loss:.4f}")

    return model, scheduler

# ========================================
# 5. 采样/生成
# ========================================
@torch.no_grad()
def generate_samples(
    model, scheduler, n_samples=16, device='cuda',
    image_size=28, channels=1
):
    """从训练好的DDPM生成图像"""
    model.eval()  # eval()开启评估模式(关闭Dropout等)

    # 从纯噪声开始
    x = torch.randn(n_samples, channels, image_size, image_size).to(device)

    # 逐步去噪 T -> 0
    for t in reversed(range(scheduler.T)):
        x = scheduler.sample_step(model, x, t)

    # 将 [-1, 1] 映射回 [0, 1]
    x = (x + 1) / 2
    x = x.clamp(0, 1)

    return x

# ========================================
# 主入口
# ========================================
if __name__ == '__main__':
    # 训练模型
    model, scheduler = train_ddpm(epochs=10, batch_size=128)

    # 生成样本
    device = next(model.parameters()).device
    samples = generate_samples(model, scheduler, n_samples=16, device=device)

    print(f"Generated {samples.shape[0]} samples, shape: {samples.shape}")

    # 可选:可视化(需要matplotlib)
    # import matplotlib.pyplot as plt
    # fig, axes = plt.subplots(4, 4, figsize=(8, 8))
    # for i, ax in enumerate(axes.flat):
    #     ax.imshow(samples[i, 0].cpu().numpy(), cmap='gray')
    #     ax.axis('off')
    # plt.savefig('ddpm_samples.png')
    # plt.show()

代码要点说明

组件 说明
NoiseScheduler 管理前向/逆向过程的所有参数
SinusoidalPositionEmbedding 将整数时间步编码为连续向量,与 Transformer 的位置编码相同
SimpleUNet 简化版 UNet ,含编码器-瓶颈-解码器+跳跃连接
add_noise 实现闭式前向加噪 \(x_t = \sqrt{\bar\alpha_t} x_0 + \sqrt{1-\bar\alpha_t} \epsilon\)
sample_step 实现单步逆向去噪
损失函数 简单的 MSE :\(\lVert\epsilon - \epsilon_\theta(x_t, t)\rVert^2\)
梯度裁剪 clip_grad_norm_ 防止梯度爆炸,稳定训练

10. 面试必考题

Q1 :请解释 DDPM 的前向过程和逆向过程

参考答案

前向过程是一个固定的马尔可夫链,逐步向数据 \(\mathbf{x}_0\) 添加高斯噪声。在每一步 \(t\),通过 \(q(\mathbf{x}_t | \mathbf{x}_{t-1}) = \mathcal{N}(\sqrt{1-\beta_t}\mathbf{x}_{t-1}, \beta_t\mathbf{I})\) 添加少量噪声。关键性质是可以用闭式解直接计算任意时刻的 \(\mathbf{x}_t\)\(\mathbf{x}_t = \sqrt{\bar\alpha_t}\mathbf{x}_0 + \sqrt{1-\bar\alpha_t}\boldsymbol\epsilon\)

逆向过程学习一个参数化的马尔可夫链 \(p_\theta(\mathbf{x}_{t-1}|\mathbf{x}_t)\),从纯噪声 \(\mathbf{x}_T \sim \mathcal{N}(0,I)\) 逐步去噪恢复数据。通常参数化为预测噪声 \(\boldsymbol\epsilon_\theta(\mathbf{x}_t, t)\),训练目标是简单的 MSE 损失。

要点:只有逆向过程需要学习,前向过程是固定的。


Q2 :扩散模型与 GAN 相比有哪些优缺点

参考答案

优势: 1. 训练稳定——无对抗训练,不存在模式崩塌 2. 生成多样性好——覆盖全部数据分布 3. 似然可计算——有理论保证 4. 架构灵活——不需要对抗训练的精细平衡

劣势: 1. 采样速度慢——需要数百到数千步迭代( GAN 只需一步) 2. 计算资源更大——训练和推理成本都高

现状:通过 DDIM 、 DPM-Solver 、一致性模型等加速技术,扩散模型的采样速度已大幅提升(可降至 1-4 步),基本弥补了速度劣势。


Q3 :什么是 Classifier-Free Guidance ?为什么它很重要

参考答案

CFG 是一种无需额外分类器的条件引导方法。核心是在同一个模型中同时学习条件和无条件生成:

\[\hat{\boldsymbol\epsilon} = (1+w) \cdot \boldsymbol\epsilon_\theta(\mathbf{x}_t, t, c) - w \cdot \boldsymbol\epsilon_\theta(\mathbf{x}_t, t, \varnothing)\]

训练时随机 drop 掉条件(如 10%概率置为空),推理时用引导强度 \(w\) 控制条件的影响。

重要性: CFG 是连接条件(如文本)和生成结果的核心机制,几乎所有现代文生图系统( DALL-E 、 SD 、 Midjourney )都依赖 CFG 实现高质量条件生成。


Q4 : Latent Diffusion Models 的核心创新是什么

参考答案

LDM 的核心创新是将扩散过程从像素空间转移到低维潜在空间

  1. 先用预训练的 VAE 将图像编码到潜在空间(如 512×512→64×64 )
  2. 在潜在空间中运行扩散过程
  3. 最后用 VAE 解码器还原为像素图像

优势: - 计算量降低约 50 倍(维度从 786K 降至 16K ) - 扩散模型专注于语义层面的生成(细节由 VAE 处理) - VAE 是预训练的,可以跨任务复用 - 实现了高质量生成的"民主化"——消费级 GPU 即可运行


Q5 : DiT ( Diffusion Transformer )相比 UNet 有什么优势

参考答案

DiT 用Transformer替代传统 UNet 作为扩散模型的骨干网络:

  1. Scaling 效果更好——遵循 Scaling Law ,模型越大效果越好,且可预测
  2. 架构更简洁——去除了 UNet 的不规则结构( skip connections 等),纯 Transformer 更标准化
  3. 与 NLP 生态兼容——可以复用 Transformer 的训练基础设施和优化技巧
  4. 时空扩展自然——处理视频时,时间维度可以自然地作为 token 加入( Sora 就是这样做的)

条件注入使用 adaLN-Zero (自适应 Layer Normalization ),将时间步和条件信息注入到每个 Transformer Block 的归一化层。


Q6 :如何加速扩散模型的采样过程

参考答案

五种主要加速策略:

  1. 高效求解器
  2. DDIM :将随机过程转为确定性 ODE ,跳步采样( 1000 步→50 步)
  3. DPM-Solver :高阶 ODE 求解器( 10-25 步即可)

  4. 一致性蒸馏

  5. Consistency Models ( CM ):学习从任意噪声级别直接映射到 \(\mathbf{x}_0\)( 1-2 步)
  6. Latent Consistency Model ( LCM ):在潜在空间做一致性蒸馏( 1-4 步)

  7. 知识蒸馏

  8. Progressive Distillation :教师模型 2 步→学生模型 1 步,递归蒸馏
  9. SDXL Turbo/Lightning :对抗蒸馏

  10. 潜在空间压缩

  11. LDM/Stable Diffusion :在低维空间扩散,减少每步计算

  12. 缓存与硬件优化

  13. DeepCache :缓存 UNet 中间特征
  14. FlashAttention :加速 Attention 计算
  15. 模型量化: INT8/FP16 推理

11. 学习检查清单

完成本章学习后,请对照以下清单自测:

基础概念

  • 能用自己的话解释扩散模型的前向过程和逆向过程
  • 理解 \(\alpha_t\)\(\bar\alpha_t\)\(\beta_t\) 的含义和关系
  • 知道为什么可以用闭式解直接从 \(\mathbf{x}_0\) 计算 \(\mathbf{x}_t\)
  • 理解 DDPM 为什么选择预测噪声而不是直接预测图像

数学与损失

  • 能推导或解释简化损失 \(L_{\text{simple}} = \mathbb{E}\|\boldsymbol\epsilon - \boldsymbol\epsilon_\theta\|^2\)
  • 理解线性调度与余弦调度的区别和优劣
  • 知道三种等价参数化(预测噪声/预测 x_0/预测 v )

条件生成

  • 理解 Classifier Guidance 和 Classifier-Free Guidance 的区别
  • 能解释 CFG 的引导强度 \(w\) 对生成结果的影响
  • 知道训练时随机 drop 条件的原因

架构与系统

  • 理解 LDM 为什么在潜在空间运行扩散过程
  • 能描述 Stable Diffusion 的三大组件( VAE/UNet/CLIP )
  • 了解 DiT 架构的创新点和 adaLN-Zero
  • 知道从 UNet 到 Transformer 的演进趋势

实践与应用

  • 能运行和理解本章的 DDPM 代码
  • 知道至少 3 种扩散模型加速方法
  • 了解扩散模型在图像/视频/3D/音频领域的应用
  • 能对比 GAN 、 VAE 、 Diffusion 的优缺点

进阶准备

  • 对 Score-Based 模型和 SDE 框架有初步认识
  • 了解 Flow Matching 是新一代的训练范式
  • 准备好深入 扩散模型学习 中的进阶专题

12. 参考资料与延伸阅读

必读论文

  1. DDPM: Ho et al., "Denoising Diffusion Probabilistic Models" (NeurIPS 2020)
  2. Improved DDPM: Nichol & Dhariwal, "Improved Denoising Diffusion Probabilistic Models" (ICML 2021)
  3. Score SDE: Song et al., "Score-Based Generative Modeling through Stochastic Differential Equations" (ICLR 2021)
  4. CFG: Ho & Salimans, "Classifier-Free Diffusion Guidance" (NeurIPS Workshop 2021)
  5. LDM: Rombach et al., "High-Resolution Image Synthesis with Latent Diffusion Models" (CVPR 2022)
  6. DiT: Peebles & Xie, "Scalable Diffusion Models with Transformers" (ICCV 2023)

经典博客与教程

  • Lilian Weng: "What are Diffusion Models?" — 优秀的技术博客综述
  • Calvin Luo: "Understanding Diffusion Models: A Unified Perspective" — 数学统一视角
  • Hugging Face Diffusion Models Course — 动手教程

深度进阶

  • 📚 扩散模型学习 — 本教程配套的进阶专题,含 35+篇深度内容:
  • 完整数学推导(前向/逆向/ELBO )
  • DDPM 从零实现(逐行代码)
  • Stable Diffusion 深度解析
  • 条件生成完整指南
  • 视频生成( Sora 原理分析)
  • 加速采样技术汇总
  • …更多专题

附录:扩散模型技术演进

A.1 架构演进

时期 主流架构 代表模型 特点
2020-2022 UNet + DDPM DDPM, Improved DDPM 像素空间扩散
2022-2023 LDM + UNet Stable Diffusion 1.x/2.x 潜在空间扩散
2023-2024 DiT + DDPM DiT, Sora Transformer 骨干
2024-2025 DiT + Flow Matching SD3, Flux, Qwen-Image MMDiT + Rectified Flow

A.2 训练目标演进

技术 提出时间 核心思想 优势
DDPM 2020 预测噪声 简单有效
Improved DDPM 2021 学习方差调度 更好似然
Flow Matching 2022 预测速度场 更快采样
Rectified Flow 2024 修正流匹配 更高质量

A.3 采样加速技术

技术 步数 核心思想 代表模型
DDIM 10-50 确定性 ODE 采样 SD 1.x
DPM-Solver 10-25 高阶 ODE 求解器 SDXL
Consistency Models 1-2 一致性蒸馏 LCM
Flow Matching 10-25 速度场预测 SD3, Flux
LCM-LoRA 1-4 潜在一致性蒸馏 SDXL Turbo

💡 趋势DiT + Flow Matching + 快速采样器的组合正在成为新一代生成模型的标准配置。

更新说明: - 最后更新时间: 2026 年 2 月 - 新增内容: - MMDiT 架构详解和代码实现 - Flow Matching 与 Rectified Flow 的详细说明 - 扩散模型技术演进附录 - 数据来源:相关论文和开源模型文档

📝 下一步学习建议: - 如果对本章内容都已掌握,建议进入 扩散模型学习 进行系统深入学习 - 如果对 GAN/VAE 还不熟悉,先回顾 01-GAN 基础02-VAE 基础