损失函数
- 前言
- 一、分类问题的损失函数
- 1.1 二分类损失函数
- 1.1.1 数学定义
- 1.1.2 函数解释:
- 1.1.3 性质
- 1.1.4 计算演示
- 1.1.5 代码演示
- 1.2 多分类损失函数
- 1.1.1 数学定义
- 1.1.2 性质与特点
- 1.1.3 计算演示
- 1.1.4 代码演示
- 二、回归问题的损失函数
- 2.1 MAE损失
- 2.2 MSE损失
- 2.3 Smooth L1损失
- 总结
前言
- 在深度学习中, 损失函数是用来衡量模型参数的质量的函数, 衡量的方式是比较网络输出和真实输出的差异:
一、分类问题的损失函数
1.1 二分类损失函数
1.1.1 数学定义
- 处理二分类问题的时候,我们使用 sigmoid 激活函数,使用二分类的交叉熵损失函数:
L ( y , y ^ ) = − [ y l o g ( y ^ ) + ( 1 − y ) l o g ( 1 − y ^ ) ] L(y, \hat y)=−[ylog( \hat y )+(1−y)log(1− \hat y)] L(y,y^)=−[ylog(y^)+(1−y)log(1−y^)] - 这里的 l o g log log 是以自然常数 e e e(约等于2.71828)为底的,即自然对数
- 模型输出的预测概率为 y ^ \hat y y^ (在 0 到 1 之间),真实的标签为 y y y (取值为 0 或 1)
1.1.2 函数解释:
-
当 y = 1 y = 1 y=1 的时候,也就是真实值为 1 的时候
L ( 1 , y ^ ) = − l o g ( y ^ ) L(1, \hat y) =−log(\hat y) L(1,y^)=−log(y^)- 此时如果模型预测的 y ^ \hat y y^ 越接近 1,损失值 L L L 越小;如果越接近0,损失值 L L L 越大。
-
当 y = 0 y = 0 y=0 的时候,也就是真实值为 1 的时候
L ( 0 , y ^ ) = − l o g ( 1 − y ^ ) L(0, \hat y) =−log(1-\hat y) L(0,y^)=−log(1−y^)- 此时如果模型预测的 y ^ \hat y y^ 越接近 0,损失值 L L L 越小;如果越接近1,损失值 L L L 越大。
1.1.3 性质
- 非负性:交叉熵损失函数总是非负的,即 L ( y , y ^ ) ≥ 0 L(y, \hat y )≥0 L(y,y^)≥0
- 严格凸函数:在 y ^ \hat y y^ 的定义域(0, 1)内,交叉熵损失函数是严格凸的,因此有助于优化过程找到全局最优解。
- 对数尺度:由于使用对数函数,交叉熵损失函数对预测概率的微小变化非常敏感,这使得它在梯度下降等优化算法中表现良好。
1.1.4 计算演示
- 假设我们有一个样本,真实标签 y = 1 y=1 y=1,模型预测的概率为 y ^ \hat y y^ =0.9:
L ( 1 , 0.9 ) = − l o g ( 0.9 ) = − l n ( 0.9 ) ≈ 0.1054 L(1,0.9)=−log(0.9)= -ln(0.9)≈0.1054 L(1,0.9)=−log(0.9)=−ln(0.9)≈0.1054 - 真实标签 y = 1 y=1 y=1,如果模型预测的概率为 y ^ \hat y y^ = 0.5:
L ( 1 , 0.5 ) = − l o g ( 0.5 ) = − l n ( 0.5 ) ≈ 0.6931 L(1,0.5)=−log(0.5)= -ln(0.5)≈0.6931 L(1,0.5)=−log(0.5)=−ln(0.5)≈0.6931 - 可以看到,当预测概率接近真实标签时,损失值较小;反之,损失值较大。
1.1.5 代码演示
代码演示 :
import torch
from torch import nndef my_BCELoss():# 1 设置真实值和预测值# 预测值是sigmoid输出的结果y_pred = torch.tensor([0.6901, 0.5459, 0.2469], requires_grad=True)y_true = torch.tensor([0, 1, 0], dtype=torch.float32)# 2 实例化二分类交叉熵损失criterion = nn.BCELoss()# 3 计算损失my_loss = criterion(y_pred, y_true).detach().numpy()# detach() 用于将损失函数从计算图中分离出来, numpy() 将tensor转成numpy,# 这样打印出来就会是个数字 print('loss:', my_loss)if __name__ == '__main__':my_BCELoss() # loss: 0.6867941
1.2 多分类损失函数
1.1.1 数学定义
- 处理多分类的问题的时候,我们就不能使用二分类的损失函数,我们需要使用多分类的损失函数,在多分类任务通常使用 softmax 将 logits(原始分数输出) 转换为概率的形式,所以多分类的交叉熵损失也叫做softmax损失,它的计算方法是:
H ( y , y ^ ) = − ∑ i = 1 N y i l o g ( y ^ i ) H(y, \hat y)=−\sum_{i=1}^{N}y_ilog(\hat y_i) H(y,y^)=−i=1∑Nyilog(y^i)
y ^ i = s o f t m a x ( f ( x i ) ) \hat y_i = softmax(f(x_i)) y^i=softmax(f(xi))- f ( x i ) f(x_i) f(xi) 是对第 i 个类别的预测分数
- y ^ i \hat y_i y^i 是预测为第 i 类的概率
- y y y 是真实的标签分布,通常表示为 one-hot 编码向量,即只有一个元素为 1(表示真实类别),其余元素为 0,也可以不进行 热编码,在下边的代码演示中有提及
- y ^ \hat y y^ 是模型预测的概率分布,由模型的输出层经过softmax函数转换得到,表示每个类别的预测概率
- N N N是类别的总数
- 这里的 l o g log log 是以自然常数 e e e(约等于2.71828)为底的,即自然对数
注意:我们不能在网络层的输出层通过softmax函数进行激活,这是因为多分类损失函数会对输入的结果先进行softmax变化,所以会影响损失值的大小,还有准确率
1.1.2 性质与特点
- 非负性:交叉熵损失总是非负的,当且仅当预测分布与真实分布完全一致时,损失为 0
- 敏感性:交叉熵损失函数对正确分类的概率非常敏感。如果实际类别的预测概率低(即接近于0),那么损失将会非常高
- 非对称性:在处理极端概率(接近0或1)时,交叉熵损失表现出明显的非对称性。特别是当预测概率趋近于0时,损失会迅速增加
- 凸性:交叉熵损失函数是凸函数,这有助于保证优化过程的稳定性和有效性
- 适合多类别问题:交叉熵损失函数能够很好地处理多类别分类问题,通过分别计算每个类别的损失并求和来得到总损失
1.1.3 计算演示
-
例子背景
- 假设我们有一个三分类问题(例如,识别图像中的猫、狗或鸟),并且我们有一个训练好的神经网络模型。模型的输出层有3个神经元,每个神经元对应一个类别的预测得分(也称为logits)。这些得分通过softmax函数转换为概率分布,表示每个类别的预测概率。
-
数据与标签
- 假设我们有一个输入图像,其真实标签是“狗”(在one-hot编码中表示为[0, 1, 0])。模型对该图像的预测输出(经过softmax之前的logits)为[2.0, 1.0, 0.1],经过softmax转换后的概率分布为[0.33, 0.50, 0.17]。
-
计算交叉熵损失
-
真实标签(one-hot编码):y = [0, 1, 0]
-
预测概率: y ^ \hat y y^ = [0.33, 0.50, 0.17]
-
交叉熵损失的计算公式为:
H ( y , y ^ ) = − ∑ i = 1 N y i l o g ( y ^ i ) H(y, \hat y)=−\sum_{i=1}^{N}y_ilog(\hat y_i) H(y,y^)=−i=1∑Nyilog(y^i) -
将真实标签和预测概率代入公式中,我们得到:
H ( y , y ^ ) = − ( 0 ⋅ l o g ( 0.33 ) + 1 ⋅ l o g ( 0.50 ) + 0 ⋅ l o g ( 0.17 ) ) = − l o g ( 0.50 ) ≈ 0.693 H(y, \hat y)=−(0⋅log(0.33)+1⋅log(0.50)+0⋅log(0.17)) =−log(0.50) ≈0.693 H(y,y^)=−(0⋅log(0.33)+1⋅log(0.50)+0⋅log(0.17))=−log(0.50)≈0.693
-
-
因此,该输入图像的交叉熵损失约为0.693。这个值表示模型预测的概率分布与真实标签分布之间的差异程度。损失值越小,表示模型的预测越准确。
1.1.4 代码演示
代码演示 :
import torch
from torch import nn# 多分类交叉熵损失,使用nn.CrossEntropyLoss()实现。nn.CrossEntropyLoss()=softmax + 损失计算
def my_CrossEntropyLoss():# 设置真实值: 可以是热编码后的结果也可以不进行热编码y_true = torch.tensor([[0, 1, 0], [0, 0, 1]], dtype=torch.float32)# 不解码的时候, 需要传入真实类别的tensor# 注意的类型必须是64位整型数据# 意思是 真实标签是 1(即第二类)真实标签是 2(即第三类) # 对应下边的 y_pred 有 0.2 的概率是 0 有0.6 的概率是1 有0.2 的概率是 2# y_true = torch.tensor([1, 2], dtype=torch.int64)y_pred = torch.tensor([[0.2, 0.6, 0.2], [0.1, 0.8, 0.1]], dtype=torch.float32)# 实例化交叉熵损失loss = nn.CrossEntropyLoss()# 计算损失结果my_loss = loss(y_pred, y_true).numpy()print('loss:', my_loss)if __name__ == '__main__':my_CrossEntropyLoss()
二、回归问题的损失函数
2.1 MAE损失
-
Mean absolute loss(MAE)也被称为 L1 Loss,是以绝对误差作为距离。损失函数公式:
L = 1 N ∑ i = 1 N ∣ y i − y ^ ∣ L = \frac{1}{N}\sum_{i=1}^{N} |y_i-\hat y| L=N1i=1∑N∣yi−y^∣- N N N 是样本数量
- y i y_i yi 是第 i i i 个样本的真实值
- y ^ \hat y y^ 是第 i i i 个样本的预测值
-
特点:
- 由于L1 loss具有稀疏性,为了惩罚较大的值,因此常常将其作为正则项添加到其他loss中作为约束。
- L1 loss的最大问题是梯度在零点不平滑,导致会跳过极小值。
-
图像
-
代码演示
代码演示 :
import torch
from torch import nndef my_L1Loss():# 1 设置真实值和预测值y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True)y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32)# 2 实例MAE损失对象loss = nn.L1Loss()# 3 计算损失my_loss = loss(y_pred, y_true).detach().numpy()print('loss:', my_loss)if __name__ == '__main__':my_L1Loss()
2.2 MSE损失
-
Mean Squared Loss/ Quadratic Loss(MSE loss)也被称为L2 loss,或欧氏距离,它以误差的平方和的均值作为距离,损失函数公式:
L = 1 N ∑ i = 1 N ( y i − y ^ ) 2 L = \frac{1}{N}\sum_{i=1}^{N} (y_i-\hat y)^2 L=N1i=1∑N(yi−y^)2- N N N 是样本数量
- y i y_i yi 是第 i i i 个样本的真实值
- y ^ \hat y y^ 是第 i i i 个样本的预测值
-
特点:
- .L2 loss也常常作为正则项
- 当预测值与目标值相差很大时, 梯度容易爆炸
-
图像
-
代码演示
代码演示 :
import torch
from torch import nndef my_MSELoss():# 1 设置真实值和预测值y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True)y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32)# 2 实例MSE损失对象loss = nn.MSELoss()# 3 计算损失my_loss = loss(y_pred, y_true).detach().numpy()print('myloss:', my_loss)if __name__ == '__main__':my_MSELoss()
2.3 Smooth L1损失
-
smooth L1说的是光滑之后的L1。损失函数公式:
Smooth L1 ( x ) = { 0.5 × x 2 if ∣ x ∣ < 1 ∣ x ∣ − 0.5 otherwise \text{Smooth L1}(x) = \begin{cases} 0.5 \times x ^2 & \text{if } |x| < 1 \\ |x| - 0.5 & \text{otherwise} \end{cases} Smooth L1(x)={0.5×x2∣x∣−0.5if ∣x∣<1otherwise- 其中, x x x 就是真实值与预测值的差值
-
图像
-
特点:从上述图像中我们可以看出
- .在 [-1,1] 之间实际上就是 L2 损失,这样解决了 L1 的不光滑问题
- 在 [-1,1 ] 区间外,实际上就是 L1 损失,这样就解决了离群点梯度爆炸的问题
-
代码演示
代码演示 :
import torch
from torch import nndef my_SmoothL1Loss():# 1 设置真实值和预测值y_true = torch.tensor([1, 1])y_pred = torch.tensor([0.6, 0.4], requires_grad=True)# 2 实例化smoothL1损失对象loss = nn.SmoothL1Loss()# 3 计算损失my_loss = loss(y_pred, y_true).detach().numpy()print('loss:', my_loss)if __name__ == '__main__':my_SmoothL1Loss()
总结
- 我们总结了神经网络中常用的损失函数,在不同的情况下,我们需要搭配不同的损失函数。