DDPM详解:理论、损失与架构

我第一次看 DDPM 论文的时候说实话有点懵。什么马尔可夫链、变分下界、重参数技巧… 一套组合拳下来,差点把我劝退。后来才发现,这东西的核心思想其实特别纯粹。你把它拆开看,就俩过程:前向加噪,反向去噪。就这么简单。

咱们先把 DDPM 的骨架搭起来,再往里填肉。

前向扩散过程:给数据“下毒”

这个过程说白了,就是一步步往干净数据里加高斯噪声。加够 T 步,原始数据完全变成一个标准正态分布的白噪声。

你想象一下:一张清晰图片,我加一点噪点,再加一点… 最后啥也看不见了。道理一样。

关键公式定义:

  • q(xₜ | xₜ₋₁) = N(xₜ; √(1-βₜ) · xₜ₋₁, βₜI) → 单步加噪过程
  • q(xₜ | x₀) = N(xₜ; √ᾱₜ · x₀, (1 - ᾱₜ)I) → 直接从 x₀ 跳到 xₜ,不用一步步走

这里 ᾱₜ = ∏(1-βₛ) 是累计乘积。βₜ 是噪声调度表,控制每一步加多少噪声。我记得最早一批工作里,βₜ 从 0.0001 线性增加到 0.02。后来发现 cosine 调度更好用,我在分子生成项目里试过,确实收敛更稳。

反向去噪过程:从噪声中“复活”数据

反向过程就是学一个神经网络 p_θ,把纯噪声一步步变回干净数据。每一步都在预测:给定 xₜ,xₜ₋₁ 应该是什么。

公式长这样:

p_θ(xₜ₋₁|xₜ) = N(xₜ₋₁; μ_θ(xₜ, t), Σ_θ(xₜ, t))

但现实中,我们一般不直接预测 xₜ₋₁ 的分布参数。Ho 他们在 DDPM 里做了一个聪明的简化:固定方差,只让网络预测 均值。更狠的是,预测均值可以等价转换成预测噪声 ε。

嗯,这里要注意:预测噪声比预测原始数据更稳定。我刚开始复现时,非要预测 x₀ 试一下,结果模型训练怎么都不收敛。改成预测噪声,效果立竿见影。

损失函数:一个简化的变分下界

原论文推导的损失函数很复杂,有很多 KL 散度项。但作者发现把这些项去掉,只保留一个简单的 均方误差,效果反而最好。

核心损失函数:

L_simple = E_{t, x₀, ε} [ || ε - ε_θ(√ᾱₜ · x₀ + √(1-ᾱₜ) · ε, t) ||² ]

翻译成人话:
1. 随机采样一个时间步 t
2. 随机采样一个高斯噪声 ε
3. 用公式生成 xₜ
4. 让网络预测这个噪声 ε
5. 算预测噪声和真实噪声的 MSE
6. 反向传播更新网络

我在项目中遇到过一个问题:直接拿标准 MSE 训练,分子生成的质量总差一口气。后来我把损失函数里加了 坐标加权——也就是对原子位置的损失权重设得高一些——效果才提上来。所以别死套公式,懂原理,灵活改。

为什么不预测 x₀ 而预测噪声?

因为预测噪声的梯度更平滑。你想想,x₀ 的尺度一直在变,而噪声永远是标准正态分布,网络更容易学。

采样过程:从噪声逆袭成数据

训练完模型,怎么生成样本?采样过程就是把反向去噪从头到尾跑一遍。伪码如下:

1. x_T ~ N(0, I)  // 从纯噪声开始
2. for t = T 到 1:
   a. z ~ N(0, I)  // 如果 t > 1
   b. xₜ₋₁ = 1/√αₜ · (xₜ - (1-αₜ)/√(1-ᾱₜ) · ε_θ(xₜ, t)) + σₜ · z
3. 返回 x₀

这里面有三个要点:

  • 系数缩放:每一步都有个 1/√αₜ 因子,把去噪后的分布拉回正确的尺度
  • 随机项 z:除了最后一步,每一步都要加一点随机噪声,保证生成多样性
  • σₜ 控制去噪随机性:默认 σₜ² = βₜ,也可以设成 0

我早期调试时犯过一个错:忘了给中间步加 z,结果生成的结果全是一模一样的分子。折腾了两天才发现,就是缺了这步噪声注入。所以,每一步的 z 不是可选项,是必需品。

采样速度 vs 质量权衡

T=1000 步生成的样本质量最好,但慢得让人抓狂。后来有不少工作(DDIM、DPM-solver)能把步数降到 50-100,速度提升 10-20 倍。不过这是后话,你现在先把标准流程跑通。

模型架构:U-Net 加时间嵌入

DDPM 官方实现用了 U-Net 作为骨干网络。为什么选 U-Net?因为 U-Net 天然有编码器-解码器结构,能保留多尺度信息,这对恢复分子结构里的局部和全局特征都很有用。

核心设计要素:

  • 时间步嵌入 (Time Embedding):把 t 编码成一个向量,通过自适应层归一化或加法注入到各层
  • 残差块 + 自注意力:每个下采样/上采样块包含残差连接和空间注意力
  • 跳跃连接 (Skip Connection):编码器每层输出直接拼接到对应解码器层

对于分子生成,输入通常是 原子类型 + 坐标 的张量。我习惯把分子表示成 3D 网格或者点云,U-Net 里的卷积层对这两种输入形式都适用。

架构参数示例(参考 DDPM 原论文):

  • 通道数基数:128,然后 128→256→512→1024
  • 每个分辨率 2 个残差块
  • 自注意力层放在 16×16 特征图上
  • Dropout: 0.1
  • 总共参数量约 35M

不过老实说,这个架构并不是不能改的。我最近做分子构象生成,发现把自注意力换成 graph transformer,模型对分子拓扑的建模能力更强。所以,U-Net 是个好起点,但也只是起点。

下面这张图帮你看清整体流程:

DDPM 训练与采样流程结构图 原始数据 x₀ 逐步加噪 中间状态 xₜ 纯噪声 x_T 去噪网络 ε_θ 预测噪声 ε̂ 逐步去噪 生成样本 x₀̂ 损失 MSE(ε, ε̂) U-Net 网络架构(模型主干) 下采样块 1 下采样块 2 中间瓶颈 上采样块 1 输出 每块包含:残差层 + 自注意力 + 时间嵌入 + 跳跃连接

总结一下 DDPM 最让我舒服的地方:原理清晰,实现简单,效果惊艳。你只要把加噪公式和去噪损失写对,剩下的交给数据和算力。下手的第一个项目,我建议你跑通一个 2D 分子生成,看看模型能不能从噪声里画出分子结构来。那个过程真的很爽。