399. Cornell Birdcall Identification | birdsong-recognition
我的解决方案大部分基于 @hidehisaarai1213 提供的基线 SED(声音事件检测)模型。如果没有他的 Kernel,我不可能取得这样的成绩。我非常感谢他。感谢他在比赛期间分享了很多内容,我学到了很多。
未使用外部数据。
我注意到默认的 SED 模型有超过 8000 万个参数,所以我将所有模型切换为使用预训练的 densenet121 模型作为 CNN 特征提取器,并将注意力块的大小减小到 1024。由于它更小,而且我们每个音频类别只有大约 100 个文件,所以不会过拟合太多。我主要尝试了 densenet,因为以前音频比赛的顶级解决方案使用了类似 densenet 的架构。我还按照 SED notebook 评论中的建议,将注意力机制中的 clamp 替换为 tanh。
我的损失函数如下所示。我想尝试不同的参数,但最后我主要使用了默认值,也就是普通的 BCELoss。我对 2 个非 mixup 模型使用了不同的损失函数,它是基于从损失函数中随机移除主要标签预测,试图增加 secondary_label 的预测,但由于时间和资源不足,我放弃了其余模型的这种方法。
class SedScaledPosNegFocalLoss(nn.Module):
def __init__(self, gamma=0.0, alpha_1=1.0, alpha_0=1.0, secondary_factor=1.0):
super().__init__()
self.loss_fn = nn.BCELoss(reduction='none')
self.secondary_factor = secondary_factor
self.gamma = gamma
self.alpha_1 = alpha_1
self.alpha_0 = alpha_0
self.loss_keys = ["bce_loss", "F_loss", "FScaled_loss", "F_loss_0", "F_loss_1"]
def forward(self, y_pred, y_target):
y_true = y_target["all_labels"]
y_sec_true = y_target["secondary_labels"]
bs, s, o = y_true.shape
# Sigmoid 已在模型中应用
y_pred = torch.clamp(y_pred, min=EPSILON_FP16, max=1.0-EPSILON_FP16)
y_pred = y_pred.reshape(bs*s,o)
y_true = y_true.reshape(bs*s,o)
y_sec_true = y_sec_true.reshape(bs*s,o)
with torch.no_grad():
y_all_ones_mask = torch.ones_like(y_true, requires_grad=False)
y_all_zeros_mask = torch.zeros_like(y_true, requires_grad=False)
y_all_mask = torch.where(y_true > 0.0, y_all_ones_mask, y_all_zeros_mask)
y_ones_mask = torch.ones_like(y_sec_true, requires_grad=False)
y_zeros_mask = torch.ones_like(y_sec_true, requires_grad=False) *self.secondary_factor
y_secondary_mask = torch.where(y_sec_true > 0.0, y_zeros_mask, y_ones_mask)
bce_loss = self.loss_fn(y_pred, y_true)
pt = torch.exp(-bce_loss)
F_loss_0 = (self.alpha_0*(1-y_all_mask)) * (1-pt)**self.gamma * bce_loss
F_loss_1 = (self.alpha_1*y_all_mask) * (1-pt)**self.gamma * bce_loss
F_loss = F_loss_0 + F_loss_1
FScaled_loss = y_secondary_mask*F