返回列表

10th place solution

546. Google - Isolated Sign Language Recognition | asl-signs

开始: 2023-02-23 结束: 2023-05-01 音视频处理 数据算法赛

第10名解决方案

作者:Nazarko99MASTER | 发布日期:2023-05-02 | 得票数:17 | 竞赛排名:第10名

首先,我要感谢乌克兰武装部队、乌克兰安全局、乌克兰国防情报局和乌克兰国家紧急服务局,感谢他们为参与这场伟大的竞赛、完成这项工作提供了安全保障,并帮助科技和商业不停滞、持续前进。

TLDR:使用基于线性层嵌入的Transformer模型处理姿态、嘴唇、眼睛,以及用于目标手的STGCN + 线性层。

预处理
在时间维度上通过NaN值找到目标手,必要时进行翻转,选择目标手不为NaN的姿态,在时间维度上调整至16帧,最后进行归一化:

xyz = (xyz - np.nanmean(xyz[:,FACE,:],axis=(0,1))) / np.nanstd(xyz[:,FACE,:],axis=(0,1))

建模
(所有验证分数均基于 @hengck23 按参与者ID划分的数据)在卡在0.75 lb(约0.7验证分数)之后,我意识到需要改变策略。我回到仅对目标手进行建模的方式。在使用面部找到最佳归一化策略后,仅用手部数据我获得了约0.69的验证分数。接着我尝试加入姿态、嘴唇和眼睛,验证分数提升至约0.71。因此我很清楚,大部分信息都来自惯用手,于是尝试获取不同的表示方法并进行集成。首先我尝试绘制目标手,然后运行2.5D CNN或基于Transformer CNN的模型,仅用手部数据获得了约0.6的验证分数。随后我尝试对目标手运行STGCN(时空图卷积网络),获得了约0.66的分数。由于CNN处理时间过长,我决定放弃它。最终,在将姿态、嘴唇、眼睛、手部以及STGCN(手部)通过注意力块集成后,我获得了约0.72的分数。

x = torch.cat([
            self.pose_emb(x[:,:,:8,:].view(B,L,-1), x_mask)[0],
            self.lip_emb(x[:,:,8:20+8,:].view(B,L,-1), x_mask)[0],
            self.reye_emb(x[:, :, 20+8       :20+8+16, :].view(B, L, -1), x_mask)[0],
            self.leye_emb(x[:, :, 20+8+16    :20+8+16+16, :].view(B, L, -1), x_mask)[0],
            self.hand_emb(x[:, :, 20+8+16+16 :20+8+16+16+21, :].view(B, L, -1), x_mask)[0],
            self.graph_emb(x[:, :,20+8+16+16:20+8+16+16+21, :].permute(0, 3, 1, 2).unsqueeze(-1)),
            self.pos_embed[:L, :].unsqueeze(0).repeat(B,1,1)],axis=-1)

以下是我进行集成的示例。

随后我开始加入不同的数据增强方法(这里我本应投入更多时间来进一步提升分数),我获得的最大提升来自:

  • Mixup
if np.random.random() < self.args.mixup:
                    indices = torch.randperm(batch["xyz"].size(0), device=batch["xyz"].device, dtype=torch.long)
                    beta = np.random.beta(0.2, 0.2)
                    batch["xyz"] = beta * batch["xyz"] + (1 - beta) * batch["xyz"][indices]
                    batch = self.run_logits(batch)
                    batch["loss"] = beta * self.args.loss(logits=batch["logits"], labels=batch["labels"]) + (
                                1 - beta) * self.args.loss(logits=batch["logits"],
                                                           labels=batch["labels"][indices])
  • Copy paste
if np.random.random() < self.args.cutout:
                indices_to_shuffle = torch.randperm(batch["xyz"].size(0), device=batch["xyz"].device, dtype=torch.long)
                cutout_len = self.args.cutout_len
                if type(cutout_len) == list:
                    cutout_len = random.uniform(*cutout_len)
                sz = int(batch["xyz"].size(1) * cutout_len)
                ind = np.random.randint(0, batch["xyz"].size(1) - sz)
                indices_to_cut = torch.range(ind, ind+sz, device=batch["xyz"].device, dtype=torch.long)
                beta = 1 - cutout_len
                batch["xyz"][:, indices_to_cut] = batch["xyz"][indices_to_shuffle][:,indices_to_cut]
                batch["mask"][:, indices_to_cut] = batch["mask"][indices_to_shuffle][:,indices_to_cut]

                batch = self.run_logits(batch)
                batch["loss"] = beta * self.args.loss(logits=batch["logits"], labels=batch["labels"]) + (
                        1 - beta) * self.args.loss(logits=batch["logits"],
                                                   labels=batch["labels"][indices_to_shuffle])

在此之后,我得到了最终分数:验证分数约为0.735-0.74,单模型在公开排行榜上的得分为0.77,集成模型为0.79。

未生效的方法:

  • 对惯用手使用CNN,未带来明显提升。
  • 基于词嵌入的不同损失函数。
  • 有趣的想法:使用ArcFace标签(参与者ID+手势,接近5230个标签)训练模型,基于标签的验证划分使验证分数提升了约0.02,因为排行榜中也包含来自训练集的样本。我尝试将其加入集成,但未获得显著提升,因此决定放弃。

再次感谢所有在艰难时期支持乌克兰的人们。
荣耀归于乌克兰。

同比赛其他方案