Skip to content

在机器学习和深度学习中,损失函数是用来衡量模型参数的质量的函数,衡量的方式是比较网络输出和真实输出的差异:


1. 多分类损失函数 ​

1.1 公式定义 ​

在多分类任务通常使用 Softmax 将 logits 转换为概率的形式,所以多分类的交叉熵损失也叫做 Softmax 损失,计算方式如下:

首先,将模型的原始输出(logits)通过 Softmax 函数转换为概率分布:

\[p_i = \frac{e^{z_i}}{\sum_{j=1}^C e^{z_j}} \]

其中:

  • \(z_i\) 是第 \(i\) 类的 logit 值,
  • \(C\) 是类别总数,
  • \(p_i\) 是第 \(i\) 类的预测概率。

然后,计算真实标签与预测概率之间的交叉熵损失:

\[L = -\sum_{i=1}^C y_i \log(p_i) \]

其中:

  • \(y_i\) 是第 \(i\) 类的真实标签(one-hot 编码形式,即真实类别为 1,其余为 0),
  • \(p_i\) 是第 \(i\) 类的预测概率,
  • \(C\) 是类别总数。

通常将 Softmax 和交叉熵合并表示为:

\[L = -\log\left(\frac{e^{z_k}}{\sum_{j=1}^C e^{z_j}}\right) \]

其中 \(z_k\) 是真实类别对应的 logit 值。

1.2 计算示例 ​

假设:

  • 模型输出的 logits(未归一化的分数):

    \[\mathbf{z} = [2.0, 1.0, 0.1] \]
  • 真实标签(one-hot 编码):

    \[\mathbf{y} = [0, 1, 0] \]

首先,将 logits 通过 Softmax 函数转换为概率分布:

\[p_i = \frac{e^{z_i}}{\sum_{j=1}^C e^{z_j}} \]

得到:

\[\begin{align*} e^{z_1} &= e^{2.0} \approx 7.389 \\ e^{z_2} &= e^{1.0} \approx 2.718 \\ e^{z_3} &= e^{0.1} \approx 1.105 \\ \sum e^{z_j} &= 7.389 + 2.718 + 1.105 \approx 11.212 \\ \end{align*} \]

所以 Softmax 概率为:

\[\begin{align*} p_1 &= \frac{7.389}{11.212} \approx 0.659 \\ p_2 &= \frac{2.718}{11.212} \approx 0.242 \\ p_3 &= \frac{1.105}{11.212} \approx 0.099 \\ \end{align*} \]

即:

\[\mathbf{p} = [0.659, 0.242, 0.099] \]

由交叉熵损失公式:

\[L = -\sum_{i=1}^C y_i \log(p_i) \]

由于真实标签是 one-hot 编码(\(y_2 = 1\),其余为 0),所以只需计算真实类别对应的 \(-\log(p_2)\):

\[L = -(0\log(0.659) + 1\log(0.242) + 0\log(0.099)) \approx -(-1.418) \approx 1.418 \]

代码实现:

python
import torch
import torch.nn as nn

# 定义 logits 和真实标签(注意:这里 y 是类别索引,不是 one-hot)
z = torch.tensor([[2.0, 1.0, 0.1]], dtype=torch.float32)  # 必须是 2D (batch_size, num_classes)
y = torch.tensor([1], dtype=torch.long)  # 类别索引(1 表示第2类)

# 使用 nn.CrossEntropyLoss(自动包含 Softmax + 交叉熵)
criterion = nn.CrossEntropyLoss()
loss = criterion(z, y)

print("Logits:", z)
print("真实类别:", y.item())  # 1
print("交叉熵损失:", loss.item())  # ≈1.418

2. 二分类损失函数 ​

2.1 公式定义 ​

在二分类任务中,通常使用 Sigmoid 函数将模型的原始输出(logit)转换为概率值,然后计算 二元交叉熵损失,具体步骤如下:

首先适用 Sigmoid 函数将 logit 转换为概率:

\[p = \sigma(z) = \frac{1}{1 + e^{-z}} \]

其中:

  • \(z\) 是模型的原始输出(logit),
  • \(p\) 是预测为正类(类别 1)的概率。

再计算元交叉熵损失:

\[L = -\left[ y \log(p) + (1 - y) \log(1 - p) \right] \]

其中:

  • \(y\) 是真实标签(0 或 1),
  • \(p\) 是预测为正类的概率。

特点:

  • 当 \(y = 1\) 时,损失为 \(-\log(p)\)(希望 \(p\) 越大越好)。
  • 当 \(y = 0\) 时,损失为 \(-\log(1 - p)\)(希望 \(p\) 越小越好)。

2.2 计算示例 ​

假设:

  • 模型输出的 logit:
    \[z = 0.8 \]
  • 真实标签:
    \[y = 1 \]
  1. 计算 Sigmoid 概率:
    \[p = \frac{1}{1 + e^{-0.8}} \approx \frac{1}{1 + 0.449} \approx 0.690 \]
  2. 计算交叉熵损失:
    \[L = -\left[ 1 \cdot \log(0.690) + 0 \cdot \log(1 - 0.690) \right] \approx -\log(0.690) \approx 0.371 \]

代码实现:

python
import torch
import torch.nn as nn

# 定义 logit 和真实标签
z = torch.tensor([0.8], dtype=torch.float32)  # 注意是 1D 或 2D
y = torch.tensor([1.0], dtype=torch.float32)  # 二分类标签必须是浮点数

# 使用 nn.BCEWithLogitsLoss(内置 Sigmoid + 交叉熵)
criterion = nn.BCEWithLogitsLoss()
loss = criterion(z, y)

print("Logit:", z.item())  # 0.8
print("真实标签:", y.item())  # 1
print("Sigmoid 概率:", torch.sigmoid(z).item())  # ≈0.690
print("二元交叉熵损失:", loss.item())  # ≈0.371

3. 回归MAE损失函数 ​

3.1 公式定义 ​

平均绝对误差(Mean Absolute Error, MAE)是一种用于回归任务的损失函数,MAE也被称为 L1 Loss,计算预测值与真实值之间绝对差值的平均值。

公式如下:

\[L = \frac{1}{N} \sum_{i=1}^N |y_i - \hat{y}_i| \]

其中:

  • \(y_i\) 是第 \(i\) 个样本的真实值,
  • \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,
  • \(N\) 是样本数量。

特点:

  • 鲁棒性:MAE 对异常值不敏感,因为它是绝对误差的平均值,不像均方误差(MSE)会放大异常值的影响。
  • 梯度:MAE 的梯度是常数(±1),在优化时不会因为误差大小而改变步长,但在接近最小值时可能收敛较慢。
  • 输出解释:MAE 的值与实际目标值的单位一致,直观易解释(例如,MAE=2 表示平均误差为 2 个单位)。

3.2 计算示例 ​

假设:

  • 真实值:\([3.0, -0.5, 2.0, 7.0]\)
  • 预测值:\([2.5, 0.0, 2.0, 8.0]\)

计算绝对误差:

\[|3.0 - 2.5| = 0.5 \\ |-0.5 - 0.0| = 0.5 \\ |2.0 - 2.0| = 0.0 \\ |7.0 - 8.0| = 1.0 \\ \]

MAE 损失:

\[L = \frac{0.5 + 0.5 + 0.0 + 1.0}{4} = \frac{2.0}{4} = 0.5 \]

代码实现:

python
import torch
import torch.nn as nn

# 真实值和预测值(假设 batch_size=4)
y_true = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0])

# 使用 PyTorch 的 L1Loss(即 MAE)
criterion = nn.L1Loss()
loss = criterion(y_pred, y_true)

print("MAE 损失:", loss.item())  # 输出 0.5

4. 回归MSE损失函数 ​

4.1 公式定义 ​

均方误差(Mean Squared Error, MSE)是回归任务中最常用的损失函数之一,MSE也被称为L2 Loss,计算预测值与真实值之间平方差值的平均值。

公式如下:

\[L = \frac{1}{N} \sum_{i=1}^N (y_i - \hat{y}_i)^2 \]

其中:

  • \(y_i\) 是第 \(i\) 个样本的真实值,
  • \(\hat{y}_i\) 是第 \(i\) 个样本的预测值,
  • \(N\) 是样本数量。

特点:

  • 对异常值敏感:MSE会放大较大误差的影响(因为平方操作),因此对异常值比MAE更敏感。
  • 梯度特性:MSE的梯度与误差大小成正比(梯度为\(2(y_i - \hat{y}_i)\)),在误差较大时梯度也较大,有助于快速收敛,但在接近最小值时可能不稳定。
  • 输出解释:MSE的单位是目标值的平方(例如,若目标单位是"米",MSE单位是"平方米"),因此需开平方(RMSE)才能与实际单位一致。

4.2 计算示例 ​

假设:

  • 真实值:\([3.0, -0.5, 2.0, 7.0]\)
  • 预测值:\([2.5, 0.0, 2.0, 8.0]\)

计算平方误差:

\[(3.0 - 2.5)^2 = 0.25 \\ (-0.5 - 0.0)^2 = 0.25 \\ (2.0 - 2.0)^2 = 0.0 \\ (7.0 - 8.0)^2 = 1.0 \\ \]

MSE 损失:

\[L = \frac{0.25 + 0.25 + 0.0 + 1.0}{4} = \frac{1.5}{4} = 0.375 \]

代码实现:

python
import torch
import torch.nn as nn

# 真实值和预测值(假设 batch_size=4)
y_true = torch.tensor([3.0, -0.5, 2.0, 7.0])
y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0])

# 使用 PyTorch 的 MSELoss
criterion = nn.MSELoss()
loss = criterion(y_pred, y_true)

print("MSE 损失:", loss.item())  # 输出 0.375

5. 平滑L1损失函数 ​

5.1 公式定义 ​

平滑L1损失(Smooth L1 Loss)是L1 Loss(MAE)和L2 Loss(MSE)的结合体,由Ross Girshick在Fast R-CNN中提出。它在误差较小时使用平方项(类似MSE),在误差较大时使用线性项(类似MAE),兼具两者的优点。

公式如下:

\[L = \frac{1}{N} \sum_{i=1}^N z_i \]

其中:

\[z_i = \begin{cases} 0.5(y_i - \hat{y}_i)^2 & \text{if} |y_i - \hat{y}_i| < 1 \\ |y_i - \hat{y}_i| - 0.5 & \text{otherwise} \end{cases} \]

特点:

  • 鲁棒性:对异常值比MSE更鲁棒(大误差时退化为线性)
  • 梯度平滑:在误差接近0时梯度逐渐减小(避免MSE在极小误差时的不稳定)
  • 收敛性:比MAE在接近最优解时收敛更快(因小误差时使用二次项)

对比图像:

5.2 计算示例 ​

假设:

  • 真实值:\([1.2, -0.8, 3.0, -2.5]\)
  • 预测值:\([0.9, -0.5, 2.6, -1.8]\)
  • 阈值 \(\beta=1\)(默认值)

逐项计算:

\(|1.2-0.9|=0.3<1\) → \(0.5 \times 0.3^2 = 0.045\)

\(|-0.8-(-0.5)|=0.3<1\) → \(0.5 \times 0.3^2 = 0.045\)

\(|3.0-2.6|=0.4<1\) → \(0.5 \times 0.4^2 = 0.08\)

\(|-2.5-(-1.8)|=0.7<1\) → \(0.5 \times 0.7^2 = 0.245\)

总损失:

\[L = \frac{0.045 + 0.045 + 0.08 + 0.245}{4} = 0.10375 \]

代码实现:

python
import torch
import torch.nn as nn

# 真实值和预测值
y_true = torch.tensor([1.2, -0.8, 3.0, -2.5])
y_pred = torch.tensor([0.9, -0.5, 2.6, -1.8])

# PyTorch的SmoothL1Loss(默认beta=1.0)
criterion = nn.SmoothL1Loss()
loss = criterion(y_pred, y_true)

print("Smooth L1 Loss:", loss.item())  # 输出 0.10375

PyTorch中的 beta参数 控制二次项与线性项的分界点:

  • 当 |y - ŷ| < beta 时使用二次项
  • 当 |y - ŷ| >= beta 时使用线性项

不同beta值的影响:

python
# 示例:调整beta值
criterion_beta05 = nn.SmoothL1Loss(beta=0.5)  # 更早切换到线性区域
criterion_beta2 = nn.SmoothL1Loss(beta=2.0)  # 更晚切换到线性区域