关于函数:技术干货-基于MindSpore更好的理解Focal-Loss

27次阅读

共计 5748 个字符,预计需要花费 15 分钟才能阅读完成。

摘要:Focal Loss,是 Kaiming 大神团队在他们的论文 Focal Loss for Dense Object Detection 提出来的损失函数,利用它改善了图像物体检测的成果。

本文分享自华为云社区《技术干货 | 基于 MindSpore 更好的了解 Focal Loss》,原文作者:chengxiaoli。

明天更新一下恺明大神的 Focal Loss,它是 Kaiming 大神团队在他们的论文 Focal Loss for Dense Object Detection 提出来的损失函数,利用它改善了图像物体检测的成果。ICCV2017RBG 和 Kaiming 大神的新作(https://arxiv.org/pdf/1708.02…

  • 应用场景

最近始终在做人脸表情相干的方向,这个畛域的 DataSet 数量不大,而且往往存在正负样本不平衡的问题。一般来说,解决正负样本数量不平衡问题有两个路径:

1. 设计采样策略,个别都是对数量少的样本进行重采样

2. 设计 Loss,个别都是对不同类别样本进行权重赋值

我两种策略都应用过,本文讲的是第二种策略中的 Focal Loss。

论文剖析

咱们晓得 object detection 按其流程来说,个别分为两大类。一类是 two stage detector(如十分经典的 Faster R-CNN,RFCN 这样须要 region proposal 的检测算法),第二类则是 one stage detector(如 SSD、YOLO 系列这样不须要 region proposal,间接回归的检测算法)。

对于第一类算法能够达到很高的准确率,然而速度较慢。尽管能够通过缩小 proposal 的数量或升高输出图像的分辨率等形式达到提速,然而速度并没有质的晋升。

对于第二类算法速度很快,然而准确率不如第一类。

所以指标就是:focal loss 的出发点是心愿 one-stage detector 能够 达到 two-stage detector 的准确率,同时不影响原有的速度。

So,Why?and result?

这是什么起因造成的呢?the Reason is:Class Imbalance(正负样本不均衡),样本的类别不平衡导致的。

咱们晓得在 object detection 畛域,一张图像可能生成成千上万的 candidate locations,然而其中只有很少一部分是蕴含 object 的,这就带来了类别不平衡。那么类别不平衡会带来什么结果呢?援用原文讲的两个结果:

(1) training is inefficient as most locations are easy negatives that contribute no useful learning signal;
(2) en masse, the easy negatives can overwhelm training and lead to degenerate models.

意思就是负样本数量太大(属于背景的样本),占总的 loss 的大部分,而且多是容易分类的,因而使得模型的优化方向并不是咱们所心愿的那样。这样,网络学不到有用的信息,无奈对 object 进行精确分类。其实先前也有一些算法来解决类别不平衡的问题,比方 OHEM(online hard example mining),OHEM 的次要思维能够用原文的一句话概括:In OHEM each example is scored by its loss, non-maximum suppression (nms) is then applied, and a minibatch is constructed with the highest-loss examples。OHEM 算法尽管减少了错分类样本的权重,然而 OHEM 算法疏忽了容易分类的样本。

因而针对类别不平衡问题,作者提出一种新的损失函数:Focal Loss,这个损失函数是在规范穿插熵损失根底上批改失去的。这个函数能够通过缩小易分类样本的权重,使得模型在训练时更专一于难分类的样本。为了证实 Focal Loss 的有效性,作者设计了一个 dense detector:RetinaNet,并且在训练时采纳 Focal Loss 训练。试验证实 RetinaNet 不仅能够达到 one-stage detector 的速度,也能有 two-stage detector 的准确率。

公式阐明

介绍 focal loss,在介绍 focal loss 之前,先来看看穿插熵损失,这里以二分类为例,原来的分类 loss 是各个训练样本穿插熵的间接求和,也就是各个样本的权重是一样的。公式如下:

因为是二分类,p 示意预测样本属于 1 的概率(范畴为 0 -1),y 示意 label,y 的取值为{+1,-1}。当实在 label 是 1,也就是 y = 1 时,如果某个样本 x 预测为 1 这个类的概率 p =0.6,那么损失就是 -log(0.6),留神这个损失是大于等于 0 的。如果 p =0.9,那么损失就是 -log(0.9),所以 p =0.6 的损失要大于 p =0.9 的损失,这很容易了解。这里仅仅以二分类为例,多分类分类以此类推为了不便,用 pt 代替 p,如下公式 2:。这里的 pt 就是后面 Figure1 中的横坐标。

为了示意简便,咱们用 p_t 示意样本属于 true class 的概率。所以 (1) 式能够写成:


显然后面的公式 3 尽管能够管制正负样本的权重,然而没法管制容易分类和难分类样本的权重,于是就有了 Focal Loss,这里的 γ 称作 focusing parameter,γ>=0,称为调制系数:

为什么要加上这个调制系数呢?目标是通过缩小易分类样本的权重,从而使得模型在训练时更专一于难分类的样本。

通过试验发现,绘制图看如下 Figure1,横坐标是 pt,纵坐标是 loss。CE(pt)示意规范的穿插熵公式,FL(pt)示意 focal loss 中用到的改良的穿插熵。Figure1 中 γ = 0 的蓝色曲线就是规范的穿插熵损失(loss)。

这样就既做到了解决正负样本不均衡,也做到了解决 easy 与 hard 样本不均衡的问题。

论断

作者将类别不均衡作为妨碍 one-stage 办法超过 top-performing 的 two-stage 办法的次要起因。为了解决这个问题,作者提出了 focal loss,在穿插熵外面用一个调整项,为了将学习专一于 hard examples 下面,并且升高大量的 easy negatives 的权值。是 同时解决了正负样本不均衡以及辨别简略与简单样本的问题。

咱们来看一下,基于 MindSpore 实现 Focal Loss 的代码:

import mindspore
import mindspore.common.dtype as mstype
from mindspore.common.tensor import Tensor
from mindspore.common.parameter import Parameter
from mindspore.ops import operations as P
from mindspore.ops import functional as F
from mindspore import nn

class FocalLoss(_Loss):

    def __init__(self, weight=None, gamma=2.0, reduction='mean'):
        super(FocalLoss, self).__init__(reduction=reduction)
        # 校验 gamma,这里的 γ 称作 focusing parameter,γ>=0,称为调制系数
        self.gamma = validator.check_value_type("gamma", gamma, [float])
        if weight is not None and not isinstance(weight, Tensor):
            raise TypeError("The type of weight should be Tensor, but got {}.".format(type(weight)))
        self.weight = weight
        # 用到的 mindspore 算子
        self.expand_dims = P.ExpandDims()
        self.gather_d = P.GatherD()
        self.squeeze = P.Squeeze(axis=1)
        self.tile = P.Tile()
        self.cast = P.Cast()

    def construct(self, predict, target):
        targets = target
        # 对输出进行校验
        _check_ndim(predict.ndim, targets.ndim)
        _check_channel_and_shape(targets.shape[1], predict.shape[1])
        _check_predict_channel(predict.shape[1])

        # 将 logits 和 target 的形态更改为 num_batch * num_class * num_voxels.
        if predict.ndim > 2:
            predict = predict.view(predict.shape[0], predict.shape[1], -1) # N,C,H,W => N,C,H*W
            targets = targets.view(targets.shape[0], targets.shape[1], -1) # N,1,H,W => N,1,H*W or N,C,H*W
        else:
            predict = self.expand_dims(predict, 2) # N,C => N,C,1
            targets = self.expand_dims(targets, 2) # N,1 => N,1,1 or N,C,1
 
        # 计算对数概率
        log_probability = nn.LogSoftmax(1)(predict)
        # 只保留每个 voxel 的高空真值类的对数概率值。if target.shape[1] == 1:
            log_probability = self.gather_d(log_probability, 1, self.cast(targets, mindspore.int32))
            log_probability = self.squeeze(log_probability)

        # 失去概率
        probability = F.exp(log_probability)

        if self.weight is not None:
            convert_weight = self.weight[None, :, None]  # C => 1,C,1
            convert_weight = self.tile(convert_weight, (targets.shape[0], 1, targets.shape[2])) # 1,C,1 => N,C,H*W
            if target.shape[1] == 1:
                convert_weight = self.gather_d(convert_weight, 1, self.cast(targets, mindspore.int32))  # selection of the weights  => N,1,H*W
                convert_weight = self.squeeze(convert_weight)  # N,1,H*W => N,H*W
            # 将对数概率乘以它们的权重
            probability = log_probability * convert_weight
        # 计算损失小批量
        weight = F.pows(-probability + 1.0, self.gamma)
        if target.shape[1] == 1:
            loss = (-weight * log_probability).mean(axis=1)  # N
        else:
            loss = (-weight * targets * log_probability).mean(axis=-1)  # N,C

        return self.get_loss(loss)

应用办法如下:

from mindspore.common import dtype as mstype
from mindspore import nn
from mindspore import Tensor

predict = Tensor([[0.8, 1.4], [0.5, 0.9], [1.2, 0.9]], mstype.float32)
target = Tensor([[1], [1], [0]], mstype.int32)
focalloss = nn.FocalLoss(weight=Tensor([1, 2]), gamma=2.0, reduction='mean')
output = focalloss(predict, target)
print(output)

0.33365273

Focal Loss 的两个重要性质

  1. 当一个样本被分错的时候,pt 是很小的,那么调制因子(1-Pt)靠近 1,损失不被影响;当 Pt→1,因子(1-Pt)靠近 0,那么分的比拟好的(well-classified)样本的权值就被调低了。因而调制系数就趋于 1,也就是说相比原来的 loss 是没有什么大的扭转的。当 pt 趋于 1 的时候(此时分类正确而且是易分类样本),调制系数趋于 0,也就是对于总的 loss 的奉献很小。
  2. 当 γ = 0 的时候,focal loss 就是传统的穿插熵损失,当 γ 减少的时候,调制系数也会减少。专一参数 γ 平滑地调节了易分样本调低权值的比例。γ 增大能加强调制因子的影响,试验发现 γ 取 2 最好。直觉上来说,调制因子缩小了易分样本的损失奉献,拓宽了样例接管到低损失的范畴。当 γ 肯定的时候,比方等于 2,一样 easy example(pt=0.9)的 loss 要比规范的穿插熵 loss 小 100+ 倍,当 pt=0.968 时,要小 1000+ 倍,然而对于 hard example(pt < 0.5),loss 最多小了 4 倍。这样的话 hard example 的权重绝对就晋升了很多。这样就减少了那些误分类的重要性 Focal Loss 的两个性质算是外围,其实就是用一个适合的函数去度量难分类和易分类样本对总的损失的奉献。

MindSpore 官网材料:GitHub : https://github.com/mindspore-…
Gitee:https : //gitee.com/mindspore/mindspore

长按下方二维码退出 MindSpore 我的项目

点击关注,第一工夫理解华为云陈腐技术~

正文完
 0