• ACL2020文章,用Dice Loss处理NLP任务的数据不均衡问题,Tensorflow实现


    文章链接:https://zhuanlan.zhihu.com/p/128066632 (本文大部分内容都摘抄自这篇文章,主要用作个人笔记。)

    论文标题:Dice Loss for Data-imbalanced NLP Tasks

    论文作者:Xiaofei Sun, Xiaoya Li, Yuxian Meng, Junjun Liang, Fei Wu, Jiwei Li

    论文链接:https://arxiv.org/pdf/1911.02855.pdf

    数据不均衡导致的两个问题

    • 训练与测试失配。占据绝大多数的负例会支配模型的训练过程,导致模型倾向于负例,而测试时使用的F1指标需要每个类都能准确预测;
    • 简单负例过多。负例占绝大多数也意味着其中包含了很多简单样本,这些简单样本对于模型学习困难样本几乎没有帮助,反而会在交叉熵的作用下推动模型遗忘对困难样本的知识。

    使用交叉熵损失存在的问题

    • 大量简单负例会在交叉熵的作用下推动模型忽视困难正例的学习,而序列标注任务往往使用F1衡量,从而在正例上预测欠佳直接导致了F1值偏低;
    • 交叉熵“平等”地看待每一个样本,无论正负,都尽力把它们推向1(正例)或0(负例)。但实际上,对分类而言,将一个样本分类为负只需要它的概率<0.5即可,完全没有必要将它推向0;

    解决方案

    • 基于已有的Dice Loss,提出一个自适应损失:DSC,在训练时推动模型更加关注困难的样本,降低简单负例的学习度,从而在整体上提高基于F1值的效果。

    效果

    • 在多个任务上实验,包括:词性标注、命名实体识别、问答和段落识别。F1值都取得了一定的提升;

    传统二分类交叉熵

    存在问题:对每个样本,CE对它们都一视同仁,不管当前样本是简单还是复杂。当简单样本有很多的时候,模型的训练就会被这些简单样本占据,使得模型难以从复杂样本中学习。

    一种简单的改进方法是,降低模型在简单样本上的学习速率,从而得到下述加权交叉熵损失:

    存在问题:对不同样本,我们可以设置不同的权重,从而控制模型在该样本上学习的程度。但是此时,权重的选择又变得比较困难。

    DSC

    • DSC是一种用于衡量两个集合之间相似度的指标:

    令A是所有模型预测为正的样本的集合,令B为所有实际上为正类的样本集合,那么DSC就可以重写为:

    其中,TP是True Positive,FN是False Negative,FP是False Negative,D是数据集,f是一个分类模型。于是,在这个意义上,DSC是和F1等价的。推导公式如下:

    然而上述表达式是离散的。为此,我们需要把上述DSC表达式转化为连续的版本,从而视为一种soft F1,对单个样本x,我们直接定义它的DSC:

    可以看到,若x是负类,那么它的DSC就为0,从而不会对训练有贡献。为了让负类也能有所贡献,我们增加一个平滑项:

    但这样一来,又需要我们根据不同的数据集手动地调整平滑项。而且,当easy-negative样本很多的时候,即便使用上述平滑项,整个模型训练过程仍然会被它们主导。基于此,我们使用一种“自调节”的DSC:

    事实上,这比较类似Focal Loss (FL),即降低已分好类的样本的学习权重:

    不过,FL即使能对简单样本降低学习权重,但是它本质上仍然是在鼓励简单样本趋向0或1,这就和DSC有了根本上的区别。因此,我们说DSC通过“平衡”简单样本和困难样本的学习过程,从而提高了最终的F1值(因为F1要求各类都有比较好的结果)。

    Dice Loss(DL)与Tversky Loss(TL)

    总结

    本文使用现有的Dice Loss,并提出了一种新型的自适应损失DSC,用于各种数据分布不平衡的NLP任务中,以缓解训练时的交叉熵与测试时的F1的失配问题。实验表明,使用该损失可以显著提高标注任务、分类任务的F1值,并且也说明了F1效果的提升与数据不平衡的程度、数据量大小有密切的关系。

    Tensorflow版本Dice Loss

    import tensorflow as tf
    tf.enable_eager_execution()
    
    def dice_loss(n_classes, logits, label, smooth=1.e-5):
        epsilon = 1.e-6
        alpha = 2.0   # 这个是dice coe的系数,见下边的解释
        y_true = tf.one_hot(label, n_classes)
        softmax_prob = tf.nn.softmax(logits)
        print("{}".format(softmax_prob.numpy()))
        y_pred = tf.clip_by_value(softmax_prob, epsilon, 1. - epsilon)
    
        y_pred_mask = tf.multiply(y_pred, y_true)
        common = tf.multiply((tf.ones_like(y_true) - y_pred_mask), y_pred_mask)
        nominator = tf.multiply(tf.multiply(common, y_true), alpha) + smooth
        denominator = common + y_true + smooth
        dice_coe = tf.divide(nominator, denominator)
        return tf.reduce_mean(tf.reduce_max(1 - dice_coe, axis=-1))
    
    n_classes = 6
    logits = tf.constant([[0.1, 0.2, 0.8, 1.2, 1.24, 2.96],
                          [0.1, 0.2, 0.8, 1.2, 1.24, 2.96]])
    for label in range(0, n_classes):
        loss = dice_loss(n_classes, logits, [label, label+1])
        print("{}".format(loss.numpy()))
    

    原论文中的公式11,这里有系数2(loss曲线,下凹的更厉害)

    原论文中的表2,这里没有系数2(loss曲线,下凹的稍平缓)

    如下图所示

  • 相关阅读:
    KTorrent 2.1
    Krusader-双面板文件治理器
    VirtualBox 1.3.4
    QEMU 0.9.0 & QEMU Accelerator 1.3.0pre10
    Sweep:音频编辑器材
    USBSink-优盘同步备份东西
    玩转 MPlayer(1)
    玩转 MPlayer(2)
    活动目录之迁移
    再学 GDI+[26]: TGPPen 画笔对齐 SetAlignment
  • 原文地址:https://www.cnblogs.com/CheeseZH/p/13554252.html
Copyright © 2020-2023  润新知