【本期举荐专题】物联网从业人员必读:华为云专家为你具体解读LiteOS各模块开发及其实现原理。

摘要:Focal Loss的两个性质算是外围,其实就是用一个适合的函数去度量难分类和易分类样本对总的损失的奉献。

本文分享自华为云社区《技术干货 | 基于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是各个训练样本穿插熵的间接求和,也就是各个样本的权重是一样的。公式如下:

通过试验发现,绘制图看如下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代码实现

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

import mindsporeimport mindspore.common.dtype as mstypefrom mindspore.common.tensor import Tensorfrom mindspore.common.parameter import Parameterfrom mindspore.ops import operations as Pfrom mindspore.ops import functional as Ffrom mindspore import nnclass 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 mstypefrom mindspore import nnfrom mindspore import Tensorpredict = 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的两个性质算是外围,其实就是用一个适合的函数去度量难分类和易分类样本对总的损失的奉献。

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