扩散模型基础:前向扩散与逆向去噪

一句话概述

扩散模型(Diffusion Model)是近年来最强大的生成模型范式,也是Stable Diffusion、DALL-E 2、Midjourney等主流图像生成工具的核心技术。扩散模型包含两个过程:①前向扩散(Forward Diffusion)——逐步向数据中添加高斯噪声,经过T步后数据变为纯噪声;②逆向去噪(Reverse Denoising)——训练一个神经网络(通常为U-Net)从噪声中逐步恢复原始数据。前向过程是固定的(不需要学习),逆向过程通过学习"预测噪声"来实现数据生成。扩散模型的理论基础是随机微分方程(SDE)和变分推断,其生成质量在多个基准上超越了GAN。与GAN和VAE相比,扩散模型训练更稳定(无需对抗),生成多样性更好(不易模式坍缩),但推理速度较慢(需要多步迭代)。

💡 核心要点:①扩散模型通过逐步加噪(前向)和去噪(逆向)实现数据生成 ②前向过程是固定的马尔可夫链,逐步将数据变为高斯噪声 ③逆向过程训练神经网络预测添加的噪声,从噪声中恢复数据 ④生成时从纯噪声开始,经T步逆向去噪得到新数据 ⑤生成质量高、训练稳定,但推理速度比GAN慢

教学与演示

一、前向扩散:数据到噪声

是什么(定义):前向扩散过程(Forward Diffusion Process)是一个固定的马尔可夫链,逐步向数据x_0中添加高斯噪声。第t步的分布为q(x_t|x_{t-1})=N(x_t; √(1-β_t)·x_{t-1}, β_t·I),其中β_t是噪声调度(noise schedule),从β_1≈10^{-4}线性增长到β_T≈0.02。经过T步(通常T=1000),x_T近似服从N(0,I)。这个过程的优雅之处在于:给定x_0,可以直接采样任意时间步的x_t,而不需要逐步迭代——q(x_t|x_0)=N(x_t; √ᾱ_t·x_0, (1-ᾱ_t)·I),其中α_t=1-β_t,ᾱ_t=Πα_s。

大白话 扩散模型的前向过程就像"往一杯清水里一滴一滴加墨水"。刚加第一滴时,水还基本清澈(x_1≈x_0,噪声很少);加到第500滴时,水已经很浑浊了;加到第1000滴时,完全变成墨水(纯噪声)。神奇的是,数学家发现可以"跳步"——不需要一滴一滴地数,可以直接算"加500滴墨水后水有多黑",这个公式很简单:x_t = √ᾱ_t × 清水 + √(1-ᾱ_t) × 墨水。

为什么(原理):前向过程的设计遵循了"方差保持"(variance-preserving)原则——参数选择为√(1-β_t)和√β_t(而非1-β_t和β_t),使得每步的方差保持为1(如果x_0方差为1)。这使得噪声添加过程在数值上稳定,且x_T自然地收敛到N(0,I)。重参数化性质(可跳跃到任意t步)大大简化了训练——不需要模拟整个扩散过程,只需随机采样t和噪声即可构造训练样本。

import numpy as np

# 扩散模型的前向过程:逐步添加噪声
# 展示从清晰数据到纯噪声的转变

class ForwardDiffusion:
    def __init__(self, T=200):
        self.T = T
        # 噪声调度:β从0.0001线性增长到0.02
        self.betas = np.linspace(0.0001, 0.02, T)
        self.alphas = 1.0 - self.betas
        # ᾱ_t = α_1 · α_2 · ... · α_t(累积乘积)
        self.alpha_bars = np.cumprod(self.alphas)

    def forward_step(self, x_t_minus_1, t):
        """单步前向扩散:q(x_t | x_{t-1})"""
        beta_t = self.betas[t]
        noise = np.random.randn(*x_t_minus_1.shape)  # 标准高斯噪声
        x_t = np.sqrt(1 - beta_t) * x_t_minus_1 + np.sqrt(beta_t) * noise
        return x_t, noise

    def forward_direct(self, x_0, t):
        """直接采样 x_t(跳过中间步骤)"""
        alpha_bar_t = self.alpha_bars[t]
        noise = np.random.randn(*x_0.shape)
        x_t = np.sqrt(alpha_bar_t) * x_0 + np.sqrt(1 - alpha_bar_t) * noise
        return x_t, noise


diff = ForwardDiffusion(T=200)

# 创建示例数据(模拟图像的一个像素或一个特征)
np.random.seed(42)
x_0 = np.array([[[0.5, -0.3, 0.8]]])  # 1个样本,1个位置,3个特征

print("=== 扩散模型:前向扩散过程 ===\n")

print(f"初始数据 x_0: {x_0.flatten()}")
print(f"总步数 T: {diff.T}\n")

# 展示不同时间步的扩散效果
for t_fraction in [0, 0.1, 0.25, 0.5, 0.75, 1.0]:
    t = int(t_fraction * (diff.T - 1))
    x_t, noise = diff.forward_direct(x_0, t)
    alpha_bar = diff.alpha_bars[t]
    print(f"t={t:3d} (ᾱ={alpha_bar:.4f}): x_t = {np.round(x_t.flatten(), 3)}")

print("\n关键公式:x_t = √ᾱ_t · x_0 + √(1-ᾱ_t) · ε")
print("→ 直接采样的威力:不需要逐步模拟!")

# 验证:逐步模拟 vs 直接跳跃
print("\n\n=== 验证:逐步 vs 跳跃 ===")
np.random.seed(123)
x_step = x_0.copy()
for t in range(50):
    x_step, _ = diff.forward_step(x_step, t)
print(f"逐步50步:    {np.round(x_step.flatten(), 3)}")

np.random.seed(123)
# 重置随机种子使得噪声一致
x_direct, _ = diff.forward_direct(x_0, 49)
print(f"直接跳50步:  {np.round(x_direct.flatten(), 3)}")
print("→ 两者一致!这大大简化了训练")
前向扩散的核心公式\(q(x_t | x_0) = \mathcal{N}\left(x_t; \sqrt{\bar{\alpha}_t} \cdot x_0, (1 - \bar{\alpha}_t) \cdot I\right), \quad \bar{\alpha}_t = \prod_{s=1}^{t} \alpha_s, \quad \alpha_t = 1 - \beta_t\)
大白话 前向扩散最神奇的地方是"可以跳步"。你不需要"加第1滴→加第2滴→...→加第1000滴",而是可以直接用公式算"加500滴后是什么状态"。这个性质让训练变得非常简单——随机选一个时间步t,用公式算x_t,然后让神经网络猜"我加了什么噪声"。

什么用(应用):前向扩散的"直接采样"性质是扩散模型高效训练的基础。训练时,只需:①随机采样一个t~Uniform(1,T),②用公式计算x_t,③训练网络预测噪声ε。这使得每个训练步骤只需一次前向传播,不需要展开整个扩散链。DDPM使用T=1000,训练在ImageNet等大规模数据集上可行。

哪些坑(缺点):β_t的调度选择影响生成质量。线性调度(β从0.0001到0.02)简单但不一定最优——余弦调度(cosine schedule)在某些任务上效果更好,因为它在前几步保留更多信号。T的选择是速度-质量的权衡:T越大(如4000),生成质量越高但推理越慢;T越小(如200),速度快但质量略降。

二、逆向去噪:噪声到数据

是什么(定义):逆向去噪过程(Reverse Denoising Process)是扩散模型的"生成"部分。它从纯噪声x_T~N(0,I)开始,通过一个可学习的去噪网络ε_θ(x_t, t)逐步恢复数据。逆向转移概率为p_θ(x_{t-1}|x_t)=N(x_{t-1}; μ_θ(x_t,t), σ_t²I),其中μ_θ由去噪网络预测的噪声ε_θ(x_t,t)推导:μ_θ(x_t,t)=1/√α_t·(x_t - β_t/√(1-ᾱ_t)·ε_θ(x_t,t))。训练目标是让预测的噪声ε_θ(x_t,t)尽可能接近真实添加的噪声ε。

大白话 逆向去噪就像"从墨水中还原清水"。你有一杯纯墨水(纯噪声x_T),有一个"过滤器"(去噪网络),它能判断"这杯液体里有多少是墨水、有多少是水"。每次过滤,去掉一点点墨水,水就清一点点。经过1000次过滤,墨水完全去除,得到一杯清水——这杯清水就是生成的"新数据"。过滤器(网络)是怎么学会的?通过训练——给它看各种"半墨水半清水"的混合液体,告诉它"这里面有30%墨水",让它学会估计。

为什么(原理):逆向过程可以训练的根本原因是:给定x_0和x_t,反向条件分布q(x_{t-1}|x_t, x_0)也是高斯分布,其均值可以通过x_0、x_t和噪声调度参数解析计算。神经网络不需要直接学习整个分布,只需要学习预测"从x_0到x_t添加的噪声ε"——这是一个回归任务,比学习概率分布简单得多。DDPM的简化损失L_simple = E[||ε - ε_θ(x_t, t)||²],去掉了权重系数,实践证明效果更好。

import numpy as np

# 扩散模型的逆向过程:从噪声中逐步恢复数据
# 简化版——演示核心原理

class DenoisingNetwork:
    """简化的去噪网络:预测添加的噪声"""
    def __init__(self, d=3):
        np.random.seed(42)
        self.W = np.random.randn(d, d) * 0.3
        self.b = np.zeros(d)

    def predict_noise(self, x_t, t):
        """预测在时间步t添加的噪声ε"""
        # 实际中这是一个U-Net,输入x_t和时间嵌入
        # 这里简化为线性变换
        return x_t @ self.W + self.b


class ReverseDiffusion:
    def __init__(self, T=200):
        self.T = T
        self.betas = np.linspace(0.0001, 0.02, T)
        self.alphas = 1.0 - self.betas
        self.alpha_bars = np.cumprod(self.alphas)
        self.denoiser = DenoisingNetwork(d=3)

    def reverse_step(self, x_t, t):
        """单步逆向去噪:从 x_t 恢复到 x_{t-1}"""
        alpha_t = self.alphas[t]
        alpha_bar_t = self.alpha_bars[t]
        beta_t = self.betas[t]

        # 预测噪声
        eps_pred = self.denoiser.predict_noise(x_t, t)
        # 预测 x_0
        x_0_pred = (x_t - np.sqrt(1 - alpha_bar_t) * eps_pred) / np.sqrt(alpha_bar_t)
        # 计算均值
        coef1 = np.sqrt(alpha_bar_t) * beta_t / (1 - alpha_bar_t)
        # 添加采样噪声(除了最后一步)
        if t > 0:
            noise = np.random.randn(*x_t.shape) * np.sqrt(beta_t)
        else:
            noise = 0
        x_t_minus_1 = x_0_pred * coef1 + noise
        return x_t_minus_1

    def generate(self, shape, verbose=False):
        """从纯噪声生成新数据"""
        x_t = np.random.randn(*shape)  # x_T ~ N(0, I)
        if verbose:
            print(f"初始噪声 x_T:    {np.round(x_t.flatten(), 3)}")

        checkpoints = [200, 150, 100, 50, 0]
        for t in reversed(range(self.T)):
            x_t = self.reverse_step(x_t, t)
            if verbose and t in checkpoints:
                print(f"去噪后 x_{t:3d}:     {np.round(x_t.flatten(), 3)}")
        return x_t


diff = ReverseDiffusion(T=200)

print("=== 扩散模型:逆向去噪过程 ===\n")
print("从纯噪声逐步恢复到清晰数据:\n")

generated = diff.generate((1, 1, 3), verbose=True)

print(f"\n最终生成的数据: {np.round(generated.flatten(), 3)}")
print(f"\n过程:纯噪声(x_T) → 逐步去噪 → 清晰数据(x_0)")
print(f"核心:去噪网络学会从x_t中'剥离'噪声,恢复信号")
逆向去噪的训练目标\(L_{\text{simple}} = \mathbb{E}_{t, x_0, \varepsilon}\left[\|\varepsilon - \varepsilon_\theta(\sqrt{\bar{\alpha}_t} x_0 + \sqrt{1 - \bar{\alpha}_t} \varepsilon, t)\|^2\right]\)
大白话 训练扩散模型就是教网络"猜噪声"。给网络看"半清半噪"的图片,问它:"我加了多少噪声?"网络猜一个答案,和真实加的噪声比较,差距越小越好。反复练习后,网络成了"噪声专家"——任何清晰度的图片,它都能精确说出其中噪声的成分。生成时,从纯噪声开始,网络一次次"剥除"噪声,最终露出清晰的图片。

什么用(应用):逆向去噪的"预测噪声"框架是DDPM、DDIM、Score-based等所有扩散模型的共同范式。Stable Diffusion在此基础上引入文本条件(通过交叉注意力),使去噪过程受文本控制。图像编辑(如Inpainting、Super-Resolution)可以通过修改逆向过程的初始条件或中间步骤来实现。

哪些坑(缺点):逆向过程需要T步迭代(通常T=1000),推理速度慢是扩散模型的主要缺点。DDIM通过确定性采样将步数从1000减少到50-100步;蒸馏方法(如Progressive Distillation)可将步数减少到1-4步。另一个挑战是去噪网络的规模——U-Net通常有数亿参数,训练和推理资源需求大。

三、DDPM的完整流程

是什么(定义):DDPM(Denoising Diffusion Probabilistic Models)由Ho等人于2020年提出,是扩散模型的标准实现。DDPM的完整流程包括:①训练——从数据采样x_0,采样t和ε,计算x_t,用去噪网络预测ε,最小化MSE损失;②生成——从x_T~N(0,I)开始,逐步应用逆向转移(DDPM采样器),经过T步得到x_0。DDPM的逆向采样包含一个随机项(添加少量噪声),使其具有多样性。

大白话 DDPM就是"训练一个去噪专家,用它逐步清理噪声"。去噪网络通常是U-Net——一种编码器-解码器结构的卷积网络,特别擅长处理图像去噪任务。U-Net的编码器逐步下采样提取特征,解码器逐步上采样恢复细节,中间有跳跃连接(skip connection)保留高频信息。

为什么(原理):DDPM选择U-Net作为去噪网络的原因:①U-Net的多尺度结构适合处理不同分辨率的噪声模式;②跳跃连接保留了图像的高频细节(纹理、边缘),这对去噪至关重要;③时间步t通过位置编码注入U-Net的每一层,使网络知道当前处于扩散的哪个阶段。DDPM的逆向采样在每一步都添加少量随机噪声(方差σ_t²),这增加了生成样本的多样性,但也导致了推理的随机性。

import numpy as np

# DDPM完整流程演示
# 对比DDPM和DDIM的采样策略

class DDPMPipeline:
    def __init__(self, T=200):
        self.T = T
        self.betas = np.linspace(0.0001, 0.02, T)
        self.alphas = 1.0 - self.betas
        self.alpha_bars = np.cumprod(self.alphas)

    def ddpm_sampling_step(self, x_t, t, eps_pred):
        """DDPM采样步骤(含随机噪声)"""
        alpha_t = self.alphas[t]
        alpha_bar_t = self.alpha_bars[t]
        beta_t = self.betas[t]

        # 预测x_0
        x_0_pred = (x_t - np.sqrt(1 - alpha_bar_t) * eps_pred) / np.sqrt(alpha_bar_t)

        # DDPM均值
        coef_x0 = np.sqrt(alpha_bar_t) * beta_t / (1 - alpha_bar_t)
        coef_xt = alpha_t * np.sqrt(1 - alpha_bar_t) / (1 - alpha_bar_t)

        mean = coef_x0 * x_0_pred + coef_xt * x_t

        # 添加随机噪声(DDPM的关键——确保多样性)
        if t > 0:
            noise = np.random.randn(*x_t.shape) * np.sqrt(beta_t)
            x_t_minus_1 = mean + noise
        else:
            x_t_minus_1 = mean

        return x_t_minus_1

    def ddim_sampling_step(self, x_t, t, eps_pred, eta=0.0):
        """DDIM采样步骤(eta=0时为确定性采样)"""
        alpha_bar_t = self.alpha_bars[t]
        alpha_bar_prev = self.alpha_bars[t - 1] if t > 0 else 1.0

        # 预测x_0
        x_0_pred = (x_t - np.sqrt(1 - alpha_bar_t) * eps_pred) / np.sqrt(alpha_bar_t)

        # 确定性分量
        pred_dir = np.sqrt(1 - alpha_bar_prev) * eps_pred

        # 随机分量(eta=0为确定性DDIM)
        if eta > 0 and t > 0:
            sigma = eta * np.sqrt((1 - alpha_bar_prev) / (1 - alpha_bar_t) * (1 - alpha_bar_t / alpha_bar_prev))
            noise = np.random.randn(*x_t.shape) * sigma
        else:
            noise = 0

        x_t_minus_1 = np.sqrt(alpha_bar_prev) * x_0_pred + pred_dir + noise
        return x_t_minus_1


print("=== DDPM vs DDIM 采样策略 ===\n")

print("DDPM采样:")
print("  - 每步添加随机噪声 → 生成具有随机性")
print("  - 需要T=1000步 → 推理慢")
print("  - 多次生成同一噪声得到不同结果")

print("\nDDIM采样(eta=0,确定性):")
print("  - 不添加随机噪声 → 生成确定")
print("  - 可以跳步(如每20步采样1次) → 推理快50倍")
print("  - 同一噪声输入 → 同一输出")

print("\n两者关系:")
print("  - DDPM是DDIM在eta=1时的特例")
print("  - DDIM通过调节eta平衡速度和质量")
print("  - eta=0:最快(50步),eta=1:最好质量(1000步)")
DDPM的逆向采样公式\(x_{t-1} = \frac{1}{\sqrt{\alpha_t}}\left(x_t - \frac{\beta_t}{\sqrt{1 - \bar{\alpha}_t}} \varepsilon_\theta(x_t, t)\right) + \sigma_t z, \quad z \sim \mathcal{N}(0, I)\)
大白话 DDPM采样就一步:"先猜加了多少噪声,然后减掉,最后加点新噪声增加随机性"。猜噪声(ε_θ(x_t,t))是最关键的部分——猜得越好,去得越干净。加新噪声是为了增加多样性——如果不加(DDIM模式),生成就是确定性的,失去了"创造性"。

什么用(应用):DDPM/DDIM是Stable Diffusion等主流生成工具的基础。DDIM的跳步采样(如50步而非1000步)使扩散模型推理变得实用。Stable Diffusion在潜在空间(Latent Space)而非像素空间进行扩散,进一步加速了过程。ControlNet通过调节去噪过程实现精确控制。

哪些坑(缺点):DDPM采样步数多、推理速度慢;DDIM虽然快但生成质量略降。扩散模型对噪声调度敏感——β_t的选择影响生成细节和多样性。大模型去噪网络的训练需要大量GPU资源(Stable Diffusion在256块A100上训练)。

概念关系图谱

概念核心含义与AI的关系关联概念
前向扩散逐步向数据加噪直至纯噪声数据破坏过程,训练信号来源马尔可夫链、噪声调度
逆向去噪训练网络从噪声恢复数据生成过程,扩散模型的核心去噪网络、U-Net
DDPM去噪扩散概率模型扩散模型的标准实现变分推断、ELBO
DDIM去噪扩散隐式模型确定性采样,加速推理跳步采样、ODE
噪声调度(β_t)控制每步添加噪声量的序列影响生成质量和训练稳定性线性调度、余弦调度
U-Net编码器-解码器结构的CNN扩散模型的标准去噪网络跳跃连接、时间嵌入

重点答疑

Q1: 扩散模型和VAE的本质区别是什么?

VAE学习数据的潜在空间分布,生成时从潜在空间采样并解码。扩散模型不学习显式的潜在空间,而是学习"逐步去噪"的过程。VAE一次前向传播即可生成(编码→采样→解码),扩散模型需要T步迭代。VAE样本质感通常较模糊(KL散度的平均化效应),扩散模型样本质感更锐利(无需KL散度)。扩散模型训练更稳定(简单回归任务vs概率建模),但推理更慢。

Q2: 为什么扩散模型的前向过程是固定的(不需要学习)?

前向过程只是"按照预定公式加噪声"——这是一个纯数学操作,没有需要学习的参数。如果把前向过程也做成可学习的,等于增加了一个额外的生成器,回到了"两个网络对抗"的范式,失去了扩散模型训练稳定的优势。固定的前向过程确保了训练信号的"纯净性"——网络只需要学一个任务(预测噪声)。

Q3: DDIM为什么可以跳步采样(如50步而非1000步)?

DDIM将逆向过程重新解释为求解一个常微分方程(ODE)——从x_T到x_0的确定性轨迹。在ODE框架下,可以用更大的步长(如从T=1000跳到T=500,相当于步长翻倍)来近似求解,类似于用更大的步长解微分方程。步长越大,近似误差越大(生成质量下降),但速度越快。eta参数控制"确定性vs随机性"——eta=0为纯ODE(最快),eta=1回到DDPM。

章节单词汇总

英文音标术语/释义
Diffusion Model/dɪˈfjuːʒən ˈmɑːdəl/扩散模型,通过加噪-去噪实现生成
Forward Process/ˈfɔːrwərd ˈproʊses/前向过程,逐步向数据添加噪声
Reverse Process/rɪˈvɜːrs ˈproʊses/逆向过程,训练网络从噪声恢复数据
DDPM/diː diː piː em/去噪扩散概率模型
DDIM/diː diː aɪ em/去噪扩散隐式模型,加速采样
Noise Schedule/nɔɪz ˈskedʒuːl/噪声调度,β_t序列控制扩散速度
U-Net/juː net/U形网络,扩散模型的标准去噪架构
Markov Chain/ˈmɑːrkɑːv tʃeɪn/马尔可夫链,扩散过程的数学描述

面试练习

Q1 [单选] 扩散模型的前向过程中,数据最终变成什么?

  • A. 全零向量
  • B. 标准高斯噪声 N(0, I)
  • C. 低维潜在编码
  • D. 离散的token序列
解答:前向扩散经过T步后,x_T近似服从N(0,I)(标准高斯分布)。这是因为每步乘√(1-β_t)衰减信号,加√β_t的噪声,T步后信号完全被噪声淹没。

Q2 [单选] 扩散模型的逆向过程中,神经网络预测的是什么?

  • A. 原始数据x_0
  • B. 添加的噪声ε
  • C. 数据分布概率
  • D. 分类标签
解答:去噪网络ε_θ(x_t, t)预测的是前向过程中添加的噪声ε。预测噪声后,可以通过x_0 = (x_t - √(1-ᾱ_t)·ε_θ)/√ᾱ_t恢复原始数据。

Q3 [单选] DDIM相比DDPM的主要优势是什么?

  • A. 更高的生成质量
  • B. 更快的推理速度(可跳步采样)
  • C. 更少的参数
  • D. 更容易训练
解答:DDIM通过确定性ODE框架实现跳步采样(如50步vs DDPM的1000步),大幅提升推理速度。生成质量可能略有下降,但速度提升显著(20-50倍)。

Q4 [多选] 关于扩散模型,以下哪些说法是正确的?

  • A. 前向过程是固定的马尔可夫链
  • B. 可以直接采样任意时间步的x_t
  • C. 训练目标是最小化预测噪声与真实噪声的MSE
  • D. 生成过程只需1步前向传播
  • E. DDPM的逆向采样包含随机噪声项
解答:前向过程固定、可直接跳步采样、训练目标为MSE、DDPM采样含随机噪声。生成过程需要T步迭代(通常1000或50步),不是1步。

Q5 [单选] 扩散模型的标准去噪网络架构是什么?

  • A. ResNet
  • B. Transformer
  • C. U-Net
  • D. MLP
解答:U-Net是扩散模型的标准去噪网络,其编码器-解码器结构+跳跃连接特别适合图像去噪任务。时间步t通过嵌入注入各层。