前言
2018年我写过一篇博客,叫:《一文读懂目标检测:R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD》,该文相当于梳理了2019年之前CV领域的典型视觉模型,比如
- 2014 R-CNN
- 2015 Fast R-CNN、Faster R-CNN
- 2016 YOLO、SSD
- 2017 Mask R-CNN、YOLOv2
- 2018 YOLOv3
随着2019 CenterNet的发布,特别是2020发布的DETR(End-to-End Object Detection with Transformers)之后,自此CV迎来了生成式下的多模态时代
- 2020年
5月 DETR
6月 DDPM(即众人口中常说的扩散模型diffusion model)
10月 DDIM、Vision Transformer(简称ViT) - 2021年
1月 CLIP、DALL·E
3月 Swin Transformer
11月 MAE、Swin Transformer V2 - 2022年
1月 BLIP
4月 DALL·E 2
8月 Stable Diffusion、BEiT-3、Midjourney V3 - 2023年
1月 BLIP2
3月 Visual ChatGPT、GPT-4、Midjourney V5
4月 SAM(Segment Anything Model)
但看这些模型接二连三的横空出世,都不用说最后爆火的GPT4,便可知不少CV同学被卷的不行
说到GPT4,便不得不提ChatGPT,实在是太火了,改变了很多行业,使得国内外绝大部分公司的产品、服务都值得用LLM全部升级一遍(比如微软的365 Copilot、阿里所有产品、金山WPS等等)
而GPT4相比GPT3.5或GPT3最本质的改进就是增加了多模态的能力,使得ChatGPT很快就能支持图片的输入形式,从而达到图生文和文生图的效果,而AI绘画随着去年stable diffusion和Midjourney的推出,使得文生图火爆异常,各种游戏的角色设计、网上店铺的商品/页面设计都用上了AI绘画这样的工具,更有不少朋友利用AI绘画取得了不少的创收,省时省力还能赚钱,真香
但面对这么香的技术,其背后的一系列原理到底是什么呢,本文特从头开始,不只是简单的讲一下扩散模型的原理,而是在反复研读相关论文之后,准备把20年起相关的CV多模态模型全部梳理一遍,从VE、VAE、DDPM到ViT/Swin transformer、CLIP/BLIP,再到stable diffusion/Midjourney、GPT4,当然,实际写的时候,会分成两篇甚至多篇文章,比如
- 第一篇,即本文《AI绘画能力的起源:通俗理解VAE、扩散模型DDPM、ViT/Swin transformer》
- 第二篇,即下篇《AIGC下的CV多模态原理解析:从CLIP/BLIP到stable diffusion/Midjourney、GPT4》
就当2020年之后的CV视觉发展史了,且过程中会尽可能写透彻每一个模型的原理,举两个例子
- 网上介绍VAE的文章都太数学化(更怕那种表面正确其实关键的公式是错的误导人),如果更边推导边分析背后的理论意义(怎么来的 出发点是什么 为什么要这么做 这么做的意义是什么),则会更好理解,这就跟变介绍原理边coding实现 会更好理解、理解更深 一个道理
- 如果完全展开DDPM推导的所有细节,假定需要100步的话,本文正在朝展开80步而努力,截止5月份之前,绝大部分的中文资料只展开了60步
第一部分 编码器VE与变分自编码器VAE
1.1 AE:编码器(数据压缩为低维表示)-解码器(低维表示恢复为原始数据)架构
自编码器(Autoencoder,简称AE)是一种无监督学习的神经网络,用于学习输入数据的压缩表示。具体而言,可以将其分为两个部分:编码器和解码器
-
编码器:编码器是一个神经网络,负责将输入数据(如图像、文本等)压缩为一个低维表示,且表示为
扫描二维码关注公众号,回复: 14957248 查看本文章 -
解码器:解码器是另一个神经网络,负责将编码器生成的低维表示恢复为原始数据,且表示为
从而最终完成这么一个过程:,而其训练目标即是最小化输入数据与解码器重建数据之间的差异,所以自编码器常用的一个损失函数为
这个自编码的意义在于
- 模型训练结束后,我们就可以认为编码囊括了输入数据的大部分信息,也因此我们可以直接利用表达原始数据,从而达到数据降维的目的
- 解码器只需要输入某些低维向量,就能够输出高维的图片数据,那我们能否把解码器模型直接当做生成模型,在低维空间中随机生成某些向量,再喂给解码器来生成图片呢?
对于第二点,理论上可以这么做,但问题在于
- 绝大多数随机生成的只会生成一些没有意义的噪声,之所以如此,原因在于没有显性的对的分布进行建模,我们并不知道哪些能够生成有用的图片
- 而且我们用来训练的数据是有限的,可能只会对极有限的有响应。而整个低维空间又是一个比较大的空间,如果只在这个空间上随机采样的话,我们自然不能指望总能恰好采样到能够生成有用的图片的
有问题自然便得探索对应的解决方案,而VAE(自变分编码器,Variational Autoencoders)则是在AE的基础上,显性的对的分布进行建模(比如符合某种常见的概率分布),使得自编码器成为一个合格的生成模型
1.2 Variational AutoEncoder (VAE)
1.2.1 VAE:标数据的分布和目标分布尽量接近
VAE和GAN一样,都是从隐变量生成目标数据,具体而言,先用某种分布随机生成一组隐变量(假设隐变量服从正态分布),然后这个隐变量经过一个生成器生成一组数据,具体如下图所示(本1.2节的部分图来自苏建林):
而VAE和GAN都希望这组生成数据的分布和目标分布尽量接近,看似美好,但有两个问题
- 一方面,“尽量接近”并没有一个确定的关于 和 的相似度的评判标准,比如KL散度便不行,原因在于KL散度是针对两个已知的概率分布求相似度的,而 和 的概率分布目前都是未知(只有一批采样数据 没有分布表达式)
- 二方面,经过采样出来的每一个,不一定对应着每一个原来的,故最后没法直接最小化
实际是怎么做的呢,事实上,与自动编码器由编码器与解码器两部分构成相似,VAE利用两个神经网络建立两个概率密度分布模型:
- 一个用于原始输入数据的变分推断,生成隐变量的变分概率分布,称为推断网络
而VAE的核心就是,我们不仅假设 是正态分布,而且假设每个 也是正态分布。什么意思呢?即针对每个采样点获得一个专属于它和 的一个正态分布
换言之,有 个 sample,就有个正态分布 ,毕竟没有任何两个采样点是完全一致的,而后面要训练一个生成器 ,希望能够把从分布 采样出来的一个 还原为 ,而如果从 中采样一个 ,没法知道这个 对应于真实的 呢?现在 专属于,我们有理由说从这个分布采样出来的 可以还原到对应的 中去 而如何确定这 个正态分布呢,众所周知,确定一个正太分布只需确定其均值和方差 即可,故可通过已知的 和 假设的 去确定均值和方差
具体可以构建两个神经网络,去计算。值得一提的是,选择拟合而不是直接拟合,是因为总是非负的,需要加激活函数处理,而拟合不需要加激活函数,因为它可正可负 - 另一个根据生成的隐变量的变分概率分布,还原生成原始数据的近似概率分布,称为生成网络
因为已经学到了这 个正态分布,那可以直接从专属分布中采样一个出来,然后经过一个生成器得到,那接下来只需要最小化方差 就行
仔细理解的时候有没有发现一个问题?为什么在文章最开头,我们强调了没法直接比较 与 的分布,而在这里,我们认为可以直接比较这俩?注意,这里的 是专属于或针对于的隐变量,那么和 本身就有对应关系,因此右边的蓝色方框内的“生成器”,是一一对应的生成。
另外,大家可以看到,均值和方差的计算本质上都是encoder。也就是说,VAE其实利用了两个encoder去分别学习均值和方差
1.2.2 VAE的Variational到底是个啥
这里还有一个非常重要的问题:由于我们通过最小化来训练右边的生成器,最终模型会逐渐使得 和趋于一致。但是注意,因为 是重新随机采样过的,而不是直接通过均值和方差encoder学出来的,这个生成器的输入 是有噪声的
- 仔细思考一下,这个噪声的大小其实就用方差来度量。为了使得分布的学习尽量接近,我们希望噪声越小越好,所以我们会尽量使得方差趋于 0
- 但是方差不能为 0,因为我们还想要给模型一些训练难度。如果方差为 0,模型永远只需要学习高斯分布的均值,这样就丢失了随机性,VAE就变成AE了……这就是为什么VAE要在AE前面加一个Variational:我们希望方差能够持续存在,从而带来噪声!
- 那如何解决这个问题呢?其实保证有方差就行,但是VAE给出了一个优雅的答案:不仅需要保证有方差,还要让所有 趋于标准正态分布,根据定义可知 这个式子的关键意义在于告诉我吗:如果所有都趋于,那么我们可以保证也趋于,从而实现先验的假设,这样就形成了一个闭环!那怎么让所有趋于呢?还是老套路:加loss
到此为止,我们可以把VAE进一步画成:
现在我们来回顾一下VAE到底做了啥。VAE在AE的基础上
- 一方面,对均值的encoder添加高斯噪声(正态分布的随机采样),使得decoder(即生成器)有噪声鲁棒性
- 二方面,为了防止噪声消失,将所有趋近于标准正态分布,将encoder的均值尽量降为 0,而将方差尽量保持住
这样一来,当decoder训练的不好的时候,整个体系就可以降低噪声;当decoder逐渐拟合的时候,就会增加噪声
第二部分 扩散模型DDPM:先前向加噪后反向去噪从而建立噪声估计模型
在写本文之前,我反复看了网上很多阐述DDPM的文章,实话说,一开始看到那种一上来就一堆公式的,起初基本看不下去,虽然后来 慢慢的都看得下去了,但如果对于一个初次接触DDPM的初学者来说,一上来就一堆公式确实容易把人绕晕,但如果没有公式,则又没法透彻理解背后的算法步骤,两相权衡,本文将侧重算法每一步的剖析,而公式更多为解释算法原理而服务,说白了,侧重原理 其次公式,毕竟原理透彻了,公式也就自然而然的能写出来了
2020年,UC Berkeley等人的Jonathan Ho等人通过论文《Denoising Diffusion Probabilistic Models》正式提出DDPM(全称即论文名称:Denoising Diffusion Probabilistic Models,简称DDPM)
扩散模型的灵感来自非平衡热力学,通过定义了一个扩散步骤的马尔可夫链,以缓慢地将「符合高斯分布的随机噪声」添加到数据中,然后反转扩散过程以从噪声中构建所需的数据样本
每一个噪声都是在前一时刻增加噪声而来的,从最开始的时刻开始,最终得到时刻的纯噪声图像。不过问题来是为什么要加噪声?
- Diffusion的最终目标是去噪以生成图片,而为了推导出逆向的去噪方法,必须了解增加噪声的原理。同时,添加噪声的过程其实就是不断构建标签的过程。如果在前一时刻可以预测出来后一时刻的噪声,便能很方便地实现还原操作(就和人走路一样,不管你从哪来,哪怕走过万水千山,最后都可按原路返回至原出发点)
说白了 当你学会了怎么加噪(前向扩散),就一定能知道怎么去噪(逆向生成),毕竟知道怎么来 也必知道怎么回 - 且在噪声的添加过程中,每一步都要保持尽量相同的噪声扩散幅度。比如,在给上图加噪的过程中,前期的分布非常均匀,添加一些噪声便可以将原始分布改变,但到后期,需要添加更多的噪声,方可保证噪声扩散幅度相同(这就像往水中加糖,为了使糖的甜味增长相同,后期需要加更多的糖)
所以DDPM为了从随机噪声中直接生成图片,首先需要训练一个噪声估计模型,然后将输入的随机噪声还原成图片,相当于就两个关键,一个是训练过程,一个是推理过程
- 训练过程:随机生成噪声,经过步将噪声扩散到输入原始图片中,破坏后的图片,学习破坏图片的预估噪声,用L2 loss约束与原始输入噪声的距离
- 推理过程:即输入噪声,经过预估噪声模型还原成图片
2.1 DDPM的两个过程:从前向过程到逆向过程
2.1.1 前向过程(加噪):通过高斯噪音随机加噪——给图片打马赛克
前向过程(forward process)也称为扩散过程(diffusion process),简单理解就是对原始图片通过逐步添加「方差为的高斯噪声」变成,从而达到破坏图片的目的,如下图
在从到的过程中,其对应的分布是一个正太分布,且其均值是,方差为,则有
对于这个公式,解释下3点
-
正态分布的概率密度函数具有以下形式:
正态分布有两个参数:均值和方差。其中,是分布的均值,决定了分布的中心位置,是标准差,决定了分布的宽度
- 上面的方差之所以表示为,原因在于我们一般处于多维情况下,而是单位矩阵,表明每个维度有相同的标准偏差
且是事先给定的常量,代表从到这一步的方差,且正因为设置的比较小,所以使得的均值在附近,换言之,相当于就是在的基础上加了一些噪声,而且是渐进式逐步增加/扩散的,当然 从加噪大小的角度上讲,前期加噪较弱,后期加噪加强,所以在DDPM的论文中,作者取为从0.0001到0.02的线性递增序列 - 此外,值得一提的是,因为是马尔可夫链,所以其联合分布便是:
如下图所示
接下来,如果我们定义 , 且『被称为Noise schedule,通常是一些列很小的值』,以及 是高斯噪声,便可以得到的采样值
这里值得注意下,即两个高斯分布相加仍为一个高斯分布, 新的分布均值为原先两个均值的和, 方差为原先两个方差的和,且把上述公式迭代变换下,可以直接得出 到 的公式,如下:
其中 , 也是一个高斯噪声
换言之,所以 在 条件下的分布就是均值为 , 方差为 的正态分布
考虑到可能会有读者对这个 到 的一步到位感到困惑,而一般的同类文章不会展开的特别细,故本文细致展开下(能拆10步则10步 确保阅读无障碍)
对于这段公式的解释说明 待5.6日上午补充
最后,我们可以将 的表达式简化为:
2.1.2 逆向过程(去噪): 训练噪声估计模型—— 复原被加噪的图片使之清晰化
逆向过程就是通过估测噪声,多次迭代逐渐将被破坏的 恢复成 ,如下图
相当于从到的变化过程,用公式表示为
这个公式所表达的含义在于,由于真实噪声在复原过程中不允许被使用,因此本步骤的关键就在于训练一个由和估测噪声的模型,其中就是模型本身需要训练的参数,也是一个高斯噪声且,用于估测与实际的差距,且在DDPM中,使用的是U-Net作为估测噪声的模型
好,问题又来了,上面这个公式又是如何一步步推导来的呢?
- 首先,通过上一节 我们已经得到了
- 在DDPM的逆向过程中中,我们的目标是找到一个去噪函数用于在每个时间步去除噪声
故现在需要对上述等式进行变换,以便得到 关于 和 的表达式。首先,我们将上述等式除以 ,得到:- 现在我们需要用去噪函数替换掉 。在DDPM中,去噪函数的目标是从 中恢复出噪声 。因此,我们可以写:
- 将这个表达式带入上面的等式,我们得到:
- 最后,我们将等式左边的 项移到右边,并将其写成 的表达式:
这就是我们需要的表达式,它描述了如何从时间步 的数据 和去噪函数 恢复出时间步 的数据 。在反向去噪过程中,我们将使用这个表达式逐步回溯到初始时间步,以生成新的样本
通过上文可知,现在的问题关键变成了如何训练一个由和估测噪声的模型
更具体而言,正向扩散和逆扩散过程都是马尔可夫,唯一的区别就是正向扩散里每一个条件概率的高斯分布的均值和方差都是已经确定的(依赖于 和 ),而逆扩散过程里面的均值和方差需要通过网络学出来,怎么个学法呢?
- 如果我们能够逆转前向过程并从 采样,就可以从高斯噪声 还原出原图分布 。因为我们可以证明如果前向满足高斯分布且 足够小,其逆向仍然是一个高斯分布
- 然现在的问题是,我们无法直接去推断 ,好在我们可以使用「参数为 θ 的U-Net+attention 结构」去预测这样的一个逆向的分布 (类似 VAE):
- 不过在DDPM的论文中,作者把条件概率 的方差直接取了,而不是上面说的需要网络去估计的 ,所以说实际上只有均值需要网络去估计。虽然我们无法得到逆转过程的概率分布 ,但是如果知道 就可以直接写出
然后呢?君莫急,且看上一节2.1节说过,可以直接从 得到 ,那反过来,我们可以直接从得到 吗?
- 有人可能要说,直接把上一节得到的移个项不就行了?
但问题在于中的是个随机变量,意味着 也是个随机变量,其具体取值由 实际取值决定。现在我们有一个具体的 ,它对应着 的某个取值,但是什么值我们并不知道(如果仅看逆向过程),所以我们只能以前向过程的 取值为标签,训练一个模型去估计它,即:
其中就是所谓的模型,用来近似真实的(即前向过程采样出来的);相应地,就是 的近似。或者,你也可以无视,直接把 视为模型。
为了训练它,最直接的想法就是用 L2 损失或者,虽然 DDPM 的损失函数还真就是这个,但是现在未免有点想当然了,出于严谨,下文将会具体推导 - 现在,假设我们已经训练完模型了,那是不是按照就完事儿了呢?理论上没问题,但是实际效果很差,为什么呢?如果直接用 ,那么中间的都没用了,整个 DDPM 就退化成了 VAE 的结构
但是 VAE 的生成模型和后验都是自己学习出来的,二者双向奔赴共同优化去寻找最优解;而 DDPM 的后验是人为指定的(即),并且由于 ,基本上就是一个标准正态分布,磨灭掉了几乎所有的输入信息,全靠生成模型这一边去恢复,难度未免也.. - 所以实际应用中,我们是一点一点来的,比如先生成、然后……由于每一步的变化都比较小,保留了上一步足够的信息,生成模型的负担就轻了很多。所以接下来的问题 自然而然 就转换成了我们希望求解,因为我们知道前向过程,所以自然想到使用贝叶斯公式: 可惜 和 是未知的,事情到这里似乎走入了僵局,但是我们敏锐地发现和 是已知的,如果给上式加上 为条件,则立马柳暗花明
解释下上面7.1~7.5这5个步骤的推导
7.1依据的是
- 7.2中,分母部分依据的是
分子部分依据的是- 7.3依据的是分子分母同时除以
- 至于7.3到7.4
依据的是
且由前向扩散过程的特性『别忘了2.1.2节中,有』,可知- 最后,再解释下怎么从7.4到的7.5
先举一个最简单的例子,比如对于 ,稍加转化下即是,则其均值为,方差为
而这个则对应于7.5中的
则对应于7.5中的
从而有
好,接下来关键来了
根据,可知,代入上面的表达式 可得
大部分对这个的推导都是一步到位的,但本文为细致起见,故还是一步步来推导下
- 首先直接把代入进去,可得
- 接下来,我们可以进一步观察到,分子中的后半部分有这一项
注意 这里非常关键(截止23年5月份之前,暂时没看到有其他中英文资料解释了这个细节),依据的就是之前定义的,即,从而有
所以我们可以针对这一项的分子分母同时除以,得到 -
之后的推导就比较简单了,以下分别对接下来的三行公式做解释说明
接着把上阶段2得到的式子的分子拆成三项,且三项中最后两项的分子分母同时乘以
然后再把上一步骤中分子三项中的前两项通过提取出从而实现合并
前两项合并之后,再对前两项中第一项的分子分母同时乘以,然后对第三项的分子分母同时除以,即可得,原因很简单,因为: -
接下来,针对上面阶段3得到的式子的前两项再做合并,合并中用到了一个细节,即,原因也同样很简单,根据上面阶段2出现的这个式子而来,再之后就更eazy 便不再赘述了
从最终得到的结果可以看出
在给定 的条件下,后验条件高斯分布的均值只和超参数、、有关,方差只与超参数有关,从而通过以上的方差和均值,我们就得到了的解析形式
2.2 DDPM如何训练:基于噪声估计模型——最小化估计噪声与真实噪声之间的差距
接下来介绍这个模型要怎么优化,即网络该怎么训练去估计条件概率的均值 和方差
我们要最小化目标分布的负对数似然:
令
所以 就是我们的上界,我们要最小化它,接着进行变形
对于上面公式最后得到的结果
- 首先,是和优化无关的(由于前向过程 q 没有可学习参数,而 则是纯高斯噪声,因此 可以当做常量忽略),所以不用管,只用看右边的
- 然后, 是KL散度,则可以看做拉近 2 个分布的距离:
第一个分布 我们已经在上一节推导出其解析形式,这是一个高斯分布,其均值和方差为
第二个分布 是我们网络期望拟合的目标分布,也是一个高斯分布,均值用网络估计,方差被设置为了一个和 有关的常数
如果有两个分布 p,q 都是高斯分布,则他们的KL散度为
然后因为这两个分布的方差全是常数,和优化无关,所以其实优化目标就是两个分布均值的二范数 - 这个时候我们可以直接整个网络出来直接预测 ,且会尽可能去预测,因为是 的输入的,其它的量都是常熟,所以其中的未知量其实只有,所以我们干脆把定义成:
也就是说,不用网络直接预测,而是用网络先预测噪声,然后把预测出来的噪声带入到定义好的表达式中去计算出预测的均值,其实是一样的 - 所以,最终把这个公式,带入到 上一公式中得到:
经过这样一番推导之后就是个 L2 loss。网络的输入是一张和噪声线性组合的图片,然后要估计出来这个噪声:
由上可知,DDPM的关键是训练 模型,使其预测的 与真实用于破坏的 相近,用L2距离刻画相近程度就好,因此我们的Loss就是如下公式
而整个训练过程可如下图描述
DDPM论文中对应的伪代码为
相当于训练时,网络输入为 (由 和噪声 线性组合而成) 和时刻 ,输出要尽可能的拟合输入的噪声 (通过L2 loss约束):
2.3 如何通过训练好的DDPM生成图片
在得到噪声估测模型 后,想要生成模型就很简单了。从N(0,1)中随机生成一个噪声作为,然后再用该模型逐步从估测噪声,并用去噪公式逐渐恢复到 即可,见如下伪代码
相当于推理时,我们从各项独立的高斯分布 开始,一共 步,每一步其实都是用了一次reparameterization trick
每一步具体来说,我们有了 , 想要得到 ,因为我们之前逆扩散过程建模有:
所以由reparameterization trick我们有:
每一轮都这样迭代,最终就得到了生成的图片
//又不一小心把本文发布出去了,本来一直在草稿里要修改个把星期的,一方面 下文还有很多内容需要更新,二方面 上文还有很多细节有待补充完善 比如一些公式的拆解,待更..
第三部分 ViT
// 待续..
参考文献与推荐阅读
- 变分自编码器(一):原来是这么一回事
- ..
- 关于VAE的几篇文章:一文理解变分自编码器(VAE)、机器学习方法—优雅的模型(一):变分自编码器(VAE)、
- 苏剑林关于扩散模型的几篇文章:(一):DDPM = 拆楼 + 建楼、(二):DDPM = 自回归式VAE
- 怎么理解今年 CV 比较火的扩散模型(DDPM)?
-
知乎上关于扩散模型的几篇文章:全网最简单的扩散模型DDPM教程、Diffusion扩散模型大白话讲解、扩散生成模型: 唯美联姻物理概念与机器学习
- Understanding Diffusion Models: A Unified Perspective(此文写的非常细致,另,这是其PDF版本)
- 扩散模型是如何工作的:从0开始的数学原理
- What are Diffusion Models?
- Introduction to Diffusion Models for Machine Learning
- 关于扩散模型的几篇论文
CVPR 2022 Tutorial: Denoising Diffusion-based Generative Modeling: Foundations and Applications
Diffusion Models Beat GANs on Image Synthesis - 关于DDPM的几篇文章(如果相关文章与本文有冲突,建议以本文为准,因为有些文章有笔误或错误):DDPM概率扩散模型(原理+代码)、Denoising Diffusion Probabilistic Models (DDPM)、从VAE到DDPM、扩散模型原理解析