Diffusion DDPM 深度学习 项目实践

我做了一个交互式 Diffusion 学习网站

雾汐

前段时间在学习扩散模型时,我发现自己经常处在一种“公式好像看懂了,但整个过程并没有真正连起来”的状态。

例如,我知道前向过程是在不断加噪,也见过下面这条公式:

\[x_t=\sqrt{\bar{\alpha}_t}x_0+\sqrt{1-\bar{\alpha}_t}\varepsilon, \qquad \varepsilon\sim\mathcal N(0,I)\]

但只看公式时,仍然很难直观回答一些问题:时间步增大时,信号究竟以多快的速度消失?Linear 和 Cosine 调度有什么区别?训练时为什么只需要随机抽一个时间步,生成时却要从 $T$ 一直走回 $1$?模型预测噪声和恢复 $x_0$ 又是怎样联系起来的?

于是我做了一个面向 DDPM 初学者的中文交互式学习网站,名字叫 扩散原野

扩散原野

项目仓库:onnisama/Diffusion_field

一键跳转:扩散原野

为什么不是再写一份论文笔记

扩散模型的难点不只是公式多,而是同一个概念往往同时存在于几个不同层次:

  • 直觉上,它是在逐渐破坏数据,再学习如何逆转这个过程;
  • 概率上,它由前向马尔可夫链与参数化的反向条件分布组成;
  • 数学上,它会经过高斯分布、后验、KL 散度和 ELBO;
  • 训练时,网络通常学习预测加入到 $x_t$ 中的噪声;
  • 写成代码后,还要处理时间步、张量 shape、调度器和采样循环。

如果只读其中一层,很容易出现“每一段都认识,合起来却不知道模型到底在做什么”的情况。

所以这个网站没有按照传统博客的方式连续堆放长篇文字,而是尽量让同一个概念在 直觉、公式、动画、代码和小测 之间来回对应。读者可以先拖动滑块观察现象,再回头看公式;也可以先展开推导,然后在 PyTorch 代码中找到同一项究竟落在了哪一行。

从一张图怎样变成噪声开始

首页和前向扩散章节都有一个可以直接操作的加噪实验。时间步、噪声调度、总步数和随机种子都可以调整,页面同时显示:

  • 当前的 $x_0$、噪声 $\varepsilon$ 和 $x_t$;
  • $\beta_t$、$\alpha_t$ 与 $\bar{\alpha}_t$;
  • 信号系数 $\sqrt{\bar{\alpha}_t}$;
  • 噪声系数 $\sqrt{1-\bar{\alpha}_t}$;
  • 当前时间步的 SNR。

这里的图像变化并不是用模糊、透明度或几张预先准备的图片拼出来的,而是对每个像素真实计算前向闭式公式。二维点云实验也使用同样的机制,因此可以看到月牙、圆环或高斯簇怎样逐渐失去原来的几何结构,最后接近标准高斯分布。

这个实验也让我对 $\bar{\alpha}_t$ 有了更具体的认识。它不只是推导里经常出现的一串连乘,而是在回答:“到了第 $t$ 步,原始信号还保留了多少?”

把训练和采样彻底分开

刚开始学习 DDPM 时,我最容易混淆的是训练与生成。

训练一次 batch 时,并不需要真的把每张图片从第 $1$ 步循环加噪到第 $T$ 步。因为前向过程具有闭式形式,可以直接:

  1. 从数据集中取得 $x_0$;
  2. 为样本随机抽取时间步 $t$;
  3. 采样标准高斯噪声 $\varepsilon$;
  4. 一步构造对应的 $x_t$;
  5. 让网络预测 $\varepsilon_\theta(x_t,t)$;
  6. 计算噪声预测误差并反向传播。

而生成时没有真实的 $x_0$,只能先从

\[x_T\sim\mathcal N(0,I)\]

开始,再反复计算 $x_T,x_{T-1},\ldots,x_0$。因此网站分别做了训练流程播放器和采样代码播放器:当前执行步骤会高亮,旁边的状态也会同步变化。

把这两条流程放在一起看之后,“训练时随机抽一步,采样时逐步走完整条链”就不再只是需要背下来的结论了。

公式不能只负责看起来很厉害

从前向分布

\[q(x_t\mid x_{t-1}) = \mathcal N\left( x_t; \sqrt{1-\beta_t}x_{t-1}, \beta_tI \right)\]

到任意时间步的闭式采样,再到后验

\[q(x_{t-1}\mid x_t,x_0),\]

每一步都需要说明用了什么性质、每个符号表示什么,以及它最终怎样进入训练目标。复杂推导默认不会一次全部铺开,而是可以逐步展开,并标出这里使用的是高斯线性组合、重参数化、贝叶斯公式,还是 KL 分解。

网站还专门整理了原始 DDPM 论文公式 (1)–(16) 的阅读路线。希望读完之后,至少能分清以下几个容易混在一起的对象:

  • 负 ELBO;
  • 中间时刻的 KL 项;
  • 论文公式 (12) 中带时间权重的噪声误差;
  • 公式 (14) 的 $L_{\mathrm{simple}}$。

它们之间存在严格联系,但不能为了讲得简单就全部写成同一个东西。

从公式走到一份最小 PyTorch 实现

在代码章节中,我把一个最小 DDPM 拆成了若干小模块,包括:

  • Beta schedule 与各类系数预计算;
  • extractq_sample
  • 正弦时间嵌入;
  • 简化 U-Net;
  • 噪声预测损失;
  • 单步反向采样与完整采样循环;
  • checkpoint、随机种子和生成样本;
  • EMA 与混合精度训练的拓展位置。

每段代码都会标出输入、输出和张量 shape,并提供教学版与工程版的对照。默认学习路径选择 MNIST,是因为它能够把训练成本控制在比较容易接受的范围内,同时又足以跑通完整流程。

不把后续工作都算到 DDPM 头上

Diffusion 领域的发展很快,许多今天常见的内容并不属于 2020 年的原始 DDPM 论文。

网站中的论文地图把几条路线分开整理:

  • DDPM:离散时间扩散与噪声预测的核心路径;
  • Improved DDPM:Cosine schedule、方差学习等改进;
  • DDIM:不同的生成过程与更少步采样;
  • Score-SDE:连续时间的 SDE 统一视角;
  • Classifier Guidance 与 Classifier-Free Guidance:条件控制;
  • Latent Diffusion:把扩散过程转移到潜空间。

这样做主要是为了避免一个常见问题:学到 Stable Diffusion、CFG 或 $v$-prediction 后,再回头把它们全部归到 DDPM 原论文里。

目前完成的学习闭环

现在网站已经包含数学基础、DDPM 核心原理、交互实验室、PyTorch 实现、论文地图、术语表、测验和本地学习进度。

我给它安排的主线是:

数学基础 → 前向扩散 → 闭式采样 → 反向过程 → 噪声预测训练 → DDPM 采样 → PyTorch 实现 → 小测

测验不只是告诉读者选对还是选错,还会解释每个错误选项具体混淆了什么。学习进度保存在浏览器本地,不需要注册账号。

网站后面还会继续补充 DDIM、Score Matching、SDE 和条件生成等进阶内容哦~敬请关注!

评论

© 2026 芝士雾汐