⚠️⚠️⚠️本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
在CV大模型系列中,我们介绍了扩散模型的运作原理(以基石DDPM为例),介绍了Transformer架构下做CV任务的方法,也介绍了多模态大模型CLIP。有了这么多的前置知识,今天,我们终于可以来一探文生图大模型背后的运作逻辑了,在本篇文章中,我们将对OpenAI文生图代表作DALLE2(大力,too😉) 进行详细解读。
可能相比于DALLE2,更多人对Midjourney这个名字更熟悉。Midjourney背后基于的Stable Diffusion模型,和DALLE2一样都是经典的文生图模型代表作。虽然训练方法和架构有所不同,但核心逻辑都离不开CLIP和扩散模型。如有时间,在本系列的后续文章中,我们也会介绍Stable Diffusion。更多经典文生图模型的架构简介,可以参见这篇文章的最后一部分介绍。
话不多说,进入正文吧。
CV大模型系列文章导航(持续更新中):
🌸CV大模型系列之:扩散模型基石DDPM(人人都能看懂的数学原理篇)🌸
🌸CV大模型系列之:扩散模型基石DDPM(源码解读与实操篇)🌸
🌸CV大模型系列之:全面解读VIT,它到底给植树人挖了多少坑🌸
🌸CV大模型系列之:多模态经典之作CLIP,探索图文结合的奥秘🌸
🌸CV大模型系列之:MoCo v1,利用对比学习在CV任务上做无监督训练🌸
🌸CV大模型系列之:DALLE2,OpenAI文生图代表作解读🌸
一、卷生卷死的文生图发展史
上图回顾了2021-2022间文生图模型的密集发展历史,看看不同模型的诞生时间差,就可以大体感知到各家当时都卷成什么样了。我们今天要讨论的DALLE2,是DALLE的第二代,诞生于2022年4月,原名叫unCLIP。
从技术上来说,DALLE2的架构灵感来自于openAI在2021年12月推出的GLIDE模型 (G uided L anguage to I mage D iffusion for Generation and E diting,用文字指引图片的生成和编辑)。我们曾经在扩散模型的系列中说过,文生图的第一步是要使得模型能够产生尽量逼真的图片(即学习到真实世界图片的分布),第二步则是要用文字做指引(guidance)去产生符合人类意图的图片。 关于GLIDE细节,我们会在后续单开一篇文章说明。在这篇文章中,我们只需要知道GLIDE的大致作用方式即可(后文对DALLE2的模型架构中我们会说明)。
关于DALLE2效果的更多介绍,可参见官网:openai.com/dall-e-2
二、DALLE2模型架构
由图可知,DALLE2模型主要由三部分组成:
-
CLIP预训练模型 中的text encoder和image enocder(CLIP原理见这篇文章)
-
先验模型prior
-
解码器模型decoder
我们先不纠结这三个部分具体长什么样,我们先来看看它们配合在一起是怎么训练和运作的。
2.1 整体训练流程
(1)首先,我们的训练数据是(文字,图片)文本对。这和CLIP的预训练数据非常相似。在原始论文中,文字被称为caption,图像被称为image。
(2)接下来,我们取出预训练好的CLIP模型 (CLIP也是OpenAI自家的,花了大价钱做训练)。我们将文字传入text encoder,得到text embedding (图中蓝色长格子);将图片传入image encoder,得到image embedding(图中橘色长格子)。在CLIP的介绍中,我们说过,因为做了文本和图像的对比学习,所以text/image encoder产生的结果是多模态的,也就是text embedding中考虑了image的信息,同理image embedding中也考虑了text的信息。
(3)接着,就进入到prior模型 了,我们以text embedding为输入(实操中不仅仅是这个输入),image embedding为真值,训练prior 。在上面的架构图中,我们发现有上下两个prior,这其实是作者做了两次实验尝试:
- 上面的prior:用自回归(autoregressive) 进行训练,类似于我们训练gpt的方法。
- 下面的prior:用扩散模型进行训练。对扩散模型原理有疑问的,可以参见本文导航中的前三篇文章。
在作者的实验中,这两种方法最终的效果都差不多,但因为扩散模型的训练代价更小,所以最终选用了扩散模型。关于prior更详细的介绍,我们会在下文进行说明。
(4)最后,是decoder模型 。我们用prior产出的image embedding作为输入,送进decoder模型,产出最终的图片。在这里,decoder模型也是一个扩散模型。
细心的你可能发现了,在模型架构中,最开始作为标签的柯基图和最终生成的柯基图好像不太一样,但都贴合文字的描绘。这其实也是DALLE2的目的之一:使得生成的图片具有多样性。而达成这一目的手段是因为加入了decoder架构。接下来,我们就来详细分析一下DALLE2中的CLIP,prior和decoder。
2.2 CLIP
关于CLIP的原理我们已经详细介绍过了,这里就不再赘述。这一部分我们想讨论的问题是:为什么要用CLIP?
我们知道,因为在CLIP中采用了对比学习的方式,所以我们能知道文本和图片间的相似性。同时在CLIP模型中,我们训练了一个多模态的线性层,能够使得text/image encoder产生的结果中各自包含了对方的关键信息。因此,CLIP在处理(本文,图片)对这样的输入数据时,能够产生优质鲁棒的预训练特征。
另外,CLIP模型还能提供文本和图片的相似度信息 。如果你用过Midjourney等文生图工具,你可能会注意到一个类似于"文本贴合度"的参数,通过调节这个参数,你能决定生成的图片多大程度上符合文字描述。所以,我们在训练模型中,也要考虑这个参数。具体的操作的方法就是,对一份text,根据CLIP提供的相似度,以一定的概率筛选出匹配的image embedding,使得模型并不总是看到最相似的那张image,进而使得模型得到的信息更加丰富。
2.3 Prior
这里我们来详细说明下作者尝试过的两种prior模型:autoregressive和扩散模型,同时讨论引入prior的必要性。
2.3.1 Autoregressive Prior
Autoregressive Prior (以下简称AR)是一个Transformer的Encoder-Decoder架构,其中encoder的宽度为2048,共24个block;decoder的宽度为1664,24个block。
在训练的时候,我们将CLIP产出的text embedding作为prefix添加在image embedding前,一起送入Transformer进行训练。同时,还会额外引入一个token,用于表示image和text之间的相似度。
为了提升训练效率,作者在这里用了PCA的方法对embedding做了降维,详细的操作可以阅读原始论文。
2.3.2 Diffusion Prior
Diffusion Prior沿用了扩散模型的设计思想,它的核心模型是一个Transformer Decoder,它的宽度为2048,一共24个block。
因为是Transformer Decoder,它的输入自然是一个序列,这个序列的中依次包含:
-
Encoded text (原始输入文本)
-
CLIP text Emebdding(原始输入文本过CLIP text encoder后的结果)
-
Diffusion timestep embedding(扩散模型中表示"当前是第几步"的emebedding,详情可参见导航部分扩散模型相关文章)
-
Noised CLIP image embedding(CLIP图像噪声)
-
Final embedding(这个embedding的输出位置将被用于预测真实的CLIP embedding)
2.3.3 使用prior的必要性
你可能想问,既然prior和decoder都是扩散模型,那我为什么一定要添加prior,而不是只使用decoder呢?
这个问题,DALLE2的作者也曾好奇过,为了更深入探究prior的必要性,他们做了以下几个实验(相关图片结果来自:medium.com/augmented-s...
(1)不用CLIP,不用prior,只用decoder
文本的意思是想画一张油画,上面是戴着party帽的科技,但是emm...不能说是不相似,只能说是毫不相干。
(2)使用CLIP和decoder,不用prior
产生(1)结果的原因是,模型并没有完全理解文字的意思。那如果我使用CLIP,传给模型一个理解过图片信息的文字特征,会不会好些呢?
看样子是好些了,但仍是有不足的地方:这张图片尺寸不对,只生成了一部分柯基。
为什么会发生这样的结果呢?细心的你可能发现了,这里只用到了CLIP的text encoder,虽然此时的text embedding在经过CLIP的预训练后,已经蕴含了图片模态的信息,但是对它来说,它只能抓住要生成的图片的主要特征,对图片的细节(例如尺寸、风格)都是未知的。
为了解决这个问题,我们就需要"提前"让文字知道自己要生成的图片的样子 ,也就是说,最好引入Image Embedding 。那么怎么让Text Embedding和Image Embedding关联起来呢?最直接的办法就是训练一个模型,没错,这就是我们说的prior(先验) 。
(3)使用CLIP,Prior和decoder
现在,把这三者都加上去后,是不是生成的图片就能看得过去了?
2.4 Decoder
最后,就是Decoder部分了,decoder其实就是openAI之前训练过的GLIDE的变体(关于GLIDE的细节,这篇文章中是写不完的,有时间单列一篇文章介绍,这里只要知道GLIDE也是一个文生图模型) 。前面说过,decoder部分也是一个扩散模型,那你可能又有疑惑了,前面我们讨论过prior的必要性,那类似的,decoder真的是必须的吗?它的作用是什么呢?
我们依然通过实验来直观感受decoder的作用:
这张图的9宫格,都是图片经过decoder后的样子,不难发现,decoder产出的核心内容没变,只是将每张图片的细节做了变化。
还记得我们之前说过,"多样性"也是文生图模型关注的重点吗?所以decoder在这里就达到了这个目的。总结来说,prior负责让生成的图片符合文本意思,而decoder让生成的图片具备了多样性。
好!到这里我们就将DALLE2的核心技术部分介绍完了,更多实验效果和DALLE2缺陷介绍,大家可以参考论文(这论文写了27页,大部分都在说这个内容)。怎么样,如果掌握了必要的先验知识,DALLE2是不是比想象中的简单?如果后续有时间,我会继续写对DALLE2相关代码的解读,里面有更多细节。
不过接下来的时间,我还是想简单来梳理一下扩散模型的发展历程和主要架构,毕竟它是DALLE2的核心。
三、扩散模型发展历程和主要架构简述
3.1 GAN
如图,GAN由一个生成器(Generator)和一个判别器(Descriminator)组成。
其中z是一个随机噪声,通过Generator,生成一张图片 <math xmlns="http://www.w3.org/1998/Math/MathML"> X ′ X^{\prime} </math>X′。然后让Descriminator去判断哪张是生成的图片 <math xmlns="http://www.w3.org/1998/Math/MathML"> X ′ X^{\prime} </math>X′,哪张是真实的图片 <math xmlns="http://www.w3.org/1998/Math/MathML"> X X </math>X。通过这样的一种"对抗式"的训练,当Descriminator无法区分生成图片与真实图片之时,我们就达到了以假乱真的目的。
GAN生成的图片很真实,但多样性并不好。同时,GAN的数学性并不强,只是依靠神经网络的力(玄)量(学)来训练。
3.2 AE,DAE,VAE与VQVAE
如果说GAN的训练是想从随机噪声入手,去生成符合真实世界分布的图片。那么AE(AutoEncoder)系列在训练时,则是希望通过encoder-decoder架构,在一定程度上"打乱"原始图片,然后再去重建它,通过重建的过程,让模型学到图片的真实分布。
3.2.1 AE
AE的设计思想很简单,希望用一个encoder提取出原始图片的特征z(这是一个低维特征,但拥有更高的信息密度),然后通过一个decoder,从z中重建x。因此,AE算法的核心在于特征z,它也不依赖任何的数学先验分布。
3.2.2 DAE
DAE与AE的框架基本一致,不同之处在于,DAE对原始输入图片做了打乱(corrupted) 处理,然后将打乱的图片喂给encoder。但是对于decoder,却希望它能去预测打乱前的图片x。通过这样上难度的方式,增强模型的效果。
3.2.3 VAE
在前面介绍的几种模型中,严格说来都没有依赖任何数学分布,都是依靠神经网络的(玄学)力量进行运作的。但是VAE却不一样,它的训练核心就在于让模型去学得分布,这也和扩散模型DDPM的总体思想非常像。
如上图,在VAE中,
-
我们将原始图片输入给Encoder,然后让Encoder去学得一个分布 <math xmlns="http://www.w3.org/1998/Math/MathML"> N ( μ , σ 2 N(\mu, \sigma^{2} </math>N(μ,σ2)。
-
然后,我们从这个分布中,采样出一个z 。具体的采样细节为,先采样噪声 <math xmlns="http://www.w3.org/1998/Math/MathML"> ϵ ∼ N ( 0 , 1 ) \epsilon \sim N(0, 1) </math>ϵ∼N(0,1),然后利用 <math xmlns="http://www.w3.org/1998/Math/MathML"> z = μ + σ ∗ ϵ z = \mu + \sigma * \epsilon </math>z=μ+σ∗ϵ来获取最终的z
-
将z输入decoder,输出最终的预测图片 <math xmlns="http://www.w3.org/1998/Math/MathML"> x ′ x^{\prime} </math>x′,然后让这个值和真实图片尽量相似。
-
当VAE训练完毕后,使用它时,我们就可以把Encoder去掉了。然后直接采样噪声z,生成最终的图片。
与前面几种方法显著不同之处在于,VAE的z是从一个分布中采样出来的,而别的方法的z则是从原始图片中提炼出来的。这样做的好处是什么呢?当模型在训练过程中学得一个分布,而不是具体的特征时,它就能够产生多样性。 因此,VAE生成的图片内容更加丰富。但也因此,它的逼真性就没那么强了。
3.2.4 VQVAE
VQ的意思就是vector quantized,从图片上看VQVAE的架构好像有点复杂,但理解起来也不难。
-
同样,输入图片x先过Encoder,然后输出一张特征图f, 尺寸为(h, w, c)
-
这个特征图f,属于我们上文说的"贴合图片的特征",如果我们想让模型产生多样性,那最好的办法还是学习分布。与前几种架构不同的是,VQVAE并没有直接去学一个具体的分布,而是通过一个codebook来模拟这个分布
-
codebook是一个尺寸为(K,D)的矩阵,一般,K=8192, D=512或768,这个矩阵的含义是,有8192个维度为512/768的向量。你可以把它理解成8192个特征的聚类中心。
-
然后,我们拿f中的向量和codebook中的聚类中心进行比较,找出和其最相似的聚类中心 ,将它的序号存放在z中,等实际使用时,我们只需根据z中存放的序号,去codebook里把相关的向量捞取出来,生成新的特征图 <math xmlns="http://www.w3.org/1998/Math/MathML"> f q f_{q} </math>fq。
-
最后,我们把 <math xmlns="http://www.w3.org/1998/Math/MathML"> f q f_{q} </math>fq装进decoder,去产出最终的结果即可。
VQVAE的精华其实是在学得的这个codebook上,一般不用它做生成任务,而是用它去获取更高层级的图像特征,用在别的下游任务上。DALLE就是从VQVAE借鉴而来。
再接下来的DDPM、DDIM,GLIDE等我们就不细说啦,放在扩散模型相关的系列里谈。
四、参考
2、www.bilibili.com/video/BV17r...