返回列表

[28th Place Solution] Pre Training on Supplemental Data gives 0.01 LB Improvement

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

开始: 2023-05-10 结束: 2023-08-24 计算机视觉 数据算法赛
第28名解决方案:在补充数据上预训练带来0.01 LB提升

第28名解决方案:在补充数据上预训练带来0.01 LB提升

作者:HW
团队成员:未来、Zane、HW
竞赛排名:第28名
发布日期:2023-08-27

致谢

首先,我要向我的队友致以最诚挚的感谢:

他们的巨大努力对这次竞赛的成功至关重要。

同时,我也感谢以下笔记本的作者。没有这些 notebook 中的见解和技术,我们的成功是不可能实现的。非常感谢你们。

要点概览

我们的解决方案基于CTC开源 notebook。以下是我们引入的关键改进(所有分数均为公开LB):

  • 在生成tfrecord文件进行数据预处理时,仅过滤完全NaN的帧。(0.687→0.705)
  • 按照越深越好的方法增加模型块数。(0.705→0.716)
  • 将帧长度从128增加到256。(0.716→0.746)
  • 通过添加更多面部和姿态索引,将特征选择从96个增加到128个。(0.746→0.763)
  • 引入带标签平滑的CTC损失进行正则化。(0.763→0.766)
  • 在补充数据集上预训练模型。先在补充集上预训练80轮,再在主数据集上训练80轮。(0.766→0.777)
  • 采用上一届竞赛的运动特征技术。(0.777→0.781)
  • 将主数据集上的训练轮数从80增加到120 (0.781→0.782)

数据预处理

参考 @irohith 的预处理 notebook,我们通过添加更多面部和姿态索引进行了改进。我们根据方差选择新的索引——方差越高表示信息越多。

最初的索引为:

LIP = [
    61, 185, 40, 39, 37, 0, 267, 269, 270, 409,
    291, 146, 91, 181, 84, 17, 314, 405, 321, 375,
    78, 191, 80, 81, 82, 13, 312, 311, 310, 415,
    95, 88, 178, 87, 14, 317, 402, 318, 324, 308,
]
LPOSE = [13, 15, 17, 19, 21]
RPOSE = [14, 16, 18, 20, 22]
POSE = LPOSE + RPOSE

我们将其更新为:

LIP = [
    61, 185, 40, 39, 37, 0, 267, 269, 270, 409,
    291, 146, 91, 181, 84, 17, 314, 405, 321, 375,
    78, 191, 80, 81, 82, 13, 312, 311, 310, 415,
    95, 88, 178, 87, 14, 317, 402, 318, 324, 308,
]

# 新的面部ID索引
face_id = [454,356,323,361,389,288,251,264,447,366,368,
           401,397,435,284,301,372,345,383,367,365,352,433,
           376,298,265,93,234,300,132,340,353,127]

# 如果新索引不存在于LIP中,则追加
for k in face_id:
    if k not in LIP:
        LIP.append(k)

# 修剪LIP列表
l = len(LIP)
LIP  = LIP[:int(l - l/4)]

# 更新后的POSE索引
LPOSE = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31]
RPOSE = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]
POSE = LPOSE + RPOSE

过滤完全NaN的帧

原始 notebook:

hand = tf.concat([rhand, lhand], axis=1)
hand = tf.where(tf.math.is_nan(hand), 0.0, hand)
mask = tf.math.not_equal(tf.reduce_sum(hand, axis=[1, 2]), 0.0)

更新为:

hand = tf.concat([rhand, lhand,lip,rpose,lpose], axis=1)
hand = tf.where(tf.math.is_nan(hand), 0.0, hand)
mask = tf.math.not_equal(tf.reduce_sum(hand, axis=[1, 2]), 0.0)

这些新索引加上256帧长度,给我们带来了约0.02 LB的提升!

运动特征

详见 1st Place Solution Training

CTC损失与标签平滑

为了提高模型训练的稳定性,我们实现了带正则化组件的CTC损失。通过将CTC损失与Kullback-Leibler (KL) 散度结合,我们引入了标签平滑。平滑权重=0.7对我们的模型效果最佳。

def smooth_ctc_loss(labels, logits, num_classes = 60 , blank=0, weight=0.7):
    # 计算CTC损失
    label_length = tf.reduce_sum(tf.cast(labels != pad_token_idx, tf.int32), axis=-1)
    logit_length = tf.ones(tf.shape(logits)[0], dtype=tf.int32) * tf.shape(logits)[1]
    ctc_loss = tf.nn.ctc_loss(
        labels=labels,
        logits=logits,
        label_length=label_length,
        logit_length=logit_length,
        blank_index=pad_token_idx,
        logits_time_major=False
    )
    ctc_loss = tf.reduce_mean(ctc_loss)

    # 计算KL散度损失
    kl_inp = tf.nn.softmax(logits)

    # 创建目标分布
    kl_tar = tf.fill(tf.shape(logits), 1. / num_classes)

    # 计算KL散度
    kldiv_loss = (tf.keras.losses.KLDivergence(tf.keras.losses.Reduction.NONE)(kl_tar, kl_inp) 
                 + tf.keras.losses.KLDivergence(tf.keras.losses.Reduction.NONE)(kl_inp, kl_tar))/2.0

    kldiv_loss = tf.reduce_mean(kldiv_loss)

    # 组合损失
    loss = (1. - weight) * ctc_loss + weight * kldiv_loss
    return loss

在补充数据集上预训练模型

在实验过程中,我们发现了一个重要洞察:仅使用补充数据集训练获得了0.367的LB分数。这凸显了补充数据中蕴含的巨大价值。

我们决定先在补充数据集上预训练模型,然后加载这些预训练权重来在主数据集上进一步训练。我们测试了40、60和80个预训练轮数。80轮效果最佳,为LB分数带来了0.013的提升。

我们尝试过但未奏效的方法

  1. 数据增强

    • 随机仿射变换
    • 随机部分移除:我们尝试以5%的概率通过将某些部分(如嘴唇、左姿态、右姿态、左手、右手)设置为NaN来丢弃它们。
    • 短语特征切换:在同一类别内切换短语的特定特征。

    在第二名的解决方案中,@hoyso48提到某些增强在短期训练中并未改善模型,但在更长时间的训练中会产生效果。这可能也适用于我们的情况,但我们尚未验证。

  2. 切换到GIC-CTC

我们可以改进的地方

  1. 更长时间的训练。我们最终提交只训练了120轮,远少于其他顶级解决方案(通常300-500轮)。我们在最后一天看到从80轮增加到120轮带来了0.001 LB提升,但为时已晚。
  2. 更仔细地调整学习率和权重衰减。我们只是使用了公开 notebook 中的余弦衰减学习率调度器,并将权重衰减设为0.05。
  3. 在更长时间的训练中使用AWP(对抗性权重扰动)。

训练 notebook 专为Google Colab设计。首先使用gcs-path notebook检查训练 notebook 中的路径。先将USE_SUPPLY设为True训练补充集,然后将USE_VAL设为True加载预训练权重并训练主数据集。

要在Kaggle TPU上使用,请将文件地址更改为对应的Kaggle输入地址,并且需要修改CTC损失函数。参考 ASLFR CTC on TPU

同比赛其他方案