21 · 扩散 · 9 min

通过擦除噪声来生成图像

Stable Diffusion、DALL-E、Midjourney。反向去噪过程、CLIP 的作用,以及为什么 U-Net 正在让位于 Transformer

另一个家族

到目前为止,我们看到的所有内容描述的都是预测下一个 TokenTransformer。这是主导了语言、代码,并且越来越主导视频和音频的架构。

但当你在 Midjourney、DALL-E 或 Stable Diffusion 里输入提示词时,发生的不是这件事。图像不是从左到右一个像素一个像素地生成出来的。它是到处同时出现的,通过一连串步骤被精炼。

这是一个非常不同的模型家族的作品:扩散模型(diffusion models)

核心思想:学习去噪

扩散建立在一种几乎过于简单的直觉上。如果我拿一张清晰的图像,逐步往里加高斯噪声,到某一刻它就会变得和纯噪声无法区分。如果我学会反过来这个过程——一点点地把噪声去掉——那我就可以从纯噪声出发,得到一张清晰的图像

训练时:

  1. 从数据集里取一张图像
  2. 按照预定的时间表(比如 T = 1000 步)往里加噪声
  3. 训练一个网络,给它噪声化的图像和步数 t,让它预测被加进去的噪声

这样,模型就学会了对任意噪声水平识别哪些是"真实图像"、哪些是"添加的噪声"。

推理时反着来:从纯噪声出发,每一步模型预测噪声、我们减掉它、再继续。T 步之后,我们得到一张可信的图像——一张接近所学分布的图像。

试一试去噪

动画从一张纯噪声图像出发,逐步去噪。每一步都沿着训练时学到的梯度前进,由 CLIP 的文本 embedding 引导。5 步时结果模糊,50 步时变得清晰。

试着改变步数。在 5 步时,图像还是颗粒感很强——模型没有时间去精炼。在 50 步时,它变得清晰,但算力是十倍。大多数现代采样器(DDIM、DPM-Solver)在 20 到 30 步内就能达到接近最优的质量。

引导朝向一张特定的图像:guidance

纯噪声对所有提示词都是一样的。模型怎么知道我们想要一个日落而不是一只狗?答案是:我们用文本**条件化(condition)**模型。

训练时,除了噪声化的图像和当前时间步,网络还会收到与图像相关联的文本的嵌入(通常是 CLIP 风格的编码器)。这样网络学到的是条件化的噪声预测:"在图像描述的是日落的前提下,噪声看起来是这样的"

推理时,我们计算两个预测:一个有提示词的,一个没有(或用空提示词的)。**Classifier-Free Guidance(CFG,无分类器引导)**这种技术沿着提示词的方向做外推:

final_prediction = unconditional + guidance × (conditional − unconditional)

guidance 系数(CFG scale)控制我们离自然有多远、贴提示词有多近。CFG=0 时,模型忽略文本;CFG=7 时,图像忠实地跟随提示词,又不会变得不自然;超过 12 后,图像会过饱和并丢掉精细细节。

Latent diffusion:少做点工

一张 512×512 的图像有 786,432 个像素(3 通道 × 512 × 512)。在这么多像素上跑 1000 步去噪非常昂贵。Stable Diffusion 让一种小技巧流行了起来:在一个被压缩过的潜在空间里工作

在训练扩散模型之前,我们先训练一个自编码器(一个 VAE),它把图像压缩到一个 64×64×4 的潜在空间——大约小 48 倍。然后扩散只作用在这个 latent 上,而不是像素上。去噪结束后,我们再把 latent 解码回最终的图像。

这正是让 Stable Diffusion 能在消费级 GPU 上几秒钟内跑起来的原因——而一个等价的基于像素的模型则需要一整个集群。

CLIP:文本与图像之间的桥

要在文本上做条件化,就需要一种和图像共享同一个空间的文本表示。这就是 CLIP(Contrastive Language-Image Pretraining,对比式语言-图像预训练)的角色,由 OpenAI 在 4 亿对图像/标题上训练。

CLIP 学习两个编码器——一个用于图像,一个用于文本——它们在一个共享空间里产出嵌入。一段标题和它对应的图像有相近的嵌入;一段不相关的标题则离得很远。这种对齐让扩散模型能够理解它在训练时从未一字不差见过的文本提示词。

U-Net 与 Transformer(DiT)

很长一段时间里,扩散的参考架构是 U-Net:一种 U 字形的卷积网络,带有保留精细细节的 skip connection。Stable Diffusion 1.4、1.5 和 2 全都用 U-Net。

但在 2022 年,Peebles 和 Xie 提出了 DiT(Diffusion Transformer,扩散 Transformer):用一个纯 Transformer 替代 U-Net。图像的每一个"patch"被当作一个 Token 处理,对其他所有 patch 都做完全注意力。不再有卷积金字塔,不再有 skip connection——只是堆叠的 Transformer 块。

Stable Diffusion 3、FLUX、Sora(视频)——所有近期的架构都迁移到了 Transformer 上。为什么?因为 Transformer scale 得更好:我们重新得到了和语言一样的缩放定律。在一个 DiT 上把算力翻倍能可预测地把图像变得更好。

采样器:DDPM、DDIM、DPM-Solver

**采样器(sampler)**是这样一个算法:它在每一步决定如何把噪声预测和当前状态组合起来,得到下一步的状态。

Sampler典型步数特点
DDPM1000随机性、忠实于训练、慢
DDIM20–50确定性、用少得多的步数就能达到接近 DDPM 的质量
DPM-Solver10–25ODE 解算器,更快
Euler / Heun20–30经典 ODE 方法,简单且稳健
LCM4–8潜在一致性蒸馏(Latent Consistency),超快

挑选采样器是用来在速度和质量之间做权衡的最容易触及的杠杆之一。

为什么这个家族会与 Transformer 并存

你可能会问:为什么不像生成文本那样一个 Token 一个 Token 地生成图像?答案很微妙。

对文本来说,顺序是天然的:我们从左往右读,每个词都依赖之前的词。对图像来说,并没有什么规范的顺序。一个像素一个像素地生成会引入伪影(最早的像素没有最后那些像素的上下文)。扩散通过一次性并行生成整张图像、并在每一步做全局精炼,解决了这个问题。

这就是为什么基于像素的自回归方法(PixelRNN、OpenAI 早期的 ImageGPT)被放弃,转而采用扩散。唯一可能的回归路径是通过对图像 Token 做自回归的模型(DALL-E 1,以及现在 Meta 的 Chameleon)——但它们在照片级真实感上仍然落后于扩散。

扩散不是 Transformer。它是另一种、在数学上正交的、学习一个复杂分布的方式——而对图像来说,它是赢家。

更新于

Diffusion:通过去噪生成图像 · Step by Token