VTN
- 摘要
- Abstract
- 文章信息
- 引言
- 方法
- 数据预处理
- 空间特征提取
- 时间注意力编码
- 分类MLP头
- 代码实现
- 实验
- 总结
摘要
本篇博客介绍了Video Transformer Network(VTN),这是一种基于Transformer的视频识别框架,旨在替代传统依赖3D卷积网络的方法,通过全局时序建模实现高效的全视频分析。其核心思想是模块化分离时空特征学习:针对3D卷积计算效率低、长时序依赖建模困难的问题,VTN采用2D骨干网络(如ResNet、ViT)逐帧提取空间特征,结合Longformer的滑动窗口注意力机制,以线性复杂度(O(n))建模时间维度,并通过[CLS]标记的全局注意力捕捉关键帧信息。实验表明,VTN在Kinetics-400数据集上以ViT-B骨干达到78.6%的top-1准确率,同时训练速度提升16.1倍、推理速度提升5.1倍,且仅需1.5倍更少的计算量(GFLOPs)。关键技术突破包括无位置编码的高效时序建模(因短时数据集静态特征主导)和端到端全视频推理(避免传统方法的多片段拼接偏差)。尽管VTN在短时数据集上表现优异,但其长视频潜力受限于当前基准,未来可优化更复杂的位置编码、适配长视频的分层注意力机制,并扩展至时序动作定位等任务,为医疗监控、电影分析等场景提供更鲁棒的解决方案。
Abstract
This blog introduces the Video Transformer Network (VTN), a Transformer-based video recognition framework designed to replace traditional 3D convolutional networks by enabling efficient full-video analysis through global temporal modeling. Its core idea lies in modular decoupling of spatiotemporal feature learning: To address the inefficiency of 3D convolutions and their difficulty in modeling long-term dependencies, VTN employs 2D backbone networks (e.g., ResNet, ViT) to extract per-frame spatial features, combined with Longformer’s sliding window attention mechanism for linear-complexity (O(n)) temporal modeling, while leveraging the [CLS] token’s global attention to capture critical frame information. Experiments demonstrate that VTN achieves 78.6% top-1 accuracy on Kinetics-400 using a ViT-B backbone, alongside 16.1x faster training, 5.1x faster inference, and 1.5x fewer GFLOPs compared to state-of-the-art methods. Key innovations include position-free temporal modeling (enabled by static feature dominance in short-term datasets) and end-to-end full-video inference (eliminating multi-clip aggregation biases). While VTN excels on short videos, its potential for long-video analysis is constrained by current benchmarks. Future directions involve optimizing positional encoding, hierarchical attention for long sequences, and extending to tasks like temporal action localization, offering robust solutions for medical monitoring, film analysis, and beyond.
文章信息
Title:Video Transformer Network
Author:Daniel Neimark, Omri Bar, Maya Zohar, Dotan Asselmann.
Source:https://arxiv.org/abs/2102.00719
引言
在以往的视频动作识别任务中,为了处理时间维度,大多数方法是使用3D卷积网络。但传统3D卷积网络有以下局限性:
- 长距离建模不足:传统3D卷积网络(如I3D、SlowFast)依赖于局部卷积核,感受野有限,难以捕捉视频中长时序的全局依赖关系(如跨帧的动作连续性)。
- 计算效率低下:3D卷积的时空联合计算导致参数量和计算复杂度大幅增加,难以处理长视频序列(如医疗视频或电影)。
- 时空建模分离:早期基于2D卷积的方法(如TSN)需独立处理每帧特征,再通过后融合进行时间建模,导致时空信息分离。
Transformer提出后,其在计算机视觉领域也取得了较大的成功。Transformer具有强大的全局建模能力,Vision Transformer(ViT)通过自注意力机制,在图像分类任务中实现了全局上下文建模,证明了Transformer在视觉任务的潜力 。另外,Transformer的模块化设计(如多头注意力、位置编码)便于适配不同任务。
真实场景(如手术视频分析、监控视频)需处理长达数分钟的视频,而传统方法受限于固定片段输入(如Kinetics-400的10秒片段),无法端到端建模完整时序。:传统Transformer的全连接注意力复杂度为O(n²),而Longformer的滑动窗口注意力(O(n))和全局标记(Global Token)机制为长视频处理提供了可行方案。
VTN正是借鉴了视觉Transformer的全局建模优势,设计了高效的时空注意力机制,并适配了长视频端到端处理需求,这是一种一种兼顾性能与效率的视频理解框架。
方法
与其他直接从输入片段级别添加时间维度的研究不同,VTN的目标是脱离 3D 网络。VTN使用最先进的 2D 架构来学习空间特征表示,并在后续通过在生成的特征上应用注意力机制来融入时间信息。
VTN仅输入 RGB 视频帧,并且没有任何多余的功能(例如光流、横向连接流、多尺度推理、多视图推理、更长的片段微调等),就可以获得与其他最先进模型相当的结果。
上图是VTN的结构图,VTN的网络结构是模块化的,由三个连续的部分组成:一个2D的空间特征提取模型(f(x));一个基于时间注意力的编码器,本文中用的是Longformer; 一个MLP分类头,用于获得最终的类别预测。
数据预处理
数据预处理首先需要将原视频进行分段。原始视频(如三分钟视频)包含数百至数千帧,直接输入模型会导致计算量爆炸。分段处理可将视频划分为固定长度的片段(如16帧/段),降低单次推理的计算复杂度。另外,空间特征提取网络(如ResNet、ViT)本质是处理单帧图像的2D网络,分段输入允许逐帧提取特征,避免直接处理3D时空数据的高昂成本。
常用的分段方法有:滑动窗口法,将视频划分为不重叠或部分重叠的片段;均匀采样法:从长视频中均匀采样固定数量的帧(如每秒采样5帧),形成多个片段。
数据预处理将原始视频转换为适合模型处理的张量格式。数据格式为(batch_size, seq_len, C, H, W),其中,batch_size表示批次大小(如batch_size=4,表示同时处理四个视频),seq_len(T)表示视频分割的帧数(如按16帧进行分割),C表示通道数,H和W分别表示单帧图像的高度和宽度。对分段后帧数据进行空间归一化、标准化、推理增强(仅使用居中裁剪,避免训练时的随机增强,如翻转、多尺度裁剪)、时序对齐,然后合并时间与批次维度以适应2D骨干网络的输入,数据格式为:(batch_size×T, C, H, W)。
例如,原视频采样为16帧,每帧缩放至224×224分辨率,形成形状为(4, 16, 3, 224, 224)的输入张量。然后合并时间与批次维度:(4×16, 3, 224, 224) → (64, 3, 224, 224),以适应2D骨干网络逐帧处理。
空间特征提取
逐帧提取空间语义特征,生成视频帧的向量表示。空间特征提取网络可以是任何处理 2D 图像的网络,无论是深层还是浅层的,可以是经过预训练或不经过预训练的,可以是基于卷积或Transformer的,下面以ResNet类和ViT类进行说明:
ResNet-50:
最后一层卷积层输出形状为(batch_size×T, 2048, 7, 7),经全局平均池化(GAP)压缩至(batch_size×T, 2048)。然后恢复时间维度后的形状为(batch_size, T, 2048)
ViT-B/16:
每帧分割为16×16的Patch(如14×14个/帧),经线性映射为(batch_size×T, 196, 768)。通过Transformer编码器提取特征,取CLS标记形成(batch_size, T, 768)。
可以直接加载ImageNet预训练权重,无需从头训练。
时间注意力编码
时间注意力编码器的输入由两部分构成:空间特征提取结果和时间位置编码,两者通过逐元素相加实现融合。
空间特征提取结果是由空间骨干网络(如ResNet、ViT)逐帧提取的2D特征,形状为(batch_size, T, D),D是特征维度(ResNet为2048,ViT为768)
时间位置编码生成:
为每帧分配唯一的位置标识,编码其在时间轴上的顺序。常用方法为正弦/余弦函数编码,通过不同频率的正弦和余弦函数生成位置编码,公式为:
P E ( t , 2 i ) = sin ( t 1000 0 2 i / D ) , P E ( t , 2 i + 1 ) = cos ( t 1000 0 2 i / D ) PE(t,2i)=\sin{(\frac{t}{10000^{2i/D}})},PE(t,2i+1)=\cos{(\frac{t}{10000^{2i/D}})} PE(t,2i)=sin(100002i/Dt),PE(t,2i+1)=cos(100002i/Dt)其中 t 是时间位置,i 是维度索引。
时间位置编码的形状为:(1, T, D),与空间特征维度对齐 。
将时间位置编码与空间特征逐元素相加。融合后的特征既包含单帧的语义信息,又保留时间顺序关系。
时间注意力编码器结构:
时间编码器基于改进的Longformer架构,通过滑动窗口注意力与全局注意力结合,降低计算复杂度的同时捕捉长时序依赖。其核心组件如下:
- 滑动窗口注意力,若窗口大小为 w ,则每个帧仅关注前后 w 帧(如w=8),计算复杂度从O(T²)降至O(T×w)。窗口内计算Query-Key相似度,通过Softmax生成局部注意力权重 。采用多层堆叠的结构,低层使用小窗口捕捉局部动态,高层增大窗口覆盖更长时序。
- 全局注意力,引入可学习的全局向量,与所有帧交互,捕获视频级上下文。全局标记关注所有帧,所有帧也关注全局标记,增强关键动作(如起始帧)的建模能力
- 多头注意力,将特征拆分为 h 个注意力头(如h=8),每个头独立计算注意力后拼接,增强表达能力 。
- 前馈网络(FFN)与残差连接,FFN结构为两个全连接层 + ReLU激活,增强非线性。每个子层(注意力、FFN)后接残差连接与LayerNorm,以便更稳定的训练。
时间注意力编码器的输出形状不变。
分类MLP头
分类MLP头将时间编码器输出的全局视频表征(通过[CLS]标记)映射到动作类别空间,生成最终分类结果。其输入是时间编码器输出的[CLS]标记特征,形状为 (batch_size, D),其中 D 为特征维度(如ViT-B的768,ResNet-50的2048)。
核心组件如下:
- 层归一化,对[CLS]标记特征进行归一化,稳定训练过程。输出形状不变,为(batch_size, D)。
- 多层感知机,由两个线性层组成。第一层线性层将特征维度 D 映射到中间隐层, z 1 = G E L U ( W 1 ⋅ L a y e r N o r m ( x ) + b 1 ) z_1=\mathrm{GELU}(W_1\cdot\mathrm{LayerNorm}(x)+b_1) z1=GELU(W1⋅LayerNorm(x)+b1)然后经过Dropout,随机丢弃部分神经元,防止过拟合。第二层线性层将中间隐层映射到类别数 K, z 2 = W 2 ⋅ D r o p o u t ( z 1 ) + b 2 z_2=W_2\cdot\mathrm{Dropout}(z_1)+b_2 z2=W2⋅Dropout(z1)+b2
- 最后经过Softmax,将输出logits转换为类别概率分布。
代码实现
论文的源码中实现的VTN模型的定义关键video_model_builder.py中,包含骨干网络、时间编码器和多层感知机头部。以下是关键代码片段:
class VTN(nn.Module):"""VTN model builder. It uses ViT-Base as the backbone.Daniel Neimark, Omri Bar, Maya Zohar and Dotan Asselmann."Video Transformer Network."https://arxiv.org/abs/2102.00719"""def __init__(self, cfg):"""初始化VTN模型。Args:cfg (CfgNode): 模型构建配置,详细信息在配置文件的注释中。"""super(VTN, self).__init__()# 调用构建网络的方法self._construct_network(cfg)def _construct_network(self, cfg):"""构建具有给定骨干架构的VTN模型。Args:cfg (CfgNode): 模型构建配置,详细信息在配置文件的注释中。"""# 检查模型架构是否为VITif cfg.MODEL.ARCH == "VIT":# 使用预训练的ViT-Base作为骨干网络self.backbone = vit_base_patch16_224(pretrained=cfg.VTN.PRETRAINED,num_classes=0,drop_path_rate=cfg.VTN.DROP_PATH_RATE,drop_rate=cfg.VTN.DROP_RATE)else:# 如果不支持该架构,抛出异常raise NotImplementedError(f"not supporting {cfg.MODEL.ARCH}")# 获取骨干网络的嵌入维度embed_dim = self.backbone.embed_dim# 初始化分类令牌,用于后续的分类任务self.cls_token = nn.Parameter(torch.randn(1, 1, embed_dim))# 初始化时间编码器,使用VTNLongformerModelself.temporal_encoder = vtn_helper.VTNLongformerModel(embed_dim=embed_dim,max_position_embeddings=cfg.VTN.MAX_POSITION_EMBEDDINGS,num_attention_heads=cfg.VTN.NUM_ATTENTION_HEADS,num_hidden_layers=cfg.VTN.NUM_HIDDEN_LAYERS,attention_mode=cfg.VTN.ATTENTION_MODE,pad_token_id=cfg.VTN.PAD_TOKEN_ID,attention_window=cfg.VTN.ATTENTION_WINDOW,intermediate_size=cfg.VTN.INTERMEDIATE_SIZE,attention_probs_dropout_prob=cfg.VTN.ATTENTION_PROBS_DROPOUT_PROB,hidden_dropout_prob=cfg.VTN.HIDDEN_DROPOUT_PROB)# 初始化多层感知机头部,用于最终的分类self.mlp_head = nn.Sequential(# 层归一化nn.LayerNorm(cfg.VTN.HIDDEN_DIM),# 全连接层nn.Linear(cfg.VTN.HIDDEN_DIM, cfg.VTN.MLP_DIM),# GELU激活函数nn.GELU(),# 随机失活层,防止过拟合nn.Dropout(cfg.MODEL.DROPOUT_RATE),# 全连接层,输出分类结果nn.Linear(cfg.VTN.MLP_DIM, cfg.MODEL.NUM_CLASSES))def forward(self, x, bboxes=None):"""前向传播方法。Args:x: 输入数据,包含视频数据和位置编码bboxes: 边界框信息,默认为NoneReturns:模型的输出结果"""# 分离输入数据中的视频数据和位置编码x, position_ids = x# 空间骨干网络处理# 获取输入数据的形状B, C, F, H, W = x.shape# 调整维度顺序x = x.permute(0, 2, 1, 3, 4)# 合并批次和帧数维度x = x.reshape(B * F, C, H, W)# 通过骨干网络进行特征提取x = self.backbone(x)# 恢复批次和帧数维度x = x.reshape(B, F, -1)# 时间编码器处理(Longformer)# 获取输入数据的形状B, D, E = x.shape# 初始化注意力掩码attention_mask = torch.ones((B, D), dtype=torch.long, device=x.device)# 扩展分类令牌cls_tokens = self.cls_token.expand(B, -1, -1)# 将分类令牌添加到输入数据中x = torch.cat((cls_tokens, x), dim=1)# 扩展分类令牌的注意力掩码cls_atten = torch.ones(1).expand(B, -1).to(x.device)# 将分类令牌的注意力掩码添加到总注意力掩码中attention_mask = torch.cat((attention_mask, cls_atten), dim=1)# 设置分类令牌的注意力掩码为2attention_mask[:, 0] = 2# 对输入数据、注意力掩码和位置编码进行填充,以适应时间编码器的窗口大小x, attention_mask, position_ids = vtn_helper.pad_to_window_size_local(x,attention_mask,position_ids,self.temporal_encoder.config.attention_window[0],self.temporal_encoder.config.pad_token_id)# 初始化令牌类型编码token_type_ids = torch.zeros(x.size()[:-1], dtype=torch.long, device=x.device)# 设置分类令牌的令牌类型编码为1token_type_ids[:, 0] = 1# 处理位置编码position_ids = position_ids.long()# 计算注意力掩码不为0的位置mask = attention_mask.ne(0).int()# 获取时间编码器的最大位置嵌入数量max_position_embeddings = self.temporal_encoder.config.max_position_embeddings# 对位置编码进行取模操作,确保其在合法范围内position_ids = position_ids % (max_position_embeddings - 2)# 设置分类令牌的位置编码为最大位置嵌入数量减2position_ids[:, 0] = max_position_embeddings - 2# 设置注意力掩码为0的位置的位置编码为最大位置嵌入数量减1position_ids[mask == 0] = max_position_embeddings - 1# 通过时间编码器进行特征提取x = self.temporal_encoder(input_ids=None,attention_mask=attention_mask,token_type_ids=token_type_ids,position_ids=position_ids,inputs_embeds=x,output_attentions=None,output_hidden_states=None,return_dict=None)# 获取时间编码器的最后一层隐藏状态x = x["last_hidden_state"]# 通过多层感知机头部进行分类x = self.mlp_head(x[:, 0])return x
辅助模块:
VTNLongformerModel 类,它继承自 LongformerModel,用于配置和初始化一个适用于 VTN 的长序列 Transformer 模型,作为时间编码器处理视频帧之间的时间依赖关系。代码如下:
import torch
from transformers import LongformerModel, LongformerConfig
import torch.nn.functional as Fclass VTNLongformerModel(LongformerModel):"""用于VTN的Longformer模型,继承自Hugging Face的LongformerModel。"""def __init__(self,embed_dim=768,max_position_embeddings=2 * 60 * 60,num_attention_heads=12,num_hidden_layers=3,attention_mode='sliding_chunks',pad_token_id=-1,attention_window=None,intermediate_size=3072,attention_probs_dropout_prob=0.1,hidden_dropout_prob=0.1):"""初始化VTNLongformerModel。Args:embed_dim (int): 嵌入维度,默认为768。max_position_embeddings (int): 最大位置嵌入数量,默认为2 * 60 * 60。num_attention_heads (int): 注意力头的数量,默认为12。num_hidden_layers (int): 隐藏层的数量,默认为3。attention_mode (str): 注意力模式,默认为'sliding_chunks'。pad_token_id (int): 填充令牌的ID,默认为-1。attention_window (list): 注意力窗口大小,默认为None。intermediate_size (int): 中间层的维度,默认为3072。attention_probs_dropout_prob (float): 注意力概率的随机失活率,默认为0.1。hidden_dropout_prob (float): 隐藏层的随机失活率,默认为0.1。"""# 初始化LongformerConfigself.config = LongformerConfig()# 设置注意力模式self.config.attention_mode = attention_mode# 设置中间层的维度self.config.intermediate_size = intermediate_size# 设置注意力概率的随机失活率self.config.attention_probs_dropout_prob = attention_probs_dropout_prob# 设置隐藏层的随机失活率self.config.hidden_dropout_prob = hidden_dropout_prob# 设置注意力扩张率self.config.attention_dilation = [1, ] * num_hidden_layers# 设置注意力窗口大小self.config.attention_window = [256, ] * num_hidden_layers if attention_window is None else attention_window# 设置隐藏层的数量self.config.num_hidden_layers = num_hidden_layers# 设置注意力头的数量self.config.num_attention_heads = num_attention_heads# 设置填充令牌的IDself.config.pad_token_id = pad_token_id# 设置最大位置嵌入数量self.config.max_position_embeddings = max_position_embeddings# 设置隐藏层的维度self.config.hidden_size = embed_dim# 调用父类的初始化方法,不添加池化层super(VTNLongformerModel, self).__init__(self.config, add_pooling_layer=False)# 移除词嵌入层,避免分布式训练时的未使用参数错误self.embeddings.word_embeddings = None
辅助函数 pad_to_window_size_local,用于对输入的 input_ids、attention_mask 和 position_ids 进行填充,使其长度能被 2 * one_sided_window_size 整除,以适配 Longformer 自注意力机制的 sliding_chunks 实现。代码如下:
def pad_to_window_size_local(input_ids: torch.Tensor, attention_mask: torch.Tensor, position_ids: torch.Tensor,one_sided_window_size: int, pad_token_id: int):'''辅助函数,用于填充输入数据和掩码,以适应Longformer自注意力机制的滑动窗口实现。基于Hugging Face的_transformers中的_pad_to_window_size函数。Input:input_ids (torch.Tensor): 输入数据的ID,形状为(bsz x seqlen)。attention_mask (torch.Tensor): 注意力掩码,形状为(bsz x seqlen)。one_sided_window_size (int): 每个令牌一侧的窗口大小。pad_token_id (int): 填充令牌的ID。Returns:(input_ids, attention_mask, position_ids) 填充后长度可被2 * one_sided_window_size整除的张量。'''# 计算窗口大小w = 2 * one_sided_window_size# 获取输入数据的序列长度seqlen = input_ids.size(1)# 计算需要填充的长度padding_len = (w - seqlen % w) % w# 对输入数据进行填充input_ids = F.pad(input_ids.permute(0, 2, 1), (0, padding_len), value=pad_token_id).permute(0, 2, 1)# 对注意力掩码进行填充,填充部分的注意力为Falseattention_mask = F.pad(attention_mask, (0, padding_len), value=False)# 对位置编码进行填充,填充部分的注意力为Falseposition_ids = F.pad(position_ids, (1, padding_len), value=False)return input_ids, attention_mask, position_ids
实验
为了探讨attention对VTN感知完整视频序列的影响,训练了两个VTN网络,在 Longformer 中使用三层,但每层只有一个头。在一个网络中,头部像往常一样进行训练,而在第二个网络中,不是基于查询 / 键点积和 softmax 计算注意力,而是用一个硬编码的均匀分布来替换注意力矩阵,该均匀分布在反向传播期间不更新。
实验表明,尽管训练趋势相似,但通过学习得到的注意力表现更好。相比之下,均匀注意力的验证在几个周期后崩溃,表明该网络的泛化能力较差。
上表为不同空间主干网络下的VTN的效果,ViT - B - VTN 是性能最佳的模型,同时也表明了预训练数据集很重要。使用相同的 ViT 骨干网络,仅在 DeiT(在 ImageNet 上预训练)和 ViT(在 ImageNet - 21K 上预训练)之间进行更改,就能在结果上获得提升。
通过使用单头训练网络处理与图 2 相同的视频来可视化 [CLS] 标记注意力权重,并在图 3 中描绘了第一个注意力层的所有权重与视频帧对齐。与速降类别相关的片段中的权重要高得多。
总结
Video Transformer Network (VTN) 提出了一种基于Transformer的视频识别框架,通过模块化设计取代传统3D卷积网络,显著提升了效率和长序列处理能力。其核心结构分为三部分:空间骨干网络(如ResNet或ViT)逐帧提取静态特征,时间编码器(基于Longformer的滑动窗口注意力)建模时序依赖,分类MLP头聚合全局特征并输出类别。工作流程上,视频帧经空间骨干生成特征序列,通过时间编码器融合全局上下文(以[CLS]标记为核心),最后经MLP头预测动作类别。VTN的优势在于高效性(训练快16.1倍、推理快5.1倍)和端到端处理能力,支持全视频分析;但其性能受限于短时数据集(如Kinetics-400),长视频潜力尚未充分验证。未来研究可探索更复杂的时间位置编码、适配更长视频的架构优化,以及跨任务扩展(如时序动作定位),为医疗监控、电影分析等场景提供更鲁棒的解决方案。