从零造一个 DALL·E 2:AI 绘画背后的秘密,我一口气讲清楚
你有没有想过,当你输入“一只穿着宇航服的柴犬在火星上自拍”,AI 是怎么在几秒钟内就画出一张像模像样的图的?它真的理解“柴犬”、“宇航服”、“火星”这些词吗?它脑子里到底装了什么东西?
今天,我就把 DALL·E 2 的整个工作原理,像剥洋葱一样一层一层剥给你看。不用任何数学公式,不贴一行代码,只用大白话。读完这篇文章,你不仅能跟朋友吹牛,甚至能自己动手搭一个迷你版。准备好了吗?我们开始。
注:结尾附完整代码地址下载
一、先搞懂地基:扩散模型到底是啥?
要理解 DALL·E 2,必须先理解一个概念:扩散模型。这个名字听起来像物理课,其实它比煮泡面还简单。
1.1 你亲手做一次“扩散”
想象你有一张偶像的签名照(干净、清晰)。你把它放在桌子上,然后你拿了一张半透明的硫酸纸盖在上面,用铅笔乱涂一通。现在照片变得模糊了一点。你再盖第二张纸,再涂。重复五十次、一百次。到最后,你什么都看不清了,只剩下一团灰色的乱线。这个过程就叫“正向扩散”——把图像一步一步变成噪声。
现在反过来:你教会一个 AI,让它看着那团乱线,一步一步地“反涂”,把每一层铅笔痕擦掉,最后还原出签名照。这就是“逆向去噪”。
DALL·E 2 画画的方式,就是:从一个完全随机的“乱线图”开始,然后几百步地去噪,每一步都让图像更清晰一点,最后刚好变成你描述的那个画面。
1.2 为什么用“步”而不是一步到位?
你可能会想:为什么不能一步就把噪声变干净?因为一步太难了。就像让你从一堆拼图碎片直接看出完整图案,那几乎不可能。但如果你每次只拼两三个碎片,拼几百次,就简单多了。扩散模型就是这种“化整为零”的思路:把大问题拆成上千个小问题,每个小问题都很简单——只是“稍微去掉一点点噪声”。
而且,这种多步的方法还有一个巨大好处:你可以随时喊停。比如,你不想要完全清晰的图像,只想要一个朦胧的草稿,那走到一半就可以输出。这让 AI 变得非常灵活。
二、DALL·E 2 不是一个人:它的三个钢铁队友
DALL·E 2 不是单个 AI,而是三个模型串在一起工作。你可以把它想象成一家高级餐厅的后厨:一个负责翻译菜单,一个负责设计菜谱,一个负责炒菜。
2.1 队友一号:CLIP——会翻译的“双语天才”
CLIP 是 OpenAI 的另一个明星模型。它的本事是什么呢?它看过 4 亿张“图片—文字”配对。比如看过一张猫的图片,旁边配着文字“一只猫”;看过一张车的图片,配着“一辆红色跑车”。看得多了,它就学会了一种能力:把任意图片变成一串数字(图像嵌入),也把任意文字变成另一串数字(文本嵌入),并且让意思相近的图片和文字的数字串也相近。
举个例子:你给 CLIP 看一张柯基的图片,它输出一串数字 A。你输入文字“短腿小狗”,它输出一串数字 B。A 和 B 在数学上离得很近。你输入“宇宙飞船”,它输出 C,A 和 C 就隔得很远。CLIP 就像是给图片和文字都贴上了同一种“语义标签”。
CLIP 的三层结构:它其实由两部分组成——一个图像编码器(一般是 Vision Transformer 或 ResNet)和一个文本编码器(Transformer)。图像编码器把像素变成嵌入,文本编码器把单词变成嵌入。然后 CLIP 对比这两个嵌入,让配对的图文嵌入靠近,不配对的远离。
聊一个关键点:CLIP 训练完成后,在后面的任务中彻底“冻结”。什么叫冻结?就是它的参数再也不变了,不再学习新东西,只负责干活。为什么?因为一旦 CLIP 继续学习,它输出的嵌入就会飘忽不定,后面两个队友会疯掉。所以 DALL·E 2 的训练顺序是:先独立训练好 CLIP,然后冻住它,再也不改。
2.2 队友二号:先验模型——听了上句,猜出下句的“预言家”
现在,CLIP 已经可以输出文本嵌入了。但是,真正要生成图像,最方便的方式是用图像嵌入。因为图像嵌入里直接包含了像素级别的统计信息,解码器更容易用。可是我们手上只有文本嵌入啊,怎么办?
这就需要第二位队友:先验模型。它的任务说出来很简单:你给我一段文字,我还你一个对应的 CLIP 图像嵌入。也就是说,它听了“一只猫”,就在脑子里想象出猫的“图像指纹”。
你可能要问了:CLIP 的文本嵌入和图像嵌入不是已经对齐了吗?直接拿文本嵌入不能用吗?能用,但效果差一点。因为 CLIP 只保证了它们“相近”,但没有保证它们“一模一样”。真实的世界里,一句“一只狗”可能对应无数种狗的图像嵌入——金毛的、柯基的、哈士奇的。先验模型的任务就是根据文本,从这无数种可能性中采样出一个最合理的图像嵌入。
先验模型长什么样?它使用的是一种叫 Decoder-Only Transformer 的结构,跟 GPT 是一家人。但它吃的不是文字,而是六样东西拼起来的“大杂烩”。这六样是:
原始的文字标题(比如“一只猫”)。
CLIP 给出的文本嵌入。
当前时间步的嵌入(告诉模型现在进行到第几步去噪了)。
加了噪声的图像嵌入(训练时故意添加的,让模型学会去噪;推理时就用纯噪声代替)。
一个可学习的嵌入(相当于模型的一个“草稿本”)。
因果注意力掩码(确保模型只能看到当前和之前的输入,不能偷看未来,这是 Decoder-Only 的特点)。
这六样东西排成一排,送进 Transformer 里算来算去,最后模型输出一个预测的图像嵌入。
训练先验模型时,学的东西很特别:一般的扩散模型学的是预测“噪声”,但 DALL·E 2 的先验模型直接学预测“干净的图像嵌入”。为什么?因为论文作者发现,直接预测目标比预测噪声收敛更快,效果也更好。损失函数就是预测嵌入和真实嵌入之间的“均方误差”——通俗讲,就是看两个数字串有多像,越像越好。
推理时的一个小聪明:先验模型在真正使用时,会一次生成两个候选的图像嵌入,然后分别跟文本嵌入计算“点积”(可以理解为相似度),选那个更相似的作为最终输出。为什么要多此一举?因为随机采样可能有坏运气,生成两次选好的,能显著提升质量。这叫做“重采样技巧”。
2.3 队友三号:解码器——真正的“神笔马良”
最后一棒,也是最累的一棒,叫解码器。它拿到先验模型给出的图像嵌入,然后从纯噪声开始,一步一步地画图。
解码器用的是一种叫做UNet的架构。UNet 是什么?它是一个长得像 U 形的神经网络,左边一层一层往下采样(压缩图像,提取特征),右边一层一层往上采样(恢复分辨率,画细节),中间还有“跳跃连接”把左边的细节直接送到右边,防止信息丢失。这个结构在图像分割、生成领域已经被证明非常强大。
解码器的两大法宝:残差块和注意力块
解码器不是简单的一堆卷积堆叠。它里面有两种精密的“积木块”:
- 残差块
:负责学习图像的局部特征,比如边缘、纹理、颜色。每个残差块内部有两层卷积,中间夹着激活函数和归一化。特别的是,残差块会用一种“缩放与偏移”的方式把条件信息(比如图像嵌入和时间步嵌入)融入进去:先对特征做缩放(乘以一个数),再加一个偏移量。这比直接把条件信息加上去效果更好。残差块最后还会把输入直接加到输出上(就是“残差连接”),这样能防止网络太深导致训练困难。
- 注意力块
:负责学习图像中不同位置之间的长距离关系。比如画一张脸,左眼和右眼距离很远,但它们的纹理、颜色应该相似。普通的卷积很难捕捉这种远距离依赖,但注意力机制可以。DALL·E 2 的注意力块还做了一件聪明事:它不仅让图像内部的像素互相对齐,还把文本编码也加进来,作为额外的“键”和“值”。这样一来,图像在生成时就能不断参考文字描述——比如你说“红色的鼻子”,模型就会让鼻子区域的红色更突出。
解码器里的时间步信息:时间步是扩散模型的生命线。因为模型在早期步(噪声很大)和晚期步(噪声很小)需要做完全不同的事情。为了让模型知道现在是第几步,解码器会把时间步也编码成一个嵌入,然后加到条件信息里。这个时间步编码用的是正弦位置编码——跟 Transformer 里的位置编码一模一样,只不过这里编码的不是位置,而是时间。
解码器的训练:训练解码器时,CLIP 和先验模型都已经被冻住了。训练数据里,我们有一张清晰图像和一个文字描述。首先,随机选一个时间步 t,给图像加上 t 步的噪声。然后把噪声图像、时间步 t、以及先验模型预测的图像嵌入(从文字描述得来)一起送进解码器。解码器输出预测的噪声。损失函数就是预测噪声和真实噪声之间的均方误差。反复训练后,解码器就越来越擅长去噪了。
三、把三块拼起来:DALL·E 2 的一整个工作流程
现在你知道了三个队友各自的看家本领,把它们串起来,就是完整的 DALL·E 2。
3.1 训练阶段(教会 AI)
第一步:训练 CLIP。这是最独立的一步。你只需要海量的图文对(比如网上随便爬的图片和它的 alt 标签)。CLIP 学会之后,就把它冻在冰箱里。
第二步:训练先验模型。你现在有了冻好的 CLIP。还是用那些图文对,但这次你要用 CLIP 提取出文本嵌入和图像嵌入。然后,对图像嵌入随机加噪声,让先验模型学会从加噪的嵌入中预测原始嵌入。训练完成后,冻住先验模型。
第三步:训练解码器。此时 CLIP 和先验模型都冻住了。你拿一张清晰图,加噪声,然后把文字描述通过 CLIP 和先验模型得到图像嵌入,作为条件,让解码器预测噪声。反复练。
这三步是顺序进行的:必须等前面的训练好并冻结,才能训练后面。
3.2 推理阶段(AI 真正干活)
假设你已经训练好了三个模型,现在用户输入一句提示词:“一只柴犬坐在冲浪板上”。
第一步:这句话被送进 CLIP 的文本编码器,得到一个文本嵌入。
第二步:这个文本嵌入,加上一个全零的噪声图像嵌入(因为推理时没有真实图像),再加上最大时间步(比如 1000)的嵌入,一起送进先验模型。先验模型输出两个候选的图像嵌入,分别与文本嵌入算相似度,选相似度高的那个作为最终图像嵌入。
第三步:解码器拿到这个图像嵌入,然后开始循环:
当前图像初始化为纯高斯噪声。
从时间步 t = 999 往下到 0:
根据当前图像、时间步 t、以及条件嵌入(图像嵌入+时间步嵌入融合而成),让 UNet 预测当前步的噪声。
从当前图像中减去一部分预测噪声(减去多少由一个调度系数决定),如果还没到最后一步,就再加一点随机噪声(保证多样性)。
把图像数值限制在合理范围(比如 -1 到 1)。
循环结束后,当前图像就是最终生成的图像。
第四步:输出这幅图。你就看到了“柴犬冲浪”。
这个循环通常要做 1000 步,每一步计算量都不小,但现代 GPU 可以在几秒内完成。而且实际应用中,可以用更少的步数(比如 250 步)来加速,图像质量下降不多。
四、那些你想不到的细节:DALL·E 2 的隐藏智慧
4.1 为什么用余弦调度,而不是线性调度?
在扩散模型里,每一步加多少噪声,是由一个“方差调度”决定的。最早 DDPM 论文用的是线性调度:噪声量随 t 均匀增加。但后来发现,当图像分辨率不高时(比如 32x32),线性调度会在后期加太多噪声,导致图像太早变成纯噪声,模型学不到东西。于是有人提出了余弦调度——噪声量在中间步增加得慢,两头增加得快。这样能让模型在中等噪声区域有更多学习机会,表现更好。
4.2 为什么对 CLIP 图像嵌入加噪声,而不是对原始图像加噪声?
在先验模型里,我们是对“CLIP 图像嵌入”加噪声,而不是对原始像素图加噪声。这是一个容易搞混的点。因为先验模型处理的对象是嵌入向量(一个一维数组),不是二维图像。所以“加噪声”就是在这个向量上加随机数,很简单。同理,解码器才是对原始像素图像加噪声。
4.3 那个“可学习嵌入”是什么鬼?
在先验模型的输入里,有一项叫“可学习嵌入”。它本质上是一个随机初始化的小向量,随着模型一起训练。它的作用是什么呢?相当于给 Transformer 预留了一个“草稿位”。Transformer 在处理完前面的输入后,最终在这个位置上输出结果。这是一种常用的技巧:你想让模型输出某个东西,就在输入序列里留一个空位,让模型自己去填。
4.4 为什么解码器要额外编码文本,而不是只用图像嵌入?
解码器的条件信息中,除了先验模型给的图像嵌入,还会把原始文本再次经过一个文本编码器(一个小型 Transformer)后得到文本编码,然后拼接到注意力层的键和值上。为什么不完全依赖图像嵌入?因为论文作者发现,这能帮助模型学习到 CLIP 没有完美捕捉到的自然语言细节。虽然实际效果提升有限,但加了也没坏处。
五、总结:DALL·E 2 到底牛在哪?
现在,我们把整个故事串一遍:
- 底层原理
:扩散模型——正向一步步加噪声,逆向一步步去噪。
- 三个模型
:
- CLIP
:图文对齐,输出文本嵌入和图像嵌入,冻住。
- 先验模型
:Decoder-Only Transformer,从文本嵌入预测图像嵌入。
- 解码器
:UNet,从图像嵌入和噪声图开始,迭代去噪生成最终图像。
- CLIP
- 创新点
:用先验模型作为桥梁,把文本和图像嵌入解耦,让训练更稳定;用余弦调度提升小图生成质量;用双采样选优提升推理效果。
- 局限性
:对空间关系(“左边”“右边”)经常搞错;生成复杂场景时可能出现奇怪的结构;对罕见物体的生成容易变形。
DALL·E 2 的诞生,不仅仅是技术的进步,更是让普通人也能拥有“想象力变现”的能力。现在,当你再看到一张 AI 绘画的杰作时,你应该能想象得到:背后有一个 CLIP 在翻译,一个先验模型在猜图像指纹,一个解码器在一遍遍地洗掉噪声,最终从混沌中创造出秩序。
这就是 DALL·E 2 —— 一个从噪声中诞生的魔法师。
完整代码下载:https://pan.baidu.com/s/1RpaWuLQXLLyRTSCqEMQkvA?pwd=vv2i
