焦点损失函数 Focal Loss 与 GHM
文章来自公众号【机器学习炼丹术】
1 focal loss的概述
焦点损失函数 Focal Loss(2017年何凯明大佬的论文)被提出用于密集物体检测任务。
当然,在目标检测中,可能待检测物体有1000个类别,然而你想要识别出来的物体,只是其中的某一个类别,这样其实就是一个样本非常不均衡的一个分类问题。
而Focal Loss简单的说,就是解决样本数量极度不平衡的问题的。
说到样本不平衡的解决方案,相比大家是知道一个混淆矩阵的f1-score的,但是这个好像不能用在训练中当成损失。而Focal loss可以在训练中,让小数量的目标类别增加权重,让分类错误的样本增加权重。
先来看一下简单的二值交叉熵的损失:

- y’是模型给出的预测类别概率,y是真实样本。就是说,如果一个样本的真实类别是1,预测概率是0.9,那么\(-log(0.9)\)就是这个损失。
- 讲道理,一般我不喜欢用二值交叉熵做例子,用多分类交叉熵做例子会更舒服。
【然后看focal loss的改进】:

这个增加了一个\((1-y')^\gamma\)的权重值,怎么理解呢?就是如果给出的正确类别的概率越大,那么\((1-y')^\gamma\)就会越小,说明分类正确的样本的损失权重小,反之,分类错误的样本的损权重大。
【focal loss的进一步改进】:

这里增加了一个\(\alpha\),这个alpha在论文中给出的是0.25,这个就是单纯的降低正样本或者负样本的权重,来解决样本不均衡的问题。
两者结合起来,就是一个可以解决样本不平衡问题的损失focal loss。
【总结】:
- \(\alpha\)解决了样本的不平衡问题;
- \(\beta\)解决了难易样本不平衡的问题。让样本更重视难样本,忽视易样本。
- 总之,Focal loss会的关注顺序为:样本少的、难分类的;样本多的、难分类的;样本少的,易分类的;样本多的,易分类的。
2 GHM
- GHM是Gradient Harmonizing Mechanism。
这个GHM是为了解决Focal loss存在的一些问题。
【Focal Loss的弊端1】
让模型过多的关注特别难分类的样本是会有问题的。样本中有一些异常点、离群点(outliers)。所以模型为了拟合这些非常难拟合的离群点,就会存在过拟合的风险。
2.1 GHM的办法
Focal Loss是从置信度p的角度入手衰减loss的。而GHM是一定范围内置信度p的样本数量来衰减loss的。
首先定义了一个变量g,叫做梯度模长(gradient norm):

可以看出这个梯度模长,其实就是模型给出的置信度\(p^*\)与这个样本真实的标签之间的差值(距离)。g越小,说明预测越准,说明样本越容易分类。
下图中展示了g与样本数量的关系:

【从图中可以看到】
- 梯度模长接近于0的样本多,也就是易分类样本是非常多的
- 然后样本数量随着梯度模长的增加迅速减少
- 然后当梯度模长接近1的时候,样本的数量又开始增加。
GHM是这样想的,对于梯度模长小的易分类样本,我们忽视他们;但是focal loss过于关注难分类样本了。关键是难分类样本其实也有很多!,如果模型一直学习难分类样本,那么可能模型的精确度就会下降。所以GHM对于难分类样本也有一个衰减。
那么,GHM对易分类样本和难分类样本都衰减,那么真正被关注的样本,就是那些不难不易的样本。而抑制的程度,可以根据样本的数量来决定。
这里定义一个GD,梯度密度:
\]
- \(GD(g)\)是计算在梯度g位置的梯度密度;
- \(\delta(g_k,g)\)就是样本k的梯度\(g_k\)是否在\([g-\frac{\epsilon}{2},g+\frac{\epsilon}{2}]\)这个区间内。
- \(l(g)\)就是\([g-\frac{\epsilon}{2},g+\frac{\epsilon}{2}]\)这个区间的长度,也就是\(\epsilon\)
总之,\(GD(g)\)就是梯度模长在\([g-\frac{\epsilon}{2},g+\frac{\epsilon}{2}]\)内的样本总数除以\(\epsilon\).
然后把每一个样本的交叉熵损失除以他们对应的梯度密度就行了。
\]
- \(CE(p_i,p_i^*)\)表示第i个样本的交叉熵损失;
- \(GD(g_i)\)表示第i个样本的梯度密度;
2.2 论文中的GHM
论文中呢,是把梯度模长划分成了10个区域,因为置信度p是从0~1的,所以梯度密度的区域长度就是0.1,比如是0~0.1为一个区域。
下图是论文中给出的对比图:

【从图中可以得到】
- 绿色的表示交叉熵损失;
- 蓝色的是focal loss的损失,发现梯度模长小的损失衰减很有效;
- 红色是GHM的交叉熵损失,发现梯度模长在0附近和1附近存在明显的衰减。
当然可以想到的是,GHM看起来是需要整个样本的模型估计值,才能计算出梯度密度,才能进行更新。也就是说mini-batch看起来似乎不能用GHM。
在GHM原文中也提到了这个问题,如果光使用mini-batch的话,那么很可能出现不均衡的情况。
【我个人觉得的处理方法】
- 可以使用上一个epoch的梯度密度,来作为这一个epoch来使用;
- 或者一开始先使用mini-batch计算梯度密度,然后模型收敛速度下降之后,再使用第一种方式进行更新。
3 python实现
上面讲述的关键在于focal loss实现的功能:
- 分类正确的样本的损失权重小,分类错误的样本的损权重大。
- 样本过多的类别的权重较小
在CenterNet中预测中心点位置的时候,也是使用了Focal Loss,但是稍有改动。
3.1 概述

这里面和上面讲的比较类似,我们忽视脚标。
- 假设\(Y=1\),那么预测的\(\hat{Y}\)越靠近1,说明预测的约正确,然后\((1-\hat{Y})^\alpha\)就会越小,从而体现分类正确的样本的损失权重小;otherwize的情况也是这样。
- 但是这里的otherwize中多了一个\((1-Y)^\beta\),这个是用来平衡样本不均衡问题的,在后面的代码部分会提到CenterNet的热力图。就会明白这个了。
3.2 代码讲解
下面通过代码来理解:
class FocalLoss(nn.Module):
def __init__(self):
super().__init__()
self.neg_loss = _neg_loss
def forward(self, output, target, mask):
output = torch.sigmoid(output)
loss = self.neg_loss(output, target, mask)
return loss
这里面的output可以理解为是一个1通道的特征图,每一个pixel的值都是模型给出的置信度,然后通过sigmoid函数转换成0~1区间的置信度。
而target是CenterNet的热力图,这一点可能比较难理解。打个比方,一个10*10的全都是0的特征图,然后这个特征图中只有一个pixel是1,那么这个pixel的位置就是一个目标检测物体的中心点。有几个1就说明这个图中有几个要检测的目标物体。
然后,如果一个特征图上,全都是0,只有几个孤零零的1,未免显得过于稀疏了,直观上也非常的不平滑。所以CenterNet的热力图还需要对这些1为中心做一个高斯

可以看作是一种平滑:

可以看到,数字1的四周是同样的数字。这是一个以1为中心的高斯平滑。
这里我们回到上面说到的\((1-Y)^\beta\):

对于数字1来说,我们计算loss自然是用第一行来计算,但是对于1附近的其他点来说,就要考虑\((1-Y)^\beta\)了。越靠近1的点的\(Y\)越大,那么\((1-Y)^\beta\)就会越小,这样从而降低1附近的权重值。其实这里我也讲不太明白,就是根据距离1的距离降低负样本的权重值,从而可以实现样本过多的类别的权重较小。
我们回到主题,对output进行sigmoid之后,与output一起放到了neg_loss中。我们来看什么是neg_loss:
def _neg_loss(pred, gt, mask):
pos_inds = gt.eq(1).float() * mask
neg_inds = gt.lt(1).float() * mask
neg_weights = torch.pow(1 - gt, 4)
loss = 0
pos_loss = torch.log(pred) * torch.pow(1 - pred, 2) * pos_inds
neg_loss = torch.log(1 - pred) * torch.pow(pred, 2) * \
neg_weights * neg_inds
num_pos = pos_inds.float().sum()
pos_loss = pos_loss.sum()
neg_loss = neg_loss.sum()
if num_pos == 0:
loss = loss - neg_loss
else:
loss = loss - (pos_loss + neg_loss) / num_pos
return loss
先说一下,这里面的mask是根据特定任务中加上的一个小功能,就是在该任务中,一张图片中有一部分是不需要计算loss的,所以先用过mask把那个部分过滤掉。这里直接忽视mask就好了。
从neg_weights = torch.pow(1 - gt, 4)可以得知\(\beta=4\),从下面的代码中也不难推出,\(\alpha=2\),剩下的内容就都一样了。
把每一个pixel的损失都加起来,除以目标物体的数量即可。
焦点损失函数 Focal Loss 与 GHM的更多相关文章
- 【深度学习】Focal Loss 与 GHM——解决样本不平衡问题
Focal Loss 与 GHM Focal Loss Focal Loss 的提出主要是为了解决难易样本数量不平衡(注意:这有别于正负样本数量不均衡问题)问题.下面以目标检测应用场景来说明. 一些 ...
- Focal Loss 损失函数简述
Focal Loss 摘要 Focal Loss目标是解决样本类别不平衡以及样本分类难度不平衡等问题,如目标检测中大量简单的background,很少量较难的foreground样本.Focal Lo ...
- 目标检测 | RetinaNet:Focal Loss for Dense Object Detection
论文分析了one-stage网络训练存在的类别不平衡问题,提出能根据loss大小自动调节权重的focal loss,使得模型的训练更专注于困难样本.同时,基于FPN设计了RetinaNet,在精度和速 ...
- 论文阅读笔记四十四:RetinaNet:Focal Loss for Dense Object Detection(ICCV2017)
论文原址:https://arxiv.org/abs/1708.02002 github代码:https://github.com/fizyr/keras-retinanet 摘要 目前,具有较高准确 ...
- Focal Loss理解
1. 总述 Focal loss主要是为了解决one-stage目标检测中正负样本比例严重失衡的问题.该损失函数降低了大量简单负样本在训练中所占的权重,也可理解为一种困难样本挖掘. 2. 损失函数形式 ...
- 深度学习笔记(八)Focal Loss
论文:Focal Loss for Dense Object Detection 论文链接:https://arxiv.org/abs/1708.02002 一. 提出背景 object detect ...
- Focal Loss笔记
论文:<Focal Loss for Dense Object Detection> Focal Loss 是何恺明设计的为了解决one-stage目标检测在训练阶段前景类和背景类极度不均 ...
- Focal Loss for Dense Object Detection 论文阅读
何凯明大佬 ICCV 2017 best student paper 作者提出focal loss的出发点也是希望one-stage detector可以达到two-stage detector的准确 ...
- Focal loss论文解析
Focal loss是目标检测领域的一篇十分经典的论文,它通过改造损失函数提升了一阶段目标检测的性能,背后关于类别不平衡的学习的思想值得我们深入地去探索和学习.正负样本失衡不仅仅在目标检测算法中会出现 ...
随机推荐
- MYSQL 之 JDBC(五): 增删改查(三)PreparedStatement
是Statement的子接口,可以传入带占位符的sql语句,并且提供了补充占位符变量的方法. 使用Statement需要进行拼写SQL语句,很辛苦,很容易出错. 引号的问题处理很复杂,不利于维护. 可 ...
- nginx配置使用, 入门到实践
1. 本文做自己学习配置使用, 转自: https://mp.weixin.qq.com/s?__biz=Mzg2MjEwMjI1Mg%3D%3D&chksm=ce0dae4df97a275b ...
- Windows故障转移群集(WSFC)的备份和恢复
使用wbadmin进行备份和恢复将C盘数据备份到E盘查看备份的版本以及包含的items模拟群集角色被误删除进行恢复操作检查恢复的效果 WSFC群集的备份和恢复功能是使用Windows Server B ...
- unity-热更-InjectFix(一)
1 C#热更新预备知识 1.1 mono.cecil注入 使用Mono.Cecil实现IL代码注入 注入之后修改dll,新增mdb文件: 注意,待了解参数注释打开会报错: 1.2 InjectFix ...
- python也能玩视频剪辑!moviepy操作记录总结
前几篇文章咱们介绍了一下图片的处理方式,今天咱们说说视频的处理.python能够支持视频的处理么?当然是肯定的,人生苦读,我用python.万物皆可python. moviepy库安装 今天咱们需要使 ...
- Java中使用方法的注意事项
Java方法使用的注意事项 本文列举了几个小白在java中使用方法应该注意的几个地方 1. 方法应该定义在类中2.方法中不可以再嵌套方法3.方法定义的前后顺序无所谓4.想要执行方法必须要调用5.如果方 ...
- JVM——内存区域:运行时数据区域详解
关注微信公众号:CodingTechWork,一起学习进步. 引言 我们经常会被问到一个问题是Java和C++有何区别?我们除了能回答一个是面向对象.一个是面向过程编程以外,我们还会从底层内存管理 ...
- ant design pro 实战 : 使用 ztree
应当指出,antd 是有 ztree 组件的,但是太简单,无法满足复杂的业务需求. 所以我还是决定使用zTree. 用 npm | cnpm 或者 yarn | tyarn 安装,这一步略. 在 js ...
- 6.ALOHA协议
动态媒体接入控制/多点接入特点:信道并非在用户通信时固定分配给用户. 一.纯ALOHA协议 纯 ALOHA协议思想:不监听信道,不按时间槽发送,随机重发.想发就发 二.时隙ALOHA协议 时隙 ALO ...
- vs coed的使用(二) 如何运行cpp文件(不用插件比如code runner)
一.前提 1.配置好编译运行的环境,比如系统变量.vs code的settings.json 2.检查配置好的环境没有问题 我配置结果 [环境变量] [系统变量] 确定settings.json里面的 ...