欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 新车 > 【第二十八周】:Temporal Segment Networks:用于视频动作识别的时间分段网络

【第二十八周】:Temporal Segment Networks:用于视频动作识别的时间分段网络

2025/3/24 16:25:32 来源:https://blog.csdn.net/m0_59510256/article/details/146439758  浏览:    关键词:【第二十八周】:Temporal Segment Networks:用于视频动作识别的时间分段网络

TSN

  • 摘要
  • Abstract
  • 文章信息
  • 引言
  • 方法
    • 时间分段采样
    • 分段聚合
    • 输入模态
    • 聚合函数
    • 多尺度时序窗口集成(M-TWI)
    • 训练
  • 代码实现
  • 实验结果
  • 总结

摘要

本篇博客介绍了时间分段网络(Temporal Segment Network, TSN),这是一种针对视频动作识别的高效深度学习框架,其核心思想是通过分段稀疏采样和全局时序建模解决传统方法在长时程动作建模中的计算冗余与局部片段覆盖不足问题。TSN将视频均匀划分为多个时间段,每个段随机抽取一个短片段,利用共享权重的卷积网络提取片段特征,并通过聚合函数(如平均池化、Top-K池化)融合全局时序信息生成视频级预测。针对未修剪视频的背景干扰问题,TSN提出多尺度时序窗口集成(M-TWI),结合滑动窗口与自适应池化抑制噪声。关键技术包括:通过稀疏采样覆盖长视频内容,实现计算成本与视频时长无关;多模态输入(RGB、光流、RGB差异)互补时空特征;跨模态初始化与部分批量归一化缓解小数据集训练过拟合。实验表明,TSN在HMDB51(71.0%)、UCF101(94.9%)等数据集上达到当时最优性能。其优势在于高效性与泛化能力,但依赖光流计算且预训练迁移成本较高。未来可探索轻量化模态融合、端到端多模态联合优化,以及动态背景下的长时动作建模增强。

Abstract

This blog introduces the Temporal Segment Network (TSN), an efficient deep learning framework for video action recognition. Its core concept addresses computational redundancy and insufficient local segment coverage in traditional methods for long-term action modeling through segment-based sparse sampling and global temporal modeling. TSN divides videos into multiple temporal segments, randomly samples a short snippet from each segment, extracts features using convolution networks with shared weights, and generates video-level predictions by aggregating global temporal information through fusion functions (e.g., average pooling, Top-K pooling). To mitigate background noise in untrimmed videos, TSN proposes Multi-scale Temporal Window Integration (M-TWI), combining sliding windows with adaptive pooling to suppress interference. Key techniques include: 1) Sparse sampling enabling efficient coverage of long videos with computation cost independent of video duration; 2) Multi-modal inputs (RGB, optical flow, RGB difference) complementing spatiotemporal features; 3) Cross-modality initialization and partial batch normalization alleviating overfitting on small datasets. Experiments show TSN achieves state-of-the-art performance on datasets including HMDB51 (71.0%) and UCF101 (94.9%). Its strengths lie in efficiency and generalization capability, though limitations include reliance on optical flow computation and high pretraining transfer costs. Future directions may explore lightweight modality fusion, end-to-end multi-modal joint optimization, and enhanced long-term action modeling in dynamic backgrounds.


文章信息

Title:Temporal Segment Networks for Action Recognition in Videos
Author:Limin Wang, Yuanjun Xiong, Zhe Wang, Yu Qiao, Dahua Lin, Xiaoou Tang, and Luc Van Gool.
Source:https://arxiv.org/abs/1705.02953


引言

  视频动作识别在安防、行为分析等领域有重要应用,其核心挑战在于同时捕捉视频的外观特征和动态时序信息,这面临尺度变化、视角改变等难题。。
  卷积神经网络(ConvNets)在图像分类中表现优异,但在视频动作识别中优势有限。原因如下:
首先,尽管长时结构已被证明在理解视频中的动态性有至关重要的作用,但当前大多数方法仅关注短时运动(如10帧),缺乏对长时程时间结构的建模。而且一些最近的改进主要依赖于预定义采样间隔的密集采样,这对计算资源的要求极高,且可能会导致视频的重要信息丢失。
其次,现有的动作识别方法大多是针对修剪视频设计,难以直接应用于未修剪视频(如网络视频),其中背景干扰严重。
第三,动作识别的公开数据集的规模小,导致模型易过拟合。而且光流提取等预处理步骤计算成本高,限制了实时性需求。
以上问题促使作者从以下三个方面研究动作识别问题:

  1. 如何有效地学习捕捉长时间结构的视频表示;
  2. 如何利用这些学习的 ConvNet 模型来更真实地设置未修剪的视频;
  3. 如何在给定有限训练样本的情况下有效地学习 ConvNet 模型并将其应用于大规模数据。

方法

论文提出一种基于稀疏的时间采样策略和视频级监督框架,TSN。
在这里插入图片描述
TSN流程如下:

  1. 将输入视频均匀划分为 K 个时间段(Segments)(如 K=3 或 7),每个时间段覆盖视频的不同部分。
  2. 从每个时间段中 随机抽取一个短片段(Snippet)(如单帧、光流序列或 RGB 差异)。
  3. 对每个片段通过 共享权重的卷积网络(ConvNet) 提取特征,生成片段级别的类别预测分数。
  4. 使用 聚合函数 融合所有片段的预测结果,生成视频级分类分数。
  5. 对聚合后的分数进行 Softmax 归一化,输出视频的最终动作类别概率。

时间分段采样

视频中的连续帧具有高度的冗余性,即连续的帧在很大程度上是一样的。所以需要使用稀疏采样,鉴于视频的长时间结构的有效性,需要在整个视频上进行采样。
本文提出的采样方法如下:
先将整个视频V分为持续时间相等的K个段 { S 1 , S 2 . . . . . . S K } \{S_1,S_2......S_K\} {S1,S2......SK}(论文中的segment),然后在每个段中随机采样一个片段(论文中的snippet),得到片段序列 ( T 1 , T 2 . . . . . . T K ) (T_1,T_2......T_K) (T1,T2......TK),这就是后面建模需要用到的输入。
通过这种采样方式,可以避免连续帧的高度冗余,还能对整个视频进行采样。另外,这种采样方法使得此框架的计算成本不依赖于视频的持续时间,无论视频多长,都是分为K短,采样K个片段,并对K个片段进行后续计算。

分段聚合

分段聚合将片段级预测聚合到视频级分数中。
经过分段采样得到的片段序列 ( T 1 , T 2 . . . . . . T K ) (T_1,T_2......T_K) (T1,T2......TK)经过共享参数的卷积后得到各自对应的动作类别得分(每个片段分别预测),得到片段级预测,表示为: F = ( F ( T 1 ; W ) , F ( T 2 ; W ) , ⋯ , F ( T K ; W ) ) . \begin{array}{l}\\ F=\left(\mathcal{F}(T_1;\mathbf{W}\right),\mathcal{F}(T_2;\mathbf{W}),\cdots,\mathcal{F}(T_{K};\mathbf{W})).\end{array} F=(F(T1;W),F(T2;W),,F(TK;W)).其中 W \mathbf{W} W表示卷积操作的参数。
将得到的片段级预测结果经过分段聚合函数 G \mathcal{G} G得到视频级预测,表示为: G = G ( F ( T 1 ; W ) , F ( T 2 ; W ) , ⋯ , F ( T K ; W ) ) . \begin{array}{l}\\ G=\mathcal{G}(\mathcal{F}(T_1;\mathbf{W}),\mathcal{F}(T_2;\mathbf{W}),\cdots,\mathcal{F}(T_{K};\mathbf{W})).\end{array} G=G(F(T1;W),F(T2;W),,F(TK;W)).常用的聚合方法有:平均池化、最大池化、加权平均。
损失的计算是在分段聚合的基础上进行的,采用的是交叉上损失函数: L ( y , G ) = − ∑ i = 1 C y i ( g i − log ⁡ ∑ j = 1 C exp ⁡ g j ) \mathcal{L}(y,\mathbf{G})=-\sum_{i=1}^Cy_i\left(g_i-\log\sum_{j=1}^C\exp g_j\right) L(y,G)=i=1Cyi(gilogj=1Cexpgj)其中, C C C是动作类别的数量, g j g_j gj G G G的第 j j j个维度, y i y_i yi是类别 i i i的真实标签。
则损失相对于模型参数 W \mathbf{W} W的梯度为: ∂ L ( y , G ) ∂ W = ∂ L ∂ G ⋅ ∂ G ∂ W \frac{\partial\mathcal{L}(y,\mathbf{G})}{\partial\mathbf{W}}=\frac{\partial\mathcal{L}}{\partial\mathbf{G}}\cdot\frac{\partial\mathbf{G}}{\partial\mathbf{W}} WL(y,G)=GLWG其中 G G G K K K F ( T K ) F(T_K) F(TK)构成,由链式法则: ∂ G ∂ W = ∑ k = 1 K ∂ G ∂ F ( T k ) ⋅ ∂ F ( T k ) ∂ W \frac{\partial\mathbf{G}}{\partial\mathbf{W}}=\sum_{k=1}^K\frac{\partial\mathbf{G}}{\partial F(T_k)}\cdot\frac{\partial F(T_k)}{\partial\mathbf{W}} WG=k=1KF(Tk)GWF(Tk)所以最终的损失函数为: ∂ L ( y , G ) ∂ W = ∂ L ∂ G ∑ k = 1 K ∂ G ∂ F ( T k ) ∂ F ( T k ) ∂ W \frac{\partial\mathcal{L}(y,\mathbf{G})}{\partial\mathbf{W}}=\frac{\partial\mathcal{L}}{\partial\mathbf{G}}\sum_{k=1}^K\frac{\partial\mathbf{G}}{\partial F(T_k)}\frac{\partial F(T_k)}{\partial\mathbf{W}} WL(y,G)=GLk=1KF(Tk)GWF(Tk)从以上公式中也可看出时间分段网络可以从整个视频中学习模型参数,而不是从一小段视频中学习模型参数。

视频级分数需要经过预测函数 H \mathcal{H} H来对最终的类别分数进行预测: T S N ( T 1 , T 2 , ⋯ , T K ) = H ( G ( F ( T 1 ; W ) , F ( T 2 ; W ) , ⋯ , F ( T K ; W ) ) ) . \begin{array} {l}\mathrm{TSN}(T_1,T_2,\cdots,T_K)= \mathcal{H}(\mathcal{G}(\mathcal{F}(T_1;\mathbf{W}),\mathcal{F}(T_2;\mathbf{W}),\cdots,\mathcal{F}(T_K;\mathbf{W}))). \end{array} TSN(T1,T2,,TK)=H(G(F(T1;W),F(T2;W),,F(TK;W))).
H \mathcal{H} H一般使用softmax函数。

输入模态

与静态图像不同,视频的额外时间维度为动作理解提供了另一个重要线索,即运动。使用密集的光流场作为运动表示的来源被证明是有效的。论文从两个方面扩展了这种方法,即准确性和速度。如下图所示,除了 RGB 和光流的原始输入模态外,作者还研究了其他两种模态:RGB差异(RGB difference)和扭曲的光流场(warped optical flow fields),这两种模态与光流场一样,作为时间流的输入。
在这里插入图片描述
图中的四列分别表示:RGB 图像、RGB 差异、光流场(x,y 方向)和变形光流场(x,y 方向)
RGB difference(RGB差异)
RGB Differences是通过计算连续帧之间RGB像素值的差异来捕捉表观运动信息的模态。它直接利用原始RGB帧的差异作为运动特征,无需复杂的光流计算。

实现方式:选择相邻的两帧图像 I t I_t It I t + 1 I_{t+1} It+1,逐像素计算两帧的绝对差值: D t = ∣ I t + 1 − I t ∣ D_t=|I_{t+1}-I_t| Dt=It+1It为了捕捉更长时间跨度的变化,可以计算多对连续帧的差分,并将它们堆叠在一起。
RGB difference通过简单差异反映物体移动或形变,避免了光流提取的高计算成本,适合快速动作建模。

Optical Flow Fields(光流场)
光流场描述视频中每个像素点在连续帧中的运动向量(水平和垂直方向位移),直接编码物体运动的时空信息。

实现方式:使用TV-L1光流算法,计算每对连续帧的光流场。光流场分为水平(x方向)和垂直(y方向)两个通道。为了捕捉更长时间跨度的变化,可将多个光流帧堆叠成输入序列。
Optical Flow Fields精准对运动建模:直接反映像素级运动,适合复杂动作。

Warped Optical Flow(变形光流场)
对原始光流进行校正,消除相机运动的影响,专注于主体(如人体)自身运动。
实现方式:变形光流场通过 全局运动估计 和 反向补偿 得到,公式为:
WarpedFlow ( x , y ) = H − 1 ⋅ OriginalFlow ( x , y ) \text{WarpedFlow}(x,y)=H^{-1}\cdot\text{OriginalFlow}(x,y) WarpedFlow(x,y)=H1OriginalFlow(x,y)其中, H H H是全局运动(如相机运动)的仿射/单应变换矩阵; H − 1 H^{-1} H1 H H H 的逆矩阵,用于反向补偿。
Warped Optical Flow能够抑制由相机移动引起的背景干扰(如跟拍镜头中的背景偏移),增强对主体运动的敏感性。

三种时间流模态对比

模态优势劣势适用场景
RGB Differences实时性强运动表征较粗糙实时系统、轻量级部署
Optical Flow运动建模精准计算成本高(依赖TV-L1光流提取)高精度离线分析
Warped Flow抗相机运动干扰校正步骤增加复杂度复杂背景(如运动赛事、街景)

聚合函数

论文中分析了五种聚合函数:最大池化、平均池化、Top K 池化、加权平均和注意力加权。

  1. 最大池化:为每个动作类别选取所有片段中得分最高的片段作为视频级预测,仅保留最显著片段的激活值。其优点是突出最具判别性的单个片段,适合快速定位关键动作片段。缺点是忽略其他片段的信息,无法建模长时程多阶段动作。
  2. 平均池化:将所有片段的类别得分取平均作为视频级预测,均匀融合所有片段信息。其优点是简单高效,充分利用全局信息,适合修剪视频。但易受背景片段干扰,未修剪视频中性能下降。
  3. Top K 池化:选择每个类别得分最高的 K K K 个片段进行平均,平衡判别性与冗余抑制。其优点是能保留重要片段,抑制噪声,适合未修剪视频。
  4. 加权平均:为每个片段分配固定权重,加权平均得分,权重为可学习参数。其优点是能通过权重区分不同阶段的重要性。但权重与视频内容无关,灵活性不足。
  5. 注意力加权:动态计算每个片段的注意力权重,加权融合得分,其中权重基于片段特征生成。其优点是能自适应聚焦关键片段,抑制背景,适合复杂场景。但其计算复杂度较高,需联合优化注意力模型。

多尺度时序窗口集成(M-TWI)

在未修剪的视频中(如监控录像、网络视频),动作可能只占视频的一小部分,大部分内容是无关的背景。传统方法直接对整个视频平均处理时,背景噪声会严重影响动作识别的准确性。
多尺度时序窗口集成(M-TWI)的步骤如下:

  1. 多尺度窗口划分:
    将视频按不同时间长度划分为窗口(如1秒、2秒、4秒、8秒、16秒)。
    目的:覆盖不同持续时间的动作(短暂动作用短窗口,长动作用长窗口)。
  2. 窗口内最大池化
    对每个窗口内的所有片段进行动作识别,仅保留得分最高的片段。
    作用:突出窗口中最可能是动作的部分,忽略低分背景片段。
  3. Top-K池化抑制背景
    对每个尺度的所有窗口,选择得分最高的前K个窗口(例如按窗口数量动态调整K值)。
    效果:筛除低分窗口(可能包含背景),仅整合高分动作窗口。
  4. 多尺度结果融合
    将不同尺度(1秒、2秒、4秒等)的Top-K窗口结果平均,得到最终预测。
    优势:综合不同时间粒度的信息,提升动作定位鲁棒性。

训练

用于动作识别的现有人工注释数据集在大小方面受到限制。在实践中,在这些数据集上训练深度 卷积神经网络容易出现过拟合。为了解决这一问题,文中采取了三种方法:

  1. 跨模态初始化(Cross Modality Initialization):利用大规模图像数据(ImageNet)预训练模型初始化网络。在空间流(RGB)上直接使用ImageNet预训练的权重。在时间流(光流、RGB差异等)上,将RGB模型的第一个卷积层权重在RGB通道上取平均,复制到时间流输入的多个通道(如光流的x/y方向),其他层直接继承RGB模型的参数。
  2. 部分批量归一化(Partial Batch Normalization):迁移学习中,批量归一化(BN)的均值和方差可能因目标数据集小而产生偏差。此方法仅更新第一个BN层以适应输入模态的分布差异,同时固定其他BN层以防止过拟合,保持预训练模型的稳定性。
  3. 数据增强(Data Augmentation):包括常规方法随机裁剪和水平翻转,还新增了角点裁剪和多尺度抖动。角点裁剪从图像四角和中心裁剪,避免模型过度关注中心区域。多尺度抖动是随机选择裁剪尺寸或调整至固定尺寸。

代码实现

下面是官方实现的TSN的网络搭建

from torch import nnfrom ops.basic_ops import ConsensusModule, Identity
from transforms import *
from torch.nn.init import normal, constantclass TSN(nn.Module):def __init__(self, num_class, num_segments, modality,base_model='resnet101', new_length=None,consensus_type='avg', before_softmax=True,dropout=0.8,crop_num=1, partial_bn=True):"""初始化TSN模型。参数:num_class (int): 分类的类别数量。num_segments (int): 视频的分段数量。modality (str): 输入模态,可选值为 'RGB', 'Flow', 'RGBDiff'。base_model (str): 基础模型,默认为 'resnet101'。new_length (int): 每个分段的长度,默认为 None。consensus_type (str): 共识模块的类型,默认为 'avg'。before_softmax (bool): 是否在共识模块前进行Softmax操作,默认为 True。dropout (float): Dropout的概率,默认为 0.8。crop_num (int): 裁剪的数量,默认为 1。partial_bn (bool): 是否使用部分批量归一化,默认为 True。"""super(TSN, self).__init__()self.modality = modalityself.num_segments = num_segmentsself.reshape = Trueself.before_softmax = before_softmaxself.dropout = dropoutself.crop_num = crop_numself.consensus_type = consensus_type# 检查在不使用Softmax前的情况下,共识模块类型是否为 'avg'if not before_softmax and consensus_type != 'avg':raise ValueError("Only avg consensus can be used after Softmax")# 根据模态设置每个分段的长度if new_length is None:self.new_length = 1 if modality == "RGB" else 5else:self.new_length = new_length# 打印模型初始化信息print(("""
Initializing TSN with base model: {}.
TSN Configurations:input_modality:     {}num_segments:       {}new_length:         {}consensus_module:   {}dropout_ratio:      {}""".format(base_model, self.modality, self.num_segments, self.new_length, consensus_type, self.dropout)))# 准备基础模型self._prepare_base_model(base_model)# 准备TSN模型的全连接层feature_dim = self._prepare_tsn(num_class)# 根据模态对基础模型进行修改if self.modality == 'Flow':print("Converting the ImageNet model to a flow init model")self.base_model = self._construct_flow_model(self.base_model)print("Done. Flow model ready...")elif self.modality == 'RGBDiff':print("Converting the ImageNet model to RGB+Diff init model")self.base_model = self._construct_diff_model(self.base_model)print("Done. RGBDiff model ready.")# 初始化共识模块self.consensus = ConsensusModule(consensus_type)# 如果不在共识模块前进行Softmax操作,则添加Softmax层if not self.before_softmax:self.softmax = nn.Softmax()# 启用部分批量归一化self._enable_pbn = partial_bnif partial_bn:self.partialBN(True)def _prepare_tsn(self, num_class):"""准备TSN模型的全连接层。参数:num_class (int): 分类的类别数量。返回:int: 特征维度。"""# 获取基础模型最后一层的输入特征维度feature_dim = getattr(self.base_model, self.base_model.last_layer_name).in_featuresif self.dropout == 0:# 如果不使用Dropout,直接替换最后一层为全连接层setattr(self.base_model, self.base_model.last_layer_name, nn.Linear(feature_dim, num_class))self.new_fc = Noneelse:# 如果使用Dropout,在最后一层前添加Dropout层,并添加新的全连接层setattr(self.base_model, self.base_model.last_layer_name, nn.Dropout(p=self.dropout))self.new_fc = nn.Linear(feature_dim, num_class)std = 0.001if self.new_fc is None:# 初始化最后一层的权重和偏置normal(getattr(self.base_model, self.base_model.last_layer_name).weight, 0, std)constant(getattr(self.base_model, self.base_model.last_layer_name).bias, 0)else:# 初始化新全连接层的权重和偏置normal(self.new_fc.weight, 0, std)constant(self.new_fc.bias, 0)return feature_dimdef _prepare_base_model(self, base_model):"""准备基础模型。参数:base_model (str): 基础模型的名称。"""if 'resnet' in base_model or 'vgg' in base_model:# 如果是ResNet或VGG模型self.base_model = getattr(torchvision.models, base_model)(True)self.base_model.last_layer_name = 'fc'self.input_size = 224self.input_mean = [0.485, 0.456, 0.406]self.input_std = [0.229, 0.224, 0.225]if self.modality == 'Flow':# 如果是光流模态,修改输入均值和标准差self.input_mean = [0.5]self.input_std = [np.mean(self.input_std)]elif self.modality == 'RGBDiff':# 如果是RGB差分模态,修改输入均值和标准差self.input_mean = [0.485, 0.456, 0.406] + [0] * 3 * self.new_lengthself.input_std = self.input_std + [np.mean(self.input_std) * 2] * 3 * self.new_lengthelif base_model == 'BNInception':# 如果是BNInception模型import tf_model_zooself.base_model = getattr(tf_model_zoo, base_model)()self.base_model.last_layer_name = 'fc'self.input_size = 224self.input_mean = [104, 117, 128]self.input_std = [1]if self.modality == 'Flow':# 如果是光流模态,修改输入均值self.input_mean = [128]elif self.modality == 'RGBDiff':# 如果是RGB差分模态,修改输入均值self.input_mean = self.input_mean * (1 + self.new_length)elif 'inception' in base_model:# 如果是Inception模型import tf_model_zooself.base_model = getattr(tf_model_zoo, base_model)()self.base_model.last_layer_name = 'classif'self.input_size = 299self.input_mean = [0.5]self.input_std = [0.5]else:# 如果是未知的基础模型,抛出错误raise ValueError('Unknown base model: {}'.format(base_model))def train(self, mode=True):"""重写默认的train()方法,冻结部分批量归一化层的参数。参数:mode (bool): 是否处于训练模式,默认为 True。返回:TSN: 模型实例。"""super(TSN, self).train(mode)count = 0if self._enable_pbn:print("Freezing BatchNorm2D except the first one.")for m in self.base_model.modules():if isinstance(m, nn.BatchNorm2d):count += 1if count >= (2 if self._enable_pbn else 1):# 冻结除第一个批量归一化层外的其他批量归一化层m.eval()# 关闭梯度更新m.weight.requires_grad = Falsem.bias.requires_grad = Falsereturn selfdef partialBN(self, enable):"""启用或禁用部分批量归一化。参数:enable (bool): 是否启用部分批量归一化。"""self._enable_pbn = enabledef get_optim_policies(self):"""获取优化策略,为不同类型的参数设置不同的学习率和衰减率。返回:list: 优化策略列表。"""first_conv_weight = []first_conv_bias = []normal_weight = []normal_bias = []bn = []conv_cnt = 0bn_cnt = 0for m in self.modules():if isinstance(m, torch.nn.Conv2d) or isinstance(m, torch.nn.Conv1d):# 处理卷积层参数ps = list(m.parameters())conv_cnt += 1if conv_cnt == 1:# 第一个卷积层的权重first_conv_weight.append(ps[0])if len(ps) == 2:# 第一个卷积层的偏置first_conv_bias.append(ps[1])else:# 其他卷积层的权重normal_weight.append(ps[0])if len(ps) == 2:# 其他卷积层的偏置normal_bias.append(ps[1])elif isinstance(m, torch.nn.Linear):# 处理全连接层参数ps = list(m.parameters())# 全连接层的权重normal_weight.append(ps[0])if len(ps) == 2:# 全连接层的偏置normal_bias.append(ps[1])elif isinstance(m, torch.nn.BatchNorm1d):# 处理一维批量归一化层参数bn.extend(list(m.parameters()))elif isinstance(m, torch.nn.BatchNorm2d):# 处理二维批量归一化层参数bn_cnt += 1# 除冻结的批量归一化层外的参数if not self._enable_pbn or bn_cnt == 1:bn.extend(list(m.parameters()))elif len(m._modules) == 0:if len(list(m.parameters())) > 0:# 如果有新的原子模块类型,抛出错误raise ValueError("New atomic module type: {}. Need to give it a learning policy".format(type(m)))return [{'params': first_conv_weight, 'lr_mult': 5 if self.modality == 'Flow' else 1, 'decay_mult': 1,'name': "first_conv_weight"},{'params': first_conv_bias, 'lr_mult': 10 if self.modality == 'Flow' else 2, 'decay_mult': 0,'name': "first_conv_bias"},{'params': normal_weight, 'lr_mult': 1, 'decay_mult': 1,'name': "normal_weight"},{'params': normal_bias, 'lr_mult': 2, 'decay_mult': 0,'name': "normal_bias"},{'params': bn, 'lr_mult': 1, 'decay_mult': 0,'name': "BN scale/shift"},]def forward(self, input):"""前向传播函数。参数:input (torch.Tensor): 输入张量。返回:torch.Tensor: 输出张量。"""# 根据模态计算每个分段的输入长度sample_len = (3 if self.modality == "RGB" else 2) * self.new_lengthif self.modality == 'RGBDiff':# 如果是RGB差分模态,计算差分sample_len = 3 * self.new_lengthinput = self._get_diff(input)# 调整输入形状并通过基础模型base_out = self.base_model(input.view((-1, sample_len) + input.size()[-2:]))if self.dropout > 0:# 如果使用Dropout,通过新的全连接层base_out = self.new_fc(base_out)if not self.before_softmax:# 如果不在共识模块前进行Softmax操作,进行Softmax操作base_out = self.softmax(base_out)if self.reshape:# 调整输出形状base_out = base_out.view((-1, self.num_segments) + base_out.size()[1:])# 通过共识模块output = self.consensus(base_out)return output.squeeze(1)def _get_diff(self, input, keep_rgb=False):"""计算RGB差分。参数:input (torch.Tensor): 输入张量。keep_rgb (bool): 是否保留RGB通道,默认为 False。返回:torch.Tensor: 差分后的张量。"""# 根据模态确定输入通道数input_c = 3 if self.modality in ["RGB", "RGBDiff"] else 2# 调整输入形状input_view = input.view((-1, self.num_segments, self.new_length + 1, input_c,) + input.size()[2:])if keep_rgb:# 如果保留RGB通道,克隆输入new_data = input_view.clone()else:# 否则,截取部分输入new_data = input_view[:, :, 1:, :, :, :].clone()for x in reversed(list(range(1, self.new_length + 1))):if keep_rgb:# 如果保留RGB通道,计算差分new_data[:, :, x, :, :, :] = input_view[:, :, x, :, :, :] - input_view[:, :, x - 1, :, :, :]else:# 否则,计算差分new_data[:, :, x - 1, :, :, :] = input_view[:, :, x, :, :, :] - input_view[:, :, x - 1, :, :, :]return new_datadef _construct_flow_model(self, base_model):"""将基础模型转换为光流模型。参数:base_model (torch.nn.Module): 基础模型。返回:torch.nn.Module: 转换后的光流模型。"""# 获取基础模型的所有模块modules = list(self.base_model.modules())# 找到第一个卷积层的索引first_conv_idx = list(filter(lambda x: isinstance(modules[x], nn.Conv2d), list(range(len(modules)))))[0]conv_layer = modules[first_conv_idx]container = modules[first_conv_idx - 1]# 克隆第一个卷积层的参数params = [x.clone() for x in conv_layer.parameters()]kernel_size = params[0].size()# 计算新的卷积核大小new_kernel_size = kernel_size[:1] + (2 * self.new_length, ) + kernel_size[2:]# 计算新的卷积核new_kernels = params[0].data.mean(dim=1, keepdim=True).expand(new_kernel_size).contiguous()# 创建新的卷积层new_conv = nn.Conv2d(2 * self.new_length, conv_layer.out_channels,conv_layer.kernel_size, conv_layer.stride, conv_layer.padding,bias=True if len(params) == 2 else False)new_conv.weight.data = new_kernelsif len(params) == 2:# 如果有偏置,设置新卷积层的偏置new_conv.bias.data = params[1].data# 获取卷积层的名称layer_name = list(container.state_dict().keys())[0][:-7]# 替换第一个卷积层setattr(container, layer_name, new_conv)return base_modeldef _construct_diff_model(self, base_model, keep_rgb=False):"""将基础模型转换为RGB差分模型。参数:base_model (torch.nn.Module): 基础模型。keep_rgb (bool): 是否保留RGB通道,默认为 False。返回:torch.nn.Module: 转换后的RGB差分模型。"""# 获取基础模型的所有模块modules = list(self.base_model.modules())# 找到第一个卷积层的索引first_conv_idx = list(filter(lambda x: isinstance(modules[x], nn.Conv2d), list(range(len(modules)))))[0]conv_layer = modules[first_conv_idx]container = modules[first_conv_idx - 1]# 克隆第一个卷积层的参数params = [x.clone() for x in conv_layer.parameters()]kernel_size = params[0].size()if not keep_rgb:# 如果不保留RGB通道,计算新的卷积核大小和卷积核new_kernel_size = kernel_size[:1] + (3 * self.new_length,) + kernel_size[2:]

实验结果

与不同数据集上的最先进方法的对比:
在这里插入图片描述
可见,无论是三段还是七段TSN,其性能都优于其他方法。
使用平均池化聚合函数下对视频分段段数的实验结果:
在这里插入图片描述
可见,随着段数的增加,性能逐渐上升,但段数达到一定值后性能会达到饱和。

总结

时间分割网络(TSN)是一种高效的长时程视频动作识别框架,其核心通过分段稀疏采样策略解决传统方法在长视频建模中的计算冗余与覆盖不足问题。TSN将视频均匀划分为多个时间段,每个段随机选取一个短片段,利用共享权重的卷积网络提取片段特征,并通过聚合函数(如平均池化或Top-K池化)融合全局信息生成视频级预测。对于未修剪视频,TSN进一步引入多尺度时序窗口集成(M-TWI),通过多尺度滑动窗口和背景抑制策略提升鲁棒性。其优势在于计算成本与视频时长无关,支持实时推理,并在多个数据集上达到最优性能。然而,TSN依赖预训练模型初始化,且光流计算仍存在效率瓶颈。未来研究可聚焦于轻量化模态设计、端到端多模态融合优化,以及提升模型在复杂动态场景中的泛化能力。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词