引言

损失函数(Loss Function)是深度学习中至关重要的组成部分,它衡量模型预测值与真实值之间的差异,指导模型参数的优化方向。PyTorch提供了丰富的损失函数库,本文将深入解析12种常用损失函数的数学原理、应用场景和代码实践。


1. nn.MSELoss - 均方误差损失

数学公式

均方误差损失(Mean Squared Error Loss)计算预测值与真实值之间差的平方的均值:

$$
\text{MSE}(x, y) = \frac{1}{n} \sum_{i=1}^{n} (x_i - y_i)^2
$$

在PyTorch中,还可以设置reduction参数:

  • 'mean'(默认):返回损失的平均值
  • 'sum':返回损失的总和
  • 'none':返回每个元素的损失

用途场景

  1. 回归问题:预测连续值,如房价预测、股票价格预测
  2. 图像重建:自编码器、图像超分辨率任务
  3. 语音合成:生成模型的重建损失
  4. 信号处理:噪声消除、信号恢复

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch
import torch.nn as nn

# 创建MSELoss实例
mse_loss = nn.MSELoss()
mse_loss_sum = nn.MSELoss(reduction='sum')
mse_loss_none = nn.MSELoss(reduction='none')

# 模拟预测值和真实值
predictions = torch.tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True)
targets = torch.tensor([1.5, 2.5, 2.8, 4.2])

# 计算损失
loss_mean = mse_loss(predictions, targets)
loss_sum = mse_loss_sum(predictions, targets)
loss_none = mse_loss_none(predictions, targets)

print(f"Mean MSE Loss: {loss_mean.item():.4f}") # 0.1450
print(f"Sum MSE Loss: {loss_sum.item():.4f}") # 0.5800
print(f"None MSE Loss: {loss_none}") # tensor values

# 反向传播
loss_mean.backward()
print(f"Gradients: {predictions.grad}")

注意事项

  1. 对异常值敏感:由于平方操作,异常值会产生较大的梯度,可能导致训练不稳定
  2. 梯度消失问题:当预测值与真实值接近时,梯度会变得很小
  3. 数值稳定性:对于非常大的值,平方可能导致数值溢出
  4. 学习率选择:由于梯度与误差成正比,需要合理设置学习率

2. nn.CrossEntropyLoss - 交叉熵损失

数学公式

交叉熵损失(Cross-Entropy Loss)结合了LogSoftmaxNLLLoss

$$
\text{CE}(x, y) = -\sum_{c=1}^{C} y_c \log(\text{softmax}(x)_c)
$$

对于单标签分类(真实标签为one-hot编码):

$$
\text{CE}(x, y) = -\log\left(\frac{e^{x_{y}}}{\sum_{j} e^{x_j}}\right)
$$

用途场景

  1. 多分类任务:图像分类(ImageNet、CIFAR)、文本分类
  2. 语言模型:预测下一个词的概率分布
  3. 命名实体识别:序列标注任务
  4. 目标检测:类别预测分支

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import torch
import torch.nn as nn

# 创建CrossEntropyLoss实例
ce_loss = nn.CrossEntropyLoss()

# 模拟模型输出(batch_size=3, num_classes=5)
# 注意:CrossEntropyLoss期望未归一化的分数(logits)
logits = torch.tensor([
[2.0, 1.0, 0.1, 0.5, 0.3], # 样本1,预测类别0概率最高
[0.5, 2.5, 0.2, 0.1, 0.3], # 样本2,预测类别1概率最高
[0.1, 0.2, 0.3, 3.0, 0.5] # 样本3,预测类别3概率最高
], requires_grad=True)

# 真实标签(类别索引)
targets = torch.tensor([0, 1, 3])

# 计算损失
loss = ce_loss(logits, targets)
print(f"Cross Entropy Loss: {loss.item():.4f}")

# 带权重的交叉熵(处理类别不平衡)
class_weights = torch.tensor([1.0, 2.0, 1.5, 0.5, 1.0]) # 类别权重
weighted_ce_loss = nn.CrossEntropyLoss(weight=class_weights)
weighted_loss = weighted_ce_loss(logits, targets)
print(f"Weighted Cross Entropy Loss: {weighted_loss.item():.4f}")

注意事项

  1. 不要在输入前加Softmax:该损失函数内部已经包含了Softmax操作
  2. 目标标签格式:类别索引(非one-hot编码)
  3. 类别不平衡:使用weight参数处理不平衡数据集
  4. 标签平滑:使用label_smoothing参数防止过拟合

3. nn.BCELoss / nn.BCEWithLogitsLoss - 二分类交叉熵

数学公式

二分类交叉熵(Binary Cross-Entropy):

$$
\text{BCE}(x, y) = -[y \log(x) + (1-y) \log(1-x)]
$$

对于批量数据:
$$
\text{BCE}(x, y) = -\frac{1}{n}\sum_{i=1}^{n} [y_i \log(x_i) + (1-y_i) \log(1-x_i)]
$$

代码示例

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

# 使用BCELoss(需要先Sigmoid)
bce_loss = nn.BCELoss()
predictions = torch.tensor([0.8, 0.2, 0.6, 0.9]) # 已经过Sigmoid
targets = torch.tensor([1.0, 0.0, 1.0, 1.0])
loss_bce = bce_loss(predictions, targets)
print(f"BCE Loss: {loss_bce.item():.4f}")

# 使用BCEWithLogitsLoss(推荐)
bce_logits_loss = nn.BCEWithLogitsLoss()
logits = torch.tensor([1.5, -1.2, 0.5, 2.3], requires_grad=True) # 原始logits
loss_bce_logits = bce_logits_loss(logits, targets)
print(f"BCE with Logits Loss: {loss_bce_logits.item():.4f}")

4. nn.NLLLoss - 负对数似然损失

数学公式

$$
\text{NLL}(x, y) = -x_{y}
$$

其中x是已经过LogSoftmax处理的输入。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import torch.nn as nn
import torch.nn.functional as F

# 创建NLLLoss实例
nll_loss = nn.NLLLoss()

# LogSoftmax输出
log_probs = torch.tensor([
[-0.5, -1.2, -2.0, -1.8, -1.5],
[-1.8, -0.3, -2.1, -1.5, -2.0],
[-2.0, -1.8, -0.8, -1.0, -1.2]
], requires_grad=True)

targets = torch.tensor([0, 1, 2])
loss = nll_loss(log_probs, targets)
print(f"NLL Loss: {loss.item():.4f}")

5. nn.L1Loss - L1损失

数学公式

$$
\text{L1}(x, y) = \frac{1}{n} \sum_{i=1}^{n} |x_i - y_i|
$$

用途场景

  1. 回归任务:对异常值更鲁棒
  2. 图像处理:图像去噪、风格迁移
  3. 稀疏编码:L1正则化诱导稀疏解

6. nn.SmoothL1Loss - 平滑L1损失

数学公式

$$
\text{SmoothL1}(x, y) = \begin{cases}
\frac{1}{2}(x - y)^2 / \beta & \text{if } |x - y| < \beta \
|x - y| - \frac{1}{2}\beta & \text{otherwise}
\end{cases}
$$

其中β(beta)是阈值参数,默认为1.0。


7. nn.KLDivLoss - KL散度损失

数学公式

$$
\text{KL}(P || Q) = \sum_{i} P(i) \log\left(\frac{P(i)}{Q(i)}\right)
$$

用途场景

  1. 知识蒸馏:教师模型向学生模型传递知识
  2. 变分自编码器(VAE):正则化隐空间分布
  3. 强化学习:策略优化(PPO、SAC)

8. nn.MarginRankingLoss - 边际排序损失

数学公式

$$
\text{loss}(x_1, x_2, y) = \max(0, -y \cdot (x_1 - x_2) + \text{margin})
$$

其中y ∈ {-1, 1}表示排序关系。


9. nn.TripletMarginLoss - 三元组边际损失

数学公式

$$
\text{loss}(a, p, n) = \max(0, d(a, p) - d(a, n) + \text{margin})
$$

用于人脸识别、行人重识别、图像检索等。


10. nn.CosineEmbeddingLoss - 余弦嵌入损失

数学公式

$$
\text{loss}(x_1, x_2, y) = \begin{cases}
1 - \cos(x_1, x_2) & \text{if } y = 1 \
\max(0, \cos(x_1, x_2) - \text{margin}) & \text{if } y = -1
\end{cases}
$$


11. nn.CTCLoss - CTC损失

数学公式

$$
\text{CTC}(x, y) = -\log P(y|x) = -\log \sum_{\pi \in \mathcal{B}^{-1}(y)} P(\pi|x)
$$

用于语音识别、OCR等序列任务。


12. nn.HingeEmbeddingLoss - 铰链嵌入损失

数学公式

$$
\text{loss}(x, y) = \begin{cases}
x & \text{if } y = 1 \
\max(0, \text{margin} - x) & \text{if } y = -1
\end{cases}
$$


总结

损失函数选择指南

任务类型 推荐损失函数 备注
二分类 BCEWithLogitsLoss 数值稳定,包含Sigmoid
多分类 CrossEntropyLoss 包含Softmax
回归 MSELoss / L1Loss MSE对异常值敏感
边界框回归 SmoothL1Loss 目标检测标配
知识蒸馏 KLDivLoss 需要温度参数
度量学习 TripletMarginLoss 人脸识别、ReID
语音识别 CTCLoss 无需对齐
排序学习 MarginRankingLoss 推荐系统

最佳实践

  1. 数值稳定性:优先使用包含内置激活函数的损失(如BCEWithLogitsLoss)
  2. 类别不平衡:使用class_weight或pos_weight参数
  3. 梯度分析:理解损失函数的梯度特性有助于选择合适的学习率
  4. 组合损失:复杂任务可以组合多种损失函数
1
2
3
4
5
# 示例:组合多种损失
def combined_loss(pred, target, alpha=0.5):
mse = nn.MSELoss()(pred, target)
l1 = nn.L1Loss()(pred, target)
return alpha * mse + (1 - alpha) * l1

希望本文能帮助你深入理解PyTorch损失函数,在实际项目中做出正确的选择!