在深度学习中,损失函数是用来衡量模型参数质量的函数。说人话就是:真实值和预测值之间的差值
分类任务中的损失函数
这里重点说交叉熵损失
①多分类任务
多分类任务需要用激活函数softmax将输出转变成概率的形式,在多分类任务中,交叉熵损失函数的计算方法为:
为了熟悉理解这个公式,我们来看下面这个例子:
计算下面的交叉熵损失:
代入公式:L =-(0log0.1+1log0.7+0*log0.2)=-log0.7
从上面的公式可以看出,当经过softmax处理得到的概率越接近1,得到的损失函数越小,越接近0。
下面用代码演示下:
# 导入需要的包
import torch
import torch.nn as nn
def test01():
# 设置真实值,可以进行热编码,也可以不进行热编码
y_true = torch.tensor([[0, 1, 0], [0, 0, 1]], dtype=torch.float32)
# 设置真实值,真实值为类别1和类别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()
# 计算损失结果
loss_result = loss(y_pred, y_true).numpy()
# 打印损失结果
print(loss_result)
if __name__ == '__main__':
test01()
这里肯定有小伙伴很好奇,在公式中我们要对真实值进行one-hot编码(热编码)之后,才能计算,为什么pytorch不用进行热编码呢?
实际上,并不是不用进行热编码,而是pytorch帮我们做了,那他怎么做的呢?假设和代码中一样,我们传入了两个类别分别是[1,2],pytorch就会先自动补齐,默认总共三个类别,分别为:0,1,2,也就是[0,1,2]。接下来,会将0,1,2分别编码成:[1,0,0],[0,1,0],[0,0,1],那么代码中的[1,2]是不是很好理解了,就对应[0,1,0]和[0,0,1]是不是和我们自己手动转化成的热编码一致。
②二分类任务
二分类使用的激活函数为 sigmoid,而交叉熵损失函数的公式为:
测试如下:
def test02():
# 设置真实值
y_true = torch.tensor([[0], [1]], dtype=torch.float32)
# 设置真实值
y_pred = torch.tensor([[0.3], [0.7]], dtype=torch.float32)
# 实例化二分类交叉熵损失
loss = nn.BCELoss()
# 计算损失结果
loss_result = loss(y_pred, y_true).numpy()
# 打印损失结果
print(loss_result)
回归任务的损失函数
①MAE损失(L1 Loss)
Mean Absolute Loss(MAE)通常用在正则化上,添加到其他的 loss 中作为约束,但是在零点不可导。公式如下:
测试代码如下:
def test03():
# 设置真实值
y_true = torch.tensor([[0.], [1.]], dtype=torch.float32)
# 设置真实值
y_pred = torch.tensor([[1.], [0.]], dtype=torch.float32)
# 实例化L1Loss
loss = nn.L1Loss()
# 计算损失结果
loss_result = loss(y_pred, y_true).numpy()
# 打印损失结果
print(loss_result)
②MSE损失(L2 Loss)
Mean Squared Loss(MSE)通常用在正则化上,当预测值和真实值相差很大时,容易发生梯度爆炸。公式如下:
测试代码如下:
def test04():
# 设置真实值
y_true = torch.tensor([[0.], [1.]], dtype=torch.float32)
# 设置真实值
y_pred = torch.tensor([[1.], [1.]], dtype=torch.float32)
# 实例化MSELoss(L2Loss)
loss = nn.MSELoss()
# 计算损失结果
loss_result = loss(y_pred, y_true).numpy()
# 打印损失结果
print(loss_result)
③smooth L1损失
简单的理解,就是把 L1 损失平滑一下,实际上就是为了解决 L1 中在零点不可导,在 L2 中梯度爆炸的问题。公式如下:
下面这个图就是三个损失函数的图像对比:
测试代码如下:
def test05():
# 设置真实值
y_true = torch.tensor([[0.], [1.]], dtype=torch.float32)
# 设置真实值
y_pred = torch.tensor([[0.7], [2.]], dtype=torch.float32)
# 实例化smooth L1损失
loss = nn.HuberLoss()
# 计算损失结果
loss_result = loss(y_pred, y_true).numpy()
# 打印损失结果
print(loss_result)