0 前言

Focal Loss是为了处理样本不平衡问题而提出的,经时间验证,在多种任务上,效果还是不错的。在理解Focal Loss前,需要先深刻理一下交叉熵损失,和带权重的交叉熵损失。然后我们从样本权重的角度出发,理解Focal Loss是如何分配样本权重的。Focal是动词Focus的形容词形式,那么它究竟Focus在什么地方呢?(详细的代码请看Gitee)。

1 交叉熵

1.1 交叉熵损失(Cross Entropy Loss)

有\(N\)个样本,输入一个\(C\)分类器,得到的输出为\(X\in \mathcal{R}^{N\times C}\),它共有\(C\)类;其中某个样本的输出记为\(x\in \mathcal{R}^{1\times C}\),即\(x[j]\)是\(X\)的某个行向量,那么某个交叉熵损失可以写为如下公式

\[\text{loss}\left( x,\text{class} \right)
=-\log \left( \frac{\exp \left( x\left[\text{class} \right] \right)}{\sum_j{\exp\left( x\left[ j \right] \right)}} \right)
=-x\left[\text{class} \right] +\log \left( \sum_j{\exp\left( x\left[ j \right] \right)} \right)
\tag{1-1}
\]

其中\(\text{class}\in [0,\ C)\)是这个样本的类标签,如果给出了类标签的权重向量\(W\in \mathcal{R}^{1\times C}\),那么带权重的交叉熵损失可以更改为如下公式

\[\operatorname{loss}(x, \text {class})=W[\text {class}]\left(-x[\text {class}]+\log \left(\sum_{j} \exp (x[j])\right)\right)
\tag{1-2}
\]

最终对这个\(N\)个样本的损失求和或者求平均

\[\ell = \begin{cases}
\sum_{i}^{N}{\text{loss}(x^{(i)},\ \text{class}^{(i)})}&\text{, sum}\\
\dfrac{1}{N}\sum_{i}^{N}{\text{loss}(x^{(i)},\ \text{class}^{(i)})}&\text{, mean}
\end{cases}
\tag{1-3}
\]

这个就是我们平时经常用到的交叉熵损失了。

1.2 二分类交叉熵损失(Binary Cross Entropy Loss)

上面所提到的交叉熵损失是适用于多分类(二分类及以上)的,但是它的公式看起来似乎与我们平时在书上或论文中看到的不一样,一般我们常见的交叉熵损失公式如下:

\[l = -y\log{\hat{y}}-(1-y)\log{(1-\hat{y})}
\]

这是一个典型的二分类交叉熵损失,其中\(y\in\{0,\ 1\}\)表示标签值,\(\hat{y}\in[0,\ 1]\)表示分类模型的类别1预测值。上面这个公式是一个综合的公式,它等价于:

\[l = \begin{cases}
-\log{\hat{y}_0} &y=0 \\
-\log{\hat{y}_1} &y=1
\end{cases}; \quad
\text{where}\quad \hat{y}_0+\hat{y}_1 = 1
\]

其中\(\hat{y}_0, \hat{y}_1\)是二分类模型输出的2个伪概率值

例:如果二分类模型是神经网络,且最后一层为: 2个神经元+Softmax,那么\(\hat{y}_0, \hat{y}_1\)就对应着这两个神经元的输出值。当然它也可以带上类别的权重。

同样地,有\(N\)个样本,输入一个2分类器,得到的输出为\(X\in \mathcal{R}^{N\times 2}\),再经过Softmax函数,\(\hat{Y}=\sigma(X)\in \mathcal{R}^{N\times 2}\),标签为\(Y\in \mathcal{R}^{N\times 2}\),每个样本的二分类损失记为\(l^{(i)}, i=0,1,2,\cdots,N\),最终对这个\(N\)个样本的损失求和或者求平均

\[\ell = \begin{cases}
\sum_{i}^{N}l^{(i)}&\text{, sum}\\
\dfrac{1}{N}\sum_{i}^{N}l^{(i)}&\text{, mean}
\end{cases}; \ \ \
l^{(i)} = -y^{(i)}\log{\hat{y}^{(i)}}-(1-y^{(i)})\log{(1-\hat{y}^{(i)})}
\]

注:如果一次只训练一个样本,即\(N=1\),那么上面带类别权重的损失中的权重是无效的。因为权重是相对的,某一个样本的权重大,那么必然需要有另一个样本的权重小,这样才能体现出这一批样本中某些样本的重要性。\(N=1\)时,已没有权重的概念,它是唯一的,也是最重要的。\(N=1\),或者说batch_size=1这种情况在训练视频\文章数据时,是会常出现的。由于我们显示/内存的限制,而视频/文章数据又比较大,一次只能训练一个样本,此时我们就需要注意权重的问题了。

2 Focal Loss

2.1 基本思想

一般来讲,Focal Loss(以下简称FL)[1]是为解决样本不平衡的问题,但是更准确地讲,它是为解决难分类样本(Hard Example)易分类样本(Easy Example)的不平衡问题。对于样本不平衡,其实通过上面的带权重的交叉熵损失便可以一定程度上解决这个问题,但是在实际问题中,以权重来解决样本不平衡问题的效果不够理想,此时我们应当思考,表面上我们的样本不平衡,但实质上导致效果不好的原因也许并不是简单地因为样本不平衡,而是因为样本中存在一些Hard Example,同时存在许多Easy Example,Easy Example虽然容易被分类器分辨,损失较小,但是由于其数量大,它们累积起来依然于大于Hard Example的Loss值,因此我们需要给Hard Example较大的权重,而Easy Example较小的权重

那么什么叫Hard Example,什么叫Easy Example呢?看下面的图就知道了。

图2-1 Hard Example 图2-2 Easy Example1 图2-3 Easy Example2 图2-4 Example Space

假设,我们的任务是训练一个分类器,分类出人和马,对于上面的三张图,图2-2和图2-3应该是非常容易判断出来的,但是图2-1就是不那么容易了,它即有人的特征,又有马的特征,非常容易混淆。这种样本虽然在数据集中出现的频率可能并不高,但是想要提高分类器的性能,需要着力解决这种样本分类问题。

提出Hard Example和Easy Example后,可以将样本空间划分为如图2-4所示的样本空间。其中纵轴为多数类样本(Majority Class)少数类样本(Minority Class),上面的带权重的交叉熵损失只能解决Majority Class和Minority Class的样本不平衡问题,并没有考虑Hard Example和Easy Example的问题,Focal Loss的提出就是为解决这个难易样本的分类问题。

2.2 Focal Loss解决方案

要解决难易样本的分类问题,首先就需要找出Hard Example和Easy Example。这对于神经网络来说,应该是一件比较容易的事情。如图2-6所示,这是一个5分类的网络,神经网络的最后一层输出时,加上一个Softmax或者Sigmoid就会得到输出的伪概率值,代表着模型预测的每个类别的概率,

图2-6 Easy Example Classifier Output 图2-7 Hard Example Classifier Output

图2-6中,样本标签为1,分类器输出值最大的为第1个神经元(以0开始计数),这刚好预测准确,而且其输出值2也比其它神经元的输出值要大不少,因此可以认为这是一个易分类样本(Easy Example);图2-7的样本标签是3,分类器输出值最大的为第4个神经元,并且这几个神经元的输出值都相差不大,神经网络无法准确判断这个样本的类别,所以可以认为这是一个难分类样本(Hard Example)。其实说白了,判断Easy/Hard Example的方法就是看分类网络的最后的输出值。如果网络预测准确,且其概率较大,那么这是一个Easy Example,如果网络输出的概率较小,这是一个Hard Example。下面用数学公式严谨地表达来Focal Loss的表达式。

令一个\(C\)类分类器的输出为\(\boldsymbol{y}\in \mathcal{R}^{C\times 1}\),定义函数\(f\)将输出\(\boldsymbol{y}\)转为伪概率值\(\boldsymbol{p}=f(\boldsymbol{y})\),当前样本的类标签为\(t\),记\(p_t=\boldsymbol{p}[t]\),它表示分类器预测为\(t\)类的概率值,再结合上面的交叉熵损失,定义Focal Loss为:

\[\text{FL} = -(1-p_t)\log(p_t)
\tag{2-1}
\]

这实质就是交叉熵损失前加了一个权重,只不过这个权重有点不一样的来头。为了更好地控制前面权重的大小,可以给前面的权重系数添加一个指数\(\gamma\),那么更改式(2-1):

\[\text{FL} = -(1-p_t)^\gamma\log(p_t)
\tag{2-2}
\]

其中\(\gamma\)一值取值为2就好,\(\gamma\)取值为0时与交叉熵损失等价,\(\gamma\)越大,就越抑制Easy Example的损失,相对就会越放大Hard Example的损失。同时为解决样本类别不平衡的问题,可以再给式(2-2)添加一个类别的权重\(\alpha_t\)(这个类别权重上面的交叉熵损失已经实现):

\[\text{FL} = -\alpha_t(1-p_t)^\gamma\log(p_t)
\tag{2-3}
\]

到这里,Focal Loss理论就结束了,非常简单,但是有效。

3 Focal Loss实现(Pytorch)

3.1 交叉熵损失实现(numpy)

为了更好的理解Focal Loss的实现,先理解交叉熵损失的实现,我这里用numpy简单地实现了一下交叉熵损失。

import numpy as np

def cross_entropy(output, target):
out_exp = np.exp(output)
out_cls = np.array([out_exp[i, t] for i, t in enumerate(target)])
ce = -np.log(out_cls / out_exp.sum(1))
return ce

代码中第5行,可能稍微有点难以理解,它不过是为了找出标签对应的输出值。比如第2个样本的标签值为3,那它分类器的输出应当选择第2行,第3列的值。

3.2 Focal Loss实现

下面的代码的1012行:依据输出,计算概率,再将其转为`focal_weight`;1516行,将类权重和focal_weight添加到交叉熵损失,得到最终的focal_loss;18~21行,实现meansum两种reduction方法,注意求平均不是简单的直接平均,而是加权平均

class FocalLoss(nn.Module):
def __init__(self, gamma=2, weight=None, reduction='mean'):
super(FocalLoss, self).__init__()
self.gamma = gamma
self.weight = weight
self.reduction = reduction def forward(self, output, target):
# convert output to pseudo probability
out_target = torch.stack([output[i, t] for i, t in enumerate(target)])
probs = torch.sigmoid(out_target)
focal_weight = torch.pow(1-probs, self.gamma) # add focal weight to cross entropy
ce_loss = F.cross_entropy(output, target, weight=self.weight, reduction='none')
focal_loss = focal_weight * ce_loss if self.reduction == 'mean':
focal_loss = (focal_loss/focal_weight.sum()).sum()
elif self.reduction == 'sum':
focal_loss = focal_loss.sum() return focal_loss

注:上面实现中,output的维度应当满足output.dim==2,并且其形状为(batch_size, C),且target.max()<C

总结

Focal Loss从2017年提出至今,该论文已有2000多引用,足以说明其有效性。其实从本质上讲,它也只不过是给样本重新分配权重,它相对类别权重的分配方法,只不过是将样本空间进行更为细致的划分,从图2-4很容易理解,类别权重的方法,只是将样本空间划分为蓝色线上下两个部分,而加入难易样本的划分,又可以将空间划分为左右两个部分,如此,样本空间便被划分4个部分,这样更加细致。其实借助于这个思想,我们是否可以根据不同任务的需求,更加细致划分我们的样本空间,然后再相应的分配不同的权重呢?

参考文献

[1] Lin, T.-Y., Goyal, P., Girshick, R., He, K., & Dollár, P. (2017). Focal loss for dense object detection. Paper presented at the Proceedings of the IEEE international conference on computer vision.

处理样本不平衡的LOSS—Focal Loss的更多相关文章

  1. 【深度学习】Focal Loss 与 GHM——解决样本不平衡问题

    Focal Loss 与 GHM Focal Loss Focal Loss 的提出主要是为了解决难易样本数量不平衡(注意:这有别于正负样本数量不均衡问题)问题.下面以目标检测应用场景来说明. 一些 ...

  2. Focal Loss笔记

    论文:<Focal Loss for Dense Object Detection> Focal Loss 是何恺明设计的为了解决one-stage目标检测在训练阶段前景类和背景类极度不均 ...

  3. focal loss和ohem

    公式推导:https://github.com/zimenglan-sysu-512/paper-note/blob/master/focal_loss.pdf 使用的代码:https://githu ...

  4. 焦点损失函数 Focal Loss 与 GHM

    文章来自公众号[机器学习炼丹术] 1 focal loss的概述 焦点损失函数 Focal Loss(2017年何凯明大佬的论文)被提出用于密集物体检测任务. 当然,在目标检测中,可能待检测物体有10 ...

  5. 技术干货 | 基于MindSpore更好的理解Focal Loss

    [本期推荐专题]物联网从业人员必读:华为云专家为你详细解读LiteOS各模块开发及其实现原理. 摘要:Focal Loss的两个性质算是核心,其实就是用一个合适的函数去度量难分类和易分类样本对总的损失 ...

  6. 深度学习笔记(八)Focal Loss

    论文:Focal Loss for Dense Object Detection 论文链接:https://arxiv.org/abs/1708.02002 一. 提出背景 object detect ...

  7. 论文阅读笔记四十四:RetinaNet:Focal Loss for Dense Object Detection(ICCV2017)

    论文原址:https://arxiv.org/abs/1708.02002 github代码:https://github.com/fizyr/keras-retinanet 摘要 目前,具有较高准确 ...

  8. Focal Loss理解

    1. 总述 Focal loss主要是为了解决one-stage目标检测中正负样本比例严重失衡的问题.该损失函数降低了大量简单负样本在训练中所占的权重,也可理解为一种困难样本挖掘. 2. 损失函数形式 ...

  9. Focal Loss(RetinaNet) 与 OHEM

    Focal Loss for Dense Object Detection-RetinaNet YOLO和SSD可以算one-stage算法里的佼佼者,加上R-CNN系列算法,这几种算法可以说是目标检 ...

随机推荐

  1. 模板—堆优化dijkstra

    ];]; void dijkstra(int s) { memset(dis,0x7f,sizeof(dis));ma(v); priority_queue<pair<int,int> ...

  2. BCompare 4 key SN 亲测可用

    支持BCompare 4.2.3 32位,亲测可用 w4G-in5u3SH75RoB3VZIX8htiZgw4ELilwvPcHAIQWfwfXv5n0IHDp5hv 1BM3+H1XygMtiE0- ...

  3. day3_python之函数返回值、语句形式、表达式形式

    一. 函数对象 1. 函数是第一类对象,即函数可以当作数据传递 #1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素 二.返回值 return的返回值没有类型 ...

  4. VS Code导入已存在的Vue.js工程

    打开vscode------->文件--------->打开文件夹--------->选择工程文件夹-------->确定 查看---->终端或者使用"Ctrl ...

  5. 深入python

    while循环知识: while是关键字 格式我们要怎么写:; while 条件 : 缩进    循环(代码块) 这里面有个死循环######条件如果一直为真,就形成了一个环,就成为了死循环 那我们如 ...

  6. background背景色

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. History和Location

    Location 对象属性属性 描述hash 设置或返回从井号 (#) 开始的 URL(锚).host 设置或返回主机名和当前 URL 的端口号.hostname 设置或返回当前 URL 的主机名.h ...

  8. servicemix-3.2.1 部署异常

    <jbi-task xmlns="http://java.sun.com/xml/ns/jbi/management-message" version="1.0&q ...

  9. Python--day44--navicat使用(知道怎么用就好,要用终端操作,用这个会被人鄙视)

  10. python基础十一之装饰器进阶

    函数的双下划线方法 def hahahha(): """测试函数""" print('zxc') print(hahahha.__name_ ...