跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
PythonAI算法

扩散模型(Diffusion Model)原理与图像生成实战

扩散模型利用前向加噪与反向去噪的马尔可夫链机制实现稳定图像生成。文章解析 DDPM 数学原理,提供基于 PyTorch 的噪声预测网络构建与训练代码,演示 MNIST 手写数字生成全流程。包含余弦噪声调度及分类引导等优化技巧,并简述 Stable Diffusion 等主流变体应用。

PgDevote发布于 2026/3/24更新于 2026/5/88 浏览
扩散模型(Diffusion Model)原理与图像生成实战

扩散模型(Diffusion Model)原理与图像生成实战

扩散模型示意图

为什么需要扩散模型

传统的生成对抗网络(GAN)虽然经典,但训练过程往往不稳定,容易出现模式崩溃。相比之下,扩散模型通过逐步添加噪声和逐步去除噪声的双向过程,实现了更稳定的训练和高质量的生成效果。它的灵感来源于非平衡热力学,核心在于将复杂的生成问题拆解为多个简单的马尔可夫链步骤。

在图像生成、文本生成及语音合成等领域,扩散模型的表现已经超越了传统方法。本文将带你深入理解其数学原理,并用 PyTorch 从零搭建一个 DDPM 模型,完成手写数字的生成任务。

核心思想:前向与反向扩散

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

  1. 前向扩散过程:从真实数据出发,逐步向数据中添加高斯噪声。经过 T 步后,数据会变成完全随机的噪声。
  2. 反向扩散过程:从随机噪声出发,训练一个神经网络逐步去除噪声。经过 T 步后,噪声会还原为真实的数据分布。

整个过程遵循马尔可夫链假设,即每一步的状态只与前一步有关。

前向扩散过程详解

前向扩散是一个固定的、无需训练的过程。目标是通过逐步添加噪声,将真实图像 $x_0$ 转换为随机噪声 $x_T$。

数学原理

每一步按照以下公式添加噪声:

$$ x_t = \sqrt{\alpha_t} x_{t-1} + \sqrt{1 - \alpha_t} \epsilon_t $$

其中 $x_t$ 是第 t 步添加噪声后的图像,$\alpha_t$ 是预先设定的噪声系数($0 < \alpha_t < 1$),$\epsilon_t$ 服从标准正态分布。

为了计算方便,通常定义累计乘积系数:

$$ \bar{\alpha}t = \prod{i=1}^t \alpha_i $$

利用累计系数,可以直接从 $x_0$ 计算出任意步的 $x_t$:

$$ x_t = \sqrt{\bar{\alpha}_t} x_0 + \sqrt{1 - \bar{\alpha}_t} \epsilon $$

注意:前向扩散的步数 T 是一个超参数。T 越大,前向扩散越充分,反向扩散的效果越好,但训练和生成的时间也会越长。

代码实现

这里我们使用 PyTorch 来模拟前向扩散过程。首先定义超参数并生成 beta 序列。

import torch
import numpy as np
import matplotlib.pyplot as plt
from torchvision.datasets import MNIST
from torchvision.transforms import ToTensor

# 定义扩散过程的超参数
T = 1000  # 扩散步数
beta_start = 0.0001
beta_end = 0.02

# 生成线性变化的 beta 序列
beta = torch.linspace(beta_start, beta_end, T)
alpha =  - beta
alpha_bar = torch.cumprod(alpha, dim=)  

 ():
    
    
    eps = torch.randn_like(x0).to(device)
    
    alpha_bar_t = alpha_bar[t].reshape(-, , , ).to(device)
    
    xt = torch.sqrt(alpha_bar_t) * x0 + torch.sqrt( - alpha_bar_t) * eps
     xt, eps



dataset = MNIST(root=, train=, download=, transform=ToTensor())
x0, _ = dataset[]
x0 = x0.unsqueeze()  

device = torch.device(  torch.cuda.is_available()  )


plt.figure(figsize=(, ))
 i, t  ([, , , , , ]):
    xt, _ = forward_diffusion(x0, torch.tensor([t]), device)
    xt = xt.squeeze().cpu().detach().numpy()
    plt.subplot(, , i + )
    plt.imshow(xt, cmap=)
    plt.title()
    plt.axis()
plt.show()
1
0
# 累计乘积
def
forward_diffusion
x0, t, device
""" x0: 原始图像 (batch_size, channels, height, width) t: 扩散步数 (batch_size,) """
# 生成高斯噪声
# 获取累计系数
1
1
1
1
# 计算 xt
1
return
# 测试前向扩散过程
# 加载 MNIST 数据集的一张图像作为示例
'./data'
True
True
0
0
# 增加 batch 维度 (1, 1, 28, 28)
'cuda'
if
else
'cpu'
# 可视化不同步数的扩散效果
15
3
for
in
enumerate
0
100
200
500
800
999
1
6
1
'gray'
f't={t}'
'off'

反向扩散过程与模型训练

反向扩散是前向扩散的逆过程。目标是训练一个神经网络 $\epsilon_\theta$,从 $x_t$ 中预测出添加的噪声 $\epsilon$,然后逐步去除噪声,还原出 $x_0$。

数学原理

反向扩散的核心公式为:

$$ p_\theta(x_{t-1}|x_t) = \mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \Sigma_\theta(x_t, t)) $$

其中均值 $\mu_\theta$ 由神经网络预测的噪声计算得到,方差 $\Sigma_\theta$ 通常设置为固定值。

训练的目标是最小化预测噪声和真实噪声之间的均方误差:

$$ L(\theta) = \mathbb{E}{x_0, \epsilon, t}[|\epsilon - \epsilon\theta(x_t, t)|^2] $$

构建噪声预测网络

我们需要一个卷积神经网络作为噪声预测器。输入是 $x_t$ 和步数 t 的嵌入,输出是预测的噪声。

import torch.nn as nn
import torch.nn.functional as F

class PositionalEncoding(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.dim = dim

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

class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, time_dim):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, 3, padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, 3, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        self.time_mlp = nn.Linear(time_dim, out_channels)
        self.skip = nn.Conv2d(in_channels, out_channels, 1) if in_channels != out_channels else nn.Identity()

    def forward(self, x, t):
        # x: (batch_size, channels, h, w)
        # t: (batch_size, time_dim)
        h = F.relu(self.bn1(self.conv1(x)))
        # 添加时间嵌入
        h += self.time_mlp(t)[:, :, None, None]
        h = F.relu(self.bn2(self.conv2(h)))
        return h + self.skip(x)

class UNet(nn.Module):
    def __init__(self, in_channels=1, out_channels=1, time_dim=256):
        super().__init__()
        self.time_dim = time_dim
        self.pos_encoding = PositionalEncoding(time_dim)
        
        # 下采样路径
        self.down1 = ResidualBlock(in_channels, 64, time_dim)
        self.down2 = ResidualBlock(64, 128, time_dim)
        self.down3 = ResidualBlock(128, 256, time_dim)
        self.pool = nn.MaxPool2d(2)
        
        # 瓶颈层
        self.bottleneck = ResidualBlock(256, 256, time_dim)
        
        # 上采样路径
        self.up1 = nn.ConvTranspose2d(256, 128, 2, stride=2)
        self.res_up1 = ResidualBlock(256, 128, time_dim)
        self.up2 = nn.ConvTranspose2d(128, 64, 2, stride=2)
        self.res_up2 = ResidualBlock(128, 64, time_dim)
        
        # 输出层
        self.out = nn.Conv2d(64, out_channels, 1)

    def forward(self, x, t):
        # x: (batch_size, 1, 28, 28)
        # t: (batch_size,)
        t = self.pos_encoding(t)
        
        # 下采样
        h1 = self.down1(x, t)
        h2 = self.down2(self.pool(h1), t)
        h3 = self.down3(self.pool(h2), t)
        
        # 瓶颈层
        bottleneck = self.bottleneck(self.pool(h3), t)
        
        # 上采样
        up1 = self.up1(bottleneck)
        up1 = torch.cat([up1, h3], dim=1)
        up1 = self.res_up1(up1, t)
        
        up2 = self.up2(up1)
        up2 = torch.cat([up2, h2], dim=1)
        up2 = self.res_up2(up2, t)
        
        up3 = self.up2(up2)
        up3 = torch.cat([up3, h1], dim=1)
        up3 = self.res_up2(up3, t)
        
        return self.out(up3)

model = UNet().to(device)
print(model)

模型训练流程

准备好网络后,我们开始训练。这里使用 MNIST 数据集,采用均方误差损失函数。

from torch.utils.data import DataLoader
from torch.optim import Adam

dataset = MNIST(root='./data', train=True, download=True, transform=ToTensor())
dataloader = DataLoader(dataset, batch_size=128, shuffle=True)

optimizer = Adam(model.parameters(), lr=1e-4)
criterion = nn.MSELoss()

def train_epoch(model, dataloader, optimizer, criterion, device):
    model.train()
    total_loss = 0
    for x0, _ in dataloader:
        x0 = x0.to(device)
        batch_size = x0.shape[0]
        
        # 随机采样步数 t
        t = torch.randint(0, T, (batch_size,), device=device)
        
        # 前向扩散生成 xt 和真实噪声
        xt, eps_true = forward_diffusion(x0, t, device)
        
        # 模型预测噪声
        eps_pred = model(xt, t)
        
        # 计算损失
        loss = criterion(eps_pred, eps_true)
        
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    return total_loss / len(dataloader)

# 开始训练
epochs = 50
for epoch in range(epochs):
    loss = train_epoch(model, dataloader, optimizer, criterion, device)
    print(f"Epoch [{epoch+1}/{epochs}], Loss: {loss:.4f}")

# 保存模型
torch.save(model.state_dict(), 'ddpm_mnist.pth')

实战:基于 DDPM 的图像生成

训练完成后,我们可以从随机噪声出发,通过反向扩散过程逐步去除噪声,生成新的图像。

反向扩散采样过程

def sample(model, batch_size, device):
    model.eval()
    # 从随机噪声开始
    xt = torch.randn((batch_size, 1, 28, 28)).to(device)
    
    with torch.no_grad():
        for t in reversed(range(1, T)):
            # 生成当前步数的 tensor
            t_tensor = torch.tensor([t], device=device).repeat(batch_size)
            
            # 预测噪声
            eps_pred = model(xt, t_tensor)
            
            # 获取系数
            alpha_t = alpha[t].to(device)
            alpha_bar_t = alpha_bar[t].to(device)
            alpha_bar_t_1 = alpha_bar[t-1].to(device)
            beta_t = beta[t].to(device)
            
            # 计算均值
            mean = (1 / torch.sqrt(alpha_t)) * (xt - (beta_t / torch.sqrt(1 - alpha_bar_t)) * eps_pred)
            
            # 计算方差
            if t == 1:
                variance = 0
            else:
                variance = beta_t
            
            # 添加噪声
            z = torch.randn_like(xt).to(device) if t > 1 else torch.zeros_like(xt).to(device)
            xt = mean + torch.sqrt(variance) * z
    
    # 归一化到 [0,1]
    xt = torch.clamp(xt, 0, 1)
    return xt

# 生成图像
model.load_state_dict(torch.load('ddpm_mnist.pth'))
generated_images = sample(model, batch_size=16, device=device)

# 可视化生成结果
plt.figure(figsize=(8, 8))
for i in range(16):
    img = generated_images[i].squeeze().cpu().detach().numpy()
    plt.subplot(4, 4, i + 1)
    plt.imshow(img, cmap='gray')
    plt.axis('off')
plt.show()

模型优化技巧

在实际应用中,还有一些技巧可以进一步提升效果:

  1. 使用余弦噪声调度:将 beta 的变化从线性改为余弦,可以提升生成图像的质量。
  2. 添加分类引导:在生成过程中加入类别信息,实现指定类别的图像生成。
  3. 使用更大的网络架构:如 UNet++、Attention UNet 等,可以提升模型的特征捕捉能力。

扩散模型的发展与应用

经典变体

  • DDIM:Denoising Diffusion Implicit Models,通过简化反向扩散过程,大幅提升生成速度。
  • Stable Diffusion:基于潜在空间的扩散模型,在保持生成质量的同时,降低了计算资源消耗。
  • Latent Diffusion Models:在压缩的潜在空间中进行扩散,适用于高分辨率图像生成。

应用场景

  • 图像生成:生成艺术画作、人像、产品设计图等。
  • 图像修复:修复老照片、去除图像中的水印和瑕疵。
  • 文本到图像生成:根据文本描述生成对应的图像,如 Midjourney、DALL·E。
  • 语音合成:生成自然流畅的语音,提升语音合成的质量。

总结

扩散模型通过前向扩散和反向扩散两个过程,实现了稳定的生成模型训练,有效解决了 GAN 的训练不稳定问题。前向扩散是固定的噪声添加过程,反向扩散则通过训练神经网络预测噪声,逐步还原真实数据。基于 DDPM 模型,我们可以实现手写数字等简单图像的生成,而优化噪声调度和网络架构能显著提升最终效果。目前,扩散模型的变体 Stable Diffusion 等已成为主流的图像生成工具,广泛应用于各类创意设计场景。

目录

  1. 扩散模型(Diffusion Model)原理与图像生成实战
  2. 为什么需要扩散模型
  3. 核心思想:前向与反向扩散
  4. 前向扩散过程详解
  5. 数学原理
  6. 代码实现
  7. 定义扩散过程的超参数
  8. 生成线性变化的 beta 序列
  9. 测试前向扩散过程
  10. 加载 MNIST 数据集的一张图像作为示例
  11. 可视化不同步数的扩散效果
  12. 反向扩散过程与模型训练
  13. 数学原理
  14. 构建噪声预测网络
  15. 模型训练流程
  16. 开始训练
  17. 保存模型
  18. 实战:基于 DDPM 的图像生成
  19. 反向扩散采样过程
  20. 生成图像
  21. 可视化生成结果
  22. 模型优化技巧
  23. 扩散模型的发展与应用
  24. 经典变体
  25. 应用场景
  26. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 英伟达开源 DreamDojo:4.4 万小时“梦境”破解机器人数据鸿沟
  • Python 通过 ctypes 调用 C++ DLL 的原理与实战
  • 具身智能机器人运控通讯架构与实现系列
  • 基于 Vivado 的 RISC-V 五级流水线 CPU FPGA 实现详解
  • Web3.0 开发实践:去中心化网络的核心概念与演进
  • IDEA 三大 AI 编程插件实测:Copilot、TRAE 与灵码深度对比
  • C++ 运算符重载:让自定义类型支持自然运算
  • AIGC 产品经理从 0 到 1 核心能力与面试考点解析
  • 使用 Text Generation WebUI 进行大模型 LoRA 微调指南
  • 单向链表六大核心操作详解:销毁、查找、倒置与排序
  • 人工智能与机器学习在软件工程中的应用
  • 内网渗透基础:域环境权限获取与维持
  • Git 版本控制详细使用教程
  • dbswitch 异构数据库迁移与同步工具
  • LLM 驱动的智能体框架、应用探索与未来展望
  • Git 常用命令使用指南
  • C++ 基于哈希表封装 unordered_map 和 unordered_set
  • Llama-2-7b 模型在昇腾 NPU 上的性能测评与部署优化
  • Java 内存溢出(OOM)排查实战:从复现到 MAT Dump 分析
  • 医疗 AI 可信革命全栈实现:向量索引与贝叶斯网络

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online