损失函数大致可分为两种:回归损失(针对连续型变量)和分类损失(针对离散型变量)。
一、回归损失
L1 Loss
也称为Mean Absolute Error,即平均绝对误差(MAE),它衡量的是预测值与真实值之间距离的平均误差幅度,作用范围为0到正无穷。
公式为
优点:L1损失函数的主要特点是对异常点具有较好的鲁棒性。它通过计算预测值和真实值之间差的绝对值来度量误差,并且对所有误差平等对待,因此对于异常点的敏感性较低。这使得L1损失函数在处理包含离群点的数据时,能够避免模型受到这些点的过度影响。
缺点:由图可知其在0点处的导数不连续,使得求解效率低下,导致收敛速度慢;而对于较小的损失值,其梯度也同其他区间损失值的梯度一样大,所以不利于网络的学习。
L2 Loss
L2 Loss也称为均方误差(MSE),是指模型预测值f(x)和真实值y之间差值平方的平均值,公式如下:
优点:函数曲线连续,处处可导,随着误差值的减小,梯度也减小,有利于收敛到最小值。
缺点:由于采用平方运算,当预测值和真实值的差值大于1时,会放大误差。尤其当函数的输入值距离中心值较远的时候,使用梯度下降法求解的时候梯度很大,可能造成梯度爆炸(反向传播中梯度累积导致的梯度非常大,甚至溢出NAN)。同时当有多个离群点时,这些点可能占据Loss的主要部分,需要牺牲很多有效的样本去补偿它,所以MSE损失函数受离群点的影响较大。
Smooth L1 Loss
还有一种平滑版的L1 Loss (Smooth L1 Loss),公式为
这个函数实际上是一个分段函数,它在[−1,1][−1,1]区间内使用L2损失(平方损失),而在[−1,1][−1,1]区间外使用L1损失(绝对值损失)。这种设计使得平滑L1损失在误差较小时能够保持梯度较小,而在误差较大时能够限制梯度的大小,从而在一定程度上避免了梯度爆炸的问题。
- 相比于L1 Loss,可以收敛得更快。
- 相比于L2 Loss,对离群点、异常值不敏感,梯度变化相对更小,训练时不容易跑飞。
所以在目标检测的Bounding box回归上早期会考虑Smooth L1 Loss
参考 目标检测回归损失函数——L1、L2、smooth L1 - 知乎 (zhihu.com)
IoU Loss
IoU表示两个矩形框的交并比,公式为:
python代码实现
def iou(box1, box2):
"""
计算两个边界框的IOU
参数:
box1 -- 第一个边界框的坐标,格式:(x1, y1, x2, y2)
box2 -- 第二个边界框的坐标,格式:(x1, y1, x2, y2)
返回:
iou -- 两个边界框的IOU值
"""
# 获取边界框的坐标
xi1 = max(box1[0], box2[0])
yi1 = max(box1[1], box2[1])
xi2 = min(box1[2], box2[2])
yi2 = min(box1[3], box2[3])
# 计算交集的面积
inter_area = max(0, xi2 - xi1) * max(0, yi2 - yi1)
# 计算两个边界框的面积
box1_area = (box1[2] - box1[0]) * (box1[3] - box1[1])
box2_area = (box2[2] - box2[0]) * (box2[3] - box2[1])
# 计算并集的面积
union_area = box1_area + box2_area - inter_area
# 计算IOU
iou = inter_area / union_area
# 确保IOU的值在0到1之间
assert iou >= 0
assert iou <= 1
return iou
# 示例
box1 = (10, 10, 20, 20)
box2 = (15, 15, 25, 25)
print("IOU: ", iou(box1, box2))
c++代码实现
double iou(float box1[] ,float box2[]){
float x_top = max(box1[0],box2[0]);
float y_top = max(box1[1],box2[1]);
float x_bottom = min(box1[2],box2[2]);
float y_bottom = min(box1[3],box2[3]);
float area_inter = (y_bottom - y_top) * (x_bottom - x_top);
if(x_top>x_bottom || y_top>y_bottom) area_inter = 0;
float area_all = (box1[2]-box1[0]) * (box1[3]-box1[1]) +
(box2[2]-box2[0]) * (box2[3]-box2[1]);
float iou_num;
if((area_all - area_inter) == 0)
iou_num = 1;
else
iou_num = area_inter/( area_all - area_inter );
return iou_num;
}
下图为目标检测的检测框使用L2损失和IoU损失的计算公式(其中IoU损失中,在IoU函数外加了负ln包裹)
我们知道了两种损失函数的计算公式,我们来看下面这种情况
对于目标检测任务中,预测框结果不同的情况下,L2损失函数得到的结果是一样的,这是因为L2损失函数假设每个点都是独立的,并没有考虑各个点之间的相关性,但是实际上四个顶点是有一定的相关性的,所以引入了IoU Loss。
公式二更常见一些
IoU Loss的优缺点
优点:
1、能够更好的反映重合程度
2、具有尺度不变性
缺点:
1、当bounding box 与 ground truth 不相交时,IoU为0,即在此情况下,将无法计算IoU Loss,因而导致其梯度为0并且无法进行优化。因而引入了GIoU Loss。
GIoU Loss(Generalized IoU)
如上图所示,红色框和绿色框为待计算的两个矩形框,蓝色框表示包裹两个矩形框的最小矩形框,使用C表示。GIoU的公式为
可知GIoU的取值范围为:-1,1
在Iou中 两个矩形框相距很远不相交时,结果为0,但是GIoU中,这种情况的结果为无限接近于1,GIoU解决了这样的一个问题。
GIoU Loss的公式为
但GIoU同样存在一个致命的缺点
当两个矩形框等高,且处于同一水平面,且存在相交部分或相邻时,
当两个矩形框等宽,且处于同一垂直面,且存在相交部分或相邻时,结果为0,如下图
然后便又有了DIoU
DIoU Loss
直接上公式
b为第一个框的中心点
bgt为第二个框的中心点
c表示两个矩形的最小外接矩形的对角的欧氏距离
d表示两个框的中心点的欧氏距离。
DIoU Loss的公式为 DIoU Loss = 1 - DIoU
由上图可知,DIoU比起GIoU的优势在于,能够对重合的位置进行评判
DIoU Loss的优点为:因为DIoU Loss能够直接最小化两个boxes之间的距离,因此其收敛速度比IoU Loss与GIoU Loss更快。
对于预测框在 GT 框内,中心点距离相同,形状不同的情况,DIoU 无法区分,但是,考虑形状的长宽对比后,CIoU 可以较好地解决。
CIoU Loss
直接上公式
CIoU Loss = 1 - CIoU
b:预测框的中心坐标;
b^gt:真实框的中心坐标;
(d也就是(公式中第二部分的分子中的括号里的内容):两边界框中心点之间的欧式距离;)
c:最小矩形框两对角顶点之间的欧式距离。
w^gt:(真实框)ground truth的宽;
h^gt:(真实框)ground truth的高;
w:(预测框)bounding box的宽;
h:(预测框)bounding box的高。
除以上几个IoU外,还有很多版本的IoU的改进损失函数,以下便不多例举。
二、分类损失
交叉熵(Cross Entropy Loss)
交叉熵损失函数是用于度量分类问题中预测值与真实标签之间的差距,它在深度学习中得到了广泛的应用。交叉熵损失函数在多分类问题中的表现非常好,比如在图像分类、自然语言处理等领域。
直接上公式
其中C代表类别数。p 为真实,q为预测。
例如
真实值 | 预测值 |
[0, 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0] | [ 0.1 , 0.6 ,0.3 , 0 , 0 , 0 , 0 , 0 ,0 ,0] |
[0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 0] | [ 0, 0.3 , 0.2 , 0 ,0.5 ,0 , 0 , 0 , 0 , 0] |
[0 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0] | [ 0.6 , 0.3 , 0 , 0 , 0 , 0.1 , 0 ,0 ,0 ,0] |
第一个样本:
−ln(0.6)≈0.51
第二个样本:
−ln(0.5)≈0.69
第三个样本
−ln(0.1)≈2.30
平均交叉熵损失:
−(ln(0.6)+ln(0.5)+ln(0.1))/3≈1.17
KL散度
KL散度可以用来衡量两个概率分布之间的相似性,两个概率分布越相近,KL散度越小。
直接上公式(离散值)
通常P为真实事件的概率分布,Q为理论拟合出来的该事件的概率分布。
Balanced Cross Entropy
常见的解决类不平衡方法。引入了一个权重因子α ∈ [ 0 , 1 ] ,当为正样本时,权重因子就是α,当为负样本时,权重因子为1-α。所以,损失函数也可以改写为:
class Balanced_CE_loss(torch.nn.Module):
def __init__(self):
super(Balanced_CE_loss, self).__init__()
def forward(self, input, target):
input = input.view(input.shape[0], -1)
target = target.view(target.shape[0], -1)
loss = 0.0
for i in range(input.shape[0]):
beta = 1-torch.sum(target[i])/target.shape[1]
for j in range(input.shape[1]):
loss += -(beta*target[i][j] * torch.log(input[i][j]) + (1-beta)*(1 - target[i][j]) * torch.log(1 - input[i][j]))
return loss
focal loss
出发点是解决目标检测领域中one-stage算法如YOLO系列算法准确率不高的问题。作者认为样本的类别不均衡(比如前景和背景)是导致这个问题的主要原因。比如在很多输入图片中,我们利用网格去划分小窗口,大多数的窗口是不包含目标的。如此一来,如果我们直接运用原始的交叉熵损失,那么负样本所占比例会非常大,主导梯度的优化方向,即网络会偏向于将前景预测为背景。
Focal loss则是聚焦于训练一个困难样本的稀疏集,通过直接在标准的交叉熵损失基础上做改进,引进了两个惩罚因子,来减少易分类样本的权重,使得模型在训练过程中更专注于困难样本。其基本定义如下
虽然BCE解决了正负样本不平衡问题,但并没有区分简单还是难分样本。当易区分负样本超级多时,整个训练过程将会围绕着易区分负样本进行,进而淹没正样本,造成大损失。所以这里引入了一个调制因子 ,用来聚焦难分样本,将损失函数降低 easy 样本的权重,并关注于对 hard negatives 样本的训练公式如下
上公式
或
𝛾为常数,范围在 [0,5],当其为0时,FL就和普通的交叉熵损失函数一致了。 𝛾 不同取值,FL曲线如下:
调质因子{(1-p)𝛾}可以减低易分样本的损失贡献,从而增加难分样本的损失比例,解释如下:当Pt趋向于1,即说明该样本是易区分样本,此时调制因子是趋向于0,说明对损失的贡献较小,即减低了易区分样本的损失比例。当pt很小,也就是假如某个样本被分到正样本,但是该样本为前景的概率特别小,即被错分到正样本了,此时 调制因子是趋向于1,对loss也没有太大的影响。
再引入以上Balanced Cross Entropy中针对正负样本的权重因子,得到最终focal loss:
①调制因子是用来减低易分样本的损失贡献 ,无论是前景类还是背景类,pt越大,就说明该样本越容易被区分,调制因子也就越小。
②α用于调节正负样本损失之间的比例,前景类别使用 αt 时,对应的背景类别使用 1 − αt 。
③γ 和 α 都有相应的取值范围,他们的取值相互间也是有影响的,在实际使用过程中应组合使用。