【本期推荐专题】物联网从业人员必读:华为云专家为你详细解读LiteOS各模块开发及其实现原理。html
摘要:Focal Loss的两个性质算是核心,其实就是用一个合适的函数去度量难分类和易分类样本对总的损失的贡献。
本文分享自华为云社区《技术干货 | 基于MindSpore更好的理解Focal Loss》,原文做者:chengxiaoli。git
今天更新一下恺明大神的Focal Loss,它是 Kaiming 大神团队在他们的论文Focal Loss for Dense Object Detection提出来的损失函数,利用它改善了图像物体检测的效果。ICCV2017RBG和Kaiming大神的新做(https://arxiv.org/pdf/1708.02...算法
最近一直在作人脸表情相关的方向,这个领域的 DataSet 数量不大,并且每每存在正负样本不均衡的问题。通常来讲,解决正负样本数量不均衡问题有两个途径:segmentfault
1. 设计采样策略,通常都是对数量少的样本进行重采样网络
2. 设计 Loss,通常都是对不一样类别样本进行权重赋值app
本文讲的是第二种策略中的 Focal Loss。函数
咱们知道object detection按其流程来讲,通常分为两大类。一类是two stage detector(如很是经典的Faster R-CNN,RFCN这样须要region proposal的检测算法),第二类则是one stage detector(如SSD、YOLO系列这样不须要region proposal,直接回归的检测算法)。学习
对于第一类算法能够达到很高的准确率,可是速度较慢。虽然能够经过减小proposal的数量或下降输入图像的分辨率等方式达到提速,可是速度并无质的提高。优化
对于第二类算法速度很快,可是准确率不如第一类。spa
因此目标就是: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实现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的两个性质算是核心,其实就是用一个合适的函数去度量难分类和易分类样本对总的损失的贡献。