返回列表

[Private 28th/public 14th solution]-Tri's part-custom loss

446. CommonLit Readability Prize | commonlitreadabilityprize

开始: 2021-05-03 结束: 2021-08-02 智能评测 数据算法赛
[私榜第28名/公榜第14名方案] - Tri的部分自定义损失函数

[私榜第28名/公榜第14名方案] - Tri的部分自定义损失函数

作者: Minh Tri Phan (shinomoriaoshi)
比赛: CommonLit Readability Prize

首先,我要非常感谢我优秀的队友 @horsek@lftuwujie,在这次比赛中我从他们身上学到了很多。其次,感谢所有参赛者、竞赛主办方以及 Kaggle 组织了这场精彩的比赛。此外,祝贺所有位于金牌区的队伍。

在比赛开始时,我告诉 @horsek,让我们把它当作学习 NLP 的机会,因为我之前没有这方面的知识,这是我的第一次 NLP 比赛。

在这篇文章中,我想分享的不仅是我的模型,还有我在这次比赛中的旅程,从起点到像我这样的新手如何形成思路。

起点

我的旅程始于这个精彩的笔记本 (CommonLit Readability Prize - RoBERTa Torch Fit),请给他/她点赞。

然而,我没有直接 fork 它,而是阅读并重写了自己的笔记本。这帮助我进行调试,并形成了一些想法来进一步定制和优化流程。

经过这一步,我最好的单模型是 RoBERTa-large,在公榜上得分 0.468,在私榜上得分 0.469。我的推理笔记本在这里

该模型的设置如下:

  • Epochs: 8
  • Batch size: 8
  • 学习率 (LR): 2e-5
  • 每 2 步评估一次
  • 隐藏层差异化学习率
  • 模型结构: 骨干网络 --> 隐藏层的加权平均/最大池化 --> 文档长度上的注意力池化 --> 输出
  • 损失函数: RMSE
  • 优化器和调度器: AdamW, get_linear_schedule_with_warmup

自定义损失函数

在这次比赛(以及我所有的比赛)中,我学习了很多相关文献。这一步帮助我理解了比赛的概念并找到了新的思路。我得到的第一个想法是二次加权 Kappa 损失,第二个是自定义 Bradley-Terry 损失。

二次加权 Kappa (QWK) 损失

这个损失的想法来自这篇论文。其主要思想是将节选的目标值分箱到类别中,并训练模型将它们分类到这些类别中。在我看来,这一步相当于标签平滑,因为它将具有相似目标的节选归入同一个箱。QWK 损失不是必须的,你可以用交叉熵损失来实现它。这是 QWK 损失的实现:

class QuadraticWeightedKappaLoss(nn.Module):
    def __init__(self, num_cat = 7, device = 'cpu'):
        super(QuadraticWeightedKappaLoss, self).__init__()
        self.num_cat = num_cat
        cats = torch.arange(num_cat).to(device)
        self.weights = (cats.view(-1,1) - cats.view(1,-1)).pow(2) / (num_cat - 1)**2

    def _confusion_matrix(self, pred_cat, true_cat):
        confusion_matrix = torch.zeros((self.num_cat, self.num_cat)).to(pred_cat.device)
        for t, p in zip(true_cat.view(-1), pred_cat.view(-1)):
            confusion_matrix[t.long(), p.long()] += 1
        return confusion_matrix

    def forward(self, pred_cat, true_cat):
        # 混淆矩阵
        O = self._confusion_matrix(pred_cat, true_cat)

        # 统计每个类别的元素数量
        true_hist = torch.bincount(true_cat, minlength = self.num_cat)
        pred_hist = torch.bincount(pred_cat, minlength = self.num_cat)

        # 期望值
        E = torch.outer(true_hist, pred_hist)

        # 归一化
        O = O / torch.sum(O)
        E = E / torch.sum(E)

        # 加权 Kappa
        numerator = torch.sum(self.weights * O)