654. BirdCLEF+ 2025 | birdclef-2025
首先,这是我第一次在 Kaggle 上获奖,非常感谢组织者和所有参与者。
我的解决方案是一个 SED(声音事件检测)解决方案,灵感来自 BirdCLEF 2023 第 2 名解决方案,结合了自定义的 soft AUC loss 和半监督学习。
我一直认为最好的损失函数应该是 metric 本身,所以我尝试寻找 AUC loss 并找到了以下实现:
class AUCLoss(nn.Module):
def __init__(self, margin=1.0, pos_weight=1.0, neg_weight=1.0):
super().__init__()
self.margin = margin
self.pos_weight = pos_weight
self.neg_weight = neg_weight
def forward(self, preds, labels, sample_weights=None):
pos_preds = preds[labels == 1]
neg_preds = preds[labels == 0]
if len(pos_preds) == 0 or len(neg_preds) == 0:
return torch.tensor(0.0, device=preds.device)
if sample_weights is not None:
sample_weights = torch.stack([sample_weights]*labels.shape[1], dim=1)
pos_weights = sample_weights[labels == 1] # [正样本权重]
neg_weights = sample_weights[labels == 0] # [负样本权重]
else:
pos_weights = torch.ones_like(pos_preds) * self.pos_weight
neg_weights = torch.ones_like(neg_preds) * self.neg_weight
diff = pos_preds.unsqueeze(1) - neg_preds.unsqueeze(0) # [正样本数,负样本数]
loss_matrix = torch.log(1 + torch.exp(-diff * self.margin)) # [正样本数,负样本数]
weighted_loss = loss_matrix * pos_weights.unsqueeze(1) * neg_weights.unsqueeze(0)
return weighted_loss.mean()
这个 AUC loss 似乎非常抗过拟合。在所有实验中,使用交叉熵损失训练的模型的 CV 分数显著优于使用 soft AUC loss 训练的模型,但 LB 分数却显著较差。
这个 AUC loss 存在一个问题:它不支持像交叉熵损失那样的软标签。对于知识蒸馏和半监督学习,我需要一个支持软标签的损失函数,所以我对上述 AUC loss 做了一些修改:
class SoftAUCLoss(nn.Module):
def __init__(self, margin=1.0, pos_weight=1.0, neg_weight=1.0):
super().__init__()
self.margin = margin
self.pos_weight = pos_weight
self.neg_weight = neg_weight
def forward(self, preds, labels, sample_weights=None):
pos_preds = preds[labels>0.5]
neg_preds = preds[labels<0.5]
pos_labels = labels[labels>0.5]
neg_labels = labels[labels<0.5]
if len(pos_preds) == 0 or len(neg_preds) == 0:
return torch.tensor(0.0, device=preds.device)
pos_weights = torch.ones_like(pos_preds) * self.pos_weight * (pos_labels-0.5)
neg_weights = torch.ones_like(neg_preds) * self.neg_weight * (0.5-neg_labels)
if sample_weights is not None:
sample_weights = torch.stack([sample_weights]*labels.shape[1], dim=1)
pos_weights = pos_weights * sample_weights
neg_weights = neg_weights * sample_weights
diff = pos_preds.unsqueeze(1) - neg_preds.unsqueeze(0) # [正样本数,负样本数]
loss_matrix = torch.log(1 + torch.exp(-diff * self.margin)) # [正样本数,负样本数]
weighted_loss = loss_matrix * pos_weights.unsqueeze(1) * neg_weights.unsqueeze(0)
return weighted_loss.mean()
这个 soft AUC loss 加上半监督学习,将我单个 tf_efficientnetv2_b0 模型的 LB 分数从 0.850 提高到了 0.901。更重要的是,我在私有 LB 上从第 11 名提升到第 4 名,可能归功于使用了这个损失函数。
16 个模型,包括 efficientnet_lite0-4, efficientnet_b2-3, efficientnetv2_b2-3 和 efficientnetv2_s。训练 17-25 个 epoch,学习率 5e-4。使用了 3 种 mel 频谱图参数和 2 种数据增强。数据选取了前 10 秒数据和随机 10 秒数据。