感谢组织者举办这次比赛,同时也感谢往年许多 Kaggle 参赛者,他们的解决方案给了我很大的启发。
BirdCLEF 是一项具有挑战性的比赛,虽然数据集分布偏移(dataset shift)不是特别大,但缺乏来自相同分布的验证集使得训练和评估相当困难。
我在比赛期间采用了以下方法:
我使用了整个 train_audio 数据集,并直接在排行榜上进行验证。我发现以下模型架构表现最好:
(SED + CE loss) > (SED + FocalBCE) > (CNN + FocalBCE) > (CNN + CE)
这只是一个初步实验,因此可能受到其他因素的影响,并不具有普遍适用性。但由于在线提交机会有限,我在其余实验中坚持使用了 SED + CE 损失。
在此阶段训练的模型被称为 stage1 模型。
正如在以前的比赛中所见,利用未标记数据 (train_soundscapes) 可以带来显著的增益。
我使用了以下方法:
遵循社区讨论,我移除了包含明显人声的样本 —— 这个清洗后的数据集称为 train_audio_clean。
为了进一步提高多样性并使数据更干净,我使用训练好的模型移除了显然不包含目标物种的片段,以 5 秒为间隔采样,形成一个新的数据集:train_audio_clean_v2。
这两个数据集都用于训练。
我使用了具有以下分辨率的梅尔频谱图:
fmin = 0, fmax = 16000, n_fft=1536/2048
我采用了一种修改后的混合损失函数与 CE (交叉熵)。
虽然公共 Notebook 显示 ConvNeXt 表现良好,但使用标准 CE 损失会导致很多噪声预测(许多假阳性/误报)。
因此,我逐渐 incorporated 了以下损失以更好地稳定训练:
threshold = -5
neg_mask = (y == 0.0)
negative_logits = logits[neg_mask]
penalty = F.relu(negative_logits - threshold)
sorted_penalty, _ = torch.sort(penalty)
cutoff_index = int(len(sorted_penalty) * 0.95)
selected_penalty = sorted_penalty[:cutoff_index]
mean_penalty = selected_penalty.mean()
loss = ce_loss + 0.1 * mean_penalty
在某些模型中,我还对置信度过低的正样本进行了惩罚。
def get_mean_scales(ref_freq):
alpha_max = 0.3
alpha_min = 0.1
# 稀有类别更多召回
alpha = alpha_min + ref_freq * (alpha_max - alpha_min)
return alpha
...
for c in len(n_classes):
a = alpha[c]
pred_prob[:, c] = pred_prob[:, c] * (1-a) + pred_prob[:, c].mean(keepdims=True) * a
多样性通过改变所使用的数据集以及混合进的 pp_clean_data 的比例和质量来控制。
我最终总共集成了 10 个模型,公共榜:0.915 私有榜:0.921。
然而,由于排行榜 (LB) 的不稳定性,我未能选择最佳的模型组合。