摘要 机器学习任务中适用损失函数来计算描述预测结果和实际事实之间的差距,通过损失函数来量化这个差距从而判断预测的错误程度。因此选择适当的损失函数将有助于获得较好的结果。这里将尝试分析理解计算机视觉中一些基本的损失函数和图像分类、图像检测以及图像分割任务中常用的主流损失函数。
这是计算损失函数最基本的损失函数,PyTorch中也将其命名为torch.nn.MSELoss。这依赖于两个向量[预测和真实标签]之间的Euclidean距离(欧氏距离)。它有几个别称分别为
import torch
import numpy as np
a = torch.tensor([[1, 2], [3, 4]], dtype=torch.float)
b = torch.tensor([[3, 5], [8, 6]], dtype=torch.float)
loss_fn1 = torch.nn.MSELoss()
loss1 = loss_fn1(a.float(), b.float())
print(loss1) # 10.5000
使用场景
回归任务、数值特征不大、问题维度不高
分类的损失函数一般都要求算法的每个标量输出输入概率 p在0至1之间且和为1。但是预测值并非总是如此,我们可以使用Softmax 函数(非线性函数)将预测值变为概率在0至1之间且和为1。因此Softmax 函数也称为归一化指数函数,其公式如下:
softmax常用来进行多分类,假如有一个4x1向量z^{[L]}=[5,2,-1,3],softmax的计算过程如下所示
Pytorch使用范例:
import torch
import torch.nn.functional as F
x= torch.Tensor( [ [1,2,3,4],[1,2,3,4],[1,2,3,4]])
y1= F.softmax(x, dim = 0) #对每一列进行softmax
y2 = F.softmax(x,dim =1) #对每一行进行softmax
简单来说就是平滑版的L1 Loss。
原理
SoothL1Loss的函数如下:
仔细观察可以看到,当预测值和ground truth差别较小的时候(绝对值差小于1),其实使用的是L2 Loss;而当差别大的时候,是L1 Loss的平移。SooothL1Loss其实是L2Loss和L1Loss的结合,它同时拥有L2 Loss和L1 Loss的部分优点。
在图像分类中,经常使用softmax+交叉熵作为损失函数
交叉熵主要刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。假设概率分布p为期望输出,概率分布q为实际输出,H(p,q)为交叉熵,则
那么该公式如何表示,举个例子,假设N=3,期望输出为 p=(1,0,0) 实际输出 q1=(0.5,0.2,0.3),q2=(0.8,0.1,0.1) ,那么:
通过上面可以看出,q2与p更为接近,它的交叉熵也更小。
CrossEntropyLoss()损失函数结合了nn.LogSoftmax()和nn.NLLLoss()两个函数。它在做分类(具体几类)训练的时候是非常有用的,如上所述,softmax用于多分类过程中,它将多个神经元的输出,映射到(0,1)区间内,可以看成概率来理解,从而来进行多分类。
import torch
loss = torch.nn.BCEWithLogitsLoss()
#preds预测值 如:[-0.4004, 0.2050, 0.7746]
preds= torch.randn(3, requires_grad=True)
#target输入是onthot向量,target是真实标签,,可以是[0,0,1,1,0...]多个值为1
target = torch.empty(3).random_(2)#[0,1,1]
output = loss(pred, target)# [0.4959]
Pytorch 多分类交叉熵 CrossEntropy Loss:
#五分类范例
loss = torch.nn.CrossEntropyLoss()
preds= torch.randn(3, 5, requires_grad=True)
#target输入不是onthot向量,target是真实标签,是稀疏的标签值;tensor([4, 2, 4])
target = torch.empty(3, dtype=torch.long).random_(5)
output = loss(predsinputs, target)
计算原理
import torch
import torch.nn as nn
x_input=torch.randn(3,3)#三分类随机生成输入
print('x_input:\n',x_input)
y_target=torch.tensor([1,2,0])#设置输出具体值 print('y_target\n',y_target)
#计算输入softmax,此时可以看到每一行加到一起结果都是1
softmax_func=nn.Softmax(dim=1)
soft_output=softmax_func(x_input)
print('soft_output:\n',soft_output)
#在softmax的基础上取log
log_output=torch.log(soft_output)
print('log_output:\n',log_output)
#对比softmax与log的结合与nn.LogSoftmaxloss(负对数似然损失)的输出结果,发现两者是一致的。
logsoftmax_func=nn.LogSoftmax(dim=1)
logsoftmax_output=logsoftmax_func(x_input)
print('logsoftmax_output:\n',logsoftmax_output)
#pytorch中关于NLLLoss的默认参数配置为:reducetion=True、size_average=True
nllloss_func=nn.NLLLoss()
nlloss_output=nllloss_func(logsoftmax_output,y_target)
print('nlloss_output:\n',nlloss_output)
#直接使用pytorch中的loss_func=nn.CrossEntropyLoss()看与经过NLLLoss的计算是不是一样
crossentropyloss=nn.CrossEntropyLoss()
crossentropyloss_output=crossentropyloss(x_input,y_target)
print('crossentropyloss_output:\n',crossentropyloss_output)
计算输出结果如下:
x_input:
tensor([[ 2.8883, 0.1760, 1.0774],
[ 1.1216, -0.0562, 0.0660],
[-1.3939, -0.0967, 0.5853]])
y_target
tensor([1, 2, 0])
soft_output:
tensor([[0.8131, 0.0540, 0.1329],
[0.6039, 0.1860, 0.2102],
[0.0841, 0.3076, 0.6083]])
log_output:
tensor([[-0.2069, -2.9192, -2.0178],
[-0.5044, -1.6822, -1.5599],
[-2.4762, -1.1790, -0.4970]])
logsoftmax_output:
tensor([[-0.2069, -2.9192, -2.0178],
[-0.5044, -1.6822, -1.5599],
[-2.4762, -1.1790, -0.4970]])
nlloss_output:
tensor(2.3185)
crossentropyloss_output:
tensor(2.3185)
直接使用pytorch中的loss_func=nn.CrossEntropyLoss()计算得到的结果与softmax-log-NLLLoss计算得到的结果是一致的。
谷歌在交叉熵的基础上,提出了label smoothing(标签平滑)防止模型在训练时过于自信地预测标签,改善泛化能力差的问题。在深度学习样本训练的过程中采用one-hot标签去进行计算交叉熵损失时,只考虑到训练样本中正确的标签位置(one-hot标签为1的位置)的损失,而忽略了错误标签位置(one-hot标签为0的位置)的损失。这样一来,模型可以在训练集上拟合的很好,但由于其他错误标签位置的损失没有计算,导致预测的时候,预测错误的概率增大。为了解决这一问题,标签平滑的正则化方法便应运而生。
对于分类问题,我们通常认为训练数据中标签向量的目标类别概率应为1,非目标类别概率应为0。传统的one-hot编码的标签向量yi为,
而label smoothing计算公式为
在训练网络时,交叉熵损失可由以下公式计算得到。
其中pi由对模型倒数第二层输出的logits向量z应用Softmax函数计算得到,
为了比较使用label smoothing和未使用的两种情况,举例分析如下:
使用举例:
class LabelSmoothing(nn.Module):
"""NLL loss with label smoothing.
"""
def __init__(self, smoothing=0.0):
"""Constructor for the LabelSmoothing module.
:param smoothing: label smoothing factor
"""
super(LabelSmoothing, self).__init__()
self.confidence = 1.0 - smoothing
self.smoothing = smoothing
# 此处的self.smoothing即我们的epsilon平滑参数。
def forward(self, x, target):
# 此处x的shape应该是(batch size * class数量),所以这里在class数量那个维度做了logsoftmax。
logprobs = torch.nn.functional.log_softmax(x, dim=-1)
# 此处的target的shape是(batch size), 应该就是每个training data的数字标签。
nll_loss = -logprobs.gather(dim=-1, index=target.unsqueeze(1))
# 把输出的shape变回(batch size)
nll_loss = nll_loss.squeeze(1)
# 在第二个维度取均值的话,应该就是对每个x,所有类的logprobs取了平均值。
smooth_loss = -logprobs.mean(dim=-1)
loss = self.confidence * nll_loss + self.smoothing * smooth_loss
return loss.mean()
应用场景
只要loss损失函数中涉及到了cross entropy,都可以应用标签平滑处理。其实质就是促使神经网络中进行softmax激活函数激活之后的分类概率结果向正确分类靠近,即正确的分类概率输出大(对应的one-hot标签为1位置的softmax概率大),并且同样尽可能的远离错误分类(对应的one-hot标签为0位置的softmax概率小),即错误的分类概率输出小。
目标检测的损失函数一般由classification loss(类别或分类损失)和bounding box regression loss(位置或回归损失)组成。
https://mp.weixin.qq.com/s/k7CsGLNoNnSbnAldkafqKQ
https://blog.csdn.net/qq_44015059/article/details/109479164
https://zhuanlan.zhihu.com/p/83131026
https://blog.csdn.net/senbinyu/article/details/108310976
https://blog.csdn.net/sinat_34474705/article/details/102692382
https://blog.csdn.net/xjp_xujiping/article/details/107589950
Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression