
扩散模型Diffusion Model是近年来生成式深度学习中非常重要的一类模型。与生成对抗网络GAN通过“生成器—判别器”的对抗训练生成样本不同扩散模型采用另一种思路先把真实数据逐步加噪声直到它接近纯随机噪声再训练神经网络学习反向去噪过程从随机噪声一步步还原出清晰样本。扩散模型的核心思想可以概括为• 前向过程把真实数据逐步加噪变成接近纯噪声的数据• 反向过程训练模型逐步去噪从噪声还原出真实样本例如在图像生成任务中扩散模型可以从一张随机噪声图开始经过多步去噪逐渐生成一张清晰图像。文本生成图像、图像编辑、图像修复、超分辨率等任务都可以在扩散模型框架下实现。一、为什么需要扩散模型生成模型的目标是学习真实数据分布并生成新的样本。在 GAN 中生成器直接把随机噪声映射为图像其中• z 表示随机噪声• G 表示生成器• x̃ 表示生成样本这种方式很直接但训练中需要同时平衡生成器和判别器。若判别器过强生成器可能难以学习若生成器只学会少数典型样本又可能出现模式崩塌。图 1从 GAN 到扩散模型扩散模型换了一种思路。它不要求模型一步从噪声生成完整图像而是把生成过程拆成很多小步骤纯噪声 → 稍微清晰一点 → 更清晰一点 → 接近真实图像 → 清晰图像从直观角度看扩散模型更像是在学习如何一步一步把噪声擦掉。这种思想带来几个好处• 训练目标相对稳定• 不需要判别器进行对抗训练• 生成样本多样性较好• 可以自然支持图像编辑、修复和条件生成• 生成过程具有较清晰的概率建模解释当然扩散模型也有代价。由于生成时通常需要多步迭代去噪采样速度往往比 GAN 慢。因此如何提高采样效率是扩散模型的重要研究方向之一。二、扩散模型的基本结构扩散模型通常包含两个过程• 前向扩散过程• 反向去噪过程图 2扩散模型的基本结构1、前向扩散逐步加入噪声前向扩散过程从真实样本 x₀ 开始逐步向其中加入高斯噪声得到一系列越来越模糊、越来越接近纯噪声的中间状态x₀ → x₁ → x₂ → ... → xₜ → ... → x_T其中• x₀ 表示真实数据例如一张真实图像• xₜ 表示第 t 步加噪后的数据• x_T 表示接近纯噪声的数据• T 表示总扩散步数在图像任务中x₀ 是清晰图像随着 t 增大图像逐渐被噪声覆盖当 t 足够大时x_T 看起来几乎就是随机噪声。可以简单理解为前向扩散 有控制地破坏数据2、反向去噪逐步还原数据反向去噪过程则从随机噪声 x_T 开始逐步去除噪声最终生成清晰样本x_T → x_{T-1} → ... → xₜ → ... → x₁ → x₀这个反向过程不是人工写死的而是由神经网络学习出来的。模型在训练时学习给定带噪图像 xₜ 和时间步 t预测其中的噪声成分 然后在生成时根据预测噪声一步步修正图像使它越来越接近真实数据。可以简单理解为反向去噪 学会逐步修复数据3、噪声预测网络扩散模型中常用一个神经网络来预测噪声。这个网络通常记为其中• εθ 表示由参数 θ 控制的噪声预测网络• xₜ 表示第 t 步的带噪数据• t 表示时间步• εθ(xₜ,t) 表示模型预测出的噪声在图像扩散模型中噪声预测网络常用 U-Net 结构。U-Net 能够同时利用局部细节和多尺度语义信息因此非常适合图像去噪任务。不过为了理解扩散模型的基本原理本文后面的 PyTorch 示例会使用一个简化网络完成 MNIST 风格图像去噪与生成演示。三、前向加噪过程扩散模型的前向过程是一个固定的、无需学习的加噪过程。它按照预设的噪声强度表在每一步向数据中加入少量噪声。图 3前向扩散过程从清晰图像到纯噪声常见写法是其中• q(xₜ|xₜ₋₁) 表示从 xₜ₋₁ 生成 xₜ 的前向加噪分布• βₜ 表示第 t 步加入噪声的强度• I 表示单位矩阵• N 表示高斯分布• √(1 − βₜ)xₜ₋₁ 表示保留一部分上一时刻的数据• βₜI 表示加入的高斯噪声方差从直观角度看每一步都做两件事• 保留一部分原始信息• 加入一部分随机噪声为了简化公式通常定义并定义累计乘积其中• αₜ 表示第 t 步保留信号的比例• ᾱₜ 表示从第 1 步到第 t 步累计保留的信号比例• ∏ 表示连乘一个非常重要的性质是可以直接从 x₀ 采样得到任意时间步的 xₜ而不必真的一步一步加噪。公式为其中• x₀ 表示原始真实样本• xₜ 表示第 t 步的带噪样本• ᾱₜ 表示累计保留信号比例• ε 表示从标准正态分布采样的噪声• √ᾱₜx₀ 表示保留下来的原始信号• √(1 − ᾱₜ)ε 表示加入的噪声部分这条公式非常关键。它说明训练时可以随机选择一个时间步 t直接把真实图像 x₀ 加噪成 xₜ然后让模型学习预测其中的噪声 ε。四、反向去噪过程前向过程是已知的加噪过程反向过程则是需要学习的去噪过程。图 4反向去噪过程从随机噪声到清晰图像理想情况下如果我们知道每一步如何从 xₜ 还原到 xₜ₋₁就可以从纯噪声 x_T 开始一步步生成清晰样本。反向过程可以写成其中• pθ(xₜ₋₁|xₜ) 表示模型学习到的反向去噪分布• θ 表示神经网络参数• xₜ 表示当前带噪样本• xₜ₋₁ 表示去噪一步后的样本实际训练中常见做法不是让模型直接预测 xₜ₋₁而是让模型预测前向过程中加入的噪声 ε其中• ε 表示真实加入的噪声• εθ(xₜ,t) 表示模型预测的噪声• ≈ 表示希望二者尽量接近为什么预测噪声有用因为如果模型知道 xₜ 中有多少噪声就可以从 xₜ 中减去这部分噪声得到更干净的样本估计。可以简单理解为模型不是直接画出图像而是学习判断“这张图里哪些部分是噪声”。生成时模型会重复执行预测噪声 → 去掉一部分噪声 → 得到更清晰的图像直到从 x_T 逐步得到 x₀。五、扩散模型的训练目标扩散模型的训练目标可以非常简洁地表达为让模型预测的噪声接近真实加入的噪声。图 5扩散模型的训练目标预测噪声训练时从真实图像 x₀ 中采样一个批次再随机采样时间步 t 和噪声 ε构造带噪图像 xₜ然后让模型预测噪声其中• ε̂ 表示模型预测的噪声• ε 表示真实加入的噪声• εθ 表示噪声预测网络常用损失函数是均方误差其中• L 表示训练损失• E 表示对样本、时间步和噪声取期望• ε 表示真实噪声• εθ(xₜ,t) 表示模型预测噪声• || · ||² 表示平方误差从机器学习角度看扩散模型训练本质上是一个监督学习问题• 输入带噪图像 xₜ 和时间步 t• 目标真实噪声 ε• 模型预测噪声 εθ(xₜ,t)• 损失预测噪声与真实噪声之间的均方误差这也是扩散模型训练相对稳定的重要原因它不像 GAN 那样需要同时平衡生成器和判别器而是直接优化一个噪声预测目标。六、扩散模型的采样过程训练完成后扩散模型可以从随机噪声开始生成样本。图 6扩散模型的采样过程采样过程大致如下1. 从标准正态分布采样 x_T2. 从 t T 开始倒序迭代3. 用模型预测当前图像中的噪声4. 根据预测噪声计算 x_{t-1}5. 重复直到得到 x_0可以用伪代码表示为x torch.randn(shape) for t in reversed(range(T)): predicted_noise model(x, t) x denoise_step(x, predicted_noise, t)其中• x 初始为随机噪声• model(x,t) 表示噪声预测网络• denoise_step 表示一次反向去噪更新• T 表示扩散步数扩散模型生成图像的过程通常是逐步细化的纯噪声→ 模糊轮廓→ 粗略结构→ 局部细节→ 清晰图像这与 GAN 的“一步生成”不同。GAN 通常直接从 z 输出图像而扩散模型通常需要多步采样。因此扩散模型的主要优势和代价也很清楚• 优势训练稳定生成质量高多样性好• 代价采样通常较慢需要多步迭代七、PyTorch 实现简化版扩散模型生成手写数字下面使用 PyTorch 构建一个简化版扩散模型用于理解扩散模型的基本训练流程。图 7扩散模型生成手写数字的训练与采样流程为了突出核心思想示例使用 MNIST 手写数字数据集并用一个简单的卷积网络预测噪声。真实高质量扩散模型通常会使用 U-Net、注意力机制和更复杂的噪声调度方法。1、导入库# 导入 PyTorch 核心模块import torchimport torch.nn as nn # 神经网络层和损失函数import torch.optim as optim # 优化器SGD, Adam等import matplotlib.pyplot as plt # 可视化显示图像 from torch.utils.data import DataLoader # 批量数据加载器from torchvision import datasets, transforms # 常用数据集和图像预处理这里使用• DataLoader 按批量读取数据• torchvision.datasets 加载 MNIST 数据集• torchvision.transforms 处理图像2、设置超参数# 设置训练设备GPU优先device torch.device(cuda if torch.cuda.is_available() else cpu) image_size 28 # 图像尺寸MNIST 28x28batch_size 128 # 批量大小num_epochs 5 # 训练轮数learning_rate 1e-3 # 学习率 T 200 # 扩散模型的总时间步数噪声调度步数其中T 表示扩散总步数。为了让示例运行更轻量这里使用 T 200。真实扩散模型中扩散步数可能更大。3、准备 MNIST 数据集# 图像预处理转为张量并标准化到 [-1, 1] 范围transform transforms.Compose([ transforms.ToTensor(), # 将 PIL 图像或 numpy 数组转为 (C,H,W) 张量值域 [0,1] transforms.Normalize((0.5,), (0.5,)) # 归一化 (x - 0.5) / 0.5 → 值域 [-1,1]]) # 加载 MNIST 训练集60000 张手写数字train_dataset datasets.MNIST( root./data, # 数据集存放目录 trainTrue, # 加载训练集 downloadTrue, # 若本地无则下载 transformtransform # 应用预处理) # 创建数据加载器批量加载、打乱顺序train_loader DataLoader( train_dataset, batch_sizebatch_size, # 每批样本数 shuffleTrue # 每个 epoch 打乱顺序)这里把 MNIST 图像归一化到大致 −1 到 1 的范围便于和高斯噪声配合。4、定义噪声调度表扩散模型需要为每个时间步设置噪声强度 βₜ。这里使用简单的线性调度# 扩散模型的噪声调度参数beta_start 1e-4 # 初始噪声水平beta_end 0.02 # 最终噪声水平 # 线性插值生成 T 个 beta 值从 beta_start 到 beta_endbetas torch.linspace(beta_start, beta_end, T).to(device) # 计算 alpha 1 - betaalphas 1.0 - betas# 累积乘积alpha_bar_t ∏_{s1}^t alpha_salpha_bars torch.cumprod(alphas, dim0)其中• betas 表示每一步加入噪声的强度• alphas 表示每一步保留信号的比例• alpha_bars 表示累计保留信号比例对应前文公式中的5、实现前向加噪函数根据公式实现前向加噪函数def add_noise(x0, t): 前向扩散根据时间步 t 对原始图像 x0 添加噪声得到带噪图像 xt 和噪声 noise noise torch.randn_like(x0) # 生成标准高斯噪声 # 取出对应时间步的 alpha_bar 值并调整形状以便广播batch, 1, 1, 1 alpha_bar_t alpha_bars[t].view(-1, 1, 1, 1) # 重参数化采样xt sqrt(alpha_bar_t) * x0 sqrt(1 - alpha_bar_t) * noise xt torch.sqrt(alpha_bar_t) * x0 torch.sqrt(1 - alpha_bar_t) * noise return xt, noise其中• x0 表示真实图像• t 表示每张图像对应的随机时间步• noise 表示真实加入的噪声• xt 表示加噪后的图像这里的 t 是一个批量向量因此 alpha_bars[t] 会为每个样本取出对应时间步的 ᾱₜ。6、定义时间嵌入噪声预测网络不仅需要看到带噪图像 xₜ还需要知道当前时间步 t。因为不同 t 对应不同噪声强度。为了简化实现可以使用一个嵌入层把时间步 t 转换为向量# 时间步嵌入层将离散的时间步 t 映射为稠密向量class TimeEmbedding(nn.Module): def __init__(self, T, embed_dim): super().__init__() # 嵌入层T 个时间步每个映射为 embed_dim 维向量 self.embedding nn.Embedding(T, embed_dim) def forward(self, t): # t: 形状 (batch,) 的时间步索引 return self.embedding(t) # 输出形状 (batch, embed_dim)其中• T 表示时间步总数• embed_dim 表示时间嵌入维度• embedding(t) 把整数时间步转换为向量真实扩散模型中常用正弦时间嵌入或更复杂的时间编码方式。这里使用 nn.Embedding 是为了方便初学者理解。7、定义噪声预测网络下面定义一个简化卷积网络用于预测噪声# 简单去噪网络接收带噪图像 x 和时间步 t预测添加的噪声class SimpleDenoiseNet(nn.Module): def __init__(self, T, time_dim32): super().__init__() # 时间步嵌入层将 t 映射为 time_dim 维向量 self.time_embed TimeEmbedding(T, time_dim) # 卷积层输入通道 图像通道(1) 时间嵌入通道(time_dim) self.conv1 nn.Conv2d(1 time_dim, 64, kernel_size3, padding1) self.conv2 nn.Conv2d(64, 64, kernel_size3, padding1) self.conv3 nn.Conv2d(64, 1, kernel_size3, padding1) self.act nn.ReLU() def forward(self, x, t): batch_size, _, height, width x.shape # 获取时间嵌入并调整形状以拼接至空间维度 t_embed self.time_embed(t) # (batch, time_dim) t_embed t_embed.view(batch_size, -1, 1, 1) # (batch, time_dim, 1, 1) t_embed t_embed.expand(-1, -1, height, width) # 扩展到 (batch, time_dim, h, w) # 沿通道维度拼接图像和时间嵌入 x torch.cat([x, t_embed], dim1) # (batch, 1time_dim, h, w) # 通过两个卷积层ReLU激活 x self.act(self.conv1(x)) x self.act(self.conv2(x)) # 输出层预测噪声 x self.conv3(x) return x这个网络的输入包括• 带噪图像 xₜ• 时间步 t 的嵌入表示输出是与 xₜ 形状相同的噪声预测图。也就是说模型要为每个像素位置预测噪声值。创建模型# 实例化去噪网络使用默认时间嵌入维度32并移动到设备model SimpleDenoiseNet(TT).to(device) # Adam 优化器优化模型所有参数optimizer optim.Adam(model.parameters(), lrlearning_rate) # 均方误差损失用于预测噪声与真实噪声之间的差异criterion nn.MSELoss()这里使用 MSELoss因为训练目标是让预测噪声接近真实噪声。8、训练扩散模型训练流程如下# 训练扩散模型学习预测添加的噪声for epoch in range(num_epochs): model.train() total_loss 0.0 for images, _ in train_loader: # 批量加载真实图像 images images.to(device) # 移至设备 batch_size_current images.size(0) # 随机采样每个样本的时间步 t ∈ [0, T-1] t torch.randint( low0, highT, size(batch_size_current,), devicedevice ) # 前向扩散为图像添加对应时间步的噪声 xt, noise add_noise(images, t) # 预测噪声 predicted_noise model(xt, t) # 计算预测噪声与真实噪声的均方误差 loss criterion(predicted_noise, noise) # 反向传播更新参数 optimizer.zero_grad() loss.backward() optimizer.step() total_loss loss.item() * batch_size_current avg_loss total_loss / len(train_loader.dataset) print(fEpoch [{epoch 1}/{num_epochs}], Loss: {avg_loss:.4f})这段代码体现了扩散模型训练的核心闭环真实图像 → 随机时间步 → 加噪得到 xₜ → 模型预测噪声 → 计算 MSE → 反向传播 → 更新参数其中• t 是随机采样的时间步• add_noise() 负责构造带噪图像• model(xt,t) 预测噪声• loss 衡量预测噪声和真实噪声之间的差距9、简化采样函数训练完成后可以从随机噪声开始逐步去噪。下面给出一个简化采样函数torch.no_grad()def sample(model, num_samples16): 从训练好的扩散模型中采样生成新图像 model.eval() # 从标准高斯分布采样初始噪声T 步后的纯噪声 x torch.randn(num_samples, 1, image_size, image_size).to(device) # 逆向去噪过程从 t T-1 到 0 for t_value in reversed(range(T)): # 构造当前时间步张量形状 (num_samples,) t torch.full((num_samples,), t_value, devicedevice, dtypetorch.long) # 预测当前步的噪声 predicted_noise model(x, t) # 获取当前步的噪声调度参数 beta_t betas[t_value] alpha_t alphas[t_value] alpha_bar_t alpha_bars[t_value] # 除最后一步外添加随机噪声最后一步不加 if t_value 0: noise torch.randn_like(x) else: noise torch.zeros_like(x) # DDPM 采样更新公式 # x_{t-1} 1/√α_t * (x_t - β_t/√(1-ᾱ_t) * ε_θ) √β_t * z x ( 1 / torch.sqrt(alpha_t) * ( x - (beta_t / torch.sqrt(1 - alpha_bar_t)) * predicted_noise ) torch.sqrt(beta_t) * noise ) # 将像素值从 [-1,1] 恢复到 [0,1] 范围便于可视化 x (x.clamp(-1, 1) 1) / 2 return x这个采样过程从随机噪声开始按时间步倒序更新。每一步都用模型预测噪声并根据预测结果修正当前图像。需要注意这是教学版简化实现目的是帮助理解扩散模型的基本流程。真实高质量扩散模型会使用更精细的反向方差设置、噪声调度、U-Net 结构和更复杂的采样器。10、生成并显示图像# 从模型中采样生成 16 张图像samples sample(model, num_samples16) # 创建 4x4 子图网格fig, axes plt.subplots(4, 4, figsize(6, 6)) # 遍历每个子图显示生成的图像for i, ax in enumerate(axes.flat): # 移除通道维度转为 numpy显示灰度图 ax.imshow(samples[i].cpu().squeeze(), cmapgray) ax.axis(off) # 隐藏坐标轴 plt.show() # 展示图像如果模型训练足够生成图像会逐渐呈现 MNIST 手写数字的形状。八、扩散模型的适用场景、局限与扩展方向扩散模型是当前生成式深度学习中的重要模型之一尤其在图像生成、图像编辑和多模态生成任务中具有重要影响。图 8扩散模型的适用场景、局限与扩展方向1、适用场景扩散模型常用于• 图像生成• 文本生成图像• 图像修复• 图像超分辨率• 图像编辑• 风格迁移• 视频生成• 三维内容生成其中文本生成图像是扩散模型最具代表性的应用方向之一。模型可以在文本条件的引导下从噪声逐步生成符合描述的图像。2、主要优势扩散模型的主要优势包括• 训练过程相对稳定• 生成样本质量高• 样本多样性较好• 不容易出现 GAN 式模式崩塌• 易于结合条件信息进行可控生成• 具有较清晰的逐步去噪解释扩散模型的思想非常直观先学习如何去噪再通过不断去噪完成生成。3、主要局限扩散模型也有明显局限• 采样通常较慢• 训练和推理计算成本较高• 高质量图像生成通常需要复杂网络结构• 对噪声调度、采样器和条件控制方式较敏感• 生成过程多步迭代不如 GAN 一步生成直接因此扩散模型虽然生成质量很强但在实际应用中仍然需要考虑效率、成本和部署难度。4、扩展方向从基础扩散模型出发可以继续学习• DDPM去噪扩散概率模型• DDIM更高效的确定性采样方法• Latent Diffusion在潜空间中进行扩散降低计算成本• Stable Diffusion基于潜空间扩散的大规模文本生成图像模型• Classifier Guidance用分类器引导生成方向• Classifier-Free Guidance无需单独分类器的条件引导方法• ControlNet通过边缘、姿态、深度图等条件控制生成结果这些模型和技术都可以从一个基本问题出发理解如何更高效、更可控地从噪声生成高质量数据 小结扩散模型通过“前向加噪—反向去噪”的方式学习数据生成过程。训练时模型学习预测加入到真实样本中的噪声生成时模型从随机噪声出发逐步去噪得到清晰样本。扩散模型训练稳定、生成质量高、多样性好是理解现代图像生成和多模态生成模型的重要基础。“点赞有美意赞赏是鼓励”