返回列表

[1st place solution] Improved Squeezeformer + TransformerDecoder + Clever augmentations

570. Google - American Sign Language Fingerspelling Recognition | asl-fingerspelling

开始: 2023-05-10 结束: 2023-08-24 计算机视觉 数据算法赛

[第1名解决方案] 改进的Squeezeformer + TransformerDecoder + 巧妙的增强方法

感谢Kaggle及所有相关人员举办如此有趣的竞赛。这是对手语孤立分类的重大扩展,非常有趣地发现语音转文本研究在多大程度上也可应用于手语拼写。与@darraghdog的合作始终是一次很棒的团队体验。

TLDR

我们的解决方案基于单个编码器-解码器架构。编码器是Squeezeformer的显著改进版本,其中特征提取经过调整以处理MediaPipe地标而非语音信号。解码器是一个简单的2层Transformer。我们还预测了置信度分数来识别损坏的样本,这在后处理中很有用。此外,我们引入了高效且富有创意的增强方法来规范模型,其中最重要的包括CutMix、FingerDropout、TimeStretch和DecoderInput Masking。我们使用PyTorch开发和训练模型,然后手动转换模型架构并将权重移植到TensorFlow,从中导出为TF-Lite。

交叉验证

我们按手语者将训练数据分为4折。开始时,这种方法的CV分数与Public LB之间几乎完全相关。随着分数提高,CV上的改进在LB上反映得较少,主要是因为LB分数总是相对较高且更早饱和。大多数时候,我们只训练和跟踪fold0的分数,而不是所有折叠。

数据预处理

总共使用了130个关键点,包括每只手21个关键点、每只手臂6个姿态关键点,以及面部其余76个关键点(嘴唇、鼻子、眼睛)。本地将130个关键点缓存到.npy文件以实现快速数据加载。
在数据增强之前,数据通过标准差/均值进行归一化,并将NaN值填充为零。

增强方法

增强方法对于防止过拟合、泛化到新 signer 和启用深度模型至关重要。我们使用了首届ASL竞赛中流行的增强方法,同时也提出了许多富有创意的新增强方法,其中一些被证明非常有效。

  • 沿时间轴调整大小
  • 沿时间轴移动序列
  • 沿时间轴的窗口调整大小(类似扭曲)
  • 关键点的左右翻转
  • 时间维度的Cutmix - 在[0,1]之间抽取随机百分比,在两个序列和相关短语上按该百分比进行截断并混合。最好只在同一 signer 内混合
  • 空间仿射 - 缩放、剪切、平移和旋转
  • 在2-3个时间窗口内丢弃/置零2-6根不同手指
  • 丢弃/置零所有面部关键点或所有姿态关键点
  • 在少数情况下(约5%的样本)丢弃/置零所有手部关键点
  • 不同大小或数量的窗口中进行时间掩码(置零)
  • 空间掩码

大多数增强应用于50%的样本,除了调整大小和空间仿射应用于约80%的样本。
增强后,超过384帧的样本沿时间轴调整大小,使用通道级线性插值。少于384帧的样本仅在训练时填充至384帧。TF-Lite在可变长度样本上运行。
预处理过程中不会丢弃任何帧。

模型

总体而言,我们观察到更深的模型能带来显著提升(如果能防止过拟合)。因此,不仅正则化技术(如增强方法)至关重要,而且计算效率的每一项改进都为使用更深模型创造了空间,其重要性不亚于模型架构本身。

我们的模型由3个部分组成:特征提取、编码器、解码器,如下图所示:

模型架构图

我们将数据解释为类似3通道图像,其中宽度由帧数定义,高度由选定的130个地标数量定义,通道由原始xyz坐标定义。
特征提取基于2D卷积,后接批归一化和线性层,用于提取每帧的扁平化特征。我们有5个这样的特征提取模块:一个用于所有地标,一个用于每种地标类型(左手、右手、面部、姿态)。所有地标模块每帧输出208维向量,其他4个各输出52维向量,然后拼接成总共208维。因此我们每帧有两个208维向量,得到编码器的输入(batch_size x 384 x 208),其中384是我们选择的最大序列长度。

编码器

我们的模型的核心组件是从Squeezeformer架构改编而来的编码器。我们没有使用实际的"squeeze"思想(即时间维度的Unet),而是采用了Squeezeformer块的通用架构,这些块由MultiHeadSelfAttention(MHSA)、卷积和FeedForward模块组成。我们对该架构做了多项改进:

ASR conformer(和Squeezeformer)使用相对位置编码,使自注意力模块能更好地泛化到不同输入长度。相对位置编码计算量大且参数众多,因为每个层都单独存储。用Llama注意力(使用旋转嵌入)替代后,训练速度提升约2倍,TF-Lite推理速度提升约3倍,从而允许使用更大的模型。此外,我们将旋转嵌入一次性缓存并随输入数据送入每一层,避免在各层中重复。这使模型参数减少了20%。我们没有看到使用Squeezeformer引入的时间缩减有任何好处,因此模型所有层都保持与原始输入相同的序列长度。正如Squeezeformer论文所述,Macaron结构中的预层归一化是冗余的,我们将其替换为可学习的缩放层来缩放和偏移激活值。

解码器

对于解码,我们使用了简单的2层Transformer解码器,类似于Hugging Face的Speech2TextDecoder,输出序列预测。然后使用交叉熵损失进行端到端训练。我们还使用了反向序列的额外交叉熵辅助损失。因果解码器主要对序列开头和先前字符使用编码器交叉注意力,对末尾使用交叉注意力。为了提高模型准确性,我们在反向序列上使用单独的因果解码器作为辅助损失,使模型更依赖编码器交叉注意力来识别标签末尾。解码器推理时使用早期停止和过去键值缓存,显著加快了推理速度。需要注意的是,我们发现在计算效率至关重要的环境中,基于Transformer的解码器优于CTC解码。

此外,我们添加了一个单一线性层,用于提取编码器输出的第一个令牌的特征来预测置信度分数,这有助于识别垃圾数据并可用于后处理。对于此目标,我们使用先前良好模型的OOF预测的归一化Levenshtein距离,并裁剪到[0,1]范围。

我们在PyTorch中探索和训练所有模型,但每次认为模型足够好用于提交时,都会手动将每个组件转换为TensorFlow,并移植PyTorch模型的权重。

训练过程

模型使用余弦学习率调度训练400个epoch,峰值学习率为0.0045,权重衰减为0.08,10个epoch的warmup,混合精度,有效批次大小为512个样本。Transformer编码器/解码器层使用0.1的dropout。训练时使用混合精度对于利用FP16推理而不损失性能至关重要。使用FP32训练并使用TF-Lite FP16推理会导致CV与LB之间约0.01的下降。

TF-Lite推理使用单个无填充样本,而模型训练使用时间填充的小批次。为避免模型学习填充数据并确保最佳推理运行时,特征提取器和Macaron结构编码器层在训练时进行了时间掩码。这需要在PyTorch中手动实现每一层,虽然花费了一些精力,但通过显著加快推理速度得到了回报。正如上一届ISL竞赛的第一名团队所解释的,这在TensorFlow中更容易实现,因为Keras有现成的掩码层。

模型训练和TF-Lite推理使用FP16,因此TF-Lite文件的大小几乎是FP32的一半。这很重要,因为40MB是我们的最终限制。两个最终模型种子大小为39988KB。

后处理

后处理的主要思想是用一个与训练/测试数据具有较小Levenshtein距离的虚拟短语替换较差预测。@anokas在他的notebook中展示了为什么'2 a-e -aroe'是一个很好的候选。大多数较差预测来自损坏的输入数据,通常只有几帧长。我们以模型预测的置信度分数为基础。当置信度分数低于0.15或序列短于15帧时,我们将预测替换为'2 a-e -aroe'。

补充数据

我们仅从补充数据中获得了边际收益。我们认为主要原因是尽管补充数据有50k个样本,但只有500个不同的短语,因此模型更倾向于学习分类而非实际逐字符解码。我们尝试了很多方法,但只有一种方法带来了小幅提升(0.838 -> 0.839):
首先我们按短语对补充数据进行分组,只剩下500个组。在每个训练epoch中,我们从每组向训练集添加一个样本。这意味着每个epoch我们使用50k个训练数据样本和仅500个补充数据样本。

集成方法

我们的最终提交是模型在完整训练数据(fullfit)上训练的2个种子集成。我们在每个解码步骤中对结果logits进行平均以实现集成。

无效尝试

  • 完全使用补充数据
  • 使用编辑距离作为损失(尝试了不同方法)
  • CTC损失(即使作为辅助损失也会降低分数)
  • 标签平滑
  • AWP - FP16下持续出现NaN
  • TTA(翻转/拉伸)
  • 隐藏层Mixup和Specaugment++
  • Beam search解码(成本过高)

消融研究(粗略估计)

增强方法

  • Cutmix +0.005
  • FingerDropout +0.005
  • Face/PoseDropout +0.005
  • 掩码解码器输入 +0.003

模型改进

  • CNN特征提取 +0.005
  • 2分支特征提取与独立归一化 +0.003
  • 相比第1轮第1名网络的Squeezeformer +0.005
  • 相比CTC的解码器 +0.003
  • 后处理置信度相比简单规则 +0.002

效率改进

  • FP16支持的更深模型 +0.003
  • Llama注意力支持的更深模型 +0.003
  • 掩码/可变序列长度支持的更深模型 +0.005
  • 解码器缓存/早期停止支持的更深模型 +0.005

后处理

  • 用虚拟短语替换较差预测 +0.006

使用的工具/代码库

  • PyTorch/TensorFlow/TF-Lite(这次不用ONNX)
  • Huggingface
  • Albumentations(采用其框架用于OneOf或Compose等功能,但自己实现了增强方法)
  • Neptune.ai是我们的MLOps平台,用于跟踪、比较和共享模型。以下是使用不同模型参数和硬件(A100显卡 vs Kaggle内核)的训练运行示例。下面Kaggle内核中的数据加载较慢,可能需要优化。

训练运行示例

代码和模型权重

https://github.com/ChristofHenkel/kaggle-asl-fingerspelling-1st-place-solution

论文

待定。由于我们的方法具有新颖性,我们考虑将其总结为论文。

感谢您的阅读,欢迎提问!

同比赛其他方案