654. BirdCLEF+ 2025 | birdclef-2025
我们要感谢 Kaggle、组织者、我们的队友以及所有参与者。参加这次比赛是一次很棒的经历。以下是我们解决方案的简要概述。
我们仅使用了 2025 年的数据集。
首先,我们使用 Silero VAD 从 train_audio 集中检测包含人声的音频文件。接着,使用 @zuoliao11 开发的 Streamlit 工具,我们手动聆听了这些文件并移除了包含人声的片段。

对于代表性不足的类别(n < ~30),我们手动选择了包含鸟鸣的片段。
对于清理后的文件,我们使用前 60 秒;对于其他文件,我们使用前 30 秒。为了平衡数据集,我们复制了样本少于 20 个的类别的文件。
我们使用了一个 声音事件检测 (SED) 模型。
骨干网络:
• 4x tf_efficientnetv2_s
• 3x tf_efficientnetv2_b3
• 4x tf_efficient_b3_ns
• 2x tf_efficient_b0_ns
我们分三个阶段训练模型。

• 随机 10 秒片段
• Mel 频谱图:
sample_rate: 32000
mel_bins: 192
fmin: 20
fmax: 15000
window_size: 2048
hop_size: 768
我们通过计算 log(melspec+1e-6) 将 Mel 频谱图转换为对数尺度。
• 数据增强:
• 重采样 (Resampling)
• 增益 (Gain)
• FilterAugment
• 频率掩蔽,时间掩蔽 (FrequencyMasking, TimeMasking)
• Mel 域上的 Mixup (Sumix on mel domain)
• 优化器: Adam + 带热身的余弦退火 (Cosine Annealing with warmup)
• 损失函数: FocalLoss (gamma=2)
• 训练轮数: 10
• 目标标签: 主要标签和次要标签
我们仅使用 train_audio 训练了 5 折模型。
如在数据部分所述,在聆听音频时,我们发现训练数据中存在许多鸟鸣,尽管它们未被标记。这是意料之中的,因为录音师主要关注目标物种,因此其他鸟鸣往往未被标注。
基于这一观察,我们认为比赛的核心挑战是准确分配次要标签。为了解决这个问题,我们使用自蒸馏来丰富 train_audio 中的次要标签。我们使用第一阶段训练的模型预测作为教师标签,并将其与原始标签混合。教师模型的预测可能包含了原始注释中缺失的真实次要标签。
我们重复自蒸馏 4-5 次。从第 2 轮开始,我们以迭代方式使用先前蒸馏的模型作为新教师。每次模型权重都会重新初始化。这种方法与这篇论文中提出的方法非常相似。
我们将 train_soundscapes 的数据添加到训练集中,并继续进行两次自蒸馏。我们在每个批次中以 1:1 的比例混合 train_audio 和 train_soundscapes(不分折)。我们进一步使用不同的随机种子训练了几个模型。
| 模型 | 阶段 1 | 阶段 2 (蒸馏 x2) | 蒸馏 x4 | 蒸馏 x5 | 阶段 3 (蒸馏 x1) | 蒸馏 x2 |
|---|---|---|---|---|---|---|
| tf_efficientnetv2_s | 0.839 | 0.863 | 0.880 | 0.884 | 0.915 | 0.921 |
| tf_efficientnetv2_b3 | 0.842 | N/A | 0.872 | - | N/A | 0.918 |
| tf_efficient_b3_ns | N/A | N/A | N/A | - | N/A | 0.921 |
| tf_efficient_b0_ns | 0.836 | 0.871 | 0.879 | 0.883 | 0.905 | 0.912 |
*N/A = 未提交到 LB
下图展示了不同模型的自蒸馏结果。

我们将第 3 阶段的模型分为两组,并在可能的情况下为每组分配不同的随机种子。
4x tf_efficientnetv2_s (seed= 0, 1, 2, 3)
3x tf_efficientnetv2_b3 (seed= 2, 3, 4)
4x tf_efficient_b3_ns (seed= 0, 1, 2, 3)
2x tf_efficient_b0_ns (seed= 0, 1)
4x tf_efficientnetv2_s (seed= 1, 2, 3, 4)
3x tf_efficientnetv2_b3 (seed=0, 1, 2)
4x tf_efficient_b3_ns (seed= 0, 1, 2, 3) *失误了,我们最终使用了与组 A 相同的种子。
2x tf_efficient_b0_ns (seed= 2, 3)
分数进行加权组合(类似于去年的第 4 名解决方案)。
Alpha = 0.5

我们使用邻帧平滑,窗口为 [0.1, 0.8, 0.1]。
我们的公共笔记本中分享的后处理方法提高了 LB 分数,但由于过拟合风险,我们最终决定不使用它。
• OpenVINO
• Concurrent.futures.ThreadPoolExecutor
| 设置 | 原始分数 (仅组 A) | 2.5 秒重叠 | 平滑 + 重叠 |
|---|---|---|---|
| 公共榜 | 0.919 | 0.928 | 0.928 |
| 私有榜 | 0.917 | 0.924 | 0.924 |
基于 CNN 的模型。
1D 模型。
过多的数据增强。