ViViT:纯 Transformer 的视频分类模型
引言:从图像到视频,Transformer 的进化
在 Vision Transformer (ViT) 成功将纯 Transformer 架构引入图像分类之后,一个自然的问题浮现:能否将同样的范式直接应用于视频?视频不仅包含空间信息,还拥有时间维度,数据量呈指数级增长。2021 年,Google DeepMind 提出的 ViViT (Video Vision Transformer) 给出了肯定的答案。它是一种纯 Transformer 的视频分类模型,完全不依赖卷积神经网络,直接从视频帧序列中提取时空特征。本教程将从零开始,带你深入理解 ViViT 的设计哲学、架构细节与实现要点。
为什么需要 ViViT?
传统视频理解模型大多基于 3D 卷积(如 C3D、I3D)或 (2+1)D 卷积。Transformer 的优势在于:
- 长程依赖建模:自注意力天然能捕捉远距离的时空关系。
- 统一处理:无需专门设计卷积核,将视频视为时空标记序列。
- 可扩展性:在大规模数据上表现优异,性能随模型容量增长。
然而,直接对视频应用全注意力会导致计算量爆炸——一段视频的 token 数通常是图像的数十倍。ViViT 的核心贡献在于提出了多种高效的时空注意力分解策略,在保持纯 Transformer 结构的同时,显著降低计算复杂度。
ViViT 的整体流程
- 视频 Tokenization:将输入视频分割为时空补丁,并映射为一组 token。
- 位置编码:为每个 token 注入空间和时间位置信息。
- Transformer 编码器:堆叠若干层 Transformer 块,提取时空特征。
- 分类头:取特殊分类标记([CLS])或全局平均池化后的特征,送入全连接层进行分类。
详细架构解析
1. 视频 Tokenization:从像素到嵌入
给定一个视频 $V \in \mathbb{R}^{T \times H \times W \times C}$,其中 $T$ 为帧数,$H \times W$ 是空间分辨率,$C$ 为通道数。
方式一:Uniform frame sampling + 3D patch embedding
类似于 ViT,我们将视频分割为非重叠的时空立方体。每个补丁大小为 $t \times h \times w$。则生成的标记数量为:
$$ N = \left\lceil \frac{T}{t} \right\rceil \times \left\lceil \frac{H}{h} \right\rceil \times \left\lceil \frac{W}{w} \right\rceil $$
每个立方体被展平并经过一个可学习的线性投影,映射到 $D$ 维向量空间。得到的序列长度 $N$ 可能非常大,例如对于 32 帧、224×224 分辨率的视频,使用 $t=2, h=16, w=16$,将产生 $16 \times 14 \times 14 = 3136$ 个标记。直接应用全注意力,计算复杂度为 $O(N^2)$,几乎不可承受。
方式二:Tubelet embedding(论文推荐)
ViViT 实际采用 tubelet embedding,即只在时间维度上不做下采样或进行聚合,而是直接取 $t \times h \times w$ 的立方体。关键参数:tubelet size $t \times h \times w$。实验证明,较小的 tubelet 能保留更细粒度的时间信息,但也会增加序列长度。
2. 位置编码:时空双重位置信息
Transformer 需要位置编码来区分 token 的顺序。ViViT 提供两种方案:
- 绝对位置编码:学习一个 $N \times D$ 的位置嵌入矩阵,直接加到 token 上。
- 分解位置编码:分别学习空间位置编码和时间位置编码,然后相加。这有助于模型更好地解耦时空信息,且能自然处理不同分辨率或帧率的输入。
实验中,分解位置编码通常与中心初始化结合,表现稍好,但差距不大。
3. Transformer 编码器的核心变体
ViViT 论文提出四种不同的模型变体,核心区别在于如何在 Transformer 层内混合时空信息,以平衡计算效率与模型能力。这里我们介绍最关键的两种。
模型 1:Spatial-Temporal Full Attention(时空全注意力)
最直接的版本,所有 $N$ 个时空标记两两计算注意力。这拥有最强的建模能力,但计算量为 $O(N^2)$。只适用于极短的视频或较小的输入。 $$ \text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V $$ 每个 query 与所有时空位置的 key 进行交互。
模型 2:Factorised Encoder(分解编码器)
为了降低计算开销,ViViT 提出空间-时间分解注意力。它由两个串联的子模块组成:
- Spatial Transformer Block(空间块):对同一时间索引内的所有空间标记执行自注意力。即对于每一帧,学习其空间关系。计算每个帧内的交互,因此复杂度降为 $O(T \cdot S^2)$,其中 $S$ 是每帧的空间标记数。
- Temporal Transformer Block(时间块):对同一空间位置的所有时间标记执行自注意力。即对于每个 patch,捕捉其随时间的变化。计算每个空间位置的时间交互,复杂度为 $O(S \cdot T^2)$。
实际部署时,我们可以交替堆叠 $L_s$ 个空间块和 $L_t$ 个时间块。这种分解将原本的 $O(T^2 S^2)$ 复杂度降至 $O(T S (T + S))$,大幅提升效率。需注意,在空间块和时间块之间,需要适当的 reshaping 和转置操作,使数据排列符合各自注意力的要求。
模型 3:Factorised Self-Attention(分解自注意力)
同一层内同时计算空间注意力和时间注意力,然后融合。具体来说:
- 先计算空间注意力:reshape 输入为 $(B \cdot T, S, D)$,做多头自注意力。
- 再计算时间注意力:reshape 输入为 $(B \cdot S, T, D)$,做多头自注意力。
- 将二者输出相加或连接。每一层都同时建模时空,表达能力更强,但计算量介于模型1和模型2之间。
模型 4:Factorised Dot-Product(分解点积注意力)
使用不同的注意力头分别处理空间和时间维度:一部分头执行空间注意力,一部分头执行时间注意力。计算更精简,但实际应用略少。
实验表明,Factorised Encoder(模型2)在计算模态和分类准确率之间取得了最佳平衡。
4. 分类策略
- CLS token:在序列开头拼接一个可学习的分类标记,仅使用该标记的最终输出来进行类别预测。
- 全局平均池化:对所有 token 输出取平均,再送入分类器。
ViViT 采用标准的 CLS token 方式,与 ViT 保持一致。
模型配置与缩放
ViViT 沿用了 ViT 的缩放规则,将模型分为不同规模:
- ViViT-Ti:Tiny
- ViViT-S:Small
- ViViT-B:Base
- ViViT-L:Large
- ViViT-H:Huge
缩放维度包括编码器层数、隐藏维度、MLP 膨胀比、注意力头数等。此外,ViViT 也可从预训练的图像 ViT 初始化,再在视频数据上调优,该技巧显著提升收敛速度和最终性能。
输入增强与训练技巧
-
多视图推理 (Multi-View Testing)
训练和测试时,可以取视频的多个空间裁剪(如左/右/中裁剪)和多时间片段,然后平均预测结果,提升模型的 robust 性。 -
中心初始化 (Central Frame Initialization)
将预训练图像 ViT 的权重用于初始化空间部分的 Transformer,时间部分则复用空间权重或从零开始训练。位置编码可相应扩充。 -
数据增强
常用的视频增强包括随机缩放裁剪、水平翻转、时序抖动(随机抽取帧)、RandAugment等。 -
正则化
使用随机深度(Stochastic Depth)、标签平滑、混合精度训练等。
性能表现与对比
ViViT 在多个视频分类基准上取得与甚至超越同期 3D CNN 模型(如 TimeSformer、SlowFast)的性能。
- Kinetics 400/600:ViViT-L/16×2 在 Kinetics 400 上 top-1 达到 82.8%,与基于 CNN 的最佳模型持平。
- Moments in Time:在更具挑战性的大规模数据集上,ViViT 展现出更好的可扩展性。
- 计算效率:分解编码器的变体以远低于全注意力的 FLOPs 达到相近精度,实现了速度与精度的优秀折中。
值得注意的是,ViViT 在足够大的预训练数据集(如 ImageNet-21K + Kinetics 联合训练)下才能充分发挥纯 Transformer 的优势,这与 ViT 的归纳偏置较弱、更依赖数据的特性一致。
从原理到实现:伪代码骨架
掌握概念后,我们通过一个高度简化的 PyTorch 风格框架,理解 ViViT 最核心的分解编码器流程。
class SpatialSelfAttention(nn.Module):
# 输入: (B, T*S, D) -> reshape 为 (B*T, S, D)
def forward(self, x, T, S):
B = x.shape[0]
x = x.reshape(B * T, S, -1)
x = self.attention(x) # 标准自注意力
x = x.reshape(B, T*S, -1)
return x
class TemporalSelfAttention(nn.Module):
# 输入: (B, T*S, D) -> reshape 为 (B*S, T, D)
def forward(self, x, T, S):
B = x.shape[0]
x = x.reshape(B, S, T, -1).transpose(1,2) # (B, T, S, D)
x = x.reshape(B * S, T, -1)
x = self.attention(x)
x = x.reshape(B, T, S, -1).transpose(1,2).reshape(B, T*S, -1)
return x
class FactorisedBlock(nn.Module):
def __init__(self, dim, num_heads, mlp_ratio=4.):
super().__init__()
self.spatial_attn = SpatialSelfAttention(dim, num_heads)
self.temporal_attn = TemporalSelfAttention(dim, num_heads)
self.mlp = Mlp(dim, mlp_ratio)
self.norm = nn.LayerNorm(dim)
def forward(self, x, T, S):
x = x + self.spatial_attn(self.norm(x), T, S)
x = x + self.temporal_attn(self.norm(x), T, S)
x = x + self.mlp(self.norm(x))
return x
完整的 ViViT 将包含 tubelet embedding、可学习的位置编码以及多个上述块堆叠而成的编码器。
小结与思考
ViViT 成功将纯 Transformer 引入视频理解,关键创新在于通过分解时空注意力控制计算复杂度。它不仅证明了 Transformer 可以胜任视频任务,还为后续的视频 Transformer 工作(如 TimeSformer、Video Swin Transformer)开辟了道路。
对本教程的反思与扩展:
- 可变的输入长度:分解式位置编码使得 ViViT 能轻松处理不同帧数的视频,这对工业落地至关重要。
- 多模态延伸:将文本、音频等模态的 token 与视频 token 拼接,即可构建多模态 Transformer,ViViT 的架构思想被许多 Video-Language 模型借鉴。
- 局限性:纯 Transformer 依赖大量数据预训练;tubelet embedding 丢失了帧内的某些高频细节;对于极长视频,分解编码器的时间注意力仍可能成为瓶颈。
如果你正在构建视频分类系统,ViViT 提供了一个高性能、可调节效率的基准模型。建议从预训练 ViT 权重开始微调,并使用 Factorised Encoder 变体以获得最佳性价比。