TadTR(TIP 2022)视频动作检测方法详解

ops/2024/10/10 17:20:08/

前言

论文:End-to-end Temporal Action Detection with Transformer
代码:TadTR

  从论文题目可以看出 TadTR 是基于 Transformer 的端到端的方法,TAD 在视频动作分类任务上更进一步,不仅对动作分类,还要检测动作发生的时间段。本文使用的代码为 OpenTAD,其中包含了多种 TAD 方法,在正式解析网络细节之前,看一下总框架。

原代码仓库框架图(与论文原图略有不同)

在这里插入图片描述
  图像序列会经过 CNN 提取特征,并用 Transformer Encoder 编码,Encoder 的输出经过 Decoder 和 FFN 得到 Segment 分割时间段和 Class Prob 动作分类得分。时间段和 Encoder 特征再经过 Actionness Regression 计算时间段是否存在动作。

  整体的思路与目标检测类似,Segment 对应检测框 box,Class Prob 对应目标分类 class,Actionness 对应目标框是否存在目标 conf。

Backbone

在这里插入图片描述
  TadTR 使用 SlowFast 作为 Backbone。SlowFast 本身做视频动作分类,相关工作请看 视频理解调研笔记 | 2021年前视频动作分类发展脉络(类似使用图像分类的网络作为目标检测的 Backbone 来抽特征)。下面对输入数据、模型结构和参数的细节进行说明。

输入

  • Input fast 是从原始视频中每隔 3 帧取 1 帧共 256 帧图像,通过间隔较短的大量帧作为运动动态信息;
  • Input slowInput fast 每隔 8 帧取 1 帧共 32 帧,通过间隔较长的少量帧作为静态图像信息;
  • 4 - BatchSize,3 - 图像 RGB 三通道,32/256 - 图像帧数, 96 × 96 96\times96 96×96 - 图像分辨率(保比缩放+中心裁剪)。
  • 对于一个较长的视频来说会取出多段的 256 帧最终基本覆盖整个视频,具体的取帧逻辑就略过了,直接以某个视频的划分结果来感受一下,这里一个视频划分出了 3 个 Batch 的数据,对应原始视频帧的索引分别为 0~765、192~957、246~1011。

结构和参数

  • SlowFast 包含 2 个 3D-ResNet 对应了快慢分支;
    • 慢分支的图像特征会多次融合快分支的运动特征,具体方式是用 3D 卷积对运动特征在时序上做下采样,然后和图像特征做 Concat;
    • 空间维度上,模型下采样 32 倍后通过一个平均池化变为 1 × 1 1\times1 1×1
    • 时间维度上全程保持不变,末尾处以线性插值的方式将两个分支的维度对齐为 128;
    • 最后两个分支的特征 Concat 到一起作为 Backbone 的输出。
  • Input fast 后的第一个 CBR 为例对结构图进行说明,CBR 代表 Conv + BatchNorm + RuLU 的模块,上方的两组参数分别为时序和空间上的 size + stride + pad,如果只有一组参数代表在时序维度的参数为 1,1,0
  • res_layer 上方参数代表 Bottleneck 的数量,Bottleneck 的结构以 slow_res_layer1 中的第一个为例进行说明,具体如下图。每个 res_layer 的第一个 Bottleneck 的残差结构都需要一个 downsample 卷积对特征的维度或空间分辨率做调整,空间参数为 1,1,01,2,0,时间参数为 1,1,0;后续的 Bottleneck 因为输入输出形状相同,残差结构去掉 downsample 卷积直接和输入相加。

在这里插入图片描述

  • Bottleneck 每个卷积核的参数可以看下表,空间上都保持一致,3 个卷积核的参数分别为 1,1,03,1,11,1,0;时间上,conv1 中除了慢分支的 layer1layer2 的为 1,1,0 其余都是 3,1,1conv2conv3 全是 1,1,0

在这里插入图片描述

Projection

在这里插入图片描述

  Projection 的输入为 Backbone 的输出,用一维卷积做特征降维,torch.nn.GroupNorm 按特征维度 32 一组划分为 8 组做 Normalization。这里的 Mask 是在对原始视频取帧时得到的,大致意味着是否是真实的帧(例如视频帧不够了需要 padding 到 256 帧那么对应 Mask 的值为 False),本文默认 Mask 全为 True 进行说明。

Transformer

1. Encoder

  Encoder 包含 TDA 和 FFN,先看 FFN 的计算流程。
FFN

  难点在于 TDA,出自 Deformable-DETR,原本用于做目标检测,受可变形卷积 DCN 启发对 Self-Attention 做改进,核心在于不再计算所有特征之间的注意力权重,而是选取几个位置的特征,且位置由网络学习得到。

在这里插入图片描述

输入

(1)query & value

  对 Projection 的输出做维度调整,即 query = value = Projection_output.permute(0, 2, 1)

(2)enPos

  图中位置编码 enPos 按下方代码由两个部分组成。其中 position 和原版 Transformer 类似,由三角函数生成;level 是可学习参数,对应特征的层级(此处只有 1 个 level),在原版 Deformable-DETR 会取出不同层级的特征图,position 体现的是空间上的位置差异,level 则体现层级的差异。

self.level_embeds = nn.Parameter(torch.Tensor(self.encoder.num_feature_levels, self.encoder.embed_dim))
pos_embed = self.position_embedding(masks) + self.level_embeds[0].view(1, 1, -1)

(3)enPoints

  其数值为 0.5 ~ 127.5 除以 128 做归一化,对应时序的位置坐标。

计算流程

B = 1 = BatchSize
N = 128
H = 8 = Head
L = 1 = Level
P = 4 = Point
D = 32 = Dimension

(1)attention

  与原始 Transformer 不同,注意力权重直接通过一个全连接层加 Softmax 得到。

(2)offset

  用一个全连接层得到坐标偏移量,与原坐标 enPoints 相加得到坐标 Locations(范围大致0~1), × 2 − 1 \times2-1 ×21 得到 Grids (范围大致-1~1),Stack 的数值全为 -1,具体意义在 value 分支一同说明。

(3)value

sampling_value_l_ = F.grid_sample(value_l_.unsqueeze(-1),sampling_grid_l_,mode="bilinear",padding_mode="zeros",align_corners=False,)

  直接看 grid_sample 操作,原图为 32 × 32 × 128 × 1 32\times32\times128\times1 32×32×128×1,第一个 32 = B × H 32=B\times H 32=B×H,第二个 32 32 32 为特征维度, 128 128 128 对应了时序的长度也可以看作词向量的个数,在这里可以把 128 × 1 128\times1 128×1 看作 h × w h\times w h×w 的图像分辨率,grid 中最后的维度 2 = ( x , y ) 2=(x,y) 2=(x,y) 坐标,其中 x = − 1 x=-1 x=1 y y y 在 offset 分支计算得到。输出可以看作 128 个时间点,每个时间点关注 4 个特定时间点的特征,特征的维度是 32。
  grid_sample 后的 Stack 操作是用于合并每个 level,后续就是常规的加权求和。这里返回看 Softmax 的维度是 L × P L\times P L×P,与原版 Transformer 对比,原版输入是每个单词的特征,经过 self-attention 输出每个单词新的特征,而新的特征是对所有单词特征加权求和所得;在 TDA 中输入是 128 个时间点的特征,输出的每个特征由 4 个时间点的特征加权求和所得。

2. Decoder

在这里插入图片描述

  输入 query 先经过经典多头注意力 nn.MultiheadAttention,其输出和 Encoder 的输出分别作为 TDA 的输入 queryvalue,得到输出 Output

  Output 通过 bbox embeddePoints 一同更新 dePointsbbox embed 具体细节如下图;最后一个 Decoder 的输出 dePoints 代表检测动作时间段 box,Class 代表动作分类。
在这里插入图片描述
  最后,box 与 Encoder 输出特征做 RoIAlign 与 FFN 得到每个片段是否存在动作的置信度,对应总框架图右上角部分。
  box 中的 2 个数值分别代表时间的中心点和时间长度,RoI 的 3 个数值分别代表 batch 索引、时间的起始和结束点(尺度为128)。

在这里插入图片描述

输入

  Decoder 的输入由 nn.Embedding 生成,query 对应起始的输入,dePosdePoints 会代替 Encoder 中 TDA 的 enPosenPoints(注 enPoints 会不断更新,而 enPos 保持不变)。40 代表 proposals 个数,意味着这段视频内最多检测出 40 个动作。

self.query_embedding = nn.Embedding(two_stage_num_proposals, self.encoder.embed_dim * 2)

在这里插入图片描述

Postprocess

  后处理根据分类得分和动作置信度综合排序得到结果,最终输出时间段(秒)、动作类别、置信度。每个 Batch 输出 200 个结果,此处 3 个 Batch 属于同一个视频,那么一个视频就得到 600 个结果。

prob = sigmoid(Class) * actionness	# [4,40,20]
score, index = topk(prob.view(bs, -1), 200, dim=1)	# [4,200]

在这里插入图片描述

Train

1. 输入

return self.losses(output, masks, gt_segments, gt_labels)

  Loss 计算使用的 output 如下所示,前三项和推理阶段相同,最后一项 aux_outputs 是 Decoder 阶段的中间输出。Decoder 包含 4 个相同的模块,前三个输出的 dePoints 和 Class 构成了 aux_outputs

在这里插入图片描述

2. 正样本选取

indices = self.matcher(outputs_without_aux, gt_segments, gt_labels)

在这里插入图片描述
  此处针对 Outputs 的前三项,即最终输出进行计算,4 个元组对应 4 个 batch,每个 batch 中第一个张量对应 proposal 的索引,第二个张量对应 gt 索引。

3. Loss 计算

  这里简单介绍每个部分 Loss 的核心计算函数,具体计算细节涉及不少代码和各种参数意义不大,如下图所示,前四项为最终输出对应的 Loss,后续为 aux 部分的 Loss,计算方式是一样的。

在这里插入图片描述

(1)class

  F.binary_cross_entropy_with_logits 计算交叉熵,正样本 → \to 1,负样本 → \to 0

(2)bbox

  F.l1_loss 计算 box 数值的 L1Loss

(3)iou

   1 − G I o U 1-\mathrm{GIoU} 1GIoU

(4)actionness

  F.l1_loss 计算 actionness 和 IoU 的 L1Loss

代码中的细枝末节

(1)dePoints 迭代与 Locations 计算

  在 Decoder 中 TDA 的 dePoints 初始形状为 4 × 40 × 1 4\times 40\times 1 4×40×1 而后续为 4 × 40 × 2 4\times 40\times 2 4×40×2,结合源码看 Locations 的计算方式。

"当 points 为 [4,40,1] 时直接与 offsets 相加"
"normalizer=128=时序长度, 对 offsets 缩放"
sampling_locations = (reference_points[:, :, None, :, None]+ sampling_offsets / offset_normalizer[None, None, None, :, None]
)"当 points 为 [4,40,2] 时第二个值用于对 offsets 缩放, num_points=4"
sampling_locations = (reference_points[:, :, None, :, None, 0]+ sampling_offsets / self.num_points * reference_points[:, :, None, :, None, 1] * 0.5
)

(2)Mask

  在 Projection 部分提到了 Mask,实际上 Mask 在许多地方都会使用,但本文默认 Mask 全为 True 而将其忽略了。Mask 随 DataLoader 获取,记录序列帧中每一帧是否是 padding 得到的,对比图像就是记录图像的像素是否是 padding 的。代码中会根据真实帧所占的比例 ratio = sum(Mask) / len(Mask) 作为一个系数在一些地方使用。

(3)正样本选取的代码实现

  这里的代码实现比较有意思,直接看最后一行,c[i] 代表每个 Batch 的 proposals 和对应 gt 的 cost 矩阵大小为 [40,n],n 为 gt 的数量。利用匈牙利算法 scipy.optimize.linear_sum_assignment 直接得到正样本的分配,在此任务中就是为每个 gt 分配一个不重复的 proposal,最终 cost 总和最低。这里的 cost 矩阵由分类得分、检测框的绝对值与 IoU 综合构成,cost 越低代表与 gt 越匹配。

# alpha=0.25, gamma=2.0
neg_cost_class = (1 - alpha) * (out_prob**gamma) * (-(1 - out_prob + 1e-8).log())	# [160,20]
pos_cost_class = alpha * ((1 - out_prob) ** gamma) * (-(out_prob + 1e-8).log())		# [160,20]
cost_class = pos_cost_class[:, tgt_ids] - neg_cost_class[:, tgt_ids]				# [160,6]
cost_bbox = torch.cdist(out_bbox, tgt_segment, p=1)									# [160,6]
cost_giou = -compute_iou_torch(proposal_cw_to_se(tgt_segment), proposal_cw_to_se(out_bbox))		# [160,6]# self.cost_bbox=5.0, self.cost_class=6.0, self.cost_giou=2.0
C = self.cost_bbox * cost_bbox + self.cost_class * cost_class + self.cost_giou * cost_giou		# [160,6]
C = C.view(bs, num_queries, -1).cpu()	# [4,40,6]indices = [linear_sum_assignment(c[i]) for i, c in enumerate(C.split(sizes, -1))]

http://www.ppmy.cn/ops/123587.html

相关文章

【网路通信基础与实践番外二】TCP协议的流量控制和拥塞控制以及二者区别和例题

TCP协议是端对端的协议,因此在数据进行传输的过程受发送方,数据通道,接收方三方状态的影响。我们用水龙头来比喻数据发送方,水管来比喻数据通道,水桶来表示数据接收方。 图(a)表示水桶太小,来不及接受注入…

RTX4060+ubuntu22.04+cuda11.8.0+cuDNN8.6.0 如何根据显卡型号和系统配置cuda和cuDNN所需的安装环境

文章目录 🌕电脑原配置🌕安装cuda和cuDNN前的环境选择🌙cuDNN与CUDA tookit和nvidia driver的对应关系🌙cuda版本选择⭐查看自己的nvidia driver版本和最大支持的CUDA版本⭐最小支持版本 🌙查看11.8.0版本的cuda和ubun…

[含文档+PPT+源码等]精品大数据项目-基于python爬虫实现的大数据岗位的挖掘与分析

大数据项目——基于Python爬虫实现的大数据岗位的挖掘与分析,其背景主要源于以下几个方面: 一、大数据时代的来临 随着互联网、物联网、云计算等技术的快速发展,数据呈现出爆炸式增长。根据国际数据公司(IDC)的预测&…

【一文讲透(番外篇)】如何编译安装KWDB v2.0.4数据库

KaiwuDB 浪潮集团是中国领先的云计算、大数据服务商,拥有浪潮信息、浪潮软件、浪潮数字企业三家上市公司。主要业务涉及计算装备、软件、云计算服务、新一代通信、大数据及若干应用场景。已为全球一百二十多个国家和地区提供IT产品和服务。 KaiwuDB 是浪潮控股的数据…

【自动化测试】任务1:商品品牌

需要软件测试备赛资料或者远程培训可联系博主,详细了解 1、任务知识储备 Python/Java:掌握使用Python/Java语言,能够进行自动化测试脚本编写;Pycharm/IDEA:掌握Pycharm/IDEA编辑器的使用,能够进行代码编写…

Java 对比两个list 找出重复的 和不重复的

使用场景&#xff1a; list 可以对比两个list 那些是重复的 那些是不重复的&#xff0c;直接把 500 个船名放到list 里面 然后再把 指挥系统查出来的400个船名放到新的list 里面 然后掉一个方法能对比出来两个list 交际 差集 并集 public static List<String> findNonI…

网络基础知识笔记(一)

什么是计算机网络 1.计算机网络发展的第一个阶段&#xff1a;(60年代) 标志性事件&#xff1a;ARPANET 关键技术&#xff1a;分组交换 计算机网络发展的第二个阶段&#xff1a;(70-80年代) 标志性事件&#xff1a;NSFNET 关键技术:TCP/IP 计算机网络发展的第三个阶段&#xff…

SafeLine - 雷池 - 不让黑客越过半步

&#x1f44b; 项目介绍 SafeLine&#xff0c;中文名 “雷池”&#xff0c;是一款简单好用, 效果突出的 Web 应用防火墙(WAF)&#xff0c;可以保护 Web 服务不受黑客攻击。 雷池通过过滤和监控 Web 应用与互联网之间的 HTTP 流量来保护 Web 服务。可以保护 Web 服务免受 SQL …