深度学习入门站 · 理解深度学习
首页 › 第 18 章

扩散模型

从噪声里一点点"雕"出图像——Stable Diffusion 背后的原理。
图 18-1:扩散模型:前向加噪与反向去噪生成
图 18-1:扩散模型:前向加噪与反向去噪生成

这一章讲的是扩散模型(Diffusion Model)——Stable Diffusion、Midjourney 这类"输入一句话、输出一张图"的 AI 工具背后的核心原理。它的思路很朴素:先往一张清晰的照片上一点点撒噪声,直到它变成满屏雪花点;再训练一个神经网络,学会把噪声一步步"擦掉"。学完这一章,你会明白扩散模型为什么生成质量最高、训练最稳定,也会知道它唯一的短板——生成慢。

前向过程:给照片"下雪"

假设你有一张清晰的照片 $x$,扩散模型的第一步是设计一个前向过程(forward process):每一步都把当前图像和一点随机噪声混合,让它"糊"一点。把上一步的结果记为 $z_{t-1}$,这一步得到 $z_t$:

$$z_t = \sqrt{1-\beta_t}\, z_{t-1} + \sqrt{\beta_t}\, \epsilon_t$$

这个式子只有两句话:第一项 $\sqrt{1-\beta_t}\, z_{t-1}$ 是"保留原来的样子"——系数小于 1,画面会一点点变淡;第二项 $\sqrt{\beta_t}\, \epsilon_t$ 是"掺入噪声"——$\epsilon_t$ 是从标准正态分布里抽的随机数,$\beta_t$ 决定这一步掺多少。$\beta_t$ 随步骤变化,这一串数值叫噪声调度(noise schedule)。每一步只和上一步有关,所以这是一个马尔可夫链;撒够 $T$ 步(常用上千步)后,原图信息被彻底淹没,$z_T$ 就成了一团标准正态噪声。

🌫️
生活类比
前向过程就像在复印机上反复复印同一张照片:每复印一次,细节就模糊一点、噪点就多一点。复印几十次后,原图信息荡然无存,只剩一片雪花噪点。扩散模型要做的,就是把整个"复印过程"倒过来。

前向过程还有一个"作弊技巧",叫扩散核(diffusion kernel):因为每一步加的噪声都是独立的高斯噪声,我们可以从原图 $x$ 一步跳到任意时间步 $t$,不必一步步慢慢加:

$$z_t = \sqrt{\alpha_t}\, x + \sqrt{1-\alpha_t}\, \epsilon, \qquad \alpha_t = \prod_{s=1}^{t}(1-\beta_s)$$

这里 $\alpha_t$ 是前 $t$ 步"保留系数"的乘积,$\epsilon$ 仍是标准正态噪声。这个式子的意思是:训练时想在第 $t$ 步加多少噪声,一步就能算出来——这正是后面训练跑得动的前提。

逆向过程:把电影倒着放

既然前向过程能把照片变成噪声,那能不能反着来:从一团纯噪声出发,一步步"去噪",最后还原出一张清晰的图片?关键问题是——每一步该去掉多少噪声、往哪个方向去?这就要训练一个神经网络来回答。

好消息是:每小步加进去的噪声都不大,所以"逆操作"也不复杂。真实世界里 $z_{t-1}$ 在已知 $z_t$ 时的分布虽然一般很复杂,但只要每步噪声足够小,它就能用一个正态分布很好地近似。于是我们把逆向过程也建模成一步步的正态分布:

$$p_\phi(z_{t-1} \mid z_t) = \mathcal{N}\big(z_{t-1};\; f_\phi(z_t, t),\; \sigma_t^2 I\big)$$

其中 $f_\phi(z_t, t)$ 是一个神经网络:输入"当前噪声图 $z_t$"和"现在是第几步 $t$",输出"上一步最可能的图",$\sigma_t^2 I$ 表示这步预测的不确定性。生成时,先从标准正态分布采样一个 $z_T$,再让网络一步步"擦"回 $z_{T-1}, z_{T-2}, \dots$,最后得到 $x$——就像把一部"画面逐渐被噪声淹没"的电影倒着放。

🗿
生活类比
生成过程像从大理石里雕出雕像:一团纯噪声(石坯)里"藏"着所有可能的图片,去噪网络就是雕刻家,每一刀(每一步)只凿掉一点点多余的石料(噪声),雕够上千步,一尊清晰的雕像(图片)就露出来了。正因为每一步只做一点点修改,网络的任务始终很简单。

训练:让网络学会"预测噪声"

直觉上,我们想让网络学会"从 $z_t$ 还原 $z_{t-1}$",但直接这么学,它要同时学会"去噪"和"画图"两件事,很难。研究者后来发现了一个巧妙的改写:让网络只预测"刚刚混进去的噪声" $\epsilon$。做法是:用扩散核随机挑一个时间步 $t$、随机生成噪声 $\epsilon$,把 $x$ 和 $\epsilon$ 按比例混合成 $z_t$,再让网络 $g_\phi(z_t, t)$ 把 $\epsilon$ 猜出来。猜错多少,就罚多少:

$$L(\phi) = \mathbb{E}_{t,\,\epsilon}\left[\left\|\, g_\phi\!\left(\sqrt{\alpha_t}\, x + \sqrt{1-\alpha_t}\, \epsilon,\; t\right) - \epsilon \,\right\|^2\right]$$

为什么"预测噪声"比"直接预测图"更好?因为 $\epsilon$ 是我们自己加的,答案现成且百分之百准确——每个训练样本都自带完美"标准答案",训练自然又稳又快。这相当于玩"大家来找茬":网络只负责指出"哪里被我加了噪声"。

import numpy as np

def forward_once(x, t, betas, rng):
    """前向过程:用扩散核一步加噪到第 t 步"""
    alpha = np.prod(1 - betas[:t + 1])    # α_t = Π(1 - β_s)
    eps = rng.standard_normal(x.shape)    # 随机噪声
    z_t = np.sqrt(alpha) * x + np.sqrt(1 - alpha) * eps
    return z_t, eps                       # 噪声图 + 标准答案

def train_step(model, x, betas, rng):
    t = rng.integers(0, len(betas))       # 1. 随机挑一个时间步
    z_t, eps = forward_once(x, t, betas, rng)  # 2. 加噪
    pred = model(z_t, t)                  # 3. 网络预测噪声
    loss = np.mean((pred - eps) ** 2)     # 4. 预测 vs 真实噪声
    return loss                           # 5. 反向传播更新参数

每张训练图片可以配上无数种不同的噪声和时间步反复使用,相当于数据无限扩充。上面就是训练循环的骨架:随机加噪 → 预测噪声 → 算误差 → 更新参数。

💡
核心思想:扩散模型的制胜法宝,是把"生成一张图"这个大难题,拆成上千个"去掉一点点噪声"的小题。每个小题的答案(噪声)都是我们自己造的,所以训练稳定、质量极高;代价是生成时必须顺序跑完上千步,所以慢。

和 VAE、GAN 比一比

前几章讲过的生成模型各有各的毛病:GAN 生成快、质量高,但"生成器 vs 判别器"的对抗训练很娇气,动不动就崩溃;VAE 有扎实的概率基础、训练稳定,但生成的图片总有点糊;归一化流则要求网络每层都可逆,结构受限。扩散模型站在另一边:训练最稳定,生成质量目前最高,缺点是生成慢——要顺序跑上千步去噪,而 GAN 只需一次前向。

🧠
一句话记住三家的性格:GAN 快但难训,VAE 稳但模糊,扩散又稳又清晰,就是慢。质量优先、不赶时间的话,扩散是当下的首选。

条件生成:让 AI 听懂"画一只戴帽子的猫"

前面讲的是"无中生有"的无条件生成——生成什么内容,网络自己说了算。可我们想要的是:告诉它"戴帽子的猫",它真画一只戴帽子的猫。这就叫条件生成(conditional generation):在去噪的每一步,把提示信息 $c$(类别或文字)也喂给网络。道理很直观:如果你知道画面里是猫,擦噪声时就知道该往"猫的形状"上靠,而不是盲擦。

怎么把 $c$ 用进去?一种做法叫分类器引导:额外训练一个分类器,每一步用它的梯度把去噪过程"推"向目标类别。更常用的无分类器引导则不再训练额外分类器,而是把 $c$ 的嵌入(文字提示先经过语言模型编码)像时间信息一样注入去噪网络,训练时随机丢弃 $c$,让模型同时学会"有提示"和"没提示"两种模式。它还有个额外好处:测试时可以调节提示的强调程度——调得越高,生成越贴合提示也越"典型"。

实际系统中,去噪网络通常用 U-Net(一个先缩小提取特征、再放大还原结构的网络),把时间步 $t$ 编码成向量注入每一层;Stable Diffusion 再先用 VAE 把图片压缩到低维潜空间、在潜空间里跑扩散,最后级联超分辨率,实现"一句提示 → 一张高清图"。

🎯 小测验

1. 扩散模型的前向过程在做什么?
2. 训练时,去噪网络 $g_\phi(z_t, t)$ 要预测什么?
3. 与 GAN 相比,扩散模型的主要缺点是什么?