DDPM 扩散模型 PyTorch 复现:MNIST 数据集 30 轮训练 Loss 降至 0.023

📅 2026/7/9 1:40:30
DDPM 扩散模型 PyTorch 复现:MNIST 数据集 30 轮训练 Loss 降至 0.023
DDPM扩散模型实战从零实现MNIST图像生成1. 扩散模型核心思想解析想象你正在观察一杯清水被墨水逐渐染黑的过程——这正是扩散模型前向过程的生动比喻。扩散模型的魅力在于它通过模拟这种渐进式污染与净化的双重过程实现了从纯噪声生成逼真图像的神奇能力。扩散模型的双阶段本质前向扩散将清晰图像通过T次加噪步骤转化为高斯噪声反向生成训练神经网络学习逆向去噪过程# 前向扩散公式的直观实现 def forward_diffusion(x0, t, alpha_bar): noise torch.randn_like(x0) xt torch.sqrt(alpha_bar[t]) * x0 torch.sqrt(1 - alpha_bar[t]) * noise return xt, noise在MNIST数据集上这个过程表现为数字图像逐渐模糊直至变成随机噪点。有趣的是当T足够大时通常取1000步最终图像xT会完全失去原始特征成为各向同性的高斯噪声。2. 工程实现关键组件2.1 噪声调度策略β调度表是控制噪声添加节奏的核心参数。在DDPM原始论文中采用线性调度从β₁1e-4到β_T0.02def linear_beta_schedule(timesteps): scale 1000 / timesteps beta_start scale * 0.0001 beta_end scale * 0.02 return torch.linspace(beta_start, beta_end, timesteps)实际应用中余弦调度往往表现更优它在开始和结束时变化平缓中间阶段变化较快def cosine_beta_schedule(timesteps, s0.008): 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)2.2 U-Net噪声预测器DDPM的核心是一个改良的U-Net结构其特殊设计包括时间步嵌入将时间步t通过正弦位置编码注入网络残差连接保持梯度流动的稳定性注意力机制在特征图上应用自注意力class TimeEmbedding(nn.Module): def __init__(self, dim): super().__init__() self.dim dim inv_freq torch.exp(torch.arange(0, dim, 2).float() * (-math.log(10000) / dim)) self.register_buffer(inv_freq, inv_freq) def forward(self, t): pos_enc t[:, None] * self.inv_freq[None, :] return torch.cat([torch.sin(pos_enc), torch.cos(pos_enc)], dim-1)3. MNIST实战全流程3.1 数据预处理要点MNIST数据需要标准化到[-1,1]范围以适应扩散过程transform Compose([ ToTensor(), Lambda(lambda x: (x - 0.5) * 2) # [0,1] - [-1,1] ]) dataloader DataLoader( MNIST(./data, trainTrue, downloadTrue, transformtransform), batch_size128, shuffleTrue )3.2 训练循环实现DDPM的训练目标简化为噪声预测的MSE损失def train_loop(model, dataloader, optimizer, ddpm, device): model.train() for x0, _ in dataloader: x0 x0.to(device) # 随机采样时间步和噪声 t torch.randint(0, ddpm.n_steps, (x0.shape[0],)).to(device) noise torch.randn_like(x0) # 前向扩散过程 xt ddpm.sample_forward(x0, t, noise) # 预测噪声 pred_noise model(xt, t) # 计算损失 loss F.mse_loss(pred_noise, noise) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step()3.3 采样生成过程逆向生成是从纯噪声开始逐步去噪的迭代过程torch.no_grad() def sample(model, ddpm, image_size, batch_size16, channels1): # 初始化纯噪声 x torch.randn((batch_size, channels, image_size, image_size)).to(device) for t in reversed(range(ddpm.n_steps)): # 当前时间步的tensor t_tensor torch.full((batch_size,), t, devicedevice) # 预测噪声 pred_noise model(x, t_tensor) # 计算均值方差 alpha_t ddpm.alphas[t] alpha_bar_t ddpm.alpha_bars[t] beta_t ddpm.betas[t] if t 0: noise torch.randn_like(x) else: noise torch.zeros_like(x) # 反向过程更新 x (1 / torch.sqrt(alpha_t)) * ( x - ((1 - alpha_t) / torch.sqrt(1 - alpha_bar_t)) * pred_noise ) torch.sqrt(beta_t) * noise # 最终生成的图像 x torch.clamp(x, -1, 1) return x4. 性能优化实战技巧4.1 训练加速策略混合精度训练显著减少显存占用scaler torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): pred_noise model(xt, t) loss F.mse_loss(pred_noise, noise) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()梯度裁剪防止梯度爆炸torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)4.2 采样质量提升DDIM采样加速生成同时保持质量def ddim_sample(model, ddpm, image_size, eta0.0, steps50): # 时间步重采样 times torch.linspace(0, ddpm.n_steps-1, steps).long().tolist() times list(reversed(times)) x torch.randn((batch_size, channels, image_size, image_size)) for t in times: # DDIM特有的更新规则 ... return x分类器引导有条件生成特定数字def classifier_guided_sample(model, classifier, ddpm, image_size, class_idx): # 在噪声预测中融入分类器梯度 ...5. 结果分析与可视化经过30轮训练后Loss可稳定降至0.023左右。下图展示了生成质量的演变过程训练轮次生成样本示例关键指标5Loss0.1515Loss0.0630Loss0.023典型问题排查指南生成图像模糊检查β调度是否合适增加模型容量或训练轮次尝试更复杂的网络结构训练Loss震荡降低学习率增大batch size添加梯度裁剪# 可视化工具函数 def plot_samples(samples, n_rows4): plt.figure(figsize(10,10)) for i in range(n_rows**2): plt.subplot(n_rows, n_rows, i1) plt.imshow(samples[i].cpu().squeeze(), cmapgray) plt.axis(off) plt.tight_layout() plt.show()