想象一间古董修复工作室:修复师面前摊着一幅泛黄的山水画,工具台上放着一台怪机器。只要按一下按钮,整个画面就被均匀地洒上一层薄灰——按一次,山峦模糊一点;按五十次,亭台只剩轮廓;按一千次,整幅画变成灰扑扑的空白。更特别的是,他训练了一位“除灰学徒”:学徒只需看一眼当前的灰白程度,就能估计刚刚洒下的那层灰有多少、飘在哪些位置。于是,重现画作变成一场反向操作——从一张纯灰的画布出发,学徒一层一层地揭掉灰尘,直到青山绿水重新浮现。 洒灰,是前向加噪;除灰,是反向去噪;学徒,是神经网络。这场“洒灰—除灰”的双人舞,就是扩散模型。而本文的主角 DDPM 与 DDIM,是这场舞最经典的两套舞步:一个踏着千步细碎台阶谨慎下行,一个像熟客抄近道、跳着台阶快速到底。理解了这两套舞步,你就拿到了通往一切扩散模型应用的大门钥匙。

岗位关联

对 AI 从业者而言,DDPM 与 DDIM 是当下图像生成、音频合成等众多落地产品的底层发动机。掌握它们的原理与差异,能让你在面对 Stable Diffusion 等主流模型时,不再“会用不会懂”,而是真正看懂采样、加速与生成背后的每一处设计。

目录
  1. 可视化演示
  2. 扩散模型思想
  3. 前向加噪过程
  4. 反向去噪过程
  5. DDPM训练
  6. DDPM采样
  7. DDIM加速
  8. 数学联系
  9. 应用与地位
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

展示DDPM前向加噪与反向去噪过程,对比DDPM随机采样与DDIM确定性采样的生成路径差异。

本节配有交互动效(进入视口后自动加载,约 3KB)。

前向扩散过程 两个高斯分布数据簇逐步添加高斯噪声,随时间步t增加逐渐变为纯噪声 噪声水平由beta调度决定,alpha_bar从1衰减至0,蓝色条表示数据保留比例 每个时间步x_t = sqrt(a_bar)*x_0 + sqrt(1-a_bar)*noise,加噪过程为马尔可夫链 DDPM前向扩散将结构化数据逐步转化为各向同性高斯噪声

扩散模型思想

修复师的“洒灰装置”其实是一件充满哲理的发明:它证明了任何一幅精致的画,都可以被足够多的灰尘彻底掩埋。那么反过来想——如果一台机器能学会“如何精确地揭掉一层灰”,那我们不就可以从一张空白灰布出发,反向重现出任何一幅画了吗?扩散模型的全部野心,就藏在这个倒过来的思路里。它不跟生成目标“正面对抗”(像 GAN 那样),而是先亲手毁掉数据,再学习修补数据。

这个思路的灵感来自物理世界的热力学扩散:一滴墨落入清水,会不可逆地弥散成一整杯均匀的淡墨水。扩散模型顺着这个现象发问:如果扩散的规律已知,我们能否训练一个“逆扩散”模型,让散开的墨重新聚回一滴?DDPM(去噪扩散概率模型)给出的答案是肯定的:前向过程负责“洒灰”(加噪),把数据变成噪声;反向过程负责“除尘”(去噪),用神经网络学会把噪声变回数据。生成新样本变成了一场“从噪声出发的采样”。

与 VAE 和 GAN 不同,扩散模型把“生成”拆解成上千个微小步骤,每一步只做一点微小预测,因此训练极其稳定,生成质量也屡创新高。它就像修复师不要求学徒一口哈气吹净千年积灰,而是要求他耐住性子,一次只擦去薄薄一层。正是这种“积小胜为大胜”的朴素哲学,让扩散模型成为当前生成领域最耀眼的方法族——我们后面讨论的 DDPM 和 DDIM,正是这套哲学最经典的两套实现方案。

前向加噪过程

现在让我们仔细看看修复师的“洒灰装置”是如何工作的。这台机器有一个旋钮,上面标着 \(\beta_1, \beta_2, \dots, \beta_T\),代表每一次按下按钮时“洒灰的浓度”。按第一次,灰比较淡(\(\beta_1\) 很小);按到最后,灰越来越浓。关键设计在于:每次洒灰只取决于当前画面,而不关心这幅画之前被洒过几次灰——就像传话游戏里每个人只把上一个人说的话传下去,前面的人说了什么一概不管。这种“只看眼前一步”的性质,在数学上叫马尔可夫链。

从定义上看,前向过程是一个逐步加噪的马尔可夫链,每一步从高斯分布中采样出下一时刻的画面:

初学者最容易忽略的一个精妙设计是:由于每一步都在叠加高斯噪声,而高斯分布叠加后仍是高斯分布,所以我们不需要真的跑完 T 步,就能直接从原图算出任意第 t 步的加噪结果。这个“一步到位”的闭式公式是整个 DDPM 训练能够高效进行的前提:

当 t 越来越大,\(\bar{\alpha}_t\) 趋近于 0,画面中原图的信息几乎消失,剩下的就是一片纯噪声——这正对应修复师按了一千次按钮后那张灰扑扑的空白画布。前向过程是一趟“确定性走向混沌”的单程旅行,而我们要学的,是怎么走回来。

q(xt∣xt−1)=N(xt;1−βt xt−1, βtI)q(x_t \mid x_{t-1}) = \mathcal{N}\left(x_t; \sqrt{1-\beta_t}\, x_{t-1},\, \beta_t \mathbf{I}\right)
它等价于说:修复师不需要真的按 t 次按钮,只要知道“总洒灰系数”\(\bar{\alpha}_t\),就能瞬间模拟出被洒了 t 次灰后的画面。
  1. 累积衰减系数\(\bar{\alpha}_t\) 是前 t 步 \(\alpha_s = 1-\beta_s\) 的连乘,代表原图还残留多少“清晰度”
    αˉt=∏s=1t(1−βs)\bar{\alpha}_t = \prod_{s=1}^{t} (1-\beta_s)
  2. 保留的信号部分原图 \(x_0\) 被整体缩小到 \(\sqrt{\bar{\alpha}_t}\) 倍,画面逐渐变淡
    αˉt x0\sqrt{\bar{\alpha}_t}\, x_0
  3. 叠加的全局噪声\(\sqrt{1-\bar{\alpha}_t}\) 是 T 步加噪累积出的总噪声强度,\(\varepsilon\) 是标准高斯噪声
    1−αˉt ε\sqrt{1-\bar{\alpha}_t}\, \varepsilon
  4. 噪声来源分布\(\varepsilon\) 从标准正态分布中抽取,保证噪声的随机性
    ε∼N(0,I)\varepsilon \sim \mathcal{N}(0, \mathbf{I})

反向去噪过程

修复师教会学徒除灰,靠的是一本“洒灰记录”:机器每洒一层灰,记录仪都会写下这次洒了多浓的灰。但真正要重现画作时,学徒面对的是没有记录的灰布——他必须仅凭当前画面的灰白程度,倒推出“上一步长什么样”。这就是反向过程:我们需要一个可学习的逆转移分布 \(p_\theta(x_{t-1} \mid x_t)\),它是一台“倒着走的洒灰机”。

可惜,数学上精确的反向过程是难以直接写出的,因为它依赖全部数据的分布。于是修复师请来了一位神经网络学徒:输入是当前灰蒙蒙的画面和当前时刻 t,输出是对反向高斯分布的估计。这个过程的数学描述是:

DDPM 在实现时做了一个聪明的简化:与其让网络直接输出整个高斯分布(又要预测均值又要预测方差,很难收敛),不如让网络只预测“这层灰的浓度”——也就是前向过程加入的噪声 \(\varepsilon\)。一旦知道了噪声,就能用数学公式反推出上一步的画面。这样一来,学徒的工作从“凭空想象原画”变成了“识别灰有多厚”,后者显然好练得多,这正是下一节训练部分的核心。

pθ(xt−1∣xt)=N(xt−1;μθ(xt,t), Σθ(xt,t))p_\theta(x_{t-1} \mid x_t) = \mathcal{N}\left(x_{t-1}; \mu_\theta(x_t, t),\, \Sigma_\theta(x_t, t)\right)
意思是:学徒看着当前画面 \(x_t\) 和时刻 \(t\),用神经网络预测出一个高斯分布,这个分布的中心 \( \mu_\theta\) 就是他认为的“上一层更清晰画面”,方差 \(\Sigma_\theta\) 则是他的“犹豫程度”。
  1. 参数化反向分布\(p_\theta\) 表示由网络参数 \(\theta\) 决定的反向转移概率,目标是逼近真实的反向过程
    pθ(xt−1∣xt)p_\theta(x_{t-1} \mid x_t)
  2. 均值预测\(\mu_\theta(x_t, t)\) 是神经网络根据当前画面和时间步输出的均值,代表“画面应该往哪个方向恢复”
    μθ(xt,t)\mu_\theta(x_t, t)
  3. 方差预测\(\Sigma_\theta(x_t, t)\) 是网络输出的方差,代表这一恢复步骤的不确定性
    Σθ(xt,t)\Sigma_\theta(x_t, t)

DDPM训练

学徒如何练就“一眼识灰”的本领?修复师的训练方法很巧妙:他每次都先随机挑一个时间步 \(t\),然后当着学徒的面按 \(t\) 次洒灰按钮,把干净的画 \(x_0\) 变成灰蒙蒙的 \(x_t\),同时把这次的总洒灰量记录在纸条上——也就是前向过程中真实加入的噪声 \(\varepsilon\)。学徒要做的,是看着 \(x_t\) 和时间 \(t\),猜出纸条上的数字。猜错得越多,惩罚越重;猜得越准,手艺越好。这个“惩罚尺度”就是损失函数。

训练目标可以写成非常简洁的均方误差形式:

训练流程其实很简单:每个 batch 里随机抽图像和随机抽时间步,用闭式公式一步算出 \(x_t\),丢进网络预测噪声,算 MSE 损失,反向传播更新参数。重复几千几万次,学徒就出师了。出师后的神经网络能对任意加噪程度准确报出“该擦掉多少灰”——注意,它并没有受过“从零生成新画”的训练,但下一节你会看到,只要会除灰,就自然获得了画新画的能力。

Lsimple=Et,x0,ε[∥ε−εθ(xt,t)∥2]\mathcal{L}_{\text{simple}} = \mathbb{E}_{t, x_0, \varepsilon}\left[\left\| \varepsilon - \varepsilon_\theta(x_t, t) \right\|^2\right]
大白话:从训练集里随机抽一张图、随机抽一个时间步、随机注入一次噪声,然后让网络预测这次注入的噪声。让“预测的噪声”和“真实的噪声”尽可能一致。
  1. 期望算子\(\mathbb{E}\) 表示对所有的随机抽样取平均,也就是“练得越多,手艺越稳”
    Et,x0,ε[⋅]\mathbb{E}_{t, x_0, \varepsilon}\left[\cdot\right]
  2. 时间步采样训练时 \(t\) 从 \(\{1,2,\dots,T\}\) 里均匀抽取,保证每一步的洒灰程度都被练到
    t∼Uniform({1,2,…,T})t \sim \text{Uniform}(\{1, 2, \dots, T\})
  3. 真实噪声\(\varepsilon\) 是前向加噪公式里真实注入的高斯噪声,相当于“标准答案纸条”
    ε\varepsilon
  4. 预测噪声\(\varepsilon_\theta(x_t, t)\) 是神经网络对噪声的估计,相当于学徒报出的答案
    εθ(xt,t)\varepsilon_\theta(x_t, t)
  5. 均方误差把两者的差平方再求平均,差距越大、损失越大,梯度更新越狠
    ∥ε−εθ(xt,t)∥2\left\| \varepsilon - \varepsilon_\theta(x_t, t) \right\|^2

DDPM采样

学徒出师这天,修复师交给他一张全新的纯灰画布,说:“现在,画一幅新山水吧。”学徒要做的事情非常朴素:从纯噪声 \(x_T\) 出发,反复执行“看灰、除灰、再看、再除”。每一步都利用网络预测的噪声,算出一个比上一步稍微清晰一点的画面,循环 T 次(通常 T 取 1000),直到走回 \(x_0\)。这个过程叫采样,也就是推理阶段的生成。

DDPM 每一步的去噪公式长这样:

每走完一步,画面就清晰一点;走完 1000 步,一幅全新的山水画就诞生了。这种“千步细走”的采样方式质量非常高,但代价也很明显:生成一张图要调用 1000 次神经网络,在 CPU 上可能要等几十秒甚至几分钟。好比学徒坚持一级一级台阶走下楼,虽然稳妥,但时间成本太高——这就引出了下一节的主角 DDIM,它发现原来可以跳着下楼梯。

xt−1=1αt(xt−βt1−αˉt εθ(xt,t))+σtz,z∼N(0,I)x_{t-1} = \frac{1}{\sqrt{\alpha_t}} \left( x_t - \frac{\beta_t}{\sqrt{1-\bar{\alpha}_t}}\, \varepsilon_\theta(x_t, t) \right) + \sigma_t z,\quad z \sim \mathcal{N}(0, \mathbf{I})
翻译成修复师的话:先按学徒的估计擦掉这层灰,把画面往清晰方向拽一步;然后,为了不让每次画出的画一模一样,再故意抖一下手——加上随机噪声 \(z\)。
  1. 步长缩放\(\frac{1}{\sqrt{\alpha_t}}\) 把画面拉回原来的尺度,抵消前向加噪时对信号的缩放
    1αt\frac{1}{\sqrt{\alpha_t}}
  2. 擦灰修正项\(x_t - \frac{\beta_t}{\sqrt{1-\bar{\alpha}_t}} \varepsilon_\theta(x_t, t)\) 表示从当前画面中减掉网络估计出的那层灰
    xt−βt1−αˉt εθ(xt,t)x_t - \frac{\beta_t}{\sqrt{1-\bar{\alpha}_t}}\, \varepsilon_\theta(x_t, t)
  3. 随机扰动项\(\sigma_t z\) 表示每一步刻意保留的随机抖动,这保证生成结果有多样性
    σtz,z∼N(0,I)\sigma_t z, \quad z \sim \mathcal{N}(0, \mathbf{I})

DDIM加速

有一天,除灰学徒仔细观察了自己的步伐,发现一个规律:每走一步,画面的“大体轮廓”其实已经浮现出来了,只是有些细节还没定型。既然如此,为什么非要 1000 步都走一遍呢?如果我能大致预测最终画面 \(x_0\) 的位置,为什么不直接跨过中间那些台阶,大步流星地朝目标走?DDIM(去噪扩散隐式模型)的精髓正在于此。

DDIM 不再要求反向过程是严格的马尔可夫链,而是构造了一族更自由的非马尔可夫采样路径。它依然沿用 DDPM 训练好的网络(完全不需重新训练),但采样时,先通过当前画面 \(x_t\) 和网络预测的噪声,估计出“画面最终会长什么样”——这一步叫预测 \(\hat{x}_0\):

有了这张“草图”,DDIM 就能在采样时自由跳跃:我不必访问 \(x_{t-1}\),而可以直接跳到几千步之前的 \(x_{t-50}\)。原本 1000 步的生成过程,可以压缩到 20~50 步,而生成质量几乎无损。更妙的是,DDIM 的每一步都可以做成完全确定性的——同一个随机种子出发,每次生成完全相同的图像,就像沿着固定扶手滑下楼梯,不会被风吹偏。这让它在需要“可控复现”的生产环境中特别受欢迎。

x^0=xt−1−αˉt εθ(xt,t)αˉt\hat{x}_0 = \frac{x_t - \sqrt{1-\bar{\alpha}_t}\, \varepsilon_\theta(x_t, t)}{\sqrt{\bar{\alpha}_t}}
学徒的想法是:把 \(x_t\) 里的噪声全部减掉,再把画面缩放回原图尺度,就得到一张“对最终作品的大致草图”。
  1. 当前噪声画面\(x_t\) 是被洒了 t 次灰后的画面,是预测的起点
    xtx_t
  2. 扣除预测噪声减去 \(\sqrt{1-\bar{\alpha}_t}\,\varepsilon_\theta(x_t,t)\),把累积的灰全部擦掉
    xt−1−αˉt εθ(xt,t)x_t - \sqrt{1-\bar{\alpha}_t}\, \varepsilon_\theta(x_t, t)
  3. 缩放到原图尺度除以 \(\sqrt{\bar{\alpha}_t}\) 把信号恢复为未衰减的状态,得到对 \(x_0\) 的估计
    ⋅αˉt\frac{\cdot}{\sqrt{\bar{\alpha}_t}}

数学联系

看到这里,你可能会问:DDPM 和 DDIM 到底是两个不同的模型,还是同一个模型的两副面孔?答案是后者。二者共享完全相同的训练目标:都是预测前向过程注入的噪声 \(\varepsilon\),连网络权重都可以直接复用。它们的分野不在训练,而在采样时如何“重组”每一步的去噪与加噪。事实上,它们都可以被塞进同一个统一的采样公式:

从这个统一视角看,DDPM 与 DDIM 的数学联系一目了然:两者都保证前向过程的边际分布 \(q(x_t \mid x_0)\) 一致,只是采样时对噪声的处理策略不同。DDPM 选择每步都注入“符合前向噪声水平”的随机扰动,让生成结果更多样;DDIM 选择把 \(\sigma_t\) 压到 0,让过程确定且可跳步。所以,DDIM 并不是 DDPM 的替代品,而是它的“加速档”——同一台发动机,换了一套更高效的传动方式。

xt−1=αˉt−1 x^0+1−αˉt−1−σt2 εθ(xt,t)+σtzx_{t-1} = \sqrt{\bar{\alpha}_{t-1}}\, \hat{x}_0 + \sqrt{1-\bar{\alpha}_{t-1}-\sigma_t^2}\, \varepsilon_\theta(x_t, t) + \sigma_t z
这个公式把采样过程分成三股力量:朝预测草图走一步(信号项)、保留尚未处理的残余噪声(噪声项)、以及由 \(\sigma_t\) 控制的随机抖动项。不同的 \(\sigma_t\) 选择,就诞生了 DDPM 和 DDIM 这对“双胞胎”。
  1. 信号前进项\(\sqrt{\bar{\alpha}_{t-1}}\, \hat{x}_0\) 表示朝预测的最终画面方向迈一步,目标是让画面逐渐清晰
    αˉt−1 x^0\sqrt{\bar{\alpha}_{t-1}}\, \hat{x}_0
  2. 残余噪声项\(\sqrt{1-\bar{\alpha}_{t-1}-\sigma_t^2}\, \varepsilon_\theta(x_t,t)\) 保留未被处理的噪声成分,它决定了跳步时画面还有多少“颗粒感”
    1−αˉt−1−σt2 εθ(xt,t)\sqrt{1-\bar{\alpha}_{t-1}-\sigma_t^2}\, \varepsilon_\theta(x_t, t)
  3. 随机抖动旋钮\(\sigma_t z\) 是随机项;\(\sigma_t=0\) 时退化为确定性采样(DDIM),\(\sigma_t\) 取特定值时恢复为随机采样(DDPM)
    σtz\sigma_t z

应用与地位

修复师的除灰学徒后来成了整个艺术圈的传奇:今天你看到的无数 AI 绘画产品,从 Stable Diffusion 到 Midjourney、DALL·E 2,背后都站着扩散模型的身影。DDPM 提供了“从噪声中生成图像”这一能力的数学基础和训练方法,而 DDIM 解决的是它“太慢”的痛点,让扩散模型从实验室走进产品——没有 DDIM 的加速,一次生成等半分钟的产品体验很难被大众接受。

扩散模型的地位不仅限于图像。语音合成(如 AudioLDM)、3D 点云生成、分子结构设计、医学影像去噪,都在借用“前向加噪—反向去噪”这一通用框架。后续的 Score-based SDE、流匹配、Rectified Flow 等方法,也都是在这一框架之上的延伸和改良。可以说,DDPM/DDIM 是理解整个扩散模型家族的第一块基石。

对 AI 从业者而言,掌握 DDPM/DDIM 的价值在于:当你看到一篇新的扩散模型论文时,你不会被花哨的改进名词唬住,而是能一眼识别出它改的是“前向过程”“噪声预测网络”还是“采样策略”——就像修复师看着任何一台新机器,都能立刻说出它是改进了洒灰方式,还是改进了除灰手法。

AI 应用场景

文生图产品的大规模推理加速

阶段:推理部署与采样优化

解决的问题:以 DDPM 为底座的文生图模型生成单张图片需要 1000 次网络前向计算,GPU 上仍需数秒甚至数十秒,难以支撑互联网产品的高并发请求。DDIM 将采样步数压缩到 20~50 步,推理时间缩短一个数量级,让“输入提示词→秒级出图”成为可能。

具体案例:某设计平台接入 Stable Diffusion 服务,用户在网页输入“赛博朋克风格的城市夜景”,系统用 DDIM 以 30 步采样在约 1 秒内返回首图,再用少量 DDPM 精细步做细节增强,兼顾速度与质量。

医学影像去噪与超分辨率

阶段:数据增强与图像重建

解决的问题:低剂量 CT 影像噪声大、细节模糊,传统滤波方法容易丢失病灶边缘。扩散模型天然擅长“加噪—去噪”的逆问题,可以用 DDPM 训练一个条件去噪网络,把低质量影像当成“洒灰后的图”,逐步还原出清晰影像。

具体案例:某三甲医院与 AI 团队合作,用 2000 组低剂量/正常剂量 CT 配对数据训练扩散去噪模型,推理阶段采用 DDIM 50 步采样,将影像噪声降低约 60%,且保留了细小的血管纹理,辅助医生更准确地判断早期病变。

语音合成与音频生成

阶段:生成模型训练与流式推理

解决的问题:语音合成要求低延迟。扩散模型在音频域生成质量高,但传统抽样太慢;借助 DDIM 的确定性跳步采样,可以在 10~20 步内生成高质量语音,同时保证同一文本多次合成的音色一致性。

具体案例:某语音交互产品用 AudioLDM 类架构做音效生成,用户上传一段“雨声”,系统用 DDIM 快速生成“雨声+脚步声”的混合音效,供短视频创作者直接使用,生成延迟从 8 秒降至 1.5 秒。

Python 演示

python87 行
import numpy as npimport torchimport torch.nn as nn # 1. 合成数据: 两个高斯簇 中心在 (-3,0) 和 (3,0)torch.manual_seed(0)N = 2000cls = torch.randint(0, 2, (N, 1))data = cls * torch.tensor([3.0, 0.0]) + (1 - cls) * torch.tensor([-3.0, 0.0])data = data + 0.5 * torch.randn(N, 2) # 2. 前向加噪调度: T 步 每步一点噪声T = 100beta = torch.linspace(0.0001, 0.02, T)alpha = 1.0 - betaalpha_bar = torch.cumprod(alpha, dim=0) def add_noise(x0, t):    # 用闭式公式一步算出 x_t 和真实噪声 eps    a_bar = alpha_bar[t].view(-1, 1)    eps = torch.randn_like(x0)    xt = torch.sqrt(a_bar) * x0 + torch.sqrt(1.0 - a_bar) * eps    return xt, eps # 3. 噪声预测网络: 输入二维坐标 + 时间步class NoisePred(nn.Module):    def __init__(self):        super().__init__()        self.net = nn.Sequential(            nn.Linear(3, 128), nn.ReLU(),            nn.Linear(128, 128), nn.ReLU(),            nn.Linear(128, 2)        )    def forward(self, x, t):        # t 归一化到 [0,1] 再拼到特征里        return self.net(torch.cat([x, (t / T).view(-1, 1)], dim=1)) model = NoisePred()opt = torch.optim.Adam(model.parameters(), lr=1e-3) # 4. 训练: 随机抽时间步 预测真实噪声for step_i in range(2000):    idx = torch.randint(0, N, (256,))    x0 = data[idx]    t = torch.randint(1, T, (256,))    xt, eps = add_noise(x0, t)    pred = model(xt, t.float())    loss = nn.functional.mse_loss(pred, eps)    opt.zero_grad()    loss.backward()    opt.step() # 5. DDPM 采样: 从纯噪声出发 走满 T 步@torch.no_grad()def sample_ddpm(n=400):    x = torch.randn(n, 2)    for tt in range(T - 1, 0, -1):        t = torch.full((n,), float(tt))        eps_pred = model(x, t)        sigma = torch.sqrt(beta[tt])        x = (x - beta[tt] / torch.sqrt(1.0 - alpha_bar[tt]) * eps_pred) / torch.sqrt(alpha[tt])        if tt > 1:            x = x + sigma * torch.randn_like(x)    return x # 6. DDIM 确定性采样: 只用 20 步@torch.no_grad()def sample_ddim(n=400, steps=20):    ts = list(np.linspace(T - 1, 0, steps, dtype=int))    x = torch.randn(n, 2)    for i in range(len(ts) - 1):        cur_t = ts[i]        nxt_t = ts[i + 1]        t = torch.full((n,), float(cur_t))        eps_pred = model(x, t)        x0_hat = (x - torch.sqrt(1.0 - alpha_bar[cur_t]) * eps_pred) / torch.sqrt(alpha_bar[cur_t])        if nxt_t == 0:            x = x0_hat        else:            x = torch.sqrt(alpha_bar[nxt_t]) * x0_hat + torch.sqrt(1.0 - alpha_bar[nxt_t]) * eps_pred    return x if __name__ == '__main__':    s1 = sample_ddpm()    s2 = sample_ddim()    print('DDPM 100步 样本均值:', s1.mean(dim=0).numpy(), '方差:', s1.var(dim=0).numpy())    print('DDIM 20步  样本均值:', s2.mean(dim=0).numpy(), '方差:', s2.var(dim=0).numpy())
目的:用 2D 合成数据完整走一遍“前向加噪→训练噪声预测网络→DDPM 采样→DDIM 加速采样”的流程,直观对比两种采样方法在相同数据上的还原效果。
  1. alpha_bar = torch.cumprod(alpha, dim=0)预先算好前向过程的累积衰减系数,让训练时能闭式加噪
  2. xt = torch.sqrt(a_bar) * x0 + torch.sqrt(1.0 - a_bar) * eps用前向闭式公式一步算出任意时刻的加噪图
  3. return self.net(torch.cat([x, (t / T).view(-1, 1)], dim=1))把时间步归一化后拼入特征,让网络知道“现在灰有多厚”
  4. pred = model(xt, t.float())网络预测的是噪声,而不是直接预测原图
  5. loss = nn.functional.mse_loss(pred, eps)用预测噪声与真实噪声的均方误差做训练损失
  6. x = (x - beta[tt] / torch.sqrt(1.0 - alpha_bar[tt]) * eps_pred) / torch.sqrt(alpha[tt])DDPM 采样核心:擦除预测的灰,再缩回原尺度
  7. if tt > 1: x = x + sigma * torch.randn_like(x)最后一步不补噪声,其余每步都加随机扰动保证多样性
  8. x0_hat = (x - torch.sqrt(1.0 - alpha_bar[cur_t]) * eps_pred) / torch.sqrt(alpha_bar[cur_t])DDIM 先预测“草图” \(x_0\)
  9. x = torch.sqrt(alpha_bar[nxt_t]) * x0_hat + torch.sqrt(1.0 - alpha_bar[nxt_t]) * eps_predDDIM 直接跳到更远的未来,大步加速
  10. ts = list(np.linspace(T - 1, 0, steps, dtype=int))用线性间隔选出 20 个采样时刻,实现跳步

常见误区

误区我一开始学 DDPM 时以为,每次训练都要从干净图像开始,一步一步加噪加 1000 次,才能得到训练样本,这样训练会非常慢。

正解完全不需要。由于高斯分布的叠加性,前向过程可以写出闭式公式,直接从 \(x_0\) 一步算出任意第 \(t\) 步的 \(x_t\)。训练时只需随机抽一个时间步 \(t\),闭式加噪一次,即可让网络学习预测该步噪声。这就像修复师不必真的按 1000 次按钮,只要按公式算出“按到第 t 次时画面该长什么样”。

为何常见:因为前向过程的定义是逐级的马尔可夫链,初学者容易被“每一步”的字面描述误导,忽略了高斯分布可闭式叠加的数学性质。

误区我觉得生成模型既然最终目标是一张清晰图片,那网络应该直接输入噪声、输出成品图,一步搞定。

正解DDPM 的反向过程是一个长达 T 次的迭代。每一步网络只预测“当前这层噪声有多大”,再通过采样公式让画面清晰一点点。最终图像是 T 次迭代累加的结果,而不是网络一次生成的。这就像除灰学徒一次只擦一层灰,绝不幻想一口哈气吹净千年积灰。

为何常见:我们看到太多“输入文本、瞬间出图”的产品界面,容易误以为扩散模型也是一步到位的,忽略了后台其实跑了几十甚至上千次迭代。

误区我以为 DDIM 是“改进版模型”,训练方式更先进、网络更轻量,所以采样才快。

正解DDIM 与 DDPM 的训练目标、网络结构、模型权重完全一致,DDIM 可以直接加载 DDPM 训练好的权重做采样。它的加速来自采样策略的改变——通过非马尔可夫的跳步路径,用更少的步数走完去噪过程。好比同一双腿,DDPM 是逐级下楼梯,DDIM 是两三级一跳,腿没变,走法变了。

为何常见:人们习惯用“新版本优于旧版本”的直觉理解模型迭代,忽略了 DDIM 与 DDPM 本质是“同一训练模型的不同采样策略”。

误区我觉得既然网络已经预测了噪声,那每步再加上随机噪声纯属多余,删掉应该能让生成更稳定。

正解\(\sigma_t z\) 是 DDPM 保持随机多样性的关键。删掉它会让生成结果变得单调甚至退化,因为前向过程本身就是随机的,反向过程需要对应的随机性来“对冲”。DDIM 之所以能删掉这个随机项(令 \(\sigma_t=0\)),是因为它重新设计了采样路径并保证每步都朝预测草图前进,这不是“删掉”而是“更换策略”。

为何常见:初学者看到公式里“多余”的噪声项,容易用“简化公式”的直觉去删减,忽略了随机性在生成模型中的核心作用。

面试题

Q1. DDPM 前向过程的本质是什么?

单选题 · easy

  • A.逐步向数据加入高斯噪声← 正确答案
  • B.逐步用网络去除噪声
  • C.一次生成整张图像
  • D.优化判别器损失函数

答案:A。前向过程是“洒灰”阶段,指按马尔可夫链逐步向干净数据叠加高斯噪声,直到数据变成纯噪声。选项 B 是反向去噪,C 是整体生成的直觉但不符合定义,D 是 GAN 的训练思路,均不属于前向过程。

Q2. DDPM 训练时网络预测的是什么?

单选题 · easy

  • A.每一步加入的噪声← 正确答案
  • B.干净原始图像
  • C.噪声调度系数
  • D.图像类别标签

答案:A。DDPM 的训练损失是预测噪声与真实噪声的均方误差,网络的核心任务是估计前向过程中注入的噪声 \(\varepsilon\),也因此被称为“噪声预测网络”。B 和 D 都不是训练目标;C 的调度系数 \(\beta_t\) 是人为预设的,不需要网络预测。

Q3. DDIM 最核心的贡献是什么?

单选题 · mid

  • A.大幅减少采样步数← 正确答案
  • B.改进训练损失函数
  • C.引入对抗训练机制
  • D.提高单步加噪均匀性

答案:A。DDIM 通过非马尔可夫跳步采样,把原本需要 1000 步的生成压缩到几十步,同时保持前向边际分布一致,是“加速采样”方向的核心贡献。它在训练上完全复用 DDPM,没有改损失函数,也不涉及对抗训练。

Q4. DDPM 采样公式末尾加 \(\sigma_t z\) 的主要作用是?

单选题 · mid

  • A.增加采样的随机多样性← 正确答案
  • B.加快网络收敛速度
  • C.降低内存占用空间
  • D.提高反向传播效率

答案:A。采样阶段的随机项 \(\sigma_t z\) 为生成过程引入随机性,使得同一起点可以产出多样化的结果,这是生成模型多样性的来源。它不参与训练,因此不影响收敛、内存或反向传播。

Q5. 关于前向过程马尔可夫链的说法,正确的有哪些?

多选题 · mid

  • A.当前状态只依赖上一状态← 正确答案
  • B.任一状态可由初始图像直接算出← 正确答案
  • C.每一步必须独立训练网络
  • D.噪声调度影响加噪速度← 正确答案

答案:ABD。马尔可夫链的核心是“A 只依赖上一状态”,正确;由于高斯分布可叠加,任意时刻 \(x_t\) 可由 \(x_0\) 闭式算出,B 正确;网络的训练并不需要逐步独立训练,C 错误;\(\beta_t\) 调度决定每步加噪强度,D 正确。

Q6. DDIM 确定性采样的含义是?

单选题 · mid

  • A.相同起点生成相同结果← 正确答案
  • B.不同起点生成相同结果
  • C.完全不使用任何噪声
  • D.采样过程不可复现

答案:A。DDIM 令 \(\sigma_t=0\),采样路径完全由起点和网络权重决定,因此相同随机种子与起点会得到完全相同的结果,这对复现和可控生成很有价值。B 错在“不同起点”不会相同;C 的“完全无噪声”表述不准确,前向噪声仍然存在;D 与确定性正好相反。

Q7. DDIM 能在减少步数下保持生成质量的数学原因是?

单选题 · hard

  • A.保持前向边际分布一致← 正确答案
  • B.增加网络参数数量
  • C.降低噪声调度方差
  • D.使用更高质量数据集

答案:A。DDIM 构造了一族非马尔可夫前向过程,使得每个时刻的边际分布 \(q(x_t \mid x_0)\) 与 DDPM 保持一致,因此共享同一训练目标的网络可以直接迁移,跳步也不会破坏分布一致性。B、C、D 都不是其数学原理。

Q8. 关于 DDPM 与 DDIM 的联系,正确的有哪些?

多选题 · hard

  • A.两者训练目标完全相同← 正确答案
  • B.DDIM 需要重新训练网络
  • C.两者前向边际分布一致← 正确答案
  • D.两者采样公式完全不同

答案:AC。DDIM 与 DDPM 共享相同的训练目标和网络权重,A 正确、B 错误;DDIM 的核心保证就是边际分布与 DDPM 一致,C 正确;两者可以统一到同一个采样公式中,只是 \(\sigma_t\) 的取值不同,D 错误。

Q9. 扩散模型的反向过程用什么来逼近真实的逆转移分布?

单选题 · easy

  • A.神经网络参数化分布← 正确答案
  • B.简单线性回归模型
  • C.马尔可夫蒙特卡洛
  • D.支持向量机分类器

答案:A。反向过程 \(p_\theta(x_{t-1} \mid x_t)\) 用神经网络参数化高斯分布的均值与方差来逼近真实逆过程。B、D 是传统机器学习模型,表达力不足;C 是采样统计方法,不是函数逼近器。

Q10. 统一采样公式中 \(\sqrt{1-\bar{\alpha}_{t-1}-\sigma_t^2}\,\varepsilon_\theta\) 项的语义是什么?

单选题 · extreme

  • A.尚未去除的残余噪声← 正确答案
  • B.已去除的干净图像
  • C.每步加入的新随机噪声
  • D.训练时的损失值

答案:A。这一项表示从上一时刻到最终状态之间“还没处理完”的噪声方向,它把预测噪声按剩余比例保留在画面中。B 是 \(\hat{x}_0\) 的角色,C 是 \(\sigma_t z\) 的角色,D 与采样公式无关。