欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > 剖析扩散模型(Denoising Diffusion Probabilistic Models)

剖析扩散模型(Denoising Diffusion Probabilistic Models)

2025/5/5 3:00:44 来源:https://blog.csdn.net/m0_46610658/article/details/147493615  浏览:    关键词:剖析扩散模型(Denoising Diffusion Probabilistic Models)

文章目录

  • 1. 前言
  • 2. 前向扩散过程(Forward Diffusion)
  • 3. 反向生成过程(Reverse Process)
  • 4. 训练和推理过程中的伪代码
  • 5. 训练过程代码实现(Training)
    • 5.1 时间嵌入模块——TimeEmbedding
    • 5.2 前向扩散过程——GaussianDiffusionTrainer
  • 6. 推理过程代码实现(Sampling)

1. 前言

  扩散模型参考的论文:Denoising Diffusion Probabilistic Models。扩散模型(Denoising Diffusion Probabilistic Models, DDPM)是一种生成模型,它通过学习逆转一个逐步向数据中添加噪声的过程来生成新数据。这种方法受到了非平衡热力学的启发,其中系统从有序状态逐渐过渡到无序状态(即增加了噪声)。DDPM的目标是学习如何逆转这个过程,从而从完全随机的状态恢复出有意义的数据。扩散模型的基本思想是:通过添加噪声将原始数据逐步扰乱成高斯分布(前向扩散过程),然后学习如何逐步去噪恢复原始数据(反向生成过程)

补充知识(重参数采样(参数重整化))

  1. 如果随机变量 X \mathbf{X} X 服从均值为 μ \mu μ,方差为 σ 2 \sigma^2 σ2 的高斯分布(正态分布),即 X ∼ N ( μ , σ 2 ) \mathbf{X} \sim \mathcal{N}(\mu, \sigma^2) XN(μ,σ2)。对随机变量 X \mathbf{X} X 进行标准化的步骤为: X − μ σ = Z ∼ N ( 0 , 1 ) \frac{\mathbf{X} - \mu}{\sigma} = \mathbf{Z} \sim \mathcal{N}(0, 1) σXμ=ZN(0,1),这里的 Z \mathbf{Z} Z 是一个新的随机变量,随机变量 Z \mathbf{Z} Z 服从均值为0,方差为1的标准正态分布。 X − μ \mathbf{X} - \mu Xμ 表示将原分布中心平移到原点(去中心化),除以 σ \sigma σ 表示将尺度标准化为单位标准差。

  2. 我们先从标准正态分布 Z ∼ N ( 0 , 1 ) \mathbf{Z} \sim \mathcal{N}(0, 1) ZN(0,1) 进行采样,经过线性变换 X = μ + σ ⋅ Z \mathbf{X}=\mu+\sigma\cdot \mathbf{Z} X=μ+σZ 得到的数据分布,等价于直接从均值为 μ \mu μ,方差为 σ 2 \sigma^2 σ2 的正态分布 X ∼ N ( μ , σ 2 ) \mathbf{X} \sim \mathcal{N}(\mu, \sigma^2) XN(μ,σ2) 中进行采样。下面给出具体推导和验证:
    (1)标准正态分布的性质: Z ∼ N ( 0 , 1 ) \mathbf{Z} \sim \mathcal{N}(0, 1) ZN(0,1),即均值为 0,方差为 1。
    (2)线性变换的均值和方差:对 Z \mathbf{Z} Z 进行线性变换 X = μ + σ ⋅ Z \mathbf{X}=\mu+\sigma\cdot \mathbf{Z} X=μ+σZ,均值为: E [ X ] = E [ μ + σ ⋅ Z ] = σ ⋅ E [ Z ] + μ = σ ⋅ 0 + μ = μ \mathbb{E}[\mathbf{X}] =\mathbb{E}[\mathbf{\mu+\sigma\cdot \mathbf{Z}}]= \sigma \cdot \mathbb{E}[\mathbf{Z}] + \mu = \sigma \cdot 0 + \mu = \mu E[X]=E[μ+σZ]=σE[Z]+μ=σ0+μ=μ;方差为: D [ X ] = D [ μ + σ ⋅ Z ] = σ 2 ⋅ D [ Z ] = σ 2 ⋅ 1 = σ 2 \mathbb{D}[\mathbf{X}] =\mathbb{D}[\mathbf{\mu+\sigma\cdot \mathbf{Z}}] = \sigma^2 \cdot \mathbb{D}[\mathbf{Z}] =\sigma^2 \cdot 1= \sigma^2 D[X]=D[μ+σZ]=σ2D[Z]=σ21=σ2
    (3)正态分布的封闭性:线性变换不改变正态性,因此对 Z \mathbf{Z} Z 进行线性变换 X = μ + σ ⋅ Z \mathbf{X}=\mu+\sigma\cdot \mathbf{Z} X=μ+σZ 仍服从正态分布,即 X ∼ N ( μ , σ 2 ) \mathbf{X} \sim \mathcal{N}(\mu, \sigma^2) XN(μ,σ2)

2. 前向扩散过程(Forward Diffusion)

  前向加噪过程是一个固定的马尔可夫链,对干净的原始图像数据 x 0 \mathbf{x}_0 x0 逐步添加噪声依次得到 x 1 , x 2 , … , x T \mathbf{x}_1, \mathbf{x}_2, \ldots,\mathbf{x}_T x1,x2,,xT,直到第 T T T 步时图像数据 x T \mathbf{x}_T xT 接近高斯噪声(纯噪声,完全看不出内容)。下面给出前向扩散过程中的核心数学公式
  (1)基于前一时刻( t − 1 t-1 t1)的图像数据 x t − 1 \mathbf{x}_{t-1} xt1 和当前时刻( t t t)加入的标准高斯噪声 ε t ∼ N ( 0 , 1 ) \pmb{\varepsilon}_{t}\sim \mathcal{N}(0, 1) εtN(0,1),我们可获取 t t t 时刻噪声图像 x t \mathbf{x}_t xt 对应的数学表达式:
x t = 1 − β t x t − 1 + β t ε t ( 1 ) \mathbf{x}_t = \sqrt{1 - \beta_t} \, \mathbf{x}_{t-1} + \sqrt{\beta_t} \, \pmb{\varepsilon}_{t} \qquad \qquad (1) xt=1βt xt1+βt εt(1)

  其中 β t \beta_t βt 是在 ( 0 , 1 ) (0,1) (0,1) 之间逐步增长的噪声强度( β t \beta_t βt 可看作加入的高斯噪声权重),一般来说随着 t t t 的增大, β t \beta_t βt 的取值就越大。图像数据从 x t − 1 \mathbf{x}_{t-1} xt1 x t \mathbf{x}_{t} xt 的加噪过程服从正态分布,如下所示:
q ( x t ∣ x t − 1 ) = N ( x t ; 1 − β t x t − 1 , β t I ) ( 2 ) q(\mathbf{x}_t | \mathbf{x}_{t-1}) = \mathcal{N}(\mathbf{x}_t; \sqrt{1 - \beta_t}\mathbf{x}_{t-1}, \beta_t \mathbf{I}) \qquad \qquad (2) q(xtxt1)=N(xt;1βt xt1,βtI)(2)
  其中, x t \mathbf{x}_t xt 是以均值为 1 − β t x t − 1 \sqrt{1 - \beta_t}\mathbf{x}_{t-1} 1βt xt1,方差为 β t \beta_t βt 的高斯分布, I \mathbf{I} I 表示单位矩阵。

  (2)任意时刻的噪声图像 x t \mathbf{x}_t xt 可直接由 x 0 \mathbf{x}_0 x0 计算(无需迭代),我们定义 α t = 1 − β t \alpha_t=1 - \beta_t αt=1βt,计算公式如下:
x t = α ‾ t x 0 + 1 − α ‾ t ε t ( 3 ) \mathbf{x}_t= \sqrt{\overline{\alpha}_t} \, \mathbf{x}_0 + \sqrt{1 - \overline{\alpha}_t} \, \pmb{\varepsilon}_t \qquad \qquad (3) xt=αt x0+1αt εt(3)

  其中, α ˉ t = ∏ s = 1 t ( 1 − β s ) = ( 1 − β 1 ) ⋅ ( 1 − β 2 ) … ( 1 − β t ) = α 1 ⋅ α 2 … α t \bar{\alpha}_t = \prod_{s=1}^{t}(1 - \beta_s)=(1 - \beta_1)\cdot(1 - \beta_2)\ldots(1 - \beta_t)=\alpha_1 \cdot \alpha_2 \ldots \alpha_t αˉt=s=1t(1βs)=(1β1)(1β2)(1βt)=α1α2αt。图像数据从 x 0 \mathbf{x}_{0} x0 x t \mathbf{x}_{t} xt 的加噪过程服从正态分布,如下所示:
q ( x t ∣ x 0 ) = N ( x t ; α ˉ t x 0 , ( 1 − α ˉ t ) I ) ( 4 ) q(\mathbf{x}_t \mid \mathbf{x}_0) = \mathcal{N}(\mathbf{x}_t; \sqrt{\bar{\alpha}_t}\mathbf{x}_0, (1 - \bar{\alpha}_t)\mathbf{I}) \qquad \qquad (4) q(xtx0)=N(xt;αˉt x0,(1αˉt)I)(4)

  其中, x t \mathbf{x}_t xt 是以均值为 α ˉ t x 0 \sqrt{\bar{\alpha}_t}\mathbf{x}_0 αˉt x0,方差为 ( 1 − α ˉ t ) (1 - \bar{\alpha}_t) (1αˉt) 的高斯分布, I \mathbf{I} I 表示单位矩阵。

3. 反向生成过程(Reverse Process)

  这个过程是一个可学习的马尔可夫链,目标是从纯高斯噪声 x T \mathbf{x}_T xT 开始一步步去噪直到生成清晰的图像。反向去噪的核心数学公式定义为:
x t − 1 = 1 α t ( x t − 1 − α t 1 − α ‾ t ε θ ( x t , t ) ) + σ t z ( 5 ) \mathbf{x}_{t-1} = \frac{1}{\sqrt{\alpha_t}} \left( \mathbf{x}_t - \frac{1 - \alpha_t}{\sqrt{1 - \overline{\alpha}_t}} \, \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) \right) + \sigma_t \mathbf{z} \qquad \qquad (5) xt1=αt 1(xt1αt 1αtεθ(xt,t))+σtz(5)

  其中, ε θ ( x t , t ) \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) εθ(xt,t) 是 UNet网络输出的预测噪声; σ t = β t ⋅ 1 − α ˉ t − 1 1 − α ˉ t \sigma_t=\sqrt{\beta_t \cdot \frac{1 - \bar{\alpha}_{t-1}}{1 - \bar{\alpha}_t}} σt=βt1αˉt1αˉt1 z \mathbf{z} z 是标准高斯噪声 z ∼ N ( 0 , 1 ) \mathbf{z}\sim \mathcal{N}(0, 1) zN(0,1)。高斯噪声数据从 x t \mathbf{x}_{t} xt x t − 1 \mathbf{x}_{t-1} xt1 的去噪过程服从正态分布,如下所示:
p θ ( x t − 1 ∣ x t , x 0 ) = N ( x t − 1 ; 1 α t ( x t − 1 − α t 1 − α ‾ t ε θ ( x t , t ) ) , β t ⋅ 1 − α ˉ t − 1 1 − α ˉ t I ) ( 6 ) p_{\theta}(\mathbf{x}_{t-1} \mid \mathbf{x}_t, \mathbf{x}_0) = \mathcal{N}(\mathbf{x}_{t-1};\frac{1}{\sqrt{\alpha_t}} \left(\mathbf{x}_t - \frac{1 - \alpha_t}{\sqrt{1 - \overline{\alpha}_t}} \, \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) \right), \beta_t \cdot \frac{1 - \bar{\alpha}_{t-1}}{1 - \bar{\alpha}_t}\mathbf{I}) \qquad \qquad (6) pθ(xt1xt,x0)=N(xt1;αt 1(xt1αt 1αtεθ(xt,t)),βt1αˉt1αˉt1I)(6)

  其中, x t − 1 \mathbf{x}_{t-1} xt1 是以均值为 1 α t ( x t − 1 − α t 1 − α ‾ t ε θ ( x t , t ) ) \frac{1}{\sqrt{\alpha_t}} \left( \mathbf{x}_t - \frac{1 - \alpha_t}{\sqrt{1 - \overline{\alpha}_t}} \, \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) \right) αt 1(xt1αt 1αtεθ(xt,t)),方差为 β t ⋅ 1 − α ˉ t − 1 1 − α ˉ t \beta_t \cdot \frac{1 - \bar{\alpha}_{t-1}}{1 - \bar{\alpha}_t} βt1αˉt1αˉt1 的高斯分布, I \mathbf{I} I 表示单位矩阵。

4. 训练和推理过程中的伪代码

在这里插入图片描述

1. 训练流程(Training)
  从上面公式 (3) 可知: x t = α ‾ t x 0 + 1 − α ‾ t ε t \mathbf{x}_t= \sqrt{\overline{\alpha}_t} \, \mathbf{x}_0 + \sqrt{1 - \overline{\alpha}_t} \, \pmb{\varepsilon}_t xt=αt x0+1αt εt,因此 ε θ ( α ‾ t x 0 + 1 − α ‾ t ε , t ) = ε θ ( x t , t ) \pmb{\varepsilon}_{\theta}(\sqrt{\overline{\alpha}_t} \, \mathbf{x}_0 + \sqrt{1 - \overline{\alpha}_t} \, \pmb{\varepsilon},t)=\pmb{\varepsilon}_\theta(\mathbf{x}_t, t) εθ(αt x0+1αt ε,t)=εθ(xt,t) 表示UNet网络在 t t t 时刻输出的预测噪声。

(1)第一步:随机采样时间步数(时间 t t t 不固定),并对时间步数 t t t 进行位置编码。例如 t = 100 t=100 t=100(从1到T=1000中随机选),随后编码—>Time step embedding(t),这里的Time step embedding 使用标准的位置编码方式。
(2)第二步:随机采样标准高斯噪声 ε ∼ N ( 0 , 1 ) \pmb{\varepsilon} \sim \mathcal{N}(0, 1) εN(0,1),利用公式 (3) x t = α ‾ t x 0 + 1 − α ‾ t ε \mathbf{x}_t= \sqrt{\overline{\alpha}_t} \, \mathbf{x}_0 + \sqrt{1 - \overline{\alpha}_t} \, \pmb{\varepsilon} xt=αt x0+1αt ε 对原始图像数据 x 0 \mathbf{x}_0 x0 进行加噪,得到噪声图像 x 100 \mathbf{x}_{100} x100
(3)第三步:将得到的噪声图像 x 100 \mathbf{x}_{100} x100 和编码后的时间步 Time step embedding(t) 共同送到类似于UNet神经网络模型中,Unet网络模型会输出一个预测噪声 ε θ \pmb{\varepsilon}_{\theta} εθ
(4)第四步:计算预测噪声 ε θ \pmb{\varepsilon}_{\theta} εθ 与 真实噪声 ε \pmb{\varepsilon} ε 之间的误差(MSE Loss,使用均方误差损失函数),优化网络参数,使得模型越来越擅长预测噪声。

2. 推理过程(Sampling / Generation)
  推理就是生成新样本的过程,也叫采样,采样过程的核心思想是:逐步(迭代)去噪声。
(1)第一步:假设 T = 1000 \mathbf{T}=1000 T=1000,从标准正态分布中随机生成一张全是高斯噪声的图像 x 1000 \mathbf{x}_{1000} x1000
(2)第二步:把 x 1000 \mathbf{x}_{1000} x1000 t = 1000 \mathbf{t}=1000 t=1000 输入到 UNet 网络中来获取预测噪声 ε θ \pmb{\varepsilon}_{\theta} εθ
(3)第三步:将预测的噪声 ε θ \pmb{\varepsilon}_{\theta} εθ ε θ ( x t , t ) \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) εθ(xt,t)) 代入公式 (5) x t − 1 = 1 α t ( x t − 1 − α t 1 − α ‾ t ε θ ( x t , t ) ) + σ t z \mathbf{x}_{t-1} = \frac{1}{\sqrt{\alpha_t}} \left( \mathbf{x}_t - \frac{1 - \alpha_t}{\sqrt{1 - \overline{\alpha}_t}} \, \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) \right) + \sigma_t \mathbf{z} xt1=αt 1(xt1αt 1αtεθ(xt,t))+σtz 中,获取前一时刻 x 999 \mathbf{x}_{999} x999 的图像数据;
(4)第四步:逐步迭代,将 x T = x 999 , x 998 , … , x 2 \mathbf{x}_{T}=\mathbf{x}_{999},\mathbf{x}_{998}, \ldots,\mathbf{x}_{2} xT=x999,x998,,x2 t = 999 , 998 , … , 2 \mathbf{t}=999,998,\ldots,2 t=999,998,,2 依次输入到 UNet 网络模型中来获取预测噪声,直到获得 x 1 \mathbf{x}_{1} x1 时的图像数据。
(5)第五步:将 x 1 \mathbf{x}_{1} x1 t = 1 \mathbf{t}=1 t=1 输入到 UNet 网络中来预测噪声 ε θ \pmb{\varepsilon}_{\theta} εθ,接着将预测的噪声 ε θ \pmb{\varepsilon}_{\theta} εθ ε θ ( x t , t ) \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) εθ(xt,t)) 代入 x t − 1 = 1 α t ( x t − 1 − α t 1 − α ‾ t ε θ ( x t , t ) ) \mathbf{x}_{t-1} = \frac{1}{\sqrt{\alpha_t}} \left( \mathbf{x}_t - \frac{1 - \alpha_t}{\sqrt{1 - \overline{\alpha}_t}} \, \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) \right) xt1=αt 1(xt1αt 1αtεθ(xt,t)) 中,获取图像数据 x 0 \mathbf{x}_{0} x0

5. 训练过程代码实现(Training)

参考来源:【代码精读】Diffusion Model 扩散模型

5.1 时间嵌入模块——TimeEmbedding

class TimeEmbedding(nn.Module):"""定义``时间嵌入``模块"""def __init__(self, T, d_model, dim):"""初始的time-embedding是由一系列不同频率的正弦、余弦函数采样值表示,即:[[sin(w_0*x), cos(w_0*x)],[sin(w_1*x), cos(w_1*x)],...,[sin(w_T)*x, cos(w_T*x)]], 维度为 T * d_model在本实例中,频率范围是[0:T], x在1e-4~1范围,共d_model // 2个离散点;将sin, cos并在一起组成d_model个离散点Args:T: int, 总迭代步数,本实例中T=1000d_model: 输入维度(通道数/初始embedding长度)dim: 输出维度(通道数)"""assert d_model % 2 == 0super().__init__()# 前两行计算x向量,共64个点emb = torch.arange(0, d_model, step=2) / d_model * math.log(10000)emb = torch.exp(-emb)# T个时间位置组成频率部分pos = torch.arange(T).float()# 两两相乘构成T*(d_model//2)的矩阵,并assert形状emb = pos[:, None] * emb[None, :]assert list(emb.shape) == [T, d_model // 2]# 计算不同频率sin, cos值,判断形状,并reshape到T*d_modelemb = torch.stack([torch.sin(emb), torch.cos(emb)], dim=-1)assert list(emb.shape) == [T, d_model // 2, 2]emb = emb.view(T, d_model)# MLP层,通过初始编码计算提取特征后的embedding# 包含两个线性层,第一个用swish激活函数,第二个不使用激活函数self.timembedding = nn.Sequential(nn.Embedding.from_pretrained(emb),nn.Linear(d_model, dim),Swish(),nn.Linear(dim, dim),)self.initialize()def initialize(self):for module in self.modules():if isinstance(module, nn.Linear):init.xavier_uniform_(module.weight)init.zeros_(module.bias)def forward(self, t):emb = self.timembedding(t)return emb

  TimeEmbedding 类是扩散模型中用于生成时间步嵌入的核心组件,其作用是将离散的时间步转换为富含时序信息的连续向量表示。扩散模型通过多步迭代逐步去噪,模型需感知当前所处的时间步以调整去噪行为。TimeEmbedding 将时间步编码为高维向量,使模型能捕获不同时间步的动态特征。其设计借鉴了Transformer的位置编码思想,但针对扩散模型的时间特性进行了调整。

5.2 前向扩散过程——GaussianDiffusionTrainer

# ``extract``函数的作用是从v这一序列中按照索引t取出需要的数,然后reshape到输入数据x的维度
def extract(v, t, x_shape):"""Extract some coefficients at specified timesteps, then reshape to[batch_size, 1, 1, 1, 1, ...] for broadcasting purposes."""device = t.device# ``torch.gather``的用法建议看https://zhuanlan.zhihu.com/p/352877584的第一条评论# 在此处的所有调用实例中,v都是一维,可以看作是索引取值,即等价v[t], t大小为[batch_size, 1]out = torch.gather(v, index=t, dim=0).float().to(device)# 再把索引到的值reshape到[batch_size, 1, 1, ...], 维度和x_shape相同return out.view([t.shape[0]] + [1] * (len(x_shape) - 1))# ``GaussianDiffusionTrainer``包含了Diffusion Model的前向过程(加噪) & 训练过程
class GaussianDiffusionTrainer(nn.Module):def __init__(self, model, beta_1, beta_T, T):"""初始化前向模型Args:model: 骨干模型,主流为U-Net+Attentionbeta_1: beta的起始值,本实例中取1e-4beta_T: bata在t=T时的值,本实例中取0.2T: 时间步数, 本实例中取1000"""super().__init__()# 参数赋值self.model = modelself.T = T# 等间隔得到beta_1到beta_T之间共T个step对应的beta值,组成序列存为类成员(后边可以用``self.betas``访问)self.register_buffer('betas', torch.linspace(beta_1, beta_T, T).double())# 根据公式,令alphas = 1 - betasalphas = 1. - self.betas# 根据公式,计算alpha连乘结果,存为alphas_bar# ``torch.cumprod``用于计算一个序列每个数与其前面所有数连乘的结果,得到一个序列,长度等于原序列长度# 例如:# a = torch.tensor([2,3,1,4])# b = torch.cumprod(a, dim=0)其实就等于torch.tensor([2, 2*3, 2*3*1, 2*3*1*4]) = torch.tensor([2, 6, 6, 24])alphas_bar = torch.cumprod(alphas, dim=0)# calculations for diffusion q(x_t | x_{t-1}) and others# 根据公式计算sqrt(alphas_bar)以及sqrt(1-alphas_bar)分别作为正向扩散的均值和标准差,存入类成员# 可用``self.sqrt_alphas_bar``和``sqrt_one_minus_alphas_bar``来访问self.register_buffer('sqrt_alphas_bar', torch.sqrt(alphas_bar))self.register_buffer('sqrt_one_minus_alphas_bar', torch.sqrt(1. - alphas_bar))def forward(self, x_0):"""Algorithm 1."""# 从0~T中随机选batch_size个时间点t = torch.randint(self.T, size=(x_0.shape[0], ), device=x_0.device)# 参数重整化技巧,先生成均值为0方差为1的高斯分布,再通过乘标准差加均值的方式用于间接采样noise = torch.randn_like(x_0)x_t = (extract(self.sqrt_alphas_bar, t, x_0.shape) * x_0 +extract(self.sqrt_one_minus_alphas_bar, t, x_0.shape) * noise)# 做一步反向扩散,希望模型可以预测出加入的噪声,也就是公式中的z_tloss = F.mse_loss(self.model(x_t, t), noise, reduction='none')return loss

  GaussianDiffusionTrainer 类用于实现扩散模型的 前向过程(加噪) 和 训练目标(噪声预测)。核心思想是逐步向输入数据 x 0 x_0 x0 添加高斯噪声,并训练模型预测噪声。extract 函数用于从序列 v 中按索引 t 提取值,并将其形状调整为与输入数据 x 的维度匹配,以便广播计算。

  1. 计算 α t \alpha_t αt β t \beta_t βt
      假设 beta_1=0.0001beta_T=0.02,通过代码 self.register_buffer('betas', torch.linspace(beta_1, beta_T, T).double()) 可得到 β t \beta_t βt = self.betas,那么 α t \alpha_t αt = 1. - self.betas β t \beta_t βt α t \alpha_t αt 的形状大小均为 (1000, )

  2. 计算 α ˉ t \bar{\alpha}_t αˉt
      通过代码 torch.cumprod(alphas, dim=0) 来获取 α ˉ t \bar{\alpha}_t αˉt = alphas_bar

  3. 计算 α ˉ t \sqrt{\bar{\alpha}_t} αˉt 1 − α ˉ t \sqrt{1-\bar{\alpha}_t} 1αˉt
       α ˉ t \sqrt{\bar{\alpha}_t} αˉt = self.sqrt_alphas_bar 1 − α ˉ t \sqrt{1-\bar{\alpha}_t} 1αˉt = self.sqrt_one_minus_alphas_bar

  4. 随机采样时间步数
      t = torch.randint(self.T, size=(x_0.shape[0], ), device=x_0.device),假设 self.T = 1000、batch_size = 8,那么时间 t 的取值范围是 [0, 1000),例如 t = tensor([902, 605, 411, 926, 894, 297, 147, 79], device=‘cuda:0’)

  5. 利用公式(3)计算 x t \mathbf{x}_t xt

x_t = (extract(self.sqrt_alphas_bar, t, x_0.shape) * x_0 +extract(self.sqrt_one_minus_alphas_bar, t, x_0.shape) * noise)
  1. UNet 网络预测噪声
      通过代码 self.model(x_t, t) 可得到预测噪声 ε θ \pmb{\varepsilon}_{\theta} εθ ε θ \pmb{\varepsilon}_{\theta} εθ 的shape为 [8, 3, 32, 32]。随后利用均方误差损失函数计算损失——loss = F.mse_loss(self.model(x_t, t), noise, reduction='none')

6. 推理过程代码实现(Sampling)

class GaussianDiffusionSampler(nn.Module):def __init__(self, model, beta_1, beta_T, T):"""所有参数含义和``GaussianDiffusionTrainer``(前向过程)一样"""super().__init__()self.model = modelself.T = T# 这里获取betas, alphas以及alphas_bar和前向过程一模一样self.register_buffer('betas', torch.linspace(beta_1, beta_T, T).double())alphas = 1. - self.betasalphas_bar = torch.cumprod(alphas, dim=0)# 这一步是方便后面运算,相当于构建alphas_bar{t-1}alphas_bar_prev = F.pad(alphas_bar, [1, 0], value=1)[:T]  # 把alpha_bar的第一个数字换成1,按序后移# 根据公式,后向过程中的计算均值需要用到的系数用coeff1和coeff2表示self.register_buffer('coeff1', torch.sqrt(1. / alphas))self.register_buffer('coeff2', self.coeff1 * (1. - alphas) / torch.sqrt(1. - alphas_bar))# 根据公式,计算后向过程的方差self.register_buffer('posterior_var', self.betas * (1. - alphas_bar_prev) / (1. - alphas_bar))def predict_xt_prev_mean_from_eps(self, x_t, t, eps):"""该函数用于反向过程中,条件概率分布q(x_{t-1}|x_t)的均值Args:x_t: 迭代至当前步骤的图像t: 当前步数eps: 模型预测的噪声,也就是z_tReturns:x_{t-1}的均值,mean = coeff1 * x_t - coeff2 * eps"""assert x_t.shape == eps.shapereturn (extract(self.coeff1, t, x_t.shape) * x_t -extract(self.coeff2, t, x_t.shape) * eps)def p_mean_variance(self, x_t, t):"""该函数用于反向过程中,计算条件概率分布q(x_{t-1}|x_t)的均值和方差Args:x_t: 迭代至当前步骤的图像t: 当前步数Returns:xt_prev_mean: 均值var: 方差"""# below: only log_variance is used in the KL computations# 这一步我略有不解,为什么要把算好的反向过程的方差大部分替换成betas。# 我猜测,后向过程方差``posterior_var``的计算过程仅仅是betas乘上一个(1 - alpha_bar_{t-1}) / (1 - alpha_bar_{t}),# 由于1 - alpha_bar_{t}这个数值非常趋近于0,分母为0会导致nan,# 而整体(1 - alpha_bar_{t-1}) / (1 - alpha_bar_{t})非常趋近于1,所以直接用betas近似后向过程的方差,# 但是t = 1 的时候(1 - alpha_bar_{0}) / (1 - alpha_bar_{1})还不是非常趋近于1,所以这个数值要保留,# 因此就有拼接``torch.cat([self.posterior_var[1:2], self.betas[1:]])``这一步var = torch.cat([self.posterior_var[1:2], self.betas[1:]])var = extract(var, t, x_t.shape)# 模型前向预测得到eps(也就是z_t)eps = self.model(x_t, t)# 计算均值xt_prev_mean = self.predict_xt_prev_mean_from_eps(x_t, t, eps=eps)return xt_prev_mean, vardef forward(self, x_T):"""Algorithm 2."""# 反向扩散过程,从x_t迭代至x_0x_t = x_Tfor time_step in reversed(range(self.T)):print(time_step)# t = [1, 1, ....] * time_step, 长度为batch_sizet = x_t.new_ones([x_T.shape[0], ], dtype=torch.long) * time_step# 计算条件概率分布q(x_{t-1}|x_t)的均值和方差mean, var= self.p_mean_variance(x_t=x_t, t=t)# no noise when t == 0# 最后一步的高斯噪声设为0(我认为不设为0问题也不大,就本实例而言,t=0时的方差已经很小了)if time_step > 0:noise = torch.randn_like(x_t)else:noise = 0x_t = mean + torch.sqrt(var) * noiseassert torch.isnan(x_t).int().sum() == 0, "nan in tensor."x_0 = x_t# ``torch.clip(x_0, -1, 1)``,把x_0的值限制在-1到1之间,超出部分截断return torch.clip(x_0, -1, 1)
  1. 获取 α t \alpha_t αt β t \beta_t βt α ˉ t \bar{\alpha}_t αˉt α ˉ t − 1 \bar{\alpha}_{t-1} αˉt1
       β t \beta_t βt = self.betas α t \alpha_t αt = 1. - self.betas α ˉ t \bar{\alpha}_t αˉt = alphas_bar α ˉ t − 1 \bar{\alpha}_{t-1} αˉt1 = alphas_bar_prev

  2. 获取系数
      self.coeff1 = 1 α t \frac{1}{\sqrt{\alpha_t}} αt 1self.coeff2 = 1 α t ( 1 − α t 1 − α ‾ t ) \frac{1}{\sqrt{\alpha_t}} \left( \frac{1 - \alpha_t}{\sqrt{1 - \overline{\alpha}_t}}\right) αt 1(1αt 1αt)self.posterior_var = β t ⋅ 1 − α ˉ t − 1 1 − α ˉ t \beta_t \cdot \frac{1 - \bar{\alpha}_{t-1}}{1 - \bar{\alpha}_t} βt1αˉt1αˉt1

  3. 获取前一时刻高斯噪声 x t − 1 \mathbf{x}_{t-1} xt1 的方差和均值
       通过代码 var = torch.cat([self.posterior_var[1:2], self.betas[1:]]) 来获取方差 β t ⋅ 1 − α ˉ t − 1 1 − α ˉ t \beta_t \cdot \frac{1 - \bar{\alpha}_{t-1}}{1 - \bar{\alpha}_t} βt1αˉt1αˉt1
       通过代码 eps = self.model(x_t, t) 来获取网络的预测噪声 ε θ ( x t , t ) \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) εθ(xt,t)
       通过代码 xt_prev_mean = self.predict_xt_prev_mean_from_eps(x_t, t, eps=eps) 来获取预测的均值 1 α t ( x t − 1 − α t 1 − α ‾ t ε θ ( x t , t ) ) \frac{1}{\sqrt{\alpha_t}} \left( \mathbf{x}_t - \frac{1 - \alpha_t}{\sqrt{1 - \overline{\alpha}_t}} \, \pmb{\varepsilon}_\theta(\mathbf{x}_t, t) \right) αt 1(xt1αt 1αtεθ(xt,t))

  4. 计算前一时刻的高斯噪声 x t − 1 \mathbf{x}_{t-1} xt1
      通过代码 x_t = mean + torch.sqrt(var) * noise 来计算前一时刻的高斯噪声 x t − 1 \mathbf{x}_{t-1} xt1 ,该代码对应于公式(5)。

参考博客:
DDPM解读(一)| 数学基础,扩散与逆扩散过程和训练推理方法
超详细的扩散模型(Diffusion Models)原理+代码
扩散模型 (Diffusion Model) 简要介绍与源码分析

参考视频:
2025版最新的Diffusion Model | 扩散模型原理及代码实现,3小时快速上手!(附带源码)
大白话AI | 图像生成模型DDPM | 扩散模型 | 生成模型 | 概率扩散去噪生成模型
添【大白话01】一文理清 Diffusion Model 扩散模型 | 原理图解+公式推导
【较真系列】讲人话-Diffusion Model全解(原理+代码+公式)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词