前言 最近 AI 绘图非常的火,其背后用到的核心技术之一就是 Diffusion Model(扩散模型),虽然想要完全弄懂 Diffusion Model 和其中复杂的公式推导需要掌握比较多的前置数学知识,但这并不妨碍我们去理解其原理。接下来会以笔者所理解的角度去讲解什么是 Diffusion Model。
本文转载自GiantPandaCV
作者丨梁德澎欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。
计算机视觉入门1v3辅导班
什么是 Diffusion Model
前向 Diffusion 过程
Diffusion Model 首先定义了一个前向扩散过程,总共包含T个时间步,如下图所示:![](https://picx.zhimg.com/80/v2-18001eaf3bffdb9376842dc3c3b3b3ce_1440w.png)
![](https://pic1.zhimg.com/80/v2-d18d7c258aa2d105ec1377171a33e087_1440w.png)
# https://pytorch.org/docs/stable/generated/torch.linspace.html betas = torch.linspace(start=0.0001, end=0.02, steps=1000)然后在采样得到 xt 的时候并不是直接通过高斯分布 q(xt|xt-1) 采样,而是用了一个重参数化的技巧(详见参考资料[4]第5页)。 简单来说就是,如果想要从一个任意的均值 μ 方差 σ^2 的高斯分布中采样
![](https://picx.zhimg.com/80/v2-3db02ffb5557a03c120a630de5fb2ae0_1440w.png)
![](https://picx.zhimg.com/80/v2-4c78cb26254aee5eae1e44dcd1c22be8_1440w.png)
![](https://picx.zhimg.com/80/v2-d18d7c258aa2d105ec1377171a33e087_1440w.png)
# https://pytorch.org/docs/stable/generated/torch.randn_like.html betas = torch.linspace(start=0.0001, end=0.02, steps=1000) noise = torch.randn_like(x_0) xt = sqrt(1-betas[t]) * xt-1 + sqrt(betas[t]) * noise然后前向扩散过程还有个属性,就是可以直接从 x0 采样得到中间任意一个时间步的噪声图片 xt,公式如下:
![](https://pic1.zhimg.com/80/v2-0e52434d195ed9626ee682b600507eb2_1440w.png)
![](https://picx.zhimg.com/80/v2-bed31979aaed2a16f19b85d047acc2cf_1440w.png)
![](https://pic1.zhimg.com/80/v2-574202eea8b419c143c1bdb0d396e61f_1440w.png)
betas = torch.linspace(start=0.0001, end=0.02, steps=1000) alphas = 1 - betas # cumprod 相当于为每个时间步 t 计算一个数组 alphas 的前缀乘结果 # https://pytorch.org/docs/stable/generated/torch.cumprod.html alphas_cum = torch.cumprod(alphas, 0) alphas_cum_s = torch.sqrt(alphas_cum) alphas_cum_sm = torch.sqrt(1 - alphas_cum) # 应用重参数化技巧采样得到 xt noise = torch.randn_like(x_0) xt = alphas_cum_s[t] * x_0 + alphas_cum_sm[t] * noise通过上述的讲解,读者应该对 Diffusion Model 的前向扩散过程有比较清晰的理解了。 不过我们的目的不是要做图像生成吗? 现在只是从数据集中的真实图片得到一张噪声图,那具体是怎么做图像生成呢?
反向 Diffusion 过程
![](https://pic1.zhimg.com/80/v2-1d31c678e572118d53c8385d5623bdd7_1440w.png)
![](https://picx.zhimg.com/80/v2-a2530396fbcde084648ea53c05638725_1440w.png)
![](https://pic1.zhimg.com/80/v2-5e1d317b4fc513aa56b4e95585dadd96_1440w.png)
betas = torch.linspace(start=0.0001, end=0.02, steps=1000) alphas = 1 - betas alphas_cum = torch.cumprod(alphas, 0) alphas_cum_prev = torch.cat((torch.tensor([1.0]), alphas_cum[:-1]), 0) posterior_variance = betas * (1 - alphas_cum_prev) / (1 - alphas_cum)然后看均值的计算,
![](https://pic1.zhimg.com/80/v2-b77cd3b18f3374b92bdcfca85bad50ca_1440w.png)
Diffusion Model 训练目标
当一个概率分布q 求解困难的时候,我们可以换个思路(详见参考资料[5,6])。 通过人为构造一个新的分布 p,然后目标就转为缩小分布 p 和 q 之间差距。 通过不断修改 p 的参数去缩小差距,当 p 和 q 足够相似的时候就可以替代 q 了。 然后回到反向 Diffusion 过程,由于后验分布 q(xt-1|xt, x0) 没法直接求解。![](https://pic1.zhimg.com/80/v2-55121eb929d2eb102503fff1b5c91a3e_1440w.png)
![](https://pica.zhimg.com/80/v2-0efd8ad8d9a904ad923a8e8c31fe7e42_1440w.png)
![](https://picx.zhimg.com/80/v2-b2ab4c30fb49b786f4ac549b7e17ce91_1440w.png)
![](https://pica.zhimg.com/80/v2-863d978fc59fe6ff01237a658715bcb5_1440w.png)
![](https://picx.zhimg.com/80/v2-0e52434d195ed9626ee682b600507eb2_1440w.png)
![](https://picx.zhimg.com/80/v2-23b041b7669715f7c74c28e2cc54bfcf_1440w.png)
![](https://pic1.zhimg.com/80/v2-aac99fab091b2a0c29c56a3c23a496b1_1440w.png)
![](https://pic1.zhimg.com/80/v2-98e670f8d3b49afabb4610f8e2e138f5_1440w.png)
![](https://picx.zhimg.com/80/v2-1ec41277f9b9ef984cfd031c0939020e_1440w.png)
![](https://picx.zhimg.com/80/v2-024567f3ae61d6a8fa0f2b3a34807609_1440w.png)
![](https://picx.zhimg.com/80/v2-5bf3ef130cb1024bd26bd6eeb2a01325_1440w.png)
![](https://picx.zhimg.com/80/v2-bebccb3efc88c70d83cf7168ed9f2704_1440w.png)
betas = torch.linspace(start=0.0001, end=0.02, steps=1000) alphas = 1 - betas alphas_cum = torch.cumprod(alphas, 0) alphas_cum_s = torch.sqrt(alphas_cum) alphas_cum_sm = torch.sqrt(1 - alphas_cum) def diffusion_loss(model, x0, t, noise): # 根据公式计算 xt xt = alphas_cum_s[t] * x0 + alphas_cum_sm[t] * noise # 模型预测噪声 predicted_noise = model(xt, t) # 计算Loss return mse_loss(predicted_noise, noise) for i in len(data_loader): # 从数据集读取一个 batch 的真实图片 x0 = next(data_loader) # 采样时间步 t = torch.randint(0, 1000, (batch_size,)) # 生成高斯噪声 noise = torch.randn_like(x_0) loss = diffusion_loss(model, x0, t, noise) optimizer.zero_grad() loss.backward() optimizer.step()
Diffusion Model 生成图像过程
模型训练好之后,在真实的推理阶段就必须从时间步 T 开始往前逐步生成图片,算法描述如下:![](https://picx.zhimg.com/80/v2-c0ee0571f0b8f3ff4879f487d8ab0f10_1440w.png)
改进 Diffusion Model
文章 [3] 中对 Diffusion Model 提出了一些改进点。对方差 βt 的改进
前面提到 βt 的生成是将一个给定范围均匀的分成 T 份,然后每个时间步对应其中的某个点:betas = torch.linspace(start=0.0001, end=0.02, steps=1000)然后文章 [3] 通过实验观察发现,采用这种方式生成方差 βt 会导致一个问题,就是做前向扩散的时候到靠后的时间步噪声加的太多了。 这样导致的结果就是在前向过程靠后的时间步,在反向生成采样的时候并没有产生太大的贡献,即使跳过也不会对生成结果有多大的影响。 接着论文[3] 中就提出了新的 βt 生成策略,和原策略在前向扩散的对比如下图所示:
![](https://pic1.zhimg.com/80/v2-5032fdaab309c1405e787c0464d8a31e_1440w.png)
![](https://pic1.zhimg.com/80/v2-9041c537334021e352dba8b88d8b424f_1440w.png)
![](https://pic1.zhimg.com/80/v2-0aafaf9610f2697ee20f889cde1fa7aa_1440w.png)
![](https://picx.zhimg.com/80/v2-fde0d42f91a6c2c36a921164cb79f75f_1440w.png)
T = 1000 s = 8e-3 ts = torch.arange(T + 1, dtype=torch.float64) / T + s alphas = ts / (1 + s) * math.pi / 2 alphas = torch.cos(alphas).pow(2) alphas = alphas / alphas[0] betas = 1 - alphas[1:] / alphas[:-1] betas = betas.clamp(max=0.999)
对生成过程时间步数的改进
原本模型训练的时候是假定在 T个时间步下训练的,在生成图像的时候,也必须从 T 开始遍历到 1 。而论文 [3] 中提出了一种不需要重新训练就可以减少生成步数的方法,从而显著提升生成的速度。 这个方法简单描述就是,原来是 T 个时间步现在设置一个更小的时间步数 S ,将 S 时间序列中的每一个时间步 s 和 T时间序列中的步数 t 对应起来,伪代码如下:T = 1000 S = 100 start_idx = 0 all_steps = [] frac_stride = (T - 1) / (S - 1) cur_idx = 0.0 s_timesteps = [] for _ in range(S): s_timesteps.append(start_idx + round(cur_idx)) cur_idx += frac_stride接着计算新的 β ,St 就是上面计算得到的 s_timesteps:
![](https://pica.zhimg.com/80/v2-3173c96cc459230ad5b4413cd114e5c4_1440w.png)
alphas = 1 - betas alphas_cum = torch.cumprod(alphas, 0) last_alpha_cum = 1.0 new_betas = [] # 遍历原来的 alpha 前缀乘序列 for i, alpha_cum in enumerate(alphas_cum): # 当原序列 T 的索引 i 在新序列 S 中时,计算新的 beta if i in s_timesteps: new_betas.append(1 - alpha_cum / last_alpha_cum) last_alpha_cum = alpha_cum简单看下实验结果:
![](https://picx.zhimg.com/80/v2-bdd550b021b864289b117444fd39e046_1440w.png)
参考资料
- [1] https://www.assemblyai.com/blog/diffusion-models-for-machine-learning-introduction/
- [2] https://arxiv.org/pdf/2006.11239.pdf
- [3] https://arxiv.org/pdf/2102.09672.pdf
- [4] https://arxiv.org/pdf/2208.11970.pdf
- [5] https://www.zhihu.com/question/41765860/answer/1149453776
- [6] https://www.zhihu.com/question/41765860/answer/331070683
- [7]https://zh.wikipedia.org/wiki/%E9%A9%AC%E5%B0%94%E5%8F%AF%E5%A4%AB%E9%93%BE
- [8] https://github.com/rosinality/denoising-diffusion-pytorch
- [9] https://github.com/openai/improved-diffusion
计算机视觉入门1v3辅导班
【技术文档】《从零搭建pytorch模型教程》122页PDF下载
QQ交流群:444129970。群内有大佬负责解答大家的日常学习、科研、代码问题。
其它文章 深度理解变分自编码器(VAE) | 从入门到精通 计算机视觉入门1v3辅导班 计算机视觉交流群 用于超大图像的训练策略:Patch Gradient Descent CV小知识讨论与分析(5)到底什么是Latent Space? 【免费送书活动】关于语义分割的亿点思考 新方案:从错误中学习,点云分割中的自我规范化层次语义表示 经典文章:Transformer是如何进军点云学习领域的? CVPR 2023 Workshop | 首个大规模视频全景分割比赛 如何更好地应对下游小样本图像数据?不平衡数据集的建模的技巧和策 Transformer交流群 经典文章:Transformer是如何进军点云学习领域的? CVPR 2023 Workshop | 首个大规模视频全景分割比赛 如何更好地应对下游小样本图像数据?不平衡数据集的建模的技巧和策 U-Net在2022年相关研究的论文推荐 用少于256KB内存实现边缘训练,开销不到PyTorch千分之一 PyTorch 2.0 重磅发布:一行代码提速 30% Hinton 最新研究:神经网络的未来是前向-前向算法 聊聊计算机视觉入门 FRNet:上下文感知的特征强化模块 DAMO-YOLO | 超越所有YOLO,兼顾模型速度与精度 《医学图像分割》综述,详述六大类100多个算法 如何高效实现矩阵乘?万文长字带你从CUDA初学者的角度入门 近似乘法对卷积神经网络的影响 BT-Unet:医学图像分割的自监督学习框架 语义分割该如何走下去? 轻量级模型设计与部署总结 从CVPR22出发,聊聊CAM是如何激活我们文章的热度! 入门必读系列(十六)经典CNN设计演变的关键总结:从VGGNet到EfficientNet 入门必读系列(十五)神经网络不work的原因总结 入门必读系列(十四)CV论文常见英语单词总结 入门必读系列(十三)高效阅读论文的方法 入门必读系列(十二)池化各要点与各方法总结 TensorRT教程(三)TensorRT的安装教程 TensorRT教程(一)初次介绍TensorRT TensorRT教程(二)TensorRT进阶介绍
标签:Diffusion,alphas,x0,torch,弄懂,betas,Model,cum,xt From: https://www.cnblogs.com/wxkang/p/17131653.html