基础时序 Transformer和增强版时空 Transformer
sign_transformer.py是基础时序 Transformer把每一帧的 25 个关键点展平成一个向量然后用 Transformer Encoder 建模“帧与帧之间的时间关系”。enhanced_sign_transformer.py是增强版时空 Transformer在基础模型上额外加入了运动特征、骨骼特征、空间注意力和注意力池化。一、输入数据是什么CSL2018 pose-gloss 每个样本进入模型时是keypoints: [B, T, J, C]含义是B batch size一次送进模型多少个样本 T 时间帧数比如 128 J 关节点数量比如 25 C 坐标维度比如 3也就是 x/y/z所以一个 batch 的形状通常是[B, 128, 25, 3]还有一个valid_mask: [B, T]它表示每一帧是不是有效帧。因为有些视频帧数不足 128会被 padding 补齐valid_maskFalse的帧就是补出来的模型应该忽略它。二、基础模型 sign_transformer.py文件sign_transformer.py核心流程是[B, T, 25, 3] - 展平每一帧 [B, T, 75] - 线性映射到 embed_dim [B, T, 128] - 加位置编码 [B, T, 128] - Transformer Encoder [B, T, 128] - mean pooling [B, 128] - 分类器 [B, 500]代码里这句frame_featureskeypoints.reshape(batch_size,num_frames,-1)意思是把每一帧的25 x 3展平成75维[25, 3] - [75]然后embeddingsself.input_projection(frame_features)把75维变成embed_dim默认是128维[B, T, 75] - [B, T, 128]这里有两个“128”容易混在一起T 128 embed_dim 128它们虽然数值都可能是 128但含义完全不同。1. T128 是时间长度T表示一条手语序列有多少帧。[B, T, 75]如果T128意思是一个样本有 128 帧 每一帧是 75 维特征也就是[B, 128, 75]这里的128是“帧数”。2. embed_dim128 是每一帧的特征宽度Transformer Encoder 处理的是一串 token。在你的任务里每一帧 一个 token每个 token 需要有一个向量表示。这个向量长度就是embed_dim。所以[B, 128, 75] - [B, 128, 128]意思是128 帧没有变 每一帧的特征从 75 维变成 128 维不是把时间长度变了而是把每一帧的表达能力扩展了。3. 为什么 75 维还要变成 128 维75 维是原始坐标拼出来的25 个点 * 3 个坐标 75它只是原始输入不一定适合 Transformer 直接处理。这句代码self.input_projectionnn.Linear(num_joints*coord_dim,embed_dim)做的是一个可学习的线性变换75维原始坐标 - 128维高层特征可以理解为模型先把原始坐标“翻译”成 Transformer 更容易处理的特征空间。类似这样原始坐标 [x1,y1,z1,x2,y2,z2,...] 投影后 [f1,f2,f3,...,f128]这 128 个f不是原始坐标而是模型学出来的特征。4. Transformer 不是必须 128但必须统一你说“所有维度都是 75”确实也可以让 Transformer 用 75 维。理论上可以设置embed_dim:75但有个限制embed_dim%num_heads0如果num_heads4那么75 / 4除不尽所以不能用 75。你可以用embed_dim 64 embed_dim 96 embed_dim 128 embed_dim 160 embed_dim 256这些都可以只要能被num_heads整除。5. 为什么常用 128因为128是一个比较常见的隐藏特征维度比 75 更有表达能力计算量还不算太大可以被num_heads4整除每个 attention head 维度是128 / 4 32也就是 4 个注意力头每个头处理 32 维特征。6. 用一个具体例子假设batch_size 32 num_frames 128 num_joints 25 coord_dim 3 embed_dim 128输入keypoints [32, 128, 25, 3]展平每一帧[32, 128, 25, 3] - [32, 128, 75]线性投影[32, 128, 75] - [32, 128, 128]注意中间那个 128 一直是帧数 最后那个 128 是每帧特征维度所以[B, T, 75] - [B, T, 128]如果代入具体数字就是[32, 128, 75] - [32, 128, 128]这不是“128 帧变成 128 维”而是128 帧保持不变 每帧从 75 维坐标特征变成 128 维隐藏特征7. 类比一下一条手语视频像一句话128 帧 128 个词 75 维 每个词原始描述 128 维 每个词经过 embedding 后的表示Transformer 不直接处理“原始词”而是处理“词向量”。这里也是一样原始关键点坐标 - 关键点特征向量 - Transformer所以这一步叫input_projection也可以理解成姿态关键点的 embedding 层。三、Transformer Encoder 在这里做什么这部分是核心encodedself.encoder(embeddings,src_key_padding_mask~valid_mask,)你可以这样理解每一帧现在是一个 token。第 1 帧 token 第 2 帧 token 第 3 帧 token ... 第 128 帧 tokenTransformer Encoder 会让每一帧去“看”其他帧学习时间关系当前帧和前面动作有什么关系 当前帧和后面动作有什么关系 哪些帧组合起来代表某个手语动作比如一个手语动作不能只看单帧必须看整个动作变化。Transformer Encoder 就是在建模这种时序依赖。这里的num_heads4表示多头注意力。可以粗略理解为模型用 4 种不同视角去看时间序列一个头可能关注起始动作 一个头可能关注手部移动 一个头可能关注结束姿态 一个头可能关注整体变化num_layers2表示堆叠 2 层 Transformer Encoder。四、位置编码 position_embedding 是什么Transformer 本身不知道第几帧在前、第几帧在后。所以代码加了positionstorch.arange(num_frames,devicekeypoints.device)embeddingsembeddingsself.position_embedding(positions).unsqueeze(0)这相当于告诉模型这是第 1 帧 这是第 2 帧 这是第 3 帧 ...否则 Transformer 只看到一堆帧特征不知道它们的顺序。五、mean pooling 是什么基础模型最后做maskvalid_mask.unsqueeze(-1).to(dtypeencoded.dtype)pooled(encoded*mask).sum(dim1)/mask.sum(dim1).clamp_min(1.0)意思是把所有有效帧的特征取平均[B, T, 128] - [B, 128]然后送入分类器returnself.classifier(pooled)输出[B, 500]也就是每个样本属于 500 个类别的分数。基础模型的问题是所有有效帧权重一样。但手语动作里有些帧很关键有些帧只是过渡动作。所以后面增强模型加了 attention pooling。六、增强模型 enhanced_sign_transformer.py文件enhanced_sign_transformer.py它保留了基础模型的主干特征提取 - 时间 Transformer Encoder - 池化 - 分类但前后加了几个增强模块。配置开关有use_motion use_bone use_spatial_attention pooling对应use_motionTrue 加速度/运动特征 use_boneTrue 加骨骼结构特征 use_spatial_attentionTrue 加帧内关节点空间注意力 poolingattention 使用时间注意力池化七、Motion Feature 是什么代码motion[:,1:]keypoints[:,1:]-keypoints[:,:-1]意思是计算相邻帧的差第 t 帧位置 - 第 t-1 帧位置它表示动作变化手往哪里动 动了多少 速度方向是什么如果原始输入每个点是[x, y, z]加入 motion 后每个点变成[x, y, z, dx, dy, dz]所以特征维度从3变成6。对于 25 个点25 * 3 75 25 * 6 150八、Bone Feature 是什么代码bones[:,:,child]keypoints[:,:,child]-keypoints[:,:,parent]意思是计算两个相邻关节点之间的向量child_joint - parent_joint比如手腕 - 手指 肩膀 - 手肘 手肘 - 手腕它表达的是骨架结构方向不只是绝对坐标。原始坐标告诉模型点在哪里Bone Feature 告诉模型骨骼朝哪个方向 局部姿态结构是什么如果同时使用 position、motion、bone那么每个关节点特征是位置: 3维 运动: 3维 骨骼: 3维 总共: 9维所以如果不使用空间注意力展平后一帧是25 * 9 225九、Spatial Attention 是什么如果不用空间注意力模型还是把每帧展平[25, feature_dim] - [25 * feature_dim]这里的feature_dim指的是位置: 3维运动: 3维骨骼: 3维这些维度这样模型对关节点之间的关系建模比较粗。如果启用use_spatial_attentionTrue会走这里embeddingsself._spatial_encode(keypoint_features,valid_mask)_spatial_encode里面做的是每一帧内部 25 个关节点分别作为 token 让 25 个关节点互相做 self-attention 得到该帧的空间特征也就是说基础 Transformer 是帧与帧之间做 attentionSpatial Attention 是同一帧内部关节点与关节点之间做 attention区别是时间注意力第 1 帧、第 2 帧、第 3 帧之间的关系 空间注意力手腕、手指、手肘、肩膀之间的关系所以增强模型如果开了use_spatial_attentionTrue它是一个更像“时空模型”的结构空间注意力先学习每帧内 25 个关节点之间的关系 时间 Transformer再学习 128 帧之间的动作变化十、Attention Pooling 是什么基础模型用 mean pooling所有有效帧平均增强模型可以用poolingattention代码scoresself.attention_pooling(encoded).squeeze(-1)weightstorch.softmax(scores,dim1).unsqueeze(-1)return(encoded*weights).sum(dim1)它会给每一帧一个权重第 1 帧权重 0.01 第 2 帧权重 0.03 ... 关键帧权重 0.20 ...这样模型可以自动关注关键动作帧而不是所有帧平均。十一、两个文件的核心区别可以这样总结sign_transformer.py 基础时序 Transformer 位置特征 时间 Transformer Encoder mean poolingenhanced_sign_transformer.py 增强时空 Transformer 位置特征 运动特征 骨骼特征 可选空间注意力 时间 Transformer Encoder mean pooling 或 attention pooling更直观地说基础模型 每一帧展平成一个整体看 128 帧之间的关系。 增强模型 先让模型看到动作变化、骨骼结构、关节点关系再看 128 帧之间的关系。