ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

细粒度动作识别新思路:交叉注意力与稀疏专家结合

细粒度动作识别新思路:交叉注意力与稀疏专家结合 在视频理解任务里动作识别早已不是“能不能认出来”的问题而是“能认出多细”的问题。一个模型能判断“人在打篮球”和能判断“人在跳投三分”两者之间的差距并不仅仅是精度数字而是模型是否真正学会了在极短的时空窗口里捕获判别性线索。细粒度动作识别Fine-Grained Action Recognition所处的位置正好是普通动作识别已经饱和、但细粒度视觉理解还未被解决的地带。我们经常看到的一个矛盾是视频分类模型的顶层特征非常丰富但真正区分细粒度动作的时刻可能只有几帧甚至只是手部、脚部或交互物体在画面中的一个局部区域。如果整个网络只是把所有帧的特征做一次平均池化再丢给分类头那些关键的局部模式会被大量背景帧和相似动作帧淹没。于是研究者开始用注意力机制去强调关键帧和关键区域也开始用多专家结构去拆分不同动作类别之间的模式冲突。而这篇论文标题给出的思路我认为更值得关注的点在于它把“注意力”和“稀疏专家”这两条技术线组合在了一起形成一条完整的处理链路先用交叉注意力找出值得关注的时空点再用潜在空间中的稀疏专家对这些关注点做细粒度区分。这篇文章会围绕 Fine-Grained Action Recognition、Cross-Attention、Latent Sparse Experts 三个关键词做拆解给出我对该方法论的理解同时给出一个可运行的概念代码骨架帮助你理解这类模型背后的核心机制。这篇文章不是对论文官方实现的逐行复刻而是把论文标题和技术原理翻译成可以迁移到实际项目里的工程视角。1. 细粒度动作识别真正难在哪先看一组很现实的问题。普通视频分类任务类别往往是“跑步”“做饭”“握手”“鼓掌”类与类之间的运动和外观差异很大。哪怕特征提取不算特别精细模型只需要抓住一个大致的场景语义也能得到不错的准确率。但细粒度动作识别的类别可能变成“篮球比赛中的跳投、勾手、上篮、扣篮、抛投”这些动作都发生在篮球场上人物姿态相似、背景几乎一样区别可能就在于起跳后手臂的挥动弧度、球离手的时机、身体重心的偏移方向。这种识别任务会给模型带来三个层面的困难。第一判别性信息的时空跨度非常小。以“扣篮”和“上篮”为例二者都是持球向篮筐移动并起跳差异往往只体现在最后“手部向下按压球”和“手部向上托举球”的短暂片段。如果模型没有把注意力放在正确的帧和时间段它会很容易把两个动作理解为同一种模式。第二全局特征表示会稀释局部线索。Transformer 在视频任务中通常会把整段视频切成若干 patch token然后在时间维度和空间维度上做自注意力。这个过程的计算量已经很大但更重要的是它默认所有 patch 的贡献是相对均衡的。细粒度识别需要的恰恰是不均衡——某些 patch 对类别判断的贡献远大于其他 patch。第三类别增多之后单一分类器容量不够。细粒度动作的类别之间往往存在“部分共享、部分冲突”的特征关系。比如“投篮”和“传球”都会出现“手臂伸出”的局部动作“投篮”又和“罚球”共享更多动作语义。如果模型只使用一个全局分类器它被迫在同一组权重里同时拟合这些重叠又冲突的模式模型容量会明显不足。从这些困难可以得出一个判断细粒度动作识别本质上不是“再加一层注意力”就能解决的问题。它要求模型具备两个能力一是从不同信息源里动态选择与当前动作最相关的时空内容二是用更灵活的决策结构去拟合细粒度类别之间的微小差异。这篇论文标题中出现的 Cross-Attention 和 Latent Sparse Experts分别对应的正是这两种能力。2. 交叉注意力让不同信息源互相注视交叉注意力Cross-Attention不是一个新概念它在多模态模型、Transformer 解码器、目标检测的 Transformer 版本里都非常普遍。它的核心思想可以用一句话概括不是让每个 token 只看同一条序列里的其他 token而是让来自一个序列的查询去另一个序列的键值对里寻找相关信息。要理解它为什么对细粒度动作识别有价值需要先面对一个基础问题。在实际任务中我们手里的视频信息往往不止一种形态。典型的形态组合包括 RGB 帧序列与光流序列、骨骼关键点序列与 RGB 帧序列、视频帧的局部音频与全局视觉帧、或者是同一视频经过不同网络分支得到的多尺度表示。无论哪种组合模型都要面对一个对齐问题在某一时刻动作的局部空间变化到底应该和哪一种辅助信息对应起来。普通做法是把两条特征在拼接后直接输入分类层。这种做法的缺陷在于拼接是一种“弱对齐”。它把所有特征放在同一长向量里却不告诉模型哪一部分特征应该重点参考哪一部分辅助内容。细粒度识别中关键信息往往不在整段视频的宏观语义里而在“第几帧的哪个区域和哪个动作原语相关”。这种细粒度的相关性用拼接很难捕捉。交叉注意力提供了一种更合理的交互方式。假设模型有一条 RGB 帧特征序列我们把它变换成查询向量同时我们还有一个辅助信息源比如由局部片段得到的子动作特征或由姿态估计得到的骨骼特征把它变换成键和值。在每一层交叉注意力中RGB 特征会去辅助信息源里寻找与当前判别线索最匹配的内容并把这些内容加权融合回自身。如果把比较限制在注意力机制本身它的价值和自注意力是互补的。自注意力擅长捕捉单条序列内部的全局依赖比如“前 10 帧摸到球后 20 帧起跳”这种时间上的长程关联。而交叉注意力擅长捕捉两条序列之间的语义对应比如“视频第 12 帧这个手臂区域正好对应骨骼序列中手臂弯曲角度最明显的那个关节”。细粒度识别中恰好两类依赖都存在只使用一种注意力机制都不完整。在实际实现中交叉注意力的输入形状通常可以统一成三类中间结果query 来自被增强的主干特征key 和 value 来自辅助特征。计算方式与自注意力几乎一致唯一区别是 key 与 value 的来源不再等于 query 的来源。如果读者熟悉多头注意力会发现交叉注意力只需修改输入的组织方式即可。3. 潜在稀疏专家细粒度分类的容量分配策略细粒度数据集的另一个现实问题是“类别很多、每个类别的样本又不够多”。当类别之间的差异越来越细微想让一个全连接分类头在最后的特征空间里学习到足够的判别边界难度会越来越大。这时一种思路是 MoEMixture of Experts专家混合把一个大分类器拆成多个并行的专家网络然后让每个输入只激活其中一部分专家。先回顾 MoE 的基本逻辑。在 MoE 结构中输入 token 会经过一个路由网络该网络输出每个专家的权重在理想状态下只选择权重最高的 Top-K 个专家参与计算。这样做的结果是不同专家会各自偏向处理一类模式模型总参数量可以很大但每个输入实际消耗的参数量相对有限。这种结构在语言模型的扩展中已经用得很多其核心收益是突破单模型容量限制同时控制推理成本。标题中的 Latent Sparse Experts我理解并不是简单地把 MoE 搬到视频里而是带有两个关键修饰。Latent 意味着专家选择过程不是直接作用在原始视频 patch token 上而是先把输入映射到一个潜在语义空间再在这个空间里进行稀疏选择。Sparse Experts 意味着虽然整个模型可能包含许多专家但对于一个动作实例它只从可用专家中挑选一小部分参与最终识别。这种设计对细粒度动作识别有非常直观的意义。细粒度动作的类别分布并不是均匀的。例如某些精细动作依赖于手部轨迹某些动作依赖于腿部的连续节奏还有些动作依赖于与物体的接触方式。如果一个数据集里包含大量不同类型的细粒度动作那么“手部相关动作”和“腿部相关动作”在特征空间里天然存在冲突。使用一组共享权重去拟合所有模式会造成梯度更新的相互干扰而使用稀疏专家相当于让模型在潜在空间里自动找到“当前动作属于哪一类模式”再把它交给对应专家去处理。和 MoE 相关的工程问题也需要提前说明。模型中可能会出现路由崩溃也叫专家坍缩即所有输入都倾向于选择同一个专家。细粒度识别中类别相似度高更容易出现这种问题因为所有类别的初始特征非常接近路由网络很难在早期训练阶段拉开区分度。解决思路通常是引入负载均衡损失鼓励每个专家的使用率保持近似均衡也可以通过路由噪声或温度控制让决策过程更平滑。另外如果想清楚稀疏专家和交叉注意力的协作关系可以这样理解交叉注意力解决的是“看什么地方”的问题它把关键时空区域的线索从复杂视频中提取出来稀疏专家解决的是“用什么参数解释这些线索”的问题它让当前视频片段能自动挑选匹配的模式参数。整个系统像是先派出一组观察员去现场收集具有判别性的微小细节再由一组领域专家来判断这些细节更接近哪一种精细动作类别。观察员对应注意力专家对应 MoE。4. Cross-Attention Latent Sparse Experts 的整体设计推测由于输出材料只提供论文标题而没有完整原文这里先从方法和工程经验出发建立一个可以用于理解该论文的参考架构。需要特别说明的是这个架构不是对论文官方实现的声称更多是用来解释标题里几个关键词如何组合的思维框架。先描述一下这条信息流。对于输入视频先把连续帧采样成若干关键片段再通过视频主干网络转换成一串 patch-level 或帧级特征 token。当需要引入辅助信息源时我们把主干特征记为 F1辅助特征记为 F2。交叉注意力模块会以 F1 查询、以 F2 为键值进行信息交互。融合后的特征再被送入潜在稀疏专家模块。这个模块并不会直接把全部 token 平均池化后做分类而是先让每个 token 通过一个低维映射进入潜在空间路由网络在这里计算它需要哪些专家然后由被选中的专家对该 token 进行细粒度特征变换最后将各 token 的专家输出做加权聚合送入动作分类层。通俗地解释这个流程比传统动作识别多出了两个关键阶段。第一阶段是“细粒度线索聚集”。交叉注意力会关注到动作中最容易产生混淆的局部片段而不是像平均池化那样均匀对待所有帧。第二阶段是“多样化模式分配”。专家系统会把动作的局部语义拆分成多种模式从而避免不同精细类别之间的梯度冲突。如果把视角放到损失函数与监督信号上这类模型也不会只使用一个简单的分类损失。更合理的设计是在分类损失之外增加路由负载均衡损失让专家利用率保持健康如果数据集中存在层次化动作标签比如“篮球运动-投篮-跳投”模型还可以引入层级一致性约束让粗粒度类别与细粒度类别在潜在空间中的关系保持稳定。从方法论角度看这篇论文的贡献可能不是某一个全新的注意力公式而是把两类成熟的机制结合到了细粒度动作识别这个具体问题上。第一个关键操作是把需要细粒度区分的动作理解问题分解成“时空点筛选”和“相似模式判别”两个阶段第二个关键操作是在潜在空间里引入稀疏性降低模型在大量相似类别上的参数冲突。对于实际做视频理解项目的人来说这两个方向即使不做论文级别的完整实现也有很大的迁移价值。5. 环境准备与复现的一般配方如果读者希望把这类模型在自己数据上跑通不必纠结于当前论文使用了什么基础设施先搭建一套适合视频 Transformer 实验的 PyTorch 环境即可。这里给出一份通用依赖清单版本号不做死板规定以本机实际可用为准。Python 3.9 或更高版本PyTorch 1.13 或 2.x建议使用带 CUDA 的版本torchvision用于图像预处理与部分视觉骨干einops用于张量维度重排decord 或 PyAV用于高效解码视频帧timm 或 mmcv用于加载预训练视觉骨干如果使用的是 Linux 服务器可以用如下命令创建环境此处不锁版本读者可根据实际项目选择。conda create -n video_moe python3.9 conda activate video_moe pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install einops decord timm细粒度动作识别实验非常依赖数据预处理。常见的数据集包括健身动作类、体育动作类、手术动作类、烹饪动作类等公开数据集中比较常用的有 UCF101、HMDB51 这类通用动作识别基准但它们并不完全等价于细粒度动作设定。真正更贴近细粒度场景的公开数据集通常围绕特定领域例如体操动作、医疗操作或复杂工具使用等。使用这些数据集前要确认许可与标注规范。由于不同数据集的帧率、分辨率和类别数差异很大环境搭建时建议把“视频读取-采样-增强”做成独立的数据管线不要与模型强耦合。PyTorch 的 Dataset 类会承担这部分职责。判断环境是否就绪的标准很简单先跑一次 DataLoader 遍历看视频是否能在合理时间内完成 decode 和 resize。大多数细粒度识别项目的问题都出在数据读取速度上而不是模型结构上。一个建议是先用小数据集验证整个训练框架。不要一上来就在大型数据集上做完整训练因为这会让调试周期变得非常长。即使要复现的论文是在完整数据集上的结果也可以先拆出一个小规模子集验证模型结构、损失函数和数据流是否正确再逐步扩大训练规模。6. 概念代码骨架Cross-Attention 与 Latent Sparse Experts下面提供的代码不是论文的官方实现只用于表达该论文的核心机制。我会用最少的组件说明三件事交叉注意力如何交互两条特征序列、稀疏路由如何选择专家、专家输出如何聚合回最终分类。为了便于理解代码会拆成几个小模块每个模块对应一种清晰职责。我们假设输入特征已经由视频主干提取完成并且已经过位置编码因此不再涉及视频解码和主干结构。6.1 基础稀疏专家模块在潜在稀疏专家结构里“专家”不一定要设计得特别复杂。一个常见的做法是让专家作为轻量 MLP 残差块在 token 特征已经进入潜在空间后做进一步精炼。这里用一个简单的两层 MLP 表示专家# model/latent_expert.py import torch import torch.nn as nn class LatentExpert(nn.Module): 一个轻量的专家网络。 采用残差 MLP 设计输入和输出维度保持一致。 def __init__(self, dim: int, hidden_ratio: float 2.0): super().__init__() hidden_dim int(dim * hidden_ratio) self.net nn.Sequential( nn.Linear(dim, hidden_dim), nn.GELU(), nn.Linear(hidden_dim, dim), ) def forward(self, x: torch.Tensor) - torch.Tensor: return x self.net(x)这里把专家定义成残差结构是为了降低训练难度。专家并不是要把输入完全转换成不同语义而是在原始特征基础上补充特定模式的判别信息。专家数量可以在模型初始化时传入。6.2 稀疏路由模块路由模块负责为每个 token 产生专家权重。要实现 Sparse Experts通常做法是只保留 Top-K 个专家的权重其余专家权重置为零这样在计算时就不会激活全部专家达到稀疏目的# model/expert_router.py import torch import torch.nn as nn import torch.nn.functional as F class ExpertRouter(nn.Module): 把输入 token 路由到 Top-K 个专家。 def __init__(self, dim: int, num_experts: int, top_k: int 2): super().__init__() self.num_experts num_experts self.top_k top_k self.gate nn.Linear(dim, num_experts) def forward(self, x: torch.Tensor): # x: [B, N, D] logits self.gate(x) # [B, N, E] scores F.softmax(logits, dim-1) top_scores, top_indices torch.topk(scores, self.top_k, dim-1) # 生成稀疏权重矩阵未被选中的专家权重为 0 sparse_scores torch.zeros_like(scores) sparse_scores.scatter_(-1, top_indices, top_scores) return sparse_scores, top_indices路由模块的细节决定了模型能否保持稀疏性。这里使用 softmax 之后取 Top-K是因为我们希望保留一个概率分布而不是直接使用 logits 挑选。Top-K 的值 K 一般取 1 到 3 之间K 越小稀疏性越强但每个专家看到的训练信号也越少容易引发路由不稳定。6.3 潜在稀疏专家层接下来把多个专家和路由组合成一个完整的 MoE 层。输入 token 会先进入一个低维潜在空间在潜在空间内由路由决定激活哪些专家最后再映射回原维度。这样既体现了 Latent 的含义也控制了路由的计算量# model/latent_sparse_experts.py import torch import torch.nn as nn from .expert_router import ExpertRouter from .latent_expert import LatentExpert class LatentSparseExperts(nn.Module): def __init__(self, dim: int, num_experts: int, top_k: int 2, latent_dim: int 128): super().__init__() self.dim dim self.num_experts num_experts self.top_k top_k # 潜在空间投影 self.in_proj nn.Linear(dim, latent_dim) self.out_proj nn.Linear(latent_dim, dim) self.experts nn.ModuleList( [LatentExpert(latent_dim) for _ in range(num_experts)] ) self.router ExpertRouter(latent_dim, num_experts, top_k) def forward(self, x: torch.Tensor): # x: [B, N, D] latent self.in_proj(x) # [B, N, latent_dim] sparse_scores, top_indices self.router(latent) # 先对每个专家计算输出再用稀疏权重加权求和 expert_outputs [] for expert in self.experts: expert_outputs.append(expert(latent)) # stack: [num_experts, B, N, latent_dim] expert_outputs torch.stack(expert_outputs, dim0) # 根据 sparse_scores 做加权求和 # sparse_scores: [B, N, num_experts] sparse_scores sparse_scores.permute(2, 0, 1).unsqueeze(-1) output (expert_outputs * sparse_scores).sum(dim0) return self.out_proj(output)这里的实现是一种朴素的“先算所有专家再加权”它在计算效率上并不是真正稀疏的但非常适合用来理解逻辑。在生产环境中更高效的做法是通过索引只计算被选中的专家。所谓工程上的稀疏最终目标是让大部分专家参数不参与前向计算而不只是把权重置零。6.4 交叉注意力融合模块交叉注意力模块接收两个序列。设 query 来自主干特征序列key 与 value 来自另一条辅助特征序列。这个模块的目标是把辅助信息中的判别性细节注入主特征中# model/cross_attention_fusion.py import torch import torch.nn as nn import torch.nn.functional as F from einops import rearrange class CrossAttentionFusion(nn.Module): def __init__(self, dim: int, num_heads: int 8): super().__init__() self.num_heads num_heads self.scale (dim // num_heads) ** -0.5 self.q_proj nn.Linear(dim, dim) self.k_proj nn.Linear(dim, dim) self.v_proj nn.Linear(dim, dim) self.out_proj nn.Linear(dim, dim) def forward(self, query: torch.Tensor, key_value: torch.Tensor): query 来自主特征序列, key_value 来自辅助特征序列。 B, Nq, _ query.shape _, Nk, _ key_value.shape q self.q_proj(query) k self.k_proj(key_value) v self.v_proj(key_value) q rearrange(q, b n (h d) - b h n d, hself.num_heads) k rearrange(k, b n (h d) - b h n d, hself.num_heads) v rearrange(v, b n (h d) - b h n d, hself.num_heads) attn torch.matmul(q, k.transpose(-2, -1)) * self.scale attn F.softmax(attn, dim-1) out torch.matmul(attn, v) out rearrange(out, b h n d - b n (h d)) return self.out_proj(out)在实际训练时query 序列可以是经过时间下采样的视频 tokenkey-value 序列可以来自辅助特征也可以来自一个更细粒度的局部特征分支。只要两条序列长度不相等这段代码都能正常运行因为注意力矩阵的尺寸是 Nq × Nk不要求两条序列长度一致。6.5 组装上层分类模型把所有模块组装起来可以得到一个供理解使用的顶层结构。这个结构不是完整论文模型而是把上面的组件串成一条可以训练的流程# model/fine_grained_moe_video_model.py import torch import torch.nn as nn from .cross_attention_fusion import CrossAttentionFusion from .latent_sparse_experts import LatentSparseExperts class FineGrainedMoEModel(nn.Module): def __init__(self, dim: int, num_experts: int 8, top_k: int 2, num_classes: int 100): super().__init__() self.cross_attn CrossAttentionFusion(dimdim) self.latent_sparse_experts LatentSparseExperts( dimdim, num_expertsnum_experts, top_ktop_k ) self.norm nn.LayerNorm(dim) self.classifier nn.Linear(dim, num_classes) def forward(self, main_feat: torch.Tensor, aux_feat: torch.Tensor) - torch.Tensor: # main_feat、aux_feat 均由视频主干预先提取 fused self.cross_attn(main_feat, aux_feat) expert_out self.latent_sparse_experts(fused) feat self.norm(expert_out.mean(dim1)) logits self.classifier(feat) return logits代码里main_feat和aux_feat的形状通常是[B, N, D]。B 表示 batch sizeN 是 token 数量D 是特征维度。如果读者使用的视频主干输出是 4D 特征例如[B, C, T, H, W]需要先使用rearrange或flatten转换成[B, N, D]格式再接进模型。7. 训练与验证时应该观察什么即使模型结构搭建正确细粒度识别模型的训练过程也需要额外关注几个指标否则很容易出现“损失在下降精度却上不来”的尴尬。第一路由稀疏率。所谓路由稀疏率是指输入样本实际激活的专家数量占全部专家的比例。Top-K 专家模块理论上每一层对每个 token 只激活 K 个专家但如果在训练过程中某个专家被所有 token 反复选中那么另外的专家就形同虚设。理想状态下不同类别样本应当分散到不同专家上专家使用率不该极度不均。第二交叉注意力权重分布。如果注意力矩阵总是一团均匀分布说明交叉注意力没有真正建立主特征与辅助特征之间的对应关系。可以定期把注意力权重视觉化观察权重是否集中在特定帧、特定区域或特定关键点上。如果始终均匀可以考虑调整温度参数或者检查 query 和 key 的输入特征是否缺少足够的类别判别信息。第三类别粒度层次的混淆矩阵。细粒度分类任务只看 Top-1 Accuracy 是远远不够的。比如模型把“跳投”判成“勾手”和把“跑步”判成“挥手”这两类错误在语义上的严重性完全不同。建议在验证阶段同时计算层次化指标例如在大类内部的小类准确率或者粗粒度类别是否被正确区分。为了便于实验记录可以在配置文件中定义基础参数# config/experiment.yaml model: dim: 512 num_experts: 8 top_k: 2 num_classes: 100 data: video_root: ./data/videos annotation_file: ./data/annotations.csv num_frames: 16 frame_size: 224 train: batch_size: 8 epochs: 50 learning_rate: 1.0e-4 use_amp: true save_every: 5训练启动命令可以直接通过 Python 脚本运行。没有外部依赖的情况下不必先引入复杂训练框架python train.py --config config/experiment.yaml如果训练完全跑不动不要先怀疑模型结构。第一步查看视频解码器是否成为瓶颈第二步查看 GPU 显存占用是否合理第三步检查 loss 是否出现 NaN。视频类模型最容易出问题的位置是序列长度 padding 和 attention mask而不是模型内部的线性层。8. 常见问题与排查思路细粒度动作识别模型在复现与迁移时经常遇到的问题比较集中下面整理成表格方便读者在实验遇到异常时快速对照。问题现象可能原因排查方式解决方案训练早期精度不升路由模块把所有样本分给同一个专家打印路由权重分布观察专家使用率增加负载均衡损失调整 Top-K 为更大值加入路由噪声交叉注意力无法聚焦关键区域query 序列与 key 序列特征差异过大可视化注意力矩阵增加特征对齐预训练调整注意力头数尝试在 query 一侧加入局部位置偏置视频解码速度慢GPU 利用率低DataLoader 的 num_workers 不足或解码库版本不对观察 CPU 与 GPU 使用率增加 num_workers改用 GPU 友好的解码器使用缓存帧显存溢出视频 token 数过多专家模块被放大查看 token 总数和专家参数量降低采样帧数缩小 patch 数量使用梯度累积真正实现稀疏激活而不是全量计算类别混淆集中在相似动作上单层交叉注意力不足以捕获微小差异输出混淆矩阵查看错误类型堆叠多层交叉注意力引入辅助局部特征使用层级分类损失专家输出和路由输出冲突每个 token 独立选择专家缺少全局一致性观察同一视频不同 token 的路由结果在路由前引入全局上下文 token对路由结果做平滑约束在细粒度识别项目中最容易被轻视的问题是视频帧采样策略。假设一段视频时长为 5 秒而判别性动作只出现在最后 0.5 秒如果采样策略是均匀采样 16 帧那么真正有效的帧可能只占到全片段很小比例。处理这类问题的常见做法是引入多段采样、关键片段采样或者时间注意力机制让模型能够不遗漏真正重要的时间窗口。另一个值得注意的问题是辅助信息源的选择。不是所有任务都需要使用光流也不是所有任务都需要骨骼序列。如果辅助信息本身无法提供比 RGB 更细的判别线索交叉注意力只会增加计算量。比如动作差异主要由物体接触方式决定时可以引入局部区域的高分辨率特征如果动作差异主要由身体关节角度决定骨骼序列可能更合适。9. 最佳实践与工程建议把论文里的思想迁移到真实项目时有几个建议值得实践。第一先建立强 Baseline再引入稀疏专家。很多细粒度识别项目效果不好不是因为没有使用先进模块而是主干特征本身就没训练充分。建议先使用预训练视频主干配合简单的注意力池化跑通 Baseline确认在这个数据上已经能够学到合理的动作语义再逐步加入交叉注意力和稀疏专家。每一步改动都单独验证避免多个因素同时变化导致无法定位问题。第二路由模块需要额外监督。在细粒度场景下类别相似度高纯分类损失很难让路由网络学会理想分配。可以考虑添加辅助的负载均衡损失让每个专家收到的训练 token 量尽量均匀也可以利用数据集的层级标签让路由网络额外预测粗粒度类别从而让相似粗粒度动作优先落到同一组专家上。第三交叉注意力不是堆得越多越好。在某些实现中多层交叉注意力会带来严重的显存和计算开销同时如果辅助特征的信息量不足深层交叉反而会引入噪声。一个可行的策略是只在主干网络的后半段加入交叉注意力而不是每层都做。更轻量的方式是在全局池化之前只加一层交叉注意力用于把辅助信息注入分类 token。第四采样策略与数据增强需要单独调优。细粒度动作识别的另一个特征是空间裁剪方式会影响模型的判断。比如在健身动作中如果画面里同时出现多个人必须正确选择目标人物在物体交互动作中模型要关注手部与物体的接触点。这种时候可以结合目标检测或姿态估计结果把局部特征作为交叉注意力的辅助输入而不是简单地依赖全局画面。第五在迁移到自己的业务数据之前先做一次“相似动作难度评估”。把自己的类别分成容易混淆的组看一看模型主要错误发生在哪些组内。如果模型错误集中在同一粗粒度类别下的小类之间说明模型已经能够区分粗粒度动作只是在更细节的局部时空模式上仍有不足这时候调整交叉注意力的输入粒度和专家数量优先级最高。如果模型连粗粒度类别都分不清说明问题更可能出在主干特征或数据质量上。10. 总结与后续学习方向回到最初的问题这篇论文标题最值得借鉴的是把细粒度动作识别这一复杂问题拆成了两个更可操作的子问题一个子问题是“如何让特征在时间和空间上聚焦到真正有判别性的局部模式”对应 Cross-Attention另一个子问题是“如何在共享特征与类别特化特征之间找到平衡避免大量相似类别训练时的互相干扰”对应 Latent Sparse Experts。如果希望在后续学习里继续深入建议按下面顺序推进。第一步是寻找论文原文与官方公开代码阅读其网络结构定义和损失函数设置重点关注路由模块的实现细节例如是否使用了噪声路由、是否添加了负载均衡损失、专家的位置放在网络的哪些层。第二步是搭建一个可以运行的最小复现版本先丢弃大型数据集只使用少量视频验证模型前向传播和 loss 计算是否正常。第三步再在原有 Baseline 上逐步引入稀疏专家与交叉注意力每一阶段都记录指标变化。第四步可以尝试把专家机制替换成不同设计例如低秩专家、共享专家与特化专家结合来观察哪类设计在自己任务上最稳定。对于正在做实际视频项目的读者一个更稳妥的建议是不需要照搬整篇论文只需要提取交叉注意力的“主特征与辅助特征对齐”思路以及稀疏专家的“不同动作模式使用不同参数”的思路把它们分别作为可插拔模块去测试。一个模块如果没有带来明确收益就先保持简单等技术理解更深之后再考虑多模块组合。细粒度动作识别距离完全解决还有距离而解开这一问题的钥匙往往就藏在两种能力的关键交叉点上知道往哪里看以及知道用什么经验解释看到的内容。
返回列表