返回列表

1st Place Solution

399. Cornell Birdcall Identification | birdsong-recognition

开始: 2020-06-15 结束: 2020-09-15 环境监测 数据算法赛
第1名解决方案

第1名解决方案

作者:Ryan Wong
比赛:Birdcall Recognition(鸟叫声识别)

我的解决方案大部分基于 @hidehisaarai1213 提供的基线 SED(声音事件检测)模型。如果没有他的 Kernel,我不可能取得这样的成绩。我非常感谢他。感谢他在比赛期间分享了很多内容,我学到了很多。

数据增强

未使用外部数据。

  • 粉红噪声
  • 高斯噪声
  • 高斯信噪比
  • 增益(音量调整)

模型

我注意到默认的 SED 模型有超过 8000 万个参数,所以我将所有模型切换为使用预训练的 densenet121 模型作为 CNN 特征提取器,并将注意力块的大小减小到 1024。由于它更小,而且我们每个音频类别只有大约 100 个文件,所以不会过拟合太多。我主要尝试了 densenet,因为以前音频比赛的顶级解决方案使用了类似 densenet 的架构。我还按照 SED notebook 评论中的建议,将注意力机制中的 clamp 替换为 tanh。

  • 4 折模型(不使用 mixup)
  • 4 折模型(使用 mixup)
  • 5 折模型(不使用 mixup)

训练

  • 带有 warmup 的余弦退火调度器
  • Batch size 为 28
  • Mixup(用于 4 个最终模型)
  • 非 mixup 模型训练 50 个 epoch,mixup 模型训练 100 个 epoch
  • AdamW 优化器,weight_decay 为 0.01
  • 启用 SpecAugmentation
  • 训练时使用 30 秒音频片段,评估时每个音频使用 2 个 30 秒片段

损失函数

我的损失函数如下所示。我想尝试不同的参数,但最后我主要使用了默认值,也就是普通的 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