Loss Function (Cost Function) 损失/代价/成本函数
⚠️

Loss Function (Cost Function) 损失/代价/成本函数

Category
机器学习 ML
深度学习 DL
Author
Huilin Zhang
Date
Dec 3, 2023
横轴是 y_predict - y_label,纵轴是Loss值。
横轴是 y_predict - y_label,纵轴是Loss值。

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)。
notion image
notion image
notion image
notion image
💡
对于质量评价问题,所构建的网络要去尽可能的拟合训练的数据(拟合并预测一个均值,估计这就是均值叫做期望的原因),即网络拟合的概率分布要尽可能地逼近数据的真实分布。用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函数成对出现。
  • 比如用神经网络最后一层作为概率输出,一般最后一层神经网络的计算方式如下:
      1. 网络的最后一层得到每个类别的score。
      1. score经过sigmoid函数或者softmax函数进行计算得到概率输出。
      1. 第二步得到的类别概率与真实类别的one-hot形式进行交叉熵计算。
  • 下面是二分类问题的交叉熵损失计算公式:表示类别为1,表示预测类别为1的概率
  • 下面是多分类问题的交叉熵损失计算公式:类别有n个。 单分类问题的时候,n个类别是one-hot的形式,只有一个类别,其他n-1个类别为0。
💡
交叉熵损失函数与sigmoid函数配合使用(不会出现模型一开始训练速度很慢的现象),而MSE不与sigmoid函数配合使用(模型一开始的学习速度会非常慢)。原因见Reference。
  • 交叉熵损失函数与softmax函数配合使用,求导非常简单。
notion image

Binary Cross Entropy Loss(二进制交叉熵)

二进制交叉熵是多分类(普通)交叉熵(类别数N=2时)的特例,只用在二分类问题中,而且无需对label进行one-hot编码,使代码更加简洁。
💡
torch.nn.BCELoss中封装了torch.nn.functional.binary_cross_entropytorch.nn.BCEWithLogitsLoss中封装了torch.nn.functional.binary_cross_entropy_with_logits。 所以,一般调用nn就好,不用调用nn.functional
notion image

torch.nn.BCELoss(size_average=True)

默认情况下(即size_average=True),loss会在batch内取平均,如果size_average=False的话,loss会被累加。

torch.nn.BCEWithLogitsLoss()

本质上和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()的使用总结:
  1. 在使用nn.NLLLoss()时,需要结合nn.LogSoftmax()一起使用,而nn.CrossEntropyLoss()不需要。
  1. 使用nn.CrossEntropyLoss()时,网络的最后一层不需要加softmax层。
  1. 使用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。

Reference

交叉熵损失函数(Cross Entropy Error Function)与均方差损失函数(Mean Squared Error)
项目github地址: bitcarmanlee easy-algorithm-interview-and-practice 欢迎大家star,留言,一起学习进步 均方差损失函数是预测数据和原始数据对应点误差的平方和的均值。计算方式也比较简单 其中,N为样本个数。 在分类问题中,尤其是在神经网络中,交叉熵函数非常常见。因为经常涉及到分类问题,需要计算各类别的概率,所以交叉熵损失函数又都是与sigmoid函数或者softmax函数成对出现。 比如用神经网络最后一层作为概率输出,一般最后一层神经网络的计算方式如下: 1.网络的最后一层得到每个类别的scores。 2.score与sigmoid函数或者softmax函数进行计算得到概率输出。 3.第二步得到的类别概率与真实类别的one-hot形式进行交叉熵计算。 而多分类的交叉熵损失函数形式为 于是,在的值接近0或者1的时候,其导数都接近0。这样会导致模型一开始的学习速度非常慢,所以MSE一般不会与sigmoid函数配合使用。 交叉熵损失函数与sigmoid函数配合使用,最终损失函数求导的结果为 由此可见,求导的结果与与的值有关,不会出现模型开始训练速度很慢的现象。 具体的推导过程见参考文献1 交叉熵损失函数求导 前面提到,在神经网络中,交叉熵损失函数经常与softmax配合使用。 softmax函数 接下来求导 ∂ L o s s i ∂ i = − ∂ l n y i ∂ i = − ∑ j e j e i ⋅ ∂ ( e i
交叉熵损失函数(Cross Entropy Error Function)与均方差损失函数(Mean Squared Error)
Pytorch常用损失函数拆解-小新xx
本文从理论和实践两方面来全面梳理一下常用的损失函数。(避免自己总是一瓶子不满半瓶子晃荡......)。要么理论满分,编码时不会用;要么编码是会调包,但是不明白其中的计算原理。本文来科普一下。我们将每个损失函数分别从理论和pytorch中的实现两个方面来拆解一下。 另外,解释一下torch.nn.Module 和 torch.nn.functional(俗称F)中损失函数的区别。Module的损失函数例如CrossEntropyLoss、NLLLoss等是封装之后的损失函数类,是一个类,因此其中的变量可以自动维护。经常是对F中的函数的封装。而F中的损失函数只是单纯的函数。当然我们也可以自己构造自己的损失函数对象。有时候损失函数并不需要太复杂,没有必要特意封装一个类,直接调用F中的函数也是可以的。使用哪种看具体实现需求而定。 交叉熵损失,是分类任务中最常用的一个损失函数。 直接上理论公式 :$$Loss(\hat{x}, x)=-\sum_{i=1}^{n} x\log (\hat{x})$$其中$x$是真实标签,$\hat{x}$是预测的 类分布(通常是使用softmax将模型输出转换为概率分布),也就是$x$与$\hat{x}$中的元素分别表示对应类别的概率。举个例子,清晰明了 x=[0, 1, 0] # 假设该样本属于第二类$\hat{x}$=[0.1, 0.5, 0.4] # 因为是分布,所以属于各个类的和为1$Loss(\hat{x}, x) = -0 \times log(0.1) - 1 \times log(0.5) - 0 \times log(0.4) = log(0.5)$ 举例实际使用中需要注意几点 torch.nn.CrossEntropyLoss(input, target)中的标签 target使用的不是one-hot形式,而是类别的序号。形如 target = [1, 3, 2] 表示3个样本分别属于第1类、第3类、第2类。 torch.nn.CrossEntropyLoss(input, target)的 input是 没有归一化的每个类的得分 ,而不是softmax之后的分布。