☰
视频生成模型为何集体转向DiT?从U-Net到Transformer的工程实践与踩坑
2026/10/14 10:42:54 网站建设 项目流程

1. 从Sora刷屏说起:为什么视频生成突然集体“换剧本”

2024年初Sora那几段demo放出来的时候,我身边做视频生成的朋友分成了两拨。一拨在惊叹“物理一致性怎么做到的”,另一拨——包括我在内——盯着技术报告里那张架构图看了半天,然后说了一句:果然,还是DiT。

如果你这两年一直在跟视频生成模型打交道,应该会有一种强烈的既视感:Sora之后,几乎每一家拿得出手的视频模型,技术路线都在往同一个方向收拢。不是U-Net加时序模块的老路子,而是Diffusion Transformer,也就是DiT。这就像当年目标检测领域从各种手工特征集体转向R-CNN系一样,不是巧合,是范式收敛。

这篇文章我想聊的不是“DiT是什么”这种教科书定义,而是从一个实际做过视频生成pipeline的从业者角度,把这件事拆开:为什么扩散模型走到视频这一步,U-Net就不够用了?Transformer到底补上了哪块短板?单流DiT和双流DiT在工程上差在哪?以及,如果你现在想自己动手复现或者微调一个视频DiT,哪些坑是文档里不会写的。

先给不太熟悉背景的读者一个最简铺垫。扩散模型的核心思路是“加噪-去噪”:训练时把一张干净图逐步加高斯噪声,让模型学会从噪声里把原图还原出来;推理时从纯噪声出发,一步步去噪生成新图。这个框架在图像上已经被验证得非常成熟,Stable Diffusion就是典型代表。但视频比图像多了一个时间维度,问题就复杂了——你不仅要每一帧看起来合理,还要帧与帧之间连贯、物体运动符合物理直觉、镜头运动稳定。早期的视频扩散方案基本是在图像U-Net的基础上加时序注意力层,或者干脆把视频当“多帧图像”处理,效果总差一口气。

DiT的出现,本质上是把扩散模型的去噪骨干网络从卷积架构换成了Transformer架构。这个替换听起来只是“换个backbone”,但带来的连锁反应非常大。下面我分几个层面来讲。

2. U-Net在视频任务上到底卡在哪:不是能力问题,是结构问题

2.1 卷积的局部性假设和视频的长程依赖天然冲突

U-Net是为图像分割和图像生成设计的,它的核心是卷积。卷积有一个很强的先验:局部性。每个卷积核只关注邻域像素,通过层层堆叠来扩大感受野。这在图像上很好用,因为图像的语义信息确实有很强的局部结构。但视频不一样。

视频里有一个东西叫长程时序依赖。举个具体例子:一个人从画面左边走到右边,中间经过了遮挡、光照变化、背景切换,你要让模型理解“左边那个人”和“右边那个人”是同一个主体,这需要跨越几十甚至上百帧建立关联。卷积网络要靠堆很多层、用很大的感受野才能勉强捕捉到这种关系,而且效率很低。更麻烦的是,视频的时序依赖不是均匀分布的——相邻帧关系紧密,但关键帧之间可能隔很远。卷积的固定感受野没法自适应这种不均匀性。

Transformer的自注意力机制天生就是干这个的。它可以让任意两个位置直接建立联系,不管它们在时间轴上隔多远。这就是为什么NLP领域Transformer能取代RNN,视频生成领域也在经历类似的事情。

2.2 时序模块的“补丁式”设计带来的工程债

在DiT成为主流之前,很多视频扩散模型的做法是:保留图像U-Net的主体,在中间插入一些时序注意力层,或者把2D卷积膨胀成3D卷积。这种“补丁式”设计能跑通,但工程上问题很多。

我实际踩过的一个坑是:时序层的插入位置对效果影响极大。插得太浅,时序信息还没和空间特征充分融合;插得太深,计算量爆炸且训练不稳定。而且U-Net的编码器-解码器结构本身是为空间下采样设计的,你硬塞时序下采样进去,skip connection的维度对不齐,调试起来非常痛苦。

另一个问题是扩展性。U-Net的参数量和计算量随分辨率增长得很快,视频的分辨率乘上帧数之后,显存占用直接劝退。而Transformer架构在scaling law上的表现更可预测——你加参数、加数据,效果提升的曲线更平滑。这一点对于要训练大模型的公司来说太重要了。

2.3 一个具体的对比:同样生成16帧512x512视频

我之前做过一个粗略的对比实验(基于开源实现),同样是生成16帧512x512的视频:

维度U-Net+时序注意力DiT
参数量约2.5B约2.0B
单步推理显存约18GB约14GB
训练稳定性需要精细调学习率相对鲁棒
长程一致性约8帧后开始漂移16帧内基本稳定
代码复杂度高(时序层需手工设计)低(统一attention)

这个对比不是严格的学术benchmark,但能说明一个趋势:DiT在工程友好度和效果上都有优势。当然,U-Net也不是一无是处,它在低分辨率、短时长场景下训练收敛更快,这是它的结构先验带来的红利。

3. DiT的核心机制:把“去噪”变成一个序列建模问题

3.1 从latent patch到token:视频怎么变成Transformer能吃的序列

DiT最巧妙的一步,是把输入表示成一系列patch token。图像的话,把图切成16x16的patch,每个patch展平后过线性层变成token。视频的话,多一个时间维度,通常是把视频切成时空patch,比如2帧x16x16作为一个patch。

这里有个关键设计选择:patch的大小和形状直接决定了计算量和效果。patch太小,token数量爆炸,attention的O(n²)复杂度直接把你显存吃光;patch太大,细节丢失严重,生成的东西糊成一团。我试过在512x512、16帧的设置下,用2x16x16的patch,token数量大约是(512/16)(512/16)(16/2)=8192个。这个数量级下,标准attention已经比较吃力了,所以实际实现里往往会用窗口attention或者分解attention来降复杂度。

提示:如果你是自己复现,patch size的选择要结合你的显存和目标任务。做短视频(比如4秒以内)可以从2x16x16起步;做长视频或者高分辨率,必须上分层或窗口注意力,否则单卡根本跑不动。

3.2 条件注入:时间步、文本、类别怎么进网络

扩散模型去噪时,模型需要知道当前是第几步(时间步),以及要生成什么内容(文本prompt或其他条件)。DiT里这些条件是怎么注入的?

主流做法是adaLN-Zero,也就是自适应层归一化。具体来说,时间步和类别标签通过一个MLP映射成缩放和平移参数,作用在每层Transformer的LayerNorm上。文本条件则通常通过cross-attention注入,或者和图像token拼接在一起做self-attention。

这里有个容易忽略的细节:条件注入的方式对训练稳定性影响很大。我最早用简单的加法注入(把时间步embedding直接加到token上),训练loss震荡得很厉害。换成adaLN-Zero之后,收敛曲线平滑了很多。原因在于adaLN-Zero初始化时把缩放参数置零,相当于让网络从恒等映射开始学,这对深层Transformer的训练非常关键。

3.3 单流DiT vs 双流DiT:工程上的取舍

这是实际做项目时绕不开的一个选择。所谓双流DiT,是指文本和图像(视频)token各自走独立的Transformer层,只在特定层做cross-attention交互。单流DiT则是把文本和图像token拼成一个序列,一起做self-attention。

对比项双流DiT单流DiT
参数量较大(两套参数)较小(共享参数)
文本-视觉对齐通过cross-attention,可控性强通过self-attention,更灵活
训练效率较低较高
实现复杂度高低
适合场景精细文本控制大规模预训练

我个人的经验是:如果你要做的是从零预训练一个大模型,单流DiT更划算,参数效率高,scaling behavior也更好。但如果你是在已有图像模型基础上做视频微调,双流结构更容易复用预训练权重,迁移成本低。Sora的技术报告没有明确说用的是哪种,但从它强调的“可变时长、可变分辨率”来看,大概率是单流或者混合结构。

4. 视频DiT的工程实现:从论文到能跑的代码

4.1 3D位置编码:不只是加个维度那么简单

Transformer本身没有位置概念,必须显式注入位置信息。图像DiT用2D位置编码,视频就要用3D——两个空间维度加一个时间维度。

听起来简单,但实际做的时候有几个坑。第一,时间位置编码的频率设计。如果你直接用正弦余弦编码,时间维度的频率范围要和视频长度匹配。我见过有人用图像的位置编码直接复制到时间维,结果模型完全学不会时序关系。第二,位置编码的外推性。训练时用16帧,推理时想生成32帧,位置编码能不能外推?标准正弦编码理论上可以,但实际效果会下降。有些实现会用可学习的位置编码,但那样就完全没法外推了。

我目前比较推荐的做法是:空间用2D正弦编码,时间用1D正弦编码,然后拼接或者相加。如果要做可变时长,可以在时间维度上用相对位置编码或者RoPE(旋转位置编码),外推性会好很多。

4.2 注意力机制的显存优化:别一上来就full attention

视频DiT的token数量比图像大一个数量级,full attention的显存占用是O(n²)。8192个token的attention矩阵就是8192x8192,单精度下大约256MB,看起来还能接受。但别忘了这是每一层、每一个head都要算,实际训练时batch size一上来就爆了。

实际可用的方案有几种:

  • 窗口注意力:把token划分成固定大小的窗口,只在窗口内做attention。实现简单,但跨窗口信息交换需要额外的层。
  • 分解注意力:空间attention和时间attention分开做。先做空间维度的attention,再做时间维度的。这样复杂度从O((HWT)²)降到O(HWT*(H*W+T))。
  • 线性注意力:用核函数近似softmax attention,复杂度降到O(n)。但效果通常有损失,需要调。

我自己的项目里用的是分解注意力,在512x512、16帧的设置下,单卡A100 80G可以跑到batch size 8左右。如果换成full attention,batch size 2都勉强。

4.3 训练策略:从图像预训练到视频微调

如果你不是从零训练,而是想基于已有的图像DiT做视频生成,迁移学习是必经之路。这里的关键是时序层的初始化。

一个常用的技巧是:把图像DiT的权重加载进来,时序相关的参数(比如3D位置编码的时间部分、时序attention的投影层)用零初始化或者小随机数初始化。这样模型一开始的行为接近图像模型,然后逐渐学会时序关系。如果时序层也随机初始化,训练初期loss会爆炸,很难收敛。

另一个经验是学习率的分层设置。预训练好的空间层用较小的学习率(比如1e-5),新加的时序层用较大的学习率(比如1e-4)。这样既能保留预训练知识,又能让新模块快速学习。

5. 那些文档不会告诉你的踩坑记录

5.1 时序一致性和画面质量的跷跷板

做视频生成最头疼的就是:你调参让画面变清晰了,时序一致性就下降;你加强时序约束,画面又变糊。这本质上是因为模型容量有限,要在空间细节和时间连贯之间做权衡。

我试过的一个缓解方案是分阶段训练:先训空间质量,再训时序一致性,最后联合微调。具体来说,第一阶段只用单帧图像训练,让模型学会生成高质量画面;第二阶段加入时序loss,但降低空间loss的权重;第三阶段一起优化。这样比一上来就联合训练效果好不少。

5.2 显存不够时的梯度检查点和混合精度

视频DiT训练显存需求很大,梯度检查点(gradient checkpointing)几乎是必开的。它的原理是不保存中间激活值,反向传播时重新计算。代价是训练速度慢20%-30%,但显存能省一半以上。

混合精度训练也要注意:attention部分的softmax在fp16下容易溢出,需要用fp32计算。我最早没注意这点,训练到一半loss突然变NaN,排查了半天才发现是attention数值溢出。

5.3 数据质量比模型结构更重要

这一点可能有点反直觉,但在我实际做视频生成的过程中,数据质量的影响远大于模型结构的微调。你用什么数据训练,模型就学什么分布。如果训练数据里全是静态场景,模型就学不会大运动;如果数据里镜头切换频繁,模型生成的视频也会莫名其妙跳变。

我建议在数据准备阶段就做好运动强度分层:把训练数据按光流幅度分成低运动、中运动、高运动三档,训练时按比例混合。这样模型能同时学到稳定场景和剧烈运动。另外,文本标注的质量也很关键,视频的文本描述要比图像更详细,最好包含动作、镜头运动、场景变化等信息。

6. 如果你想自己动手:一个最小可跑的DiT视频生成实验

6.1 环境准备和依赖选择

假设你已经有一张24G显存以上的卡,想跑一个最小规模的视频DiT实验。我的建议是从开源实现起步,比如基于DiT的官方代码改。依赖方面,PyTorch 2.0以上、xformers(用于高效attention)、einops(用于张量操作)基本够用。

数据方面,可以用UCF101或者WebVid的一个子集,分辨率降到256x256,帧数8帧,这样单卡能跑起来。别一上来就搞512x512、16帧,调试周期太长。

6.2 关键代码结构

核心模块大概长这样(伪代码级别):

class VideoDiTBlock(nn.Module): def __init__(self, dim, num_heads): self.norm1 = adaLN(dim) self.attn = Attention(dim, num_heads) self.norm2 = adaLN(dim) self.mlp = MLP(dim) def forward(self, x, cond): # x: (B, T*H*W, D) x = x + self.attn(self.norm1(x, cond)) x = x + self.mlp(self.norm2(x, cond)) return x

重点是adaLN的实现,它接收条件embedding,输出缩放和平移参数。attention部分如果显存不够,换成窗口或分解版本。

6.3 训练和推理的注意事项

训练时batch size尽量大,视频DiT对batch size比较敏感。如果显存不够,用梯度累积。推理时可以用DDIM或者DPM-Solver加速采样,20步左右就能出不错的结果,不用跑满1000步。

还有一个实际经验:推理时的噪声调度要和训练时一致。我见过有人训练用linear beta schedule,推理用cosine,结果生成的东西颜色完全不对。这种细节看起来小,但影响很大。

7. 视频生成的下一步:DiT之后会是什么

DiT现在很火,但它肯定不是终点。从技术演进来,有几个方向值得关注。

一是更高效的attention机制。视频的token数量摆在那里,O(n²)的复杂度迟早要碰壁。线性attention、状态空间模型(比如Mamba)都在被尝试引入视频生成。我试过把DiT里的attention换成Mamba,训练速度确实快了不少,但生成质量还有差距,可能需要更多调优。

二是多模态条件的统一建模。现在的视频DiT主要还是文本条件,但实际应用中你可能还需要控制镜头运动、控制特定物体的轨迹、控制风格。这些条件怎么统一注入到一个Transformer里,是个开放问题。

三是自回归和扩散的融合。扩散模型并行去噪,自回归模型逐token生成,两者各有优劣。最近有一些工作尝试把两者结合,用扩散做局部细化,用自回归做长程规划。这个方向如果跑通,对长视频生成会很有帮助。

回到标题那句话:Sora之后,所有视频模型都在抄同一个剧本。这个剧本的核心就是DiT。但抄剧本只是第一步,怎么把剧本演好,每个团队都有自己的理解和取舍。我上面写的这些,大部分是我自己在实际项目中踩过的坑和总结的经验,不一定对,但至少是真实的。如果你也在做视频生成,欢迎交流。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询