L1 Loss (Mean Absolute Error, MAE)L1 Loss的优缺点L2 Loss (Mean Square Error, MSE)L2 Loss的优缺点L2 Loss为什么要平方?为什么回归问题要用MSE?L1和L2的选择?Smooth L1 LossCross Entropy Error LossBinary Cross Entropy Loss(二进制交叉熵)torch.nn.BCELoss(size_average=True)torch.nn.BCEWithLogitsLoss()torch.nn.functional.binary_cross_entropy_with_logits(input, target, reduction=’mean’)负对数似然(Negative log-likelihood, NLL)三元组损失(Triplet Loss)loss.item()Reference
L1 Loss (Mean Absolute Error, MAE)
L1 Loss的优缺点
- 优点:无论对于什么样的输入值,都有着稳定的梯度,不会导致梯度爆炸问题,具有较为稳健性的解;对于离群点不那么敏感。因为MAE计算的是误差的绝对值,对于任意大小的差值,其惩罚都是固定的。
- 缺点:曲线连续,但是在误差=0处不可导。大部分情况下梯度都是相等的,这意味着即使对于小的损失值,其梯度也是大的(更陡峭)。这不利于函数的收敛和模型的学习。
L2 Loss (Mean Square Error, MSE)
L2 Loss的优缺点
- 优点:函数曲线光滑、连续,处处可导,便于使用梯度下降算法;而且,随着误差的减小,梯度也在减小,这有利于收敛,即使使用固定的学习速率,也能较快的收敛到最小值。
- 缺点:真实值和预测值的差值大于1时,MSE给予较大的惩罚,放大误差;而当差值小于1时,MSE给予较小的惩罚,会缩小误差,这是平方运算决定的。也就是说,对离群点比较敏感,受其影响较大。如果样本中存在离群点,MSE会给离群点更高的权重,这就会牺牲其他正常点数据的预测效果,最终降低整体的模型性能。
L2 Loss为什么要平方?
- 经验性回答:因为做回归的时候残差有正有负,取个平方求和以后可以很简单的衡量模型的好坏。同时因为平方后容易求导数,比取绝对值还要分情况讨论好用。
- 理论性回答:在回归问题中,最大似然估计等效于对MSE损失的最小化。(根据中心极限定理,误差服从正态分布,此时使得样本似然函数最大等价于使得MSE最小。)
为什么回归问题要用MSE?
要弄懂为什么回归问题要用MSE, 首先要先明白什么是极大似然估计MLE(Maximum Likelihood Estimation)。
对于质量评价问题,所构建的网络要去尽可能的拟合训练的数据(拟合并预测一个均值,估计这就是均值叫做期望的原因),即网络拟合的概率分布要尽可能地逼近数据的真实分布。用MSE的前提是假设网络与噪声都符合正态分布,并且假设网络均值就是预测值,噪声均值为0,二者方差相同。(这里的这个“噪声”指网络预测值与label之间的噪声,不是图像失真的噪声。)
- 质量评价是一个回归问题
对于超分辨率问题,也应用MSE损失来提高指标PSNR。从统计的角度,MSE的前提是假设输出图片的像素数据符合高斯分布,网络总会输出概率最大的值(像素均值),使得图像模糊。而目标是输出高分辨率图像,这些像素值通常远离均值且概率较低。所以为了提高感知质量,超分还要有其他感知的Loss来约束网络。
- 超分是一个像素级回归问题
L1和L2的选择?
如果离群点(异常值)需要被检测出来,则可以选择MSE作为损失函数;如果离群点只是当做受损的数据处理,则可以选择MAE作为损失函数。
回归一个稀疏的heat map,用L2。
IQA就用MSE就行,用L1/L2/Smooth L1这三个差别不大,MSE能稍微好一点。
Smooth L1 Loss
Faster R-CNN以及SSD中,对边框的回归使用的损失函数都是Smooth L1,实际上就是一个分段函数,在[-1,1]之间实际上就是L2损失,这样解决了L1的不光滑问题,在[-1,1]区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题。
- 优点: 真实值和预测值的差值过大时,梯度值不至于过大(上限为1);差值很小时,梯度值足够小。
def _smooth_l1_loss(input, target, reduction='none'): # type: (Tensor, Tensor) -> Tensor t = torch.abs(input - target) ret = torch.where(t < 1, 0.5 * t ** 2, t - 0.5) if reduction != 'none': ret = torch.mean(ret) if reduction == 'mean' else torch.sum(ret) return ret
def smooth_l1_loss(input, target, beta=1. / 9, reduction = 'none'): """ very similar to the smooth_l1_loss from pytorch, but with the extra beta parameter """ n = torch.abs(input - target) cond = n < beta ret = torch.where(cond, 0.5 * n ** 2 / beta, n - 0.5 * beta) if reduction != 'none': ret = torch.mean(ret) if reduction == 'mean' else torch.sum(ret) return ret
Cross Entropy Error Loss
torch.nn.CrossEntropyLoss(input, target)
中的标签target
必须是LongTensor,使用的不是one-hot形式,而是类别的序号。形如target = [1, 0, 2]
表示3个样本分别属于第1类、第0类、第2类。
torch.nn.CrossEntropyLoss(input, target)
的input
是没有归一化的每个类的得分,而不是softmax之后的分布。
- 分类问题需要计算各类别的概率,所以用交叉熵损失函数,且常与sigmoid函数或者softmax函数成对出现。
- 比如用神经网络最后一层作为概率输出,一般最后一层神经网络的计算方式如下:
- 网络的最后一层得到每个类别的score。
- score经过sigmoid函数或者softmax函数进行计算得到概率输出。
- 第二步得到的类别概率与真实类别的one-hot形式进行交叉熵计算。
- 下面是二分类问题的交叉熵损失计算公式:表示类别为1,表示预测类别为1的概率
- 下面是多分类问题的交叉熵损失计算公式:类别有n个。 单分类问题的时候,n个类别是one-hot的形式,只有一个类别,其他n-1个类别为0。
交叉熵损失函数与sigmoid函数配合使用(不会出现模型一开始训练速度很慢的现象),而MSE不与sigmoid函数配合使用(模型一开始的学习速度会非常慢)。原因见Reference。
- 交叉熵损失函数与softmax函数配合使用,求导非常简单。
Binary Cross Entropy Loss(二进制交叉熵)
二进制交叉熵是多分类(普通)交叉熵(类别数N=2时)的特例,只用在二分类问题中,而且无需对label进行one-hot编码,使代码更加简洁。
torch.nn.BCELoss
中封装了torch.nn.functional.binary_cross_entropy
;
torch.nn.BCE
WithLogits
Loss
中封装了torch.nn.functional.binary_cross_entropy
_with_logits
。
所以,一般调用nn
就好,不用调用nn.functional
。torch.nn.BCELoss(size_average=True)
默认情况下(即
size_average=True
),loss会在batch内取平均,如果size_average=False
的话,loss会被累加。torch.nn.BCE
WithLogits
Loss()
本质上和
nn.BCELoss()
没有区别,只是在BCELoss上加了个logits函数(也就是sigmoid函数)。所以一定要注意!sigmoid不能用两次,否则会出现网络收敛不了等问题!
如果网络已经加了sigmoid,就用BCELoss;如果网络没有加sigmoid,才能用BCEWithLogitsLoss。
torch.nn.functional.binary_cross_entropy
_with_logits
(input, target, reduction=’mean’)
- 对神经网络的输出结果进行sigmoid操作,然后求交叉熵。
torch.nn.BCEWithLogitsLoss()
功能及其中参数和上面的函数一样
input
= 预测结果(未经过sigmoid)
reduction
指定对输出结果的操作,可以设定为none/mean/sum
;none
将不对结果进行任何处理,mean
对结果求均值,sum
对结果求和, 默认是mean
。
负对数似然(Negative log-likelihood, NLL)
负对数似然损失函数和交叉熵损失函数的公式类似,但是他们在pytorch中的使用有一定的差别,下面是对pytorch中的
nn.NLLLoss()
和nn.CrossEntropyLoss()
的使用总结:- 在使用
nn.NLLLoss()
时,需要结合nn.LogSoftmax()
一起使用,而nn.CrossEntropyLoss()
不需要。
- 使用
nn.CrossEntropyLoss()
时,网络的最后一层不需要加softmax
层。
- 使用
nn.NLLLoss()
是,标签不能使用one-hot编码标签,在使用nn.CrossEntropyLoss()
时,也不使用one-hot标签。
三元组损失(Triplet Loss)
loss.item()
import torch x = torch.randn(2, 2) print(x) print(x[1,1]) print(x[1,1].item()) tensor([[ 0.4702, 0.5145], [-0.0682, -1.4450]]) tensor(-1.4450) -1.445029854774475
可以看出是显示精度的区别,item()返回的是一个浮点型数据,所以我们在求loss或者accuracy时,一般使用item(),而不是直接取tensor。