EST:把微弱表情变化组织成可以学习的时间结构
Expression Snippet Transformer · 论文、源码、采样与实验协议逐项精读
一句话总结
EST 先在五帧片段内提取局部关系,再在七个片段之间建模,并用预测打乱顺序的辅助任务减少对固定峰值位置的依赖。关键学习点是采样、两级注意力、监督与实验协议。
交互:打乱片段后,SSOP的标签是什么?
用三个片段和四种固定排列做小型教学示意,原顺序为1、2、3。选择输入排列,查看整条排列对应的类别标签;不是逐位置预测原编号。真实EST为七个片段、十种固定排列。这里仅演示监督标签生成,不训练网络。
先认清任务:从一段人脸视频到一个表情类别
EST 的输入是一段已经裁剪好人脸的视频,输出是整段视频的基本表情类别。DFEW、AFEW 使用七类表情,包含快乐、愤怒、厌恶、恐惧、悲伤、惊讶和中性;另外两个受控数据集使用六类。这里的“细微变化”指同一视频中变化幅度较小的视觉线索,不能直接等同于专门研究短暂微表情的任务,也不能把七类分类当成复合情绪标注。
与前一篇 ALMT 相比,EST 只使用视觉模态;ALMT 主要预测连续情感分数,EST 预测离散表情类别。二者都用注意力,但注意力处理的对象、监督信号和评价指标不同。学习顺序上的连接是:你已经知道注意力怎样按相关性汇总特征,现在把这个机制放到视频内部,观察“局部几帧”和“全视频几段”怎样形成两级时间结构。
一帧中清晰的眉眼形态能帮助分类,但单帧也可能只是说话时张嘴、转头或遮挡。反过来,连续变化能补充证据,例如嘴角逐渐上扬,而不是某一帧偶然相似。难点在于表情运动常常比头部运动更弱。模型把注意力集中到视觉变化最大的帧,并不保证抓住表情线索。
原文 Figure 1(作者稿第 2 页):左侧普通 Transformer 与右侧 EST 的对照用于说明这个动机。黄色标记表示最高注意力位置,并不是人工提供的真实表情发生区间。读这幅图时,应把“模型注意了哪里”和“哪里对正确预测具有因果作用”分开。
EST 的核心做法分成三步:先将长序列组织成七个短片段;片段内用 AA-SFE 汇总五帧;片段之间用编码器与解码器汇总成视频表示,再通过 SSOP 辅助任务约束学习。阅读时最该抓住的两个问题是:一段内部怎样压缩,段与段之间的关系靠什么训练。
原文摘要用 “intra-snippet” 和 “inter-snippet” 区分两种关系;Section 3.3 将辅助任务称为 “shuffled snippet order prediction”。这三个短引文分别对应片段内部、片段之间和排列监督。
flowchart TD A[105 normalized face frames] --> B[75 frame temporal crop] B --> C[7 windows of 15 frames] C --> D[5 sampled frames per window] D --> E[ResNet18 frame features] E --> F[AA-SFE within each snippet] F --> G[7 snippet features] G --> H[Shuffle with one of 10 orders] H --> I[Transformer encoder and decoder] I --> J[Emotion feature T] J --> K[7 class expression head] H --> L[Detached snippet features] L --> M[Add T and concatenate] J --> M M --> N[10 class order head] K --> O[Classification loss] N --> P[Order loss times 1 over 7]
第一步:真正进入网络的 35 帧怎样选出来
原文 Section 4.2 先把视频通过插值或裁剪统一到 105 帧,并用 RetinaFace 得到 224×224 的人脸区域。注意:公开数据加载器只读取已经准备好的图片文件夹,不负责完成原始视频的人脸检测和 105 帧标准化。因此“运行 main.py”不等于从原始视频自动完成整条预处理流程。
训练时从第 0 到第 29 个索引中随机选起点 a,取其后的 75 帧。随后构造七个长度为 15 的候选窗口,相邻窗口的起点相差 10 帧,因此相邻窗口重叠五帧。每个窗口再抽五帧,最终形成七个五帧片段。35 是输入位置数量,窗口重叠使同一源帧可能出现在相邻片段里,所以不能保证每次都是 35 张互不重复的源图像。
这里 W 表示候选窗口,C 表示实际采样片段,下标 i 是片段序号。最后一个窗口覆盖 a+60 到 a+74,恰好落在 75 帧范围中。例如 a=15 时,第一个窗口是 15~29,第二个是 25~39;两者共享 25~29。这个算式能直接帮助你检查采样代码是否越界。
测试时公开实现默认固定起点 15,在每个 15 帧窗口内使用相对索引 [0,3,6,9,12]。因此测试输入是确定的,分类分支使用正常片段顺序。训练阶段随机采样、测试阶段固定采样的差别必须写进实验记录;换成均匀覆盖整段原始视频就是新的采样协议。
| 阶段 | 窗口起点 | 窗口内选帧 | 片段排列 |
|---|---|---|---|
| 训练 | a 在 0~29 随机取 | 从 15 帧中随机选五帧 | 从固定十种排列中随机取一种 |
| 测试分类 | 默认 a=15 | 0、3、6、9、12 | 原始顺序 |
| 测试顺序头 | 与测试采样相同 | 同上 | 随机选一种排列,仅用于顺序准确率 |
一个需要复现者核查的差异:论文 Section 4.4.3 明确说片段内部帧序保持不变,但 data/random_shuffle_dataset.py 的 sample() 将随机索引放入 set,再直接 list(s),没有显式排序。set 不提供时间顺序保证。忠实运行当前源码与按论文补上 sorted() 是两种可区分的实验,应分别记录;本文教学采样脚本显式排序,目的是演示时间结构,不冒充原实现的逐位复现。
预备知识:一帧、一个 token、一个片段分别是什么
ResNet-18 是逐帧特征提取器:它把一张 3×224×224 的 RGB 人脸图像变成一个 512 维向量。向量中的每个坐标是学习到的视觉响应,不是预先指定的“嘴角角度”或“恐惧程度”。批量大小为 B 时,原始输入形状为 B×7×5×3×224×224;卷积阶段先把前面三维合并成 B×35 张图,再恢复成 B×7×5×512。
token 在这里指模型作为一个处理单位使用的向量。AA-SFE 内部有五个帧 token;视频 Transformer 接收七个片段 token。把两处的注意力矩阵混起来,就会误读论文:前者计算的是同一小段内帧与帧的关系,后者计算的是整段视频中片段与片段的关系。
Q 是查询向量组成的矩阵,K 是被比较的键,V 是最后被加权汇总的值,d 是论文写法中的特征维度。直观上,每个查询先给所有键打相关性分数,再通过 softmax 变成一行权重,最后按权重混合值向量。实际多头实现把 512 维拆到四个头,每头 128 维,各头再合并;上式省略了多头拼接、残差和前馈网络,不能把一个式子当成完整 Transformer 层。
自注意力的 Q、K、V 来自同一组对象;交叉注意力的查询来自另一组对象。EST 的视频编码器让七个片段相互交流,解码器使用一个可学习的情绪查询向量,从编码结果里汇总对分类有用的信息。它与 ALMT 的语言查询不同:情绪查询没有对应一个输入句子,而是随训练更新的模型参数。
第二步:AA-SFE 怎样把五帧压成一个片段表示
原文 Figure 3(作者稿第 5 页):AA-SFE 的两级注意力结构:先做帧间关系建模,再用片段整体信息重新加权帧特征。重点不是增加一个名字,而是明确两个阶段分别在比较什么。
第一级先将片段的五个 512 维向量堆成矩阵 I,再做一次帧间自注意力,得到 I′。每个输出帧向量已经包含其他帧的信息,因此后续选择的对象不是原始孤立帧,而是与局部上下文交流后的表示。公开实现还把原始特征加回注意力输出,再做 LayerNorm,这个残差通道使模型不必完全重建原有的逐帧信息。
第二级首先在时间维上做最大池化,得到一个 512 维的整体向量。最大池化是逐坐标选择最大响应:第一维可能来自第 1 帧,第二维可能来自第 4 帧。因此这个整体向量通常不是任何一张真实帧的特征,也不能简单叫作“峰值帧”。它描述这个局部片段中各类强响应的集合。
这里 j 是帧序号,k 是特征坐标,r′ 是第一级之后的帧向量,带帽子的 r′ 是片段整体向量。这个写法把“最大池化”具体化了:比较发生在五帧之间,结果仍保留 512 个坐标,不是直接把所有数压成一个标量。
α 是第 j 帧向量与片段整体向量的余弦相似度。点积除以两个向量长度后,主要比较方向而不是数值大小。作者希望偏离整体较大的帧得到较低权重,从而减轻偶发干扰。但是“与整体相似”不自动等于“包含表情”,如果多数帧都被同一类噪声主导,整体向量也可能带偏。
R 是第 i 个片段最终的 512 维表示。分子把五个向量乘上各自权重后相加,分母控制总权重规模。与简单平均相比,它允许不同帧贡献不同;与直接选一帧相比,它保留多帧的组合信息。该权重来自当前特征的计算,论文没有逐帧噪声标签,也没有监督每个权重应该取多少。
models/Model.py:187–203 · 固定源码版本
for i in range(self.clips):
vs_stack = tmp[:,i,:,:]
vs_stack = vs_stack.permute(1,0,2)
output,weight = self.attn(vs_stack,vs_stack,vs_stack)
if self.use_norm:
output = vs_stack + self.dropout3(output)
output = self.norm1(output).permute(1,2,0)
global_feature = self.maxpool2(output).squeeze(dim=2)
local_feature = output.permute(2,0,1) # 5*b*512
dis_list = []
for j in range(self.img_num_per_clip):
dis = self.cos(local_feature[j],global_feature)
dis_list.append(dis)
dis_alpha = torch.stack(dis_list,dim=1).unsqueeze(dim=1)
dis_alpha = torch.clamp(dis_alpha, min=1e-8)
output = output.mul(dis_alpha).sum(2).div(dis_alpha.sum(2))上面 17 行是两级处理的直接对应。vs_stack 的形状是 5×B×512;self.attn 的三个参数相同,对应第一级自注意力;output 经转置成为 B×512×5;maxpool2 汇总时间维;最后 mul、sum、div 对应原文 Eq. (3)。源码在余弦权重上额外使用 clamp(min=1e-8),把负值和过小值截断成正数。论文 Eq. (2) 的余弦理论上可能为负,因此“严格按纸面公式”和“按发布代码”在这个数值细节上有差别。
建议你先用两个二维向量手算余弦,再看五帧的向量运算。若权重是 [1,0.5,0.5,1,1],分母就是 4;如果把所有权重同时乘二,最后的加权平均不变。这个性质说明公式关注相对贡献。源码截断与分母归一化共同避免负权重抵消造成不稳定,不能误讲成对五个余弦值执行 softmax。
第三步:七个片段怎样变成整段视频的预测
原文 Figure 2(作者稿第 4 页):完整训练结构连接了片段采样、AA-SFE、编码器、情绪查询解码器、表情分类头和顺序预测头。图中的两个输出头承担不同监督信号。
七个片段向量组成 R,加入正弦与余弦位置编码后进入视频 Transformer。位置编码让特征携带时间位置身份,否则标准自注意力主要处理一组向量之间的关系,难以单凭相同的内容集合区分先后。原文使用三层编码器和三层解码器,每层四个头,默认隐藏维度为 512;不要使用 Model.py 构造函数里四层的默认形参来断言论文用了四层,因为主程序实际传入 options/base_options.py 的三层配置。
编码器输出七个带上下文的向量 H。解码器使用一个 512 维可学习情绪查询,反复从 H 读取信息,输出单个视频向量 T。随后三层全连接分类器输出类别 logits。logits 是尚未归一化的分数,训练交叉熵直接接收这些分数,推断时可以用 softmax 看概率,最后取最大分数的类别作为预测。
q 是情绪查询参数,H 同时提供键与值,MLP 是全连接网络。这个式子强调数据流:查询不是一张挑选出来的人脸图,也不是某一表情的人工原型。公开源码的分类器为 512→128→64→7,最后七维适用于 AFEW、DFEW;如果改做六类数据集,应核实标签映射及输出维度,而不是照搬七类头后直接比较论文结果。
源码还透露了一个经常被解释遗漏的细节:训练片段被排列后,位置编码也按同一排列重排,即原片段的原始时间位置身份跟着它移动。不能将它讲成“打乱后统一重新编号 0 到 6”。比较修改前后的结果时,应把“保留原位置身份”和“采用打乱后位置”当成两种不同实现,并先设计小规模行为检查。
models/Model.py:210–229 · 固定源码版本
pos = torch.tensor([self.clips] * b)
pos_enc = self.pos_enc(pos)
for i in range(b):
pos_enc[i] = pos_enc[i][per[i]]
x = ori_video_feature + pos_enc
x = x.permute(1, 0, 2)
tgt = self.query_embed.weight
tgt = tgt.unsqueeze(1).repeat(1,b,1)
emotion_clip_feature,weight_list = self.transformer(x, tgt) #.permute(1, 2, 0)
emotion_clip_feature = emotion_clip_feature.permute(1,2,0)
emotion_clip_feature = emotion_clip_feature.reshape(-1,self.d_model * self.key_clips)
#####permutation
ori_video_feature_detach = ori_video_feature.detach()
per_feature = ori_video_feature_detach + emotion_clip_feature.unsqueeze(dim=1)
per_output = self.per_branch(per_feature.view(b,-1))
output = self.classifier(emotion_clip_feature)pos_enc[i][per[i]] 是位置身份随片段移动的直接证据;query_embed 提供唯一情绪查询;classifier 产生表情分数。这个片段也包含下一节的 SSOP 输入构造,读源码时沿着变量的形状追踪,比只记模块名称更可靠。
第四步:SSOP 预测排列类别,而不是逐帧恢复视频
只有视频类别标签时,网络可能靠最容易辨认的某一段就完成训练,不必理解整段变化。作者观察到模型常依赖中间片段,于是增加一个不需要人工标注的任务:从预先选定的十种排列中随机挑一种,重排七个片段,然后预测本次用了哪一种。监督标签是采样程序自己知道的排列编号,所以这是辅助自监督信号;整套 EST 仍同时使用人工表情标签,不能称为完全无监督训练。
七个片段理论上共有 7!=5040 种排列,EST 训练的输出空间只有十种预选排列。模型不是预测 5040 类,不是给每一帧恢复时间戳,也不是生成一个排好序的新视频。公开仓库 datasets/permutation_10.npy 保存这十种排列;复现时必须固定该文件版本,临时重新生成会改变辅助任务。
T 在七个片段位置上广播相加;每个 O 都保留局部片段信息并加入视频情绪信息,再拼成 7×512=3584 维,输入顺序分类器。论文解释,仅使用压缩后的 T 难保留足够细的顺序信息,所以用片段表示补充。顺序头不是放在视频解码器里的一串七个输出 token,而是另一个接收拼接向量的全连接分支。
C 索引视频,Y 是真实表情的 one-hot 向量,Ŷ 是预测概率,S 是真实排列类别,Ŝ 是顺序预测概率,期望表示对训练样本求平均。对于单个样本,交叉熵就是负对数真实类别概率:正确类别概率越小,损失越大。PyTorch 的 CrossEntropyLoss 内部处理 log-softmax,因此训练前不必手工再给 logits 加 softmax。
损失系数 1/7 用于减轻辅助任务的影响;公开配置使用 0.1428 近似它。训练循环分别算两个交叉熵后相加,再做一次反向传播。分类标签和排列标签监督不同的目标,因此辅助损失下降不代表表情一定更准确,仍需观察独立验证集上的 WAR 与 UAR。
源码与纸面还存在梯度路径的差别:ori_video_feature.detach() 让 SSOP 中直接相加的 R 分支不接收梯度,但 T 路径仍可把 SSOP 梯度传回 Transformer 和特征提取器。准确讲法是“阻断局部旁路的直接梯度”,不是“骨干被冻结”,也不是“SSOP 没有参与学习”。这一点适合做梯度检查练习,不能在写报告时省略。
原文 Figure 7(作者稿第 12 页):直方图比较最高注意力落在哪个片段,显示加入 SSOP 后分布更分散。它支持“减少对固定中间位置的依赖”的解释,但没有证明每个片段被同等理解。更分散的注意力也可能只是变平,需要结合准确率和干预实验判断。
实验怎么读:先看相同基线,再看不平衡类别
原文使用 BU-3DFE、MMI 的被试独立十折验证,AFEW 的训练/验证划分,以及 DFEW 五折评估。公开作者稿第 8 页 BU-3DFE 的文字统计存在数据集名称与序列来源容易混淆的问题;新实验应根据你实际获得的官方授权数据、分割文件和被试编号进行核验,不应仅凭本文转述重新构造分割。AFEW 数据集虽然介绍了独立测试集,主要结果段落采用 Train/Val 评估;跨库 Section 4.4.7 又称 AFEW testing set,发布代码未在 README 中给出这部分可直接重建的分割说明,必须保留这一可核验性限制。
K 是类别数,N 是该类真实样本数,TP 是该类预测正确的数量。UAR 先算每个类别召回率,再给各类相同权重;WAR 按样本数加权,在单标签分类中等于整体准确率。例如大类 10 个样本答对 8 个,小类 2 个答对 1 个,WAR=9/12=75%,UAR=(80%+50%)/2=65%。这说明大类占优时,只看 WAR 容易忽略少数类。
| 数据集 | Transformer 基线 WAR / UAR (%) | EST WAR / UAR (%) | WAR 增益(百分点) |
|---|---|---|---|
| BU-3DFE | 85.60 / 85.60 | 88.17 / 88.17 | 2.57 |
| MMI | 90.50 / 90.03 | 92.50 / 90.31 | 2.00 |
| AFEW 8.0 | 49.72 / 43.95 | 54.26 / 49.57 | 4.54 |
| DFEW | 63.85 / 50.39 | 65.85 / 53.94 | 2.00 |
最直接的证据是与作者自身 Transformer 基线比较,因为任务、主干与训练设置较接近。在 AFEW 上 WAR 从 49.72 到 54.26,是 4.54 个百分点,相对于基线的相对增幅约为 9.13%。正文把这项变化称为 8.37% 相对提高,数值更接近除以最终值后的比例;本页采用表格数值自行计算百分点,避免把不同口径混用。
对 DFEW 还要降低表述强度:与 Former-DFER 相比,EST 的 WAR 65.85 对 65.70 只多 0.15 个百分点,UAR 53.94 对 53.69 多 0.25 个百分点。原文称取得提升,但没有在这些表格中提供多随机种子的标准差或显著性检验,所以不能依据小差值断言稳定领先。与自身基线的两点 WAR 提升和跨方法的零点一五点提升,是两条证据。
少数类问题仍然明显。作者报告 DFEW 厌恶类准确率仅 5.52%,快乐类可达 86.87%,并把它与长尾分布及类别间细微差别联系起来。总体 WAR 提升并未解决细粒度混淆;这恰好解释为什么后续学习路线应研究区分困难类别、局部区域、动作单元或类别结构,而不是只追求更大的整体准确率。
| 组件 | 参数量 M | MACs G | BU-3DFE WAR/UAR (%) |
|---|---|---|---|
| Transformer | 34.37 | 63.85 | 85.60 |
| Transformer + AA-SFE | 34.37 | 63.88 | 87.12 |
| Transformer + AA-SFE + SSOP | 42.78 | 63.89 | 88.17 |
AA-SFE 带来 1.52 个百分点,随后 SSOP 再带来 1.05 个百分点。这个逐步消融支持两个模块在该组合设置下有效,但不是完整 2×2 因子实验:缺少“只加 SSOP、不加 AA-SFE”这一行,所以不能从该表独立估计交互作用。参数量增加主要出现在顺序预测头,计算量变化却很小,因为相对逐帧卷积,大型全连接辅助头的运算量占比低;不能把“小 MACs 增量”讲成“没有参数代价”。
原文 Table 7 中层级注意力 88.17 优于单自注意力 87.63、SE-like 注意力 87.46;Table 9 中把视频中间帧置零后,带 SSOP 的 WAR 为 87.5,不带 SSOP 为 82.5。后一个实验比单纯注意力图更接近行为干预,但仍只针对一种人工遮断条件和特定数据集,不能推导出对所有遮挡、所有场景都稳健。
从 EST 怎样走向细粒度与跨场景研究
| 训练 → 评估 | Transformer WAR (%) | EST WAR (%) | 差值(百分点) |
|---|---|---|---|
| DFEW → AFEW | 48.86 | 51.14 | 2.28 |
| BU-3DFE → AFEW | 19.66 | 25.17 | 5.51 |
跨场景意味着训练与评估的数据分布不同,例如摄影条件、影片风格、人群或表演方式改变。Table 11 比同库评估更贴近这个问题,但 EST 本身没有专门的域对齐损失,也没有学习一个显式场景不变分解;它是把同一模型迁移评估,不能称为已经解决域泛化。由受控数据到 AFEW 的 25.17% 仍很低,也不能与同库 54.26% 当成条件一致的优劣对照。
细粒度方向的第一个可执行问题是:当恐惧被判成愤怒、厌恶被判成中性时,错误来自关键局部变化没有被编码,还是标签、低分辨率与类别不平衡?先从每一类各抽取一组独立验证样本,记录脸部清晰度、姿态、遮挡、时间位置和预测变化。这个错误表比“设计一个更强模块”的空泛起点更有用,且不应先用测试集筛选最适合新方法的例子。
跨场景方向的第一个问题是:是否在新场景里误把背景或姿态当成情绪?把源域划分为训练与验证,目标域保留完整评估集合,统一七类标签映射及人脸预处理;先跑源域模型直接迁移的基线。若训练时使用无标签目标域样本,应明确称为无监督域适应;若训练时完全不看目标域,则更接近域泛化或直接跨库评估。这几种协议不能混成同一个成绩。
适合从 EST 起步的改动应一次只动一个因素,例如比较 AA-SFE 加权与简单平均,或者比较正常采样与严格保持片段内顺序。先验证改动确实改变了预期行为,再讨论类别结构或域不变学习。使用多个数据集做独立同库训练,只能说明方法在多个数据集上适用,不能自动算跨场景泛化实验。
局限与复现风险:区分作者结论和阅读者判断
作者自述。Section 4.3.5 与结论指出:极细微的视觉差别仍难捕获,长尾分布导致少数类学习不足,厌恶/中性、恐惧/愤怒等混淆仍存在。作者计划进一步利用自监督机制与无标签数据。应将这些写进读书报告,而不能只摘抄摘要中“鲁棒”和“最先进”的表达。
独立判断一:自监督目标可能利用捷径。排列类别可以从局部外观强弱、裁剪变化、视频开头结尾差异中得到线索。顺序准确率高并不证明网络理解了表情运动的语义顺序。可以把时间反转、重复帧、同视频片段替换作为后续受控检验,要求表情表现与辅助任务表现一起变化;这些是本文建议的实验,尚未执行。
独立判断二:可视化的解释范围有限。Figure 7 的注意力分布与 Figure 12 的 t-SNE 可以展示模型行为与表示结构,但不能单独证明因果机制或泛化。t-SNE 的降维超参数和抽样会影响图形,看起来分开的点团不能替代独立测试集准确率、类别召回率和误差条。
独立判断三:当前源码的实验选择需要澄清。main.py 每轮使用 test_list_root 对应列表评估,并按其整体准确率保存最好模型。若该文件就是正式测试折,就存在依据测试表现选轮次的问题;若它是验证划分,则需要另设最终测试。新实验应在训练部分划出验证集确定轮次,再做一次最终测试,并说明协议与原文不同;不能靠变量名“validate”判定实际数据角色。
工程上,README 标注 Python 3.7.9,requirements 锁定较旧的 PyTorch/torchvision;源码还直接调用 .cuda(),所以参数帮助里写“gpu_ids=-1 可用 CPU”不代表整条代码实际能在 CPU 跑通。显存不足时可以先减 batch,但那会影响 BatchNorm 与优化设置,不能预先承诺成绩等同。本文没有运行 EST 长训练,也没有把作者 RTX 3090 上的 fps 当成你的 RTX 4070 Laptop 的实际速度。
发表信息使用出版者元数据:Pattern Recognition 138(2023),109368,DOI 10.1016/j.patcog.2023.109368。本文方法和数值逐项取自作者网站的 19 页稿件,页面标注日期为 2023-02-10;它与出版排版版的页码体系不同,本文引用 Figure/Table 编号并注明作者稿页码,避免把阅读版本混淆。
把阅读变成可交付练习:每一步都有验收标准
练习一,30~45 分钟:只复现采样索引。下载本页配套 est_sampling_probe.py,在一个空目录运行下面命令。脚本只用 Python 标准库,完全不需要数据集、GPU 或预训练权重。这不是 EST 训练,而是检查你是否掌握 105→75→7×5 的时间结构。
python est_sampling_probe.py --seed 8 --start 15
python est_sampling_probe.py --seed 21 --start 15验收标准:两次输出的 frame_slots 都等于 35;test_indices 两次完全相同;train_indices 随 seed 改变;每个片段有五个递增索引;所有索引在 0~104 内;assertions 为 passed。最后一段测试索引应是 [75,78,81,84,87]。将两个 JSON 输出保存下来,并手画相邻窗口的五帧重叠区域。脚本使用显式排序,是与论文叙述一致的教学版本。
练习二,45~60 分钟:形状追踪。不训练网络,沿 Model.py 在纸上写出六个形状:输入 B×7×5×3×224×224、卷积展开 B×35×3×224×224、帧特征 B×7×5×512、片段序列 B×7×512、情绪向量 B×512、顺序头输入 B×3584。验收标准:能解释每次 reshape 或 permute 只改变组织方式,注意力和池化才改变信息内容;能指出顺序头十类与表情头七类的区别。
练习三,60~90 分钟:验证梯度路径。在已能导入模型的兼容环境中,用一个至少含两条视频的合成 batch 做一次前向和只含顺序损失的反向。给 detach 前的 R 与 T 保留梯度,再比较局部旁路与 T 路径;如果要 isolating 旁路,需要显式分离两条路径,不能把“R.grad 非零”误解为 detach 没生效,因为 R 还通过 Transformer 影响 T。验收是画出两条路径并解释哪条被切断,先于训练真实数据。
练习四,半天内的准备工作:数据与协议清单。选择一个已有合法访问权限的数据集和一个固定官方 fold,检查文件夹帧数、文件名排序、标签范围、训练/验证/测试 ID 交集。输出 split_audit.json、采样可视化和一批输入形状。验收标准是三个集合不重叠、标签映射明确、至少十段视频都能无越界读出 7×5 帧;没有通过时不启动 160 轮训练。
练习五,先测 100 步,再估算整程。只有依赖、预训练权重和数据审计完成后,才做短训练,记录每步耗时、GPU 峰值显存、损失是否有限、验证预测是否覆盖合理类别。以实测每步时间乘每轮步数估计训练时长,并单列验证和图片读取开销。验收不是“loss 下降一次”,而是完整保存一次可重新加载的 checkpoint,并用同一输入得到一致的评估预测。这个阶段才有依据回答完整消融需要多久。
读完本篇应留下三份可检查的产物:一张注明形状的数据流图、一份“论文描述—源码实现—潜在差异”的对应表,以及一份固定划分的实验计划。能用这三份东西解释 AA-SFE 与 SSOP 分别处理哪种问题,再进入后续细粒度和跨场景论文,学习重点会从记名字转成比较机制与证据。
自测:SSOP 准确率只有约 50%,是否意味着它没用?
不能这样判断。十分类随机猜测约 10%,论文 Table 8 报告 45.0%~55.4% 的排列准确率;辅助任务的最终价值仍取决于独立的表情识别效果与消融证据。顺序预测不是最终任务,也不需要达到接近 100% 才可能产生训练作用。
自测:五个余弦权重加起来是否天然等于 1?
不是。源码先截断,再通过 Eq. (3) 的分母归一化。只有归一化后的相对权重和为 1,且这里不是 softmax。
自测:将 DFEW 和 AFEW 分别训练出好结果,算跨场景吗?
这属于两个数据集上的同库验证。跨场景证据需要说明用哪个域训练、在哪个未参与模型选择的域评估,以及目标域数据是否在训练中出现。