返回列表

1st place solution

612. BirdCLEF 2024 | birdclef-2024

开始: 2024-04-03 结束: 2024-06-10 生命科学 数据算法赛
第一名解决方案 - BirdCLEF 2024

第一名解决方案

作者: Kirill Chemrov & Arseny Poyda

发布日期: 2024-06-13

比赛: BirdCLEF 2024

由我和 @arsenypoyda 撰写。

关于幸福的简要介绍!

首先,我们要感谢 BirdCLEF 主办方和 Kaggle 团队举办这次比赛。两天前,我们得知获得了第一名,这真是难以置信。当然,这只是运气,因为我们与第 2 或第 3 名的差距微不足道,尽管如此。此外,在那一天,我们不仅成为了 Kaggle 大师,还获得了大学的硕士学位(双硕士 🙂)。

概述

数据/标签预处理

  • BirdCLEF 2024 train_audio
  • 伪标签 unlabeled_soundscapes

对于最终提交,我们仅使用 2024 年的数据,包括 train_audiounlabeled_soundscapes
在比赛刚开始时,我们发现随机 fold0 的结果比其他 fold 好得多。为了找出原因,我们计算了各种与信号强度相关的统计量(见下图),发现 fold0 的统计量低于其他 fold。
Folds Statistics
因此对于集成,我们不使用 fold0-4,而是使用 fold0 和统计量 T = std + var + rms + pwr 的 0.8 分位数,这效果很好。似乎嘈杂和过大的音频会损害模型。

训练数据中有一些重复的音频文件,所以我们丢弃了它们。train_audio 中的数据还通过 Google-bird-vocalization-classifier 进行了过滤:如果分类器的最大预测与主要标签不匹配,则该片段被丢弃(可能没有鸟叫声或质量很差)。如果分类器的最大预测与次要标签匹配,我们将主要标签替换为次要标签。此外,如果文件有次要标签,我们将主要标签设为 0.5,剩余的 0.5 均匀分配给次要标签。我们还以 0.05 的系数将通过 Google 分类器获得的伪标签添加到结果标签中。音景使用 Google 分类器的集成进行标记,而我们最好的模型仅使用 train_audio 训练。最后,如果声音太短,我们使用循环填充。

模型输入

模型在 10 秒的片段上进行训练,这些片段由两个相邻的 5 秒片段组成,标签取平均。其想法是,10 秒的片段可以处理完整的 chirp 或 chirp 的完整周期(如果它们被 5 秒片段裁剪)。

Mel 参数 (10 秒 -> 1x128x640):
  • n_fft = 1024
  • hop_length = 500
  • n_mels = 128
  • fmin = 40
  • fmax = 15000
  • power = 2

模型

  • efficientnet_b0 在 ImageNet 上预训练
  • regnety_008 在 ImageNet 上预训练
我们尝试过的其他模型:
  • seresnext 给出了与 efficientnet 相同的结果,但推理时间几乎长了 3 倍。
  • 我们看到 第 3 名 (NVBird) 使用 efficientvit 是因为其高速度。在我们的案例中,ViT 的表现明显较差。
  • 像 BirdCLEF 2021 第 2 名解决方案 中的 CNN 修改版和 SED 运行较慢,且提供的结果不如纯骨干网络。我们确信过于复杂的模型并不比简单的模型更好。

我们没有尝试更大的模型,因为我们没有计算资源,只使用 Kaggle kernels。

训练!

参数:
  • CrossEntropyLoss
  • AdamW
  • CosineAnnealingLR 调度器
  • 初始学习率 1e-3...3e-3
  • 7–12 个 epoch
  • Batch size = 96
    在 Kaggle kernel P100 上训练一个模型最多需要 2 小时。
数据增强:
  • 随机音频片段
  • XY 掩码
  • 水平 cutmix

首先,我们只使用 CE loss,而不是 BCE(BCE 显示的结果明显比 CE 差)。这可能与训练数据的特异性有关。标签太多(182 个),而且几乎总是只存在其中一个(增强后最多 2-3 个)。因此,问题简化为多分类问题。在训练期间,CE loss 导致多分类问题,logits 通过 SoftMax 传递。然而,在推理中,我们不使用 softmax,而是通过 sigmoid 传递 logits(后处理将在下一节详细讨论)。

其次,我们使用音频分割。在每个 epoch 期间,模型只看到每个文件的一个随机片段。问题是 train_audio 有许多小文件(+ 一些真正的大文件),而 unlabeled_soundscapes 只有少数大文件。因此,我们将音频划分为 X 秒的片段,被视为单独的文件。我们尝试了 X = {20, 30, 60},这导致了不同数量的 epoch:X 越小,epoch 数量越少(因为每个 epoch 的步数更大)。结果,我们平衡了 train_audiounlabeled_soundscapes

后处理

为了预测片段 n,模型取 10 秒:5 秒来自片段 n,2.5 秒来自前一个和后一个片段。
10sec chunk

尽管在训练期间 logits 通过 softmax 传递,但我们在推理中使用 sigmoid。推理管道中最重要的两件事是 片段平均使用 min() reductions 进行集成。在下图中,我们展示了片段 n 中每个类的最佳管道。
Pipeline Diagram

对 CE 训练的模型使用 sigmoid 会导致预测嘈杂,因此 min() 只是降低了不确定的预测。
Min Mean Comparison

推理时间优化

  • 使用 OpenVINO 编译(固定模型输入)
  • 使用 joblib 并行计算 mel spectrograms
  • 将所有计算出的 mel spectrograms 存储在 RAM 中
    结果,一个模型在 Kaggle CPU kernel 上处理整个测试数据需要约 18 分钟。考虑到创建 mel spectrograms,6 个模型的集成需要约 2 小时。

无效的方法

其他所有方法…

分数变化微不足道

  • 183 "nocall" 类
    我们将外部数据中的 nocall 样本添加到训练中,并使用额外的 183 类。对于推理,我们只取 182 个鸟叫类。公共分数没有改善。现在,我们观察到私有分数显著增加(0.655 -> 0.671)…
  • Mel spectrogram 归一化
  • 其他 Mel spectrogram 参数
  • 输入图像大小 224x224
  • 15 秒片段作为模型输入
  • Softmax 温度
  • 权重平均(SWA 和 EMA)
  • 带有 softmax 温度的伪标签
  • 在前几年的数据上预训练

分数显著下降

  • BCEloss, 带正权重的 BCEloss, focalloss
  • 其他增强(mixup, noise, pixdrop, blur, audio 1d 增强,水平翻转)
  • STFT 代替 mel spectrogram
  • 来自 Xeno-canto 的额外数据
    我们尝试了不同的方法来提高额外数据的质量,例如使用 Google 分类器或 BirdNet 过滤,取随机 fold,以及取统计量 T 的某些分位数。最好的解决方案是不使用额外数据… 私有分数也证明了这一点。
  • 用高系数对训练数据进行伪标签
    似乎对 train_audio 的伪标签是一种标签平滑的方式,所以最好使用小系数。
  • 在 10 秒片段上训练并在 5 秒片段上推理
  • 训练额外的模型来检测鸟叫
  • 此类模型的分数接近随机/常数预测
  • 多阶段训练
    我们尝试在 train_audio 上训练几个 epoch,然后在 unlabeled_soundscapes 上训练,反之亦然。我们还尝试在整个数据上训练,然后在 train_audiounlabeled_soundscapes 上微调。

成功的主要步骤

下表显示了相对于基线(我们的第 1 次提交)管道的变化,这些变化给分数带来了明显的改善。

基线

  • efficientnet_b0
  • CEloss
  • 推理时使用 softmax
  • 每个文件的前 5 秒
  • 来自 train_audio 的 fold0,无重复
主要步骤 私有分数 公共分数
基线 0.544028 0.599798
推理时使用 sigmoid 0.588338 0.628777
随机 5 秒片段 0.601803 0.638572
XY 掩码 0.601909 0.639358
水平 cutmix 0.615368 0.670460
伪标签 unlabeled_soundscapes 0.639777 0.687000
60 秒分割 0.649936 0.691752
次要标签 0.642781 0.695215
用 Google 分类器过滤 train_audio 片段 0.655190 0.703051
10 秒输入 0.670410 0.716058
集成 (平均) 5 个 efficientnet_b0 0.686169 0.724319
集成 (最小值) 5 个 efficientnet_b0 0.688977 0.734945
✅ 集成 (最小值) 6 个 efficientnet_b0 0.689146 0.738566
集成:平均 [min(3 个 efficientnet_b0), min(3 个 regnety)] 0.691749 0.733836
✅ 集成:平均 [3 个 efficientnet_b0, 3 个 regnety] 0.690391 0.729178

令人惊讶的是,结果非常稳定:公共分数和私有分数的相关性为 0.96。

推理 Notebook: https://www.kaggle.com/code/chemrovkirill/birdclef-2024-1st-place-inference

感谢阅读!

同比赛其他方案