6.5 扩散模型(三)DiT、Flow Matching与视频生成
1.从U-Net换到Transformer:DiT想要的是什么
说起来,前两篇我们讲扩散模型,一直默认它的去噪网络是U-Net。U-Net这种结构,3.4 节讲语义分割的时候我们提过,它的招牌是一条先下采样再上采样的对称路径,中间用跳着接的捷径(shortcut)把细节从浅层直接递给深层。这个设计在图像任务上确实好用,对局部细节也格外敏感,所以DDPM(Denoising Diffusion Probabilistic Models,去噪扩散概率模型)一路走来,几乎都是把它当骨干。
可是U-Net有一个绕不过去的短板,它不太容易靠堆规模变强。你想让它的通道数翻倍、层数加深,性能是会涨一点,但涨得不那么痛快,而且显存和计算的代价涨得比性能还快。这一点和语言模型那边的情形很不一样,3.4 节里我们讲过Transformer,它在大语言模型那边已经被反复验证过,参数加到几十亿、几百亿,能力还能继续往上走,这种放大就能变强的特性,英文里有个专门的说法叫scalable,可缩放。
DiT(Diffusion Transformer,扩散Transformer)做的就是这件事,把扩散模型里那个去噪网络从U-Net直接换成了Transformer。它的做法和ViT(Vision Transformer,视觉Transformer)很像,先把输入的隐图(latent image,潜空间里的图)切成一小块一小块的小图,每一块叫一个patch(图像块),每个patch再拍扁成一条向量塞进Transformer。Transformer里头就是一层一层的自注意力和前馈层,把所有patch当作一串token来处理。和DDPM原来的设定一样,它要预测的也还是每一步要去掉的那点噪声(或者说速度场,下一节会细讲),只是骨架彻底换了。
这里还有一个关键的小细节,就是条件怎么喂进去。扩散模型去噪的时候,得知道现在在第几步去噪,我们用时间步 来表示这个步号, 从 一路走到 , 是总去噪步数。还得知道要生成什么样的图,也就是文本条件,比如一段提示词。DiT用的是一种叫adaLN(adaptive Layer Normalization,自适应层归一化)的技巧,把这些条件先融合成一个向量,再用这个向量去调制每一层归一化的缩放和偏移。这么一来,整张网络每一层都能清楚地知道现在在第几步、要往什么方向去。
DiT的论文(Peebles和Xie在2023年发的那篇)最打动人的结论,就是它拿实验数据明明白白地告诉大家,扩散模型的性能也服从缩放规律(scaling law)。模型变大、计算变多,生成质量就稳定地往上走,Gflops(十亿次浮点运算,这里指模型做一次推理总共要执行的浮点运算量)和样本质量之间有一条相当干净的曲线。说穿了,这一下就把扩散模型从精心调一个U-Net那种手艺活,带到了堆规模放大Transformer的赛道上,后面的故事基本都从这里展开。
2.Flow Matching:把生成看成一次平滑的流动
讲完骨干,我们换一个角度,看看训练目标这一边。前一篇我们讲DDPM,它的训练目标是让网络预测每一步加进去的那一点噪声 ,这里的 就是从标准高斯分布里采样出来的随机噪声。这个推导在数学上是严谨的,但路径走起来稍微有点绕,要先把前向过程定义成一串马尔可夫链,再借助变分推断把目标倒推出来。
Flow Matching(流匹配)给了一个更通用也更利落的角度。它的想法说起来很简单,就是把生成过程看成一次平滑的流动。我们手上有两个分布,一个是我们熟悉的简单分布 ,通常是标准高斯噪声,另一个是数据分布 ,也就是我们想要的图所在的地方(更准确地说,是隐空间里的图所在的地方)。我们想象在这两个分布之间搭一条连续的路径,用时间变量 来描述走在这条路上的位置,这里的 和上一节的时间步意思相通,只不过取值范围换成了从 平滑地走到 。 的时候还在噪声那头, 的时候就到了数据那头。
这件事不妨拿滴墨入水来打个比方。一滴墨刚落进清水里的时候,形状是模糊的一团浊(噪声那头),随着时间慢慢散开、流动,最后稳定成水里某种好看的纹路(数据那头)。Flow Matching要学的,就是这股推动墨水从浊到清一路流动的力,也就是这条路径上的速度。
最简单的搭法叫线性插值(linear interpolation),也常被称作最优传输路径。我们记 是从 采样的噪声, 是从 采样的真实数据,那么在这条路径上任意时刻 的中间状态 就定义为:
这里 就是 和 之间的一个线性混合, 越靠近 它越像噪声,越靠近 它越像数据。
接下来我们对 关于 求导,就得到了沿着这条路径流动的速度:
这个 就是速度场(velocity field),它告诉我们在任何一个中间状态、任何一个时刻,应该往哪个方向、以多快的速度走,才能从噪声平滑地流向数据。注意这里 在线性插值下是一个常数(等于 ),这正是最优传输路径的好处,路径走的是最直的那条。
Flow Matching的训练目标,就是用一个神经网络 去逼近这个真实的速度 ,这里的 是网络的参数, 就是我们学出来的速度场模型。损失函数的形式相当朴素:
这个 就是预测速度和真实速度之间的均方误差, 表示数学期望,意思是在 、、 各自从各自的分布里采样后取平均, 表示向量长度的平方。等到训练完,采样的时候也很直观,就从 出发,这里的 表示服从某个分布采样,沿着网络预测出来的速度场 一路走,解一条常微分方程(Ordinary Differential Equation,ODE),走到 就得到了一张想要的图。
这个框架最讨喜的地方,是它把扩散模型那一套稍微绕一点的马尔可夫链推导,换成了一个干净利落的最优化目标。而且因为路径可以选最直的那条,采样的时候不用走太多步,几步甚至一步就能跑出一个像样的结果,这点比起经典DDPM动辄要上千步要轻松得多。数学上Flow Matching和扩散其实是相通的,经典DDPM可以看作Flow Matching在某种特殊路径下的特例,只是Flow Matching换了个更顺手的写法。
我记得小张前阵子看了Stable Diffusion 3的技术报告之后跟我感慨,说他以前实现DDPM的采样器(sampler)的时候,被那一堆 、、 绕得够呛,这里的 和 都是DDPM里定义噪声调度(noise schedule,控制每一步加多少噪声的方案)的中间量, 是 从1累乘到t的结果。换成Flow Matching之后,整个采样就只剩解一条ODE这一句话,清爽多了。
3.DiT加Flow Matching:新一代生成的标配
把上面两件事拼到一起,就是这两年图像生成的新标配,DiT当骨架,Flow Matching当训练目标。
这条路最早是Stable Diffusion 3蹚出来的。它把原来Stable Diffusion 1和2那套U-Net扔了,换成了一个放大的DiT。训练目标也从预测噪声 换成了预测速度 ,也就是用了Flow Matching。文本条件那一边,它接的是4.10 节讲过的CLIP和T5这种大规模预训练的文本编码器,把提示词编码成一组向量,再喂给DiT去做条件化。三件东西这么一凑,整张图的生成质量稳稳地上了一个台阶,图片里直接写文字(比如画面里画一块写着HELLO的招牌)也比以前准了很多。
Sora(OpenAI的视频生成模型)走的也是这条路。说穿了,这两年凡是叫得上名号、能产出让人眼前一亮图像或视频的生成模型,基本都在往DiT加Flow Matching这套技术栈上靠。这件事背后是有道理的,我们下面慢慢说。
4.视频生成:把时间也切成patch
讲完图像,我们再进一步,看看视频怎么生成。视频说到底就是一串连续的图,每一帧是一张图,帧与帧之间还有时间上的连贯关系。如果只把每一帧单独生成,出来的视频就会一帧一帧地抖,人物上一秒穿红衣下一秒变蓝衣,根本没法看。所以视频生成真正的难点,是让模型同时理解空间上的内容和时间上的连贯。
Sora给出的做法很优雅,它把视频也变成了patch。3.4 节我们说过ViT把图像切成空间上的patch,Sora做的事情是把视频切成时空上的patch(spacetime patch,时空块),也就是说,不仅在画面的长和宽上切,还在时间维上切。一小段时间内、画面上一小块区域的内容,打包成一个token。一整段视频,就成了一个长长的token序列。这个表示有个好处,它把图像和视频统一了起来,一段视频和一张大图,从模型角度看都是一串token,只不过视频的token更多一些。
有了这个统一的表示,剩下的就好办了。还是DiT当骨干,还是Flow Matching当训练目标,先在大规模的视频和图像上做联合预训练,让模型先把这个世界大概长什么样这件事学个大概,再用少量精标的数据做微调。采样的时候,从一段标准高斯噪声开始,噪声的形状和目标视频的token序列一样,沿着学到的速度场一路走,走到 就得到了一段完整的视频。
这里不妨打个比方。我之前看过一部讲电影特效的纪录片,里头讲早期的逐帧动画,每一帧都靠动画师一笔一笔画出来,画到后面整个动作的节奏就接不上了,人物的动作一卡一卡的。后来的做法是先把角色的关键动作建模成一个三维的骨架,再让电脑去补中间的过渡帧,节奏自然就顺了。Sora这种时空patch的做法,有点像后者,它学的是整段视频在时空上的流动方式,单帧长什么样反而不重要,所以能产出几秒到十几秒的连贯长镜头,物理上也相对合理,比如一个人走过一面镜子,镜子里还能看到他的倒影。
5.走向统一:和语言模型越来越像
写到这儿,我们不妨站远一点看看这条线的意义。
最早VAE(变分自编码器)和GAN(生成对抗网络)那一拨,每个方法都有自己的专属架构。VAE靠编码器加解码器,再加上1.4 节讲过的KL散度(Kullback-Leibler divergence,衡量两个分布差异的量)来训练。GAN靠生成器和判别器两边对抗来训练。后来DCGAN、StyleGAN把GAN的生成器做得很精巧,DDPM把U-Net推上了王座,每一种生成路线都有自己的专属零件,彼此不太通用。
可走到DiT加Flow Matching这一步,事情悄悄变了。骨干网络变成了Transformer,这和大语言模型是同一套东西。训练目标变成了回归一个连续的速度场,采样变成了解ODE,整套数学和大语言模型预训练那种预测下一个token的方式,在工程上可以共享很多基础设施,连条件输入用的CLIP、T5也是和语言、多模态那边共用的。说穿了,生成模型正在从一套专门为图像调出来的手艺活,慢慢长成Transformer大家族里的一个分支。
这件事的工程意义非常大。以前做一个图像生成团队,得自己维护U-Net的工程、自己调DDPM的采样器、自己写噪声调度的代码。现在这些东西都可以和大语言模型的训练栈共用,注意力怎么算、Transformer怎么并行、显存怎么切,这些底层问题的答案在两边是通用的。所以这两年大家常说的统一架构(unified architecture),说穿了就是这个意思,不同的生成任务,最后都收敛到了同一套Transformer加Flow Matching的组合上。
我记得看过一本讲工业革命的书,里头说早期的工厂每家都自己造机器、自己定零件规格,后来螺纹、齿轮、螺栓全行业统一了标准,整条产业链一下子就起来了。生成模型这两年走的,大概也是这样一条路,从各自为战的手艺活,慢慢走向一套通用的底座。
今天的DiT、Flow Matching和视频生成就先讲到这儿,下一篇我们再聊聊这条统一架构线往前走会遇到什么新的难题。
练习
Q1. DiT为什么把扩散模型的去噪网络从U-Net换成Transformer?换完之后最大的好处是什么?
因为U-Net不太容易靠堆规模变强——通道翻倍、层数加深,性能涨得不痛快,显存和计算代价却涨得比性能还快。Transformer在大语言模型那边已被反复验证过,参数加到几十亿、几百亿能力还能继续往上走(scalable,可缩放)。DiT把隐图切成patch当token塞进Transformer处理,最大的好处就是让扩散模型也服从缩放规律:模型变大、计算变多,生成质量就稳定往上走,从"精心调一个U-Net的手艺活"带进了"堆规模放大Transformer"的赛道。论文用Gflops和样本质量之间那条干净曲线证明了这一点。
Q2. Flow Matching把生成看成一次"平滑的流动",线性插值路径下 对应的速度 是什么?训练目标长什么样?
线性插值下对 关于 求导,,是个常数(这就是最优传输路径,路径走最直的那条)。训练目标就是用一个神经网络 去逼近这个真实速度,损失是均方误差 。训完采样时从噪声 出发,沿着学到的速度场 解一条常微分方程(ODE)走到 ,就得到想要的图。
Q3. 易错点:Flow Matching和经典DDPM是不是两套完全不相干的方法?
不是不相干,数学上两者是相通的,经典DDPM可以看作Flow Matching在某种特殊路径下的特例。区别在于表述角度:DDPM把前向过程定义成一串马尔可夫链、再借变分推断把目标倒推出来,路径走起来稍微绕,采样动辄上千步;Flow Matching换了个更通用利落的写法,把生成看成两个分布(噪声 和数据 )之间的连续流动,路径可选最直的最优传输,采样时解ODE、几步甚至一步就能出结果,比经典DDPM清爽得多。所以Flow Matching没有推翻扩散,而是给了一套更顺手的统一框架,新一代生成模型(Stable Diffusion 3、Sora)基本都换成了DiT+Flow Matching这套组合。