损失函数

均方误差损失

均方误差损失,MSELoss,用于回归任务,计算预测值和真实值之间的均方误差

1
torch.nn.MSELoss(reduction='mean')
  • reduction:指定如何对输出进行降维,可选值为nonemeansum
    • none:不进行降维,返回每个样本的损失
    • mean:返回损失的均值,这是默认行为
    • sum:返回损失的总和

对创建的MSELoss对象传入预测值和真实值张量,确保预测值和真实值的形状相同,返回均方误差损失张量

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch
import torch.nn as nn

# 创建预测值和真实值
pred = torch.tensor([1.0, 2.0, 3.0])
target = torch.tensor([2.0, 3.0, 4.0])

# 创建MSELoss实例
criterion = nn.MSELoss()

# 计算损失
loss = criterion(pred, target)
print(loss)

输出结果:

1
tensor(1.)

平均绝对值误差损失

与 MSE 相比,L1Loss 对异常值不那么敏感,因为它是基于绝对误差而不是平方误差

1
torch.nn.L1Loss(reduction='mean')

reduction:指定如何对输出进行降维,可选值为nonemeansum

  • none:不进行降维,返回每个样本的损失
  • mean:返回损失的均值,这是默认行为
  • sum:返回损失的总和

对创建的L1Loss对象传入预测值和真实值张量,确保预测值和真实值的形状相同,返回均方误差损失张量

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch
import torch.nn as nn

# 创建预测值和真实值
pred = torch.tensor([1.0, 2.0, 3.0])
target = torch.tensor([3.0, 4.0, 5.0])

# 创建MSELoss实例
criterion = nn.L1Loss()

# 计算损失
loss = criterion(pred, target)
print(loss)

输出结果:

1
tensor(2.)

交叉熵损失

1
torch.nn.CrossEntropyLoss()

参数:

  • weight:一个可选的向量,用于为每个类别的损失赋予不同的权重。这对于处理类别不平衡的数据集很有用。
  • ignore_index:指定一个标签值,该值在计算损失时会被忽略。
  • reduction:指定如何对输出进行降维,可选值为nonemeansum
    • none:不进行降维,返回每个样本的损失
    • mean:返回损失的均值,这是默认行为
    • sum:返回损失的总和

CrossEntropyLoss首先对预测输出应用LogSoftmax,将预测输出转换为概率分布的对数形式。然后计算每个样本的负对数似然损失。最后,根据reduction参数对结果进行降维

在损失函数中使用LogSoftmax而不是直接在模型中使用Softmax的原因:

softmax函数: \[ \hat y_j = \frac{\exp(o_j)}{\sum_k\exp(o_k)} \] 其中\(\hat y_j\)是预测的概率分布,\(o_j\)是未规范化的预测\(\mathbf{o}\)的第\(j\)个元素。

如果\(o_k\)中的一些数值非常大,那么\(\exp(o_k)\)可能大于数据类型容许的最大数字,即上溢(overflow)。这将使分母或分子变为inf(无穷大),最后得到的是0、infnan(不是数字)的\(\hat y_j\)

解决这个问题的一个技巧是:在继续softmax计算之前,先从所有\(o_k\)中减去\(\max(o_k)\)\[ \begin{aligned} \hat y_j &= \frac{\exp(o_j)}{\sum_k\exp(o_k)}\\ &= \frac{e^{o_j}}{\sum_k e^{o_k}}\\ &= \frac{e^{o_j-\max(o_k)+\max(o_k)}}{\sum_k e^{o_k-\max(o_k)+\max(o_k)}}\\ &= \frac{e^{o_j-\max(o_k)}e^{\max(o_k)}}{e^{\max(o_k)}\sum_k e^{o_k-\max(o_k)}}\\ &= \frac{e^{o_j-\max(o_k)}}{\sum_k e^{o_k-\max(o_k)}}\\ &= \frac{\exp(o_j-\max(o_k))}{\sum_k\exp(o_k-\max(o_k))} \end{aligned} \]

在减法和规范化步骤后,可能有些\(o_j-\max(o_k)\)具有较大的负值。由于精度受限,\(\exp(o_j-\max(o_k))\)将有接近零的值,即下溢(underflow)。这些值可能会四舍五入为零,使\(\hat y_j\)为零,并且使得\(\log(\hat y_j)\)的值为-inf,反向传播时可能造成梯度爆炸

尽管我们要计算指数函数,但我们最终在计算交叉熵损失时会取它们的对数。我们可以将这两个结合在一起: \[ \begin{aligned} \log(\hat y_j) &= \log\bigg(\frac{\exp(o_j-\max(o_k))}{\sum_k\exp(o_k-\max(o_k))}\bigg)\\ &= \log\big(\exp(o_j-\max(o_k))\big) - \log\Bigg(\sum_k\exp(o_k-\max(o_k))\Bigg)\\ &= o_j-\max(o_k) - \log\Bigg(\sum_k\exp(o_k-\max(o_k))\Bigg) \end{aligned} \] 因此,在训练时,我们的神经网络中不会有Softmax,而是直接在CrossEntropyLoss中计算LogSoftmaxNLLLoss(负对数似然损失)

CrossEntropyLoss对象传入的y_haty应该满足如下要求:

  • y_hat(模型输出):
    • 形状:y_hat 应该是一个二维张量(Tensor),形状为 (N, C),其中 N 是批量大小(batch size),C 是类别的数量
    • 数值:y_hat 应该是模型的原始输出,通常是经过线性层(如全连接层)的输出,但没有应用 softmax 函数。CrossEntropyLoss 内部会自动应用 LogSoftmax
    • 类型:y_hat 的数据类型通常是浮点数(如 torch.float32
  • y(真实标签):
    • 形状:y 可以是一个一维张量(Tensor),形状为 (N,),其中每个元素是一个类别索引(从 0 到 C-1)。也可以是一个二维张量,形状为 (N, 1),但内部元素仍然是一个类别索引
    • 数值:y 中的每个元素都应该是一个整数,表示对应样本的真实类别索引
    • 类型:y 的数据类型通常是长整型(如 torch.long),因为它们是类别索引

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import torch
import torch.nn as nn

# 假设我们有以下模型输出和真实标签
N, C = 3, 5 # 批量大小为3,类别数量为5
y_hat = torch.randn(N, C) # 模型输出,形状为(3, 5)
y = torch.tensor([1, 0, 4], dtype=torch.long) # 真实标签,形状为(3,)

# 创建CrossEntropyLoss对象
criterion = nn.CrossEntropyLoss()

# 计算损失
loss = criterion(y_hat, y)
print(loss)

在设计神经网络时,不需要softmax层,不仅是在训练时使用softmax很可能会导致上溢或下溢,在推理时,如果我们仅关心预测的类别,线性输出的最大值就代表了softmax后概率的最大值,可以直接取线性输出的最大值索引

二元交叉熵损失

1
torch.nn.BCELoss()

参数:

  • weightTensor类型,可选。对每个样本的损失值进行加权,常用于处理类别不平衡问题。例如,若正样本数量较少,可通过增大正样本权重来平衡训练。必须是一维张量,形状与输入数据相同。若不指定,默认所有样本权重为1
  • reductionstr类型,可选。指定损失值的聚合方式:
    • none:不聚合,返回每个样本的损失值
    • mean:计算所有样本损失的均值(默认)
    • sum:计算所有样本损失的总和

BCELoss()对象传入的y_haty应该满足如下要求:

  • y_hat(模型输出):二维张量,形状为(N, 1),也可以是一维张量,形状为(N,),其中N是批量大小(batch size),每个元素是对应数据的预测概率(即模型最后必须通过Sigmoid压缩到[0, 1]区间,否则可能导致数值溢出或梯度异常)
  • y(真实标签):可以是一维张量,形状为(N,),也可以是二维张量,形状为(N, 1),每个元素是对应数据的真实概率(0或1,要求是浮点类型的张量)

y_haty的维度形状必须相同

比如:

1
2
3
4
5
6
7
8
9
import torch
import torch.nn as nn

pred = torch.tensor([0.1, 0.2, 0.8, 0.9])
target = torch.tensor([0.0, 0.0, 1.0, 1.0])

loss_function = nn.BCELoss()
loss = loss_function(pred, target)
print(loss)
1
2
3
4
5
6
7
8
9
import torch
import torch.nn as nn

pred = torch.tensor([0.1, 0.2, 0.8, 0.9]).reshape(4, -1)
target = torch.tensor([0.0, 0.0, 1.0, 1.0]).reshape(4, -1)

loss_function = nn.BCELoss()
loss = loss_function(pred, target)
print(loss)

损失函数
https://blog.shinebook.net/2025/03/01/人工智能/pytorch/损失函数/
作者
X
发布于
2025年3月1日
许可协议