Denoising Diffusion Probabilistic Models去噪扩散模型(DDPM)
Denoising Diffusion Probabilistic Models去噪扩散模型(DDPM)
2024/2/28
论文链接:Denoising Diffusion Probabilistic Models(neurips.cc)
这篇文章对DDPM写个大概,公式推导会放在以后的文章里。
一、引言 Introduction
各类深度生成模型在多种数据模态上展示了高质量的样本。生成对抗网络(GANs)、自回归模型、流模型和变分自编码器(VAEs)已经合成了引人注目的图像和音频样本。此外,在基于能量的建模和得分匹配方面也取得了显著进展,生成的图像与GANs生成的图像相当。
扩散概率模型是一个参数化马尔科夫链,使用变分推断(Variational Inference)进行训练,以便在有限时间内产生于数据相匹配的样本。这个链的转移是学习来逆转扩散过程的,扩散过程是一种马尔可夫链,它逐渐向与采样相反的方向添加噪声到数据中,直到信号被破坏。当扩散包含的是少量的高斯噪声时,只需将采样链转移设置为条件高斯分布,这样就可以实现一个特别简单的神经网络参数化。
变分推断(Variational Inference):这是一种用于估计概率模型参数的统计方法。它通过优化一个目标函数来近似真实的后验分布,这个目标函数通常是真实后验分布与一个易于计算的分布(变分分布)之间的差异。
流模型(Flows):流模型是一种生成模型,它通过一系列可逆的变换(称为流)将数据从高维空间映射到低维空间,然后再映射回高维空间,以生成新的数据样本。流模型的优势在于其变换是可逆的,这有助于保持数据的多样性。
能量基建模(Energy-based Modeling):这是一种基于能量函数的建模方法,通常用于二分类问题。能量函数定义了输入数据与特定标签的不匹配程度。在图像生成的背景下,能量基模型可以用来评估和改进生成图像的质量。
得分匹配(Score Matching):这是一种用于训练生成模型的技术,特别是在概率密度估计中。它涉及计算真实数据分布的得分函数,并使生成模型的得分函数与之匹配,以此来提高生成样本的质量。
二、模型具体细节
扩散是指物质粒子从高浓度区域向低浓度区域移动的过程,扩散模型的灵感来自非平衡热力学,扩散模型想做的就是通过向图片中加入高斯噪声模拟这个过程,最后通过逆向过程从随机噪声中生成图片。
2.1 前向加噪
我们需要进行随机采样生成和图片尺寸大小相同的噪声图片。噪声图片中所有通道数值遵从正态分布。我们根据
其中,
为原始图片, 是高斯噪声, 是一个介于[0.0,1.0]之间的数字,用于产生 和 前的系数。
我们输入
输入
......
以此类推,我们可以得到前一时刻与后一时刻的关系:
其中
有:
随着步长
过程如上图所示,上诉过程有一个很好的特性,可以使用重参数化技巧(reparameterization trick)(参见VAE),在任何任意时间步长
为了简化后续的推导,我们引入一个新变量
接下来需要思考的是通过公式能否使
向后推,得到:
其中,
当我们合并两个具有不同方差的高斯量 和 时,新的分布是 ,这里合并的标准差是
经过推导我们可以得到公式:
通常,当样本变得更嘈杂时,我们可以承受更大的更新步骤,因此
2.2 反向过程
反向过程的目的是将有噪声的图片恢复成原始图片,如果我们可以反转上述过程,从
根据公式:
我们可以得到
其中
从中我们可以得知
这里只要我们知道了
根据实验可知,
反向过程通过
其中
2.3 Loss损失
文中对负对数似然上优化了ELBO(来自琴生不等式)
损失可以按如下方式重写:
因为我们保持
2.4 计算
在给定初始
论文中设置
然后,
对于给定的噪声
这里,
使用模型重新参数化以预测噪声
其中
这里给定,
用来训练预测噪声。
2.5 简化损失
这在
丢弃权重
三、代码实现
Denoise Diffusion 降噪扩散
1. 代码解析
1. 初始化
注意:以下代码块都是在DenoiseDiffusion
类中
eps_model
是
n_steps
是
device
是放置常量的设备
class DenoiseDiffusion:
def __init__(self, eps_model: nn.Module, n_steps: int, device: torch.device):
super().__init__()
self.eps_model = eps_model
self.beta = torch.linspace(0.0001, 0.02, n_steps).to(device)
self.alpha = 1. - self.beta
self.alpha_bar = torch.cumprod(self.alpha, dim=0)
self.n_steps = n_steps
self.sigma2 = self.beta
为了方便代码理解,这里将class DenoiseDiffusion
拆分进行解释,理解代码每一步在做什么。
self.beta = torch.linspace(0.0001, 0.02, n_steps).to(device)
这里是生成了一个tensor,该tensor包含n_steps
个数据,包含从 0.0001
到 0.02
的等间隔数值,代表了公式中的
self.alpha = 1. - self.beta
代表
self.alpha_bar = torch.cumprod(self.alpha, dim=0)
代表
self.n_steps = n_steps
代表
self.sigma2 = self.beta
代表
2. 获取 分布
关于公式
#该函数返回一个包含两个张量的元组
def q_xt_x0(self, x0: torch.Tensor, t: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
mean = gather(self.alpha_bar, t) ** 0.5 * x0
var = 1 - gather(self.alpha_bar, t)
return mean, var
gather
这个操作会根据 t
中的索引从 self.alpha_bar
中提取元素。t
是索引张量,包含了要提取的元素的索引。
mean = gather(self.alpha_bar, t) ** 0.5 * x0
计算
var = 1 - gather(self.alpha_bar, t)
计算
3. 来自 的样本
关于公式
def q_sample(self, x0: torch.Tensor, t: torch.Tensor, eps: Optional[torch.Tensor] = None):
if eps is None:
eps = torch.randn_like(x0)
mean, var = self.q_xt_x0(x0, t)
return mean + (var ** 0.5) * eps
上述代码中if eps is None:
所包含的内容代表
mean, var = self.q_xt_x0(x0, t)
代表获取
最后返回来自
4. 来自 的样本
这段代码实现公式
def p_sample (self, xt: torch.Tensor, t: torch.Tensor):
eps_theta = self.eps_model(xt, t)
alpha_bar = gather(self.alpha_bar, t)
alpha = gather(self.alpha, t)
eps_coef = (1 - alpha) / (1 - alpha_bar) ** .5
mean = 1 / (alpha ** 0.5) * (xt - eps_coef * eps_theta)
var = gather(self.sigma2, t)
eps = torch.randn(xt.shape, device=xt.device)
return mean + (var ** .5) * eps
上述代码中,eps_theta = self.eps_model(xt, t)
表示
alpha_bar = gather(self.alpha_bar, t)
是在收集
alpha = gather(self.alpha, t)
表示
eps_coef = (1 - alpha) / (1 - alpha_bar) ** .5
表示
mean = 1 / (alpha ** 0.5) * (xt - eps_coef * eps_theta)
计算的是
var = gather(self.sigma2, t)
表示的是
eps = torch.randn(xt.shape, device=xt.device)
代表
最后return mean + (var ** .5) * eps
返回样本。
5. 简化损失
这段代码实现的是
def loss(self, x0: Tensor, noise: Optional[torch.Tensor] = None):
batch_size - x0.shape[0]
t = torch.randint(0, self.n_steps, (batch_size,), device=x0.device, dtype=torch.long)
if noise is None:
noise = torch.randn_like(x0)
xt = self.q_sample(x0, t, eps=noise)
eps_theta = self.eps_model(xt, t)
return F.mse_loss(noise, eps_theta)
上述代码中,batch_size - x0.shape[0]
是为了获取批量大小。
t = torch.randint(0, self.n_steps, (batch_size,), device=x0.device, dtype=torch.long)
是对批次中的每个样品得到随机的
if noise is None:
中的代表着
xt = self.q_sample(x0, t, eps=noise)
中xt
是
eps_theta = self.eps_model(xt, t)
是获取公式
最后return F.mse_loss(noise, eps_theta)
返回MSE损失。
2. 完整代码
下面是完整的Denoise Diffusion代码
from typing import Tuple, Optional
import torch
import torch.nn.functional as F
import torch.utils.data
from torch import nn
from labml_nn.diffusion.ddpm.utils import gather
class DenoiseDiffusion:
"""
## Denoise Diffusion
"""
def __init__(self, eps_model: nn.Module, n_steps: int, device: torch.device):
super().__init__()
self.eps_model = eps_model
self.beta = torch.linspace(0.0001, 0.02, n_steps).to(device)
self.alpha = 1. - self.beta
self.alpha_bar = torch.cumprod(self.alpha, dim=0)
self.n_steps = n_steps
self.sigma2 = self.beta
def q_xt_x0(self, x0: torch.Tensor, t: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
mean = gather(self.alpha_bar, t) ** 0.5 * x0
var = 1 - gather(self.alpha_bar, t)
return mean, var
def q_sample(self, x0: torch.Tensor, t: torch.Tensor, eps: Optional[torch.Tensor] = None):
if eps is None:
eps = torch.randn_like(x0)
mean, var = self.q_xt_x0(x0, t)
return mean + (var ** 0.5) * eps
def p_sample(self, xt: torch.Tensor, t: torch.Tensor):
eps_theta = self.eps_model(xt, t)
alpha_bar = gather(self.alpha_bar, t)
alpha = gather(self.alpha, t)
eps_coef = (1 - alpha) / (1 - alpha_bar) ** .5
mean = 1 / (alpha ** 0.5) * (xt - eps_coef * eps_theta)
var = gather(self.sigma2, t)
eps = torch.randn(xt.shape, device=xt.device)
return mean + (var ** .5) * eps
def loss(self, x0: torch.Tensor, noise: Optional[torch.Tensor] = None):
batch_size = x0.shape[0]
t = torch.randint(0, self.n_steps, (batch_size,), device=x0.device, dtype=torch.long)
if noise is None:
noise = torch.randn_like(x0)
xt = self.q_sample(x0, t, eps=noise)
eps_theta = self.eps_model(xt, t)
return F.mse_loss(noise, eps_theta)
参考文献
[1].Diffusion Models 10 篇必读论文(1)DDPM - 知乎 (zhihu.com)
[2].去噪扩散模型
[3].[What are Diffusion Models? | Lil'Log (lilianweng.github.io)](https://lilianweng.github.io/posts/2021-07-11-diffusion-models/#:~:text=Diffusion models are inspired by,data samples from the noise)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~