返回列表

1st place solution: Correct Data is All You Need

553. BirdCLEF 2023 | birdclef-2023

开始: 2023-03-07 结束: 2023-05-24 环境监测 数据算法赛
2023 BirdCLEF 第一名解决方案:正确数据就是一切
作者:Volodymyr (Kaggle Grandmaster)
竞赛排名:第1名
发布时间:2023年5月25日

大家好,Kagglers!

让我们开始探索鸟类音频数据与建模的奇妙世界,但在开始前,有几句非常重要的话要说:

我要感谢乌克兰武装部队、乌克兰安全局、乌克兰国防情报局和乌克兰国家紧急服务局,感谢他们提供安全保障,使我能够参与这场精彩的竞赛,完成这项工作,并帮助科学、技术和商业继续前进。

如果您只知道A100 GPU的强大威力

我成功运行了294个实验:其中一半使用5折交叉验证,另一半使用全数据训练。所以总体而言,我们验证了许多假设,当然,大多数都被拒绝了 :) 让我们来一探究竟。

数据,到处都是数据

让我们从2023训练数据开始

如果您查看 train_metadata["primary_label"].value_counts(),您会注意到一些奇怪的最大魔法数字:

barswa     500
wlwwar     500
thrnig1    500
eaywag1    500
comsan     500
          ... 
lotcor1      1
whctur2      1
whhsaw1      1
afpkin1      1
crefra2      1
Name: primary_label, Length: 264, dtype: int64

为什么某些物种最多只有500个代表?我不知道100%确定的答案,但我有一个强有力的假设——XC API 中存在一个bug。我无法准确回忆起代码中的具体位置,但整体问题出在数据加载流程中。它的工作方式如下:

  1. 下载元文件 - JSON文件
  2. 遍历元文件中的所有URL并下载它们

但如果您某个物种有超过500个文件——在第一阶段,您会有多个JSON文件(每个JSON元文件最多包含500个文件),这就是问题所在!在第二阶段,API只考虑每个物种的一个JSON文件,而忽略后续的文件,因此每个物种最多只能获得500个文件。

注意:我不确定最新版本中是否已修复此问题,但我使用的是去年的提交版本,当时确实存在这个问题。

从这个bug我们可以清楚地看到,使用修复后的API可以极大地丰富我们的训练数据集。

其他更常规的内容

数据准备

训练数据

为了使验证更加稳健:

  • 将只有一个代表的物种样本分成两个部分。这样做是为了确保在每个CV折中,训练集和验证集都至少包含每个物种的一个样本
  • 手动删除一些重复项
  • 根据以下规则删除重复项:两个样本具有相同的:持续时间、作者、主要标签

额外训练数据

从2023/2022/2021/2020竞赛数据加上Xeno-Canto数据中,我只选择了今年主要标签的文件,并将它们添加到最终训练阶段。

预训练数据集

当我在最终训练阶段只使用2023年训练数据时,在2022/2021/2020竞赛数据上进行预训练极大地提升了分数。但在添加额外训练数据后,预训练在排行榜上不再奏效(尽管它仍然提高了本地验证分数)。在最后一周,我决定重新进行预训练实验。这使我在公共排行榜上上升了一位,在私人排行榜上上升了两位——所以,Kagglers们,别忘了重新审视那些被拒绝的假设 :)

为什么以及何时它开始有效?与之前的预训练实验相比,我做了以下改进:

  • 不仅按ID过滤2023训练数据重复项,还按"作者 + 主要标签"过滤,正如这里所建议的那样
  • 选择出现在2023/2022/2021/2020竞赛数据以及2020年额外竞赛数据中的物种,且仅当该物种有10个以上代表时。总共822个物种
  • 从Xeno Canto为选定的物种添加了额外的文件

Zenodo

我选择了nocall区域,并将它们用作背景增强。

没有奏效的数据实验

  • 在所有Xeno Canto数据上进行大规模预训练
  • 2021年第二名的背景噪声作为背景增强
  • ESC50作为背景增强
  • 从额外数据中仅选择高质量样本(>=32kHz)
  • 可能还有200多个实验中我已经忘记的其他想法

验证:要像cmAP一样柔和,不要像F1一样生硬

终于!我们不必在完全不同的训练数据上选择阈值,不必想出超级复杂的方案,也不必跌落19个名次(就像我在2021年那样)

我使用了与往年竞赛几乎相同的验证方案:

  • 5折分层交叉验证
  • 从每个5秒片段中取所有样本时间上的最大概率

重要提示:对于Padded cmAP,取各折的平均值非常重要,而不是做Out Of Fold!!!

当然,CV和LB的绝对数值是不同的:

  • 最佳公共LB:0.84444(四个4 :))
  • 最佳私人LB:0.76392
  • 最佳CV:0.9083368282233681

但排名相关性相当好。CV提升0.0x(及以上)会导致LB提升。我几乎所有实验都有CV结果,所以希望有时间发表一篇详细的消融研究和CV-LB相关性研究的论文。

训练

我看了@philippsinger演讲,才明白自己一直在严重过拟合。

由于时间和设备的限制,我选择了以下方案:

  1. 在CV上验证假设,并提交前2-3折
  2. 为了集成,在全训练数据上重新训练,这样每个设置都有一个模型

训练细节:

  • 50个epoch
  • Adam优化器
  • 余弦退火:从1e-4(或1e-3)到1e-6
  • Focal loss
  • 64批次大小
  • 5秒片段
  • 超级重要:类别采样权重
    sample_weights = (
        all_primary_labels.value_counts() / 
        all_primary_labels.value_counts().sum()
    )  ** (-0.5)
  • 预训练和微调使用相同的设置

阶段:

  1. 预训练 - 参考"预训练数据集"
  2. 仅对计分物种进行微调

模型

由于计算限制,我们无法使用深度学习的金科玉律:堆叠更多层!

所以我研究了一些推理优化技术:

  • ONNX - 这对我非常有效。它略微改善了推理时间,并减少了推理笔记本中自定义依赖项的数量
  • 量化 - 我花了一周多时间实验,但不幸没有成功 :(
  • openvino - 我没有使用或尝试这个,我只是读了第二名方案中的相关内容,然后坐立不安

总的来说,我的最终提交是由3个声音事件检测(SED)模型集成的,其骨干网络如下:

  • eca_nfnet_l0(2阶段训练;起始学习率1e-3)
  • convnext_small_fb_in22k_ft_in1k_384(2阶段训练;起始学习率1e-4)
  • convnextv2_tiny_fcmae_ft_in22k_in1k_384(1阶段训练;起始学习率1e-4)

为不同架构调整起始学习率非常重要!!!

增强

我对增强的选择非常挑剔,所以我的最终模型使用了以下增强:

  • Mixup:简单的OR Mixup,概率为0.5
  • 使用Zenodo nocall的背景噪声
  • RandomFiltering - 自定义增强:简单来说,就是一个简化的随机均衡器
  • Spec Aug:
    • 频率:
      • 最大长度:10
      • 最大线条数:3
      • 概率:0.3
    • 时间:
      • 最大长度:20
      • 最大线条数:3
      • 概率:0.3

小的推理技巧

  • 使用温度平均:pred = (pred**2).mean(axis=0) ** 0.5
  • 使用Attention SED概率 × 0.75 + Max Timewise概率 × 0.25

所有这些只带来了边际改进,但正是前三名的关键 :)

其他产生了碳足迹但没有改善我LB分数的内容

这个部分将远不完整,但让我添加一些我现在想到的内容:

  • 2021年第二名模型。我尝试过(就像在2022年一样),但不幸对我没有奏效
  • 在全部Xeno Canto上预训练
  • 在更大的片段上训练。如果在更小的片段上推理或在相同长度片段上推理,结果相同
  • 彩色噪声增强
  • CQT或LEAF
  • 特定微调:更小的学习率,更少的epoch,冻结骨干网络,骨干和头部不同的学习率
  • 在Attention SED概率 + Max Timewise概率上的损失
  • 深度监督
  • MixUp不同的alpha值
  • Transformer架构。例如ECAPA TDNN

结束语

希望您读到这里时还没有睡着。最后,我要感谢整个Kaggle社区,祝贺所有参与者和获胜者。特别感谢康奈尔鸟类学实验室、LifeCLEF、谷歌研究、Xeno-canto、@stefankahl@tomdenton@holgerklinck。你们所有人都在讨论中非常活跃,分享了数据集和有趣的材料,回答了所有问题,当然,还准备了如此酷的竞赛!

同比赛其他方案