0基础学扩散模型:从数学原理到PyTorch实战

1次阅读
没有评论

共计 2824 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

理解扩散模型的数学基础

扩散模型的核心思想其实很有趣——想象一杯清水滴入墨水,墨水会逐渐扩散直到整杯水变得均匀。这个过程反过来,就是从混沌中恢复出清晰图像的神奇能力。让我们拆解两个关键概念:

0 基础学扩散模型:从数学原理到 PyTorch 实战

  1. 马尔可夫链假设:扩散模型认为每一步的噪声添加只依赖前一步的状态,就像多米诺骨牌,当前状态只被前一块影响。数学表达为:
    $$q(x_t|x_{t-1}) = \mathcal{N}(x_t; \sqrt{1-\beta_t}x_{t-1}, \beta_t\mathbf{I})$$
    其中 $\beta_t$ 是预设的噪声计划表(noise schedule)。

  2. 变分下界(ELBO):模型通过最大化这个下界来学习逆向过程。简单说,就是让模型预测的 ” 去噪路径 ” 尽可能接近真实的扩散路径:
    $$\text{ELBO} = \mathbb{E}q[\log p\theta(x_0|T)] – \sum_{t=1}^T D_{KL}(q(x_{t-1}|x_t,x_0)||p_\theta(x_{t-1}|x_t))$$

DDPM 与 DDIM 的关键对比

  • DDPM
  • 严格遵循马尔可夫过程
  • 需要完整 T 步采样(通常 T =1000)
  • 训练稳定但采样慢

  • DDIM

  • 非马尔可夫过程,允许跳步采样
  • 采样步数可缩减至 20-50 步
  • 需要更精细的超参调节

选择建议:优先用 DDPM 打基础,理解原理后再尝试 DDIM 加速。

PyTorch 实战实现

1. 高斯扩散可视化

import matplotlib.pyplot as plt
import torch

def visualize_diffusion(image, betas, steps=5):
    alphas = 1 - betas
    alpha_bars = torch.cumprod(alphas, dim=0)

    plt.figure(figsize=(15,3))
    for t in range(0, len(betas), len(betas)//steps):
        noise = torch.randn_like(image)
        noisy_img = torch.sqrt(alpha_bars[t])*image + torch.sqrt(1-alpha_bars[t])*noise
        plt.subplot(1, steps+1, t//(len(betas)//steps)+1)
        plt.imshow(noisy_img.squeeze().cpu().numpy(), cmap='gray')

2. 带 EMA 的 U -Net

class UNetBlock(nn.Module):
    def __init__(self, in_c, out_c, time_emb_dim):
        super().__init__()
        self.time_mlp = nn.Sequential(nn.Linear(time_emb_dim, out_c),
            nn.SiLU(),
            nn.Linear(out_c, out_c)
        )
        self.conv = nn.Sequential(nn.Conv2d(in_c, out_c, 3, padding=1),
            nn.BatchNorm2d(out_c),
            nn.SiLU())

    def forward(self, x, t):
        h = self.conv(x)
        time_emb = self.time_mlp(t)[:,:,None,None]
        return h + time_emb

# 使用 EMA 包装器
class EMA:
    def __init__(self, beta=0.9999):
        self.beta = beta
        self.shadow = {}

    def register(self, module):
        for name, param in module.named_parameters():
            self.shadow[name] = param.data.clone()

    def update(self, module):
        for name, param in module.named_parameters():
            self.shadow[name] = self.beta * self.shadow[name] + (1-self.beta) * param.data

3. 余弦噪声调度器

def cosine_beta_schedule(timesteps, s=0.008):
    """https://arxiv.org/abs/2102.09672"""
    steps = timesteps + 1
    x = torch.linspace(0, timesteps, steps)
    alphas_cumprod = torch.cos(((x / timesteps) + 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, 0.999)

训练优化与问题解决

典型问题 1:梯度消失

  • 现象:深层网络参数更新幅度趋近于 0
  • 解决方案
  • 使用残差连接(如 U -Net 的 skip connection)
  • 采用梯度裁剪(torch.nn.utils.clip_grad_norm_
  • 混合精度训练(AMP):
    scaler = torch.cuda.amp.GradScaler()
    with torch.cuda.amp.autocast():
        loss = model(x, t)
    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()

典型问题 2:采样速度慢

  • 加速方案
  • 知识蒸馏:训练轻量学生模型模仿大模型行为
  • DDIM 采样:将 1000 步压缩到 50 步
  • 渐进式蒸馏:迭代式压缩模型步数

避坑指南

  • 数值稳定性
  • 对预测噪声进行torch.clamp(noise, -clip_val, clip_val)
  • 使用 torch.where(t > 0, ..., ...) 处理 t = 0 边界条件

  • 超参调节

  • 余弦调度器的 s 参数建议 0.01-0.02
  • 初始 $\beta_1$ 建议 1e-4,最终 $\beta_T$ 建议 0.02-0.05

  • 资源预估

  • 256×256 图像训练需 16GB+ 显存
  • 批次大小与分辨率平方成反比(如 256²→batch=8,512²→batch=2)

开放思考:Latent Diffusion 的可能性

传统扩散直接在像素空间操作,计算代价高昂。Latent Diffusion 先在 VAE 的隐空间进行扩散,最后解码到像素空间:
1. 先用 VAE 压缩图像到隐空间(如 256×256→32x32x4)
2. 在低维空间执行扩散过程
3. 最终通过 VAE 解码器生成高清图像

关键优势
– 计算量减少约 16 倍
– 可结合 CLIP 等跨模态模型
– 更易控制生成语义特征

完整实现代码见:https://github.com/example/diffusion-tutorial(符合 PEP8 规范,含详细维度注释)

正文完
 0
评论(没有评论)