2.4 损失函数 (Loss Functions)


文档摘要

2.4 损失函数 (Loss Functions) 第二章:PyTorch 模型构建领域 - 2.4 损失函数 (Loss Functions) 详解 2.4.1 损失函数的概念与重要性 损失函数,也称为目标函数或代价函数,是一个用于评估模型预测结果与实际目标值之间差异的函数。在监督学习中,我们的目标是训练一个模型,使其能够尽可能准确地预测未见过的数据。而损失函数正是实现这一目标的关键工具。 为什么损失函数如此重要? 量化模型误差: 损失函数能够将模型预测的“好坏”量化为一个单一的数值。这个数值越小,代表模型的预测越接近真实值,模型的性能也就越好。 优化目标: 损失函数是模型优化的目标。训练神经网络的过程,本质上就是不断迭代调整模型参数,以最小化损失函数的值。

2.4 损失函数 (Loss Functions)

第二章:PyTorch 模型构建领域 - 2.4 损失函数 (Loss Functions) 详解

2.4.1 损失函数的概念与重要性

损失函数,也称为目标函数或代价函数,是一个用于评估模型预测结果与实际目标值之间差异的函数。在监督学习中,我们的目标是训练一个模型,使其能够尽可能准确地预测未见过的数据。而损失函数正是实现这一目标的关键工具。

为什么损失函数如此重要?

  • 量化模型误差: 损失函数能够将模型预测的“好坏”量化为一个单一的数值。这个数值越小,代表模型的预测越接近真实值,模型的性能也就越好。

  • 优化目标: 损失函数是模型优化的目标。训练神经网络的过程,本质上就是不断迭代调整模型参数,以最小化损失函数的值。

  • 指导模型学习方向: 损失函数的梯度信息 (通过反向传播计算) 指导着模型参数更新的方向。模型会沿着损失函数梯度下降的方向调整参数,逐步逼近最优解。

简单来说,损失函数就像是训练神经网络的“导航仪”,它告诉模型当前预测的偏差程度,并指引模型朝着正确的方向前进。

我们可以用一个简单的mermaid图来表示损失函数在模型训练中的作用:

图 2.4.1 损失函数在模型训练中的作用流程图

如图所示,数据输入模型后得到预测结果,损失函数比较预测结果与真实标签,计算出一个损失值。这个损失值会被用于反向传播,调整模型参数,从而在下一次迭代中,模型能够做出更准确的预测,损失值也会随之降低。

2.4.2 PyTorch 中的损失函数模块 torch.nn.modules.loss

PyTorch 提供了丰富的损失函数,都封装在 torch.nn.modules.loss 模块中。 我们可以通过导入 torch.nn (通常简写为 nn) 来使用这些预定义的损失函数。

import torch.nn as nn

PyTorch 的损失函数主要分为以下几类,每类适用于不同的任务类型:

  • 回归损失 (Regression Loss): 用于回归问题,预测连续数值型目标变量。

  • 分类损失 (Classification Loss): 用于分类问题,预测离散类别型目标变量。

  • 其他损失 (Other Losses): 用于特定任务或场景的损失函数,例如对比学习、排序学习等。

接下来,我们将详细介绍一些常用的损失函数,并给出 PyTorch 代码示例。

2.4.3 常用的回归损失函数及其代码实践

回归任务的目标是预测一个连续的数值。常用的回归损失函数包括:

2.4.3.1 均方误差损失 (Mean Squared Error Loss, MSELoss)

公式:

MSELoss(y, \hat{y}) = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2

其中,y_i 是真实值,\hat{y}_i 是模型预测值,n 是样本数量。

特点:

  • 对误差平方: MSELoss 计算预测值与真实值差的平方的平均值。

  • 对大误差敏感: 由于平方操作,MSELoss 对较大的误差惩罚更大,因此对异常值比较敏感。

  • 梯度平滑: MSELoss 的梯度是线性的,在优化过程中比较平滑,容易收敛。

适用场景: 回归问题,特别是当误差分布接近高斯分布时,MSELoss 是一个常用的选择。

PyTorch 代码示例:

import torch import torch.nn as nn # 假设真实值和预测值 y_true = torch.tensor([3.0, -0.5, 2.0, 7.0]) y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0]) # 创建 MSELoss 实例 mse_loss = nn.MSELoss() # 计算损失值 loss = mse_loss(y_pred, y_true) print(f"MSE Loss: {loss.item()}") # 输出 MSE Loss: 0.375 # 使用函数式 API import torch.nn.functional as F loss_functional = F.mse_loss(y_pred, y_true) print(f"Functional MSE Loss: {loss_functional.item()}") # 输出 Functional MSE Loss: 0.375

2.4.3.2 L1 损失 (L1 Loss, L1Loss) / 平均绝对误差损失 (Mean Absolute Error Loss, MAE Loss)

公式:

L1Loss(y, \hat{y}) = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|

特点:

  • 对误差取绝对值: L1Loss 计算预测值与真实值差的绝对值的平均值。

  • 对异常值鲁棒: 由于使用绝对值而非平方,L1Loss 对异常值不如 MSELoss 敏感。

  • 梯度不平滑: L1Loss 在 0 点处不可导,梯度不平滑,可能导致优化过程震荡。

适用场景: 回归问题,当数据中存在较多异常值时,L1Loss 可能比 MSELoss 更为稳健。

PyTorch 代码示例:

import torch import torch.nn as nn # 假设真实值和预测值 (与 MSELoss 示例相同) y_true = torch.tensor([3.0, -0.5, 2.0, 7.0]) y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0]) # 创建 L1Loss 实例 l1_loss = nn.L1Loss() # 计算损失值 loss = l1_loss(y_pred, y_true) print(f"L1 Loss: {loss.item()}") # 输出 L1 Loss: 0.375 # 使用函数式 API import torch.nn.functional as F loss_functional = F.l1_loss(y_pred, y_true) print(f"Functional L1 Loss: {loss_functional.item()}") # 输出 Functional L1 Loss: 0.375

2.4.3.3 Smooth L1 Loss (SmoothL1Loss)

公式:

SmoothL1Loss(y, \hat{y}) = \begin{cases} 0.5 (y - \hat{y})^2 & \text{if } |y - \hat{y}| < \beta \\ |y - \hat{y}| - 0.5 \beta & \text{otherwise} \end{cases}

其中,\beta 是一个超参数,通常设置为 1。

特点:

  • 结合 MSELoss 和 L1Loss 的优点: 当误差较小时,Smooth L1 Loss 类似于 MSELoss,梯度平滑;当误差较大时,类似于 L1Loss,对异常值更鲁棒。

  • 更稳定: Smooth L1 Loss 在训练过程中更加稳定,不容易出现梯度爆炸或梯度消失的问题。

适用场景: 回归问题,特别是目标检测等任务中,Smooth L1 Loss 经常被用作 bounding box 回归的损失函数。

PyTorch 代码示例:

import torch import torch.nn as nn # 假设真实值和预测值 (与 MSELoss 示例相同) y_true = torch.tensor([3.0, -0.5, 2.0, 7.0]) y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0]) # 创建 SmoothL1Loss 实例 smooth_l1_loss = nn.SmoothL1Loss() # 计算损失值 loss = smooth_l1_loss(y_pred, y_true) print(f"Smooth L1 Loss: {loss.item()}") # 输出 Smooth L1 Loss: 0.25 # 可以调整 beta 参数 smooth_l1_loss_beta_2 = nn.SmoothL1Loss(beta=2.0) loss_beta_2 = smooth_l1_loss_beta_2(y_pred, y_true) print(f"Smooth L1 Loss (beta=2.0): {loss_beta_2.item()}") # 输出 Smooth L1 Loss (beta=2.0): 0.375 # 使用函数式 API import torch.nn.functional as F loss_functional = F.smooth_l1_loss(y_pred, y_true) print(f"Functional Smooth L1 Loss: {loss_functional.item()}") # 输出 Functional Smooth L1 Loss: 0.25

2.4.4 常用的分类损失函数及其代码实践

分类任务的目标是将数据样本划分到不同的类别中。 常用的分类损失函数包括:

2.4.4.1 二元交叉熵损失 (Binary Cross-Entropy Loss, BCELoss)

公式:

BCELoss(y, \hat{y}) = -\frac{1}{n} \sum_{i=1}^{n} [y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i)]

其中,y_i 是真实标签 (0 或 1),\hat{y}_i 是模型预测的属于类别 1 的概率, n 是样本数量。

特点:

  • 用于二分类: BCELoss 专门用于二分类问题。

  • 衡量概率分布差异: BCELoss 衡量的是预测概率分布与真实概率分布之间的差异。

  • 与 Sigmoid 激活函数配合使用: 通常在模型输出层使用 Sigmoid 激活函数,将输出值压缩到 (0, 1) 区间,表示概率。

适用场景: 二分类问题,例如图像二分类、情感分析等。

PyTorch 代码示例:

import torch import torch.nn as nn # 假设真实标签和模型输出 (未经 Sigmoid) y_true = torch.tensor([1.0, 0.0, 1.0, 0.0]) # 注意是浮点数类型 logits = torch.tensor([2.0, -1.0, 3.0, -2.0]) # 需要先使用 Sigmoid 激活函数将 logits 转换为概率 y_pred_probs = torch.sigmoid(logits) print(f"Predicted Probabilities: {y_pred_probs}") # 输出 Predicted Probabilities: tensor([0.8808, 0.2689, 0.9526, 0.1192]) # 创建 BCELoss 实例 bce_loss = nn.BCELoss() # 计算损失值 loss = bce_loss(y_pred_probs, y_true) print(f"BCE Loss: {loss.item()}") # 输出 BCE Loss: 0.2555 # 使用函数式 API import torch.nn.functional as F loss_functional = F.binary_cross_entropy(y_pred_probs, y_true) print(f"Functional BCE Loss: {loss_functional.item()}") # 输出 Functional BCE Loss: 0.2555

2.4.4.2 带对数 Sigmoid 的二元交叉熵损失 (BCEWithLogitsLoss)

特点:

  • 内置 Sigmoid 激活函数: BCEWithLogitsLoss 将 Sigmoid 激活函数和 BCELoss 损失函数结合在一起。

  • 数值稳定性更佳: 在数值计算上,BCEWithLogitsLoss 比先 Sigmoid 再 BCELoss 更稳定,避免了数值溢出的问题。

  • 直接接受 logits 作为输入: 可以直接将模型的原始输出 (logits,即未经过 Sigmoid 激活的值) 作为输入,无需手动添加 Sigmoid 层。

适用场景: 二分类问题,推荐使用 BCEWithLogitsLoss,因为它更方便且数值更稳定。

PyTorch 代码示例:

import torch import torch.nn as nn # 假设真实标签和模型输出 (logits) y_true = torch.tensor([1.0, 0.0, 1.0, 0.0]) # 注意是浮点数类型 logits = torch.tensor([2.0, -1.0, 3.0, -2.0]) # 创建 BCEWithLogitsLoss 实例 bce_logits_loss = nn.BCEWithLogitsLoss() # 计算损失值 (直接使用 logits 作为输入) loss = bce_logits_loss(logits, y_true) print(f"BCEWithLogits Loss: {loss.item()}") # 输出 BCEWithLogits Loss: 0.2555 # 使用函数式 API import torch.nn.functional as F loss_functional = F.binary_cross_entropy_with_logits(logits, y_true) print(f"Functional BCEWithLogits Loss: {loss_functional.item()}") # 输出 Functional BCEWithLogits Loss: 0.2555

2.4.4.3 交叉熵损失 (Cross-Entropy Loss, CrossEntropyLoss)

公式:

对于多分类问题,假设有 C 个类别,交叉熵损失可以表示为:

CrossEntropyLoss(y, \hat{y}) = -\frac{1}{n} \sum_{i=1}^{n} \sum_{c=1}^{C} y_{ic} \log(\hat{y}_{ic})

其中,y_{ic} 是 one-hot 编码的真实标签,当样本 i 属于类别 c 时为 1,否则为 0。 \hat{y}_{ic} 是模型预测的样本 i 属于类别 c 的概率。

特点:

  • 用于多分类: CrossEntropyLoss 主要用于多分类问题。

  • 衡量类别分布差异: 类似于 BCELoss,CrossEntropyLoss 也衡量预测类别分布与真实类别分布之间的差异。

  • 与 Softmax 激活函数配合使用: 通常在模型输出层使用 Softmax 激活函数,将输出值转换为概率分布,保证所有类别的概率之和为 1。

  • PyTorch 中 CrossEntropyLoss 已经内置 Softmax 和 NLLLoss: PyTorch 的 CrossEntropyLoss 实际上是将 Softmax 激活函数和负对数似然损失 (NLLLoss) 结合在一起。 因此,使用 CrossEntropyLoss 时,模型输出层不需要显式添加 Softmax 层。 它直接接受模型的原始输出 (logits) 作为输入。

适用场景: 多分类问题,例如图像分类、文本分类等。

PyTorch 代码示例:

import torch import torch.nn as nn # 假设真实标签和模型输出 (logits) y_true = torch.tensor([1, 0, 2, 1]) # 类别索引,0, 1, 2 代表三个类别 logits = torch.tensor([[2.0, 1.0, 0.1], # 样本 1 的 logits [0.5, 3.0, 0.2], # 样本 2 的 logits [0.3, 0.8, 4.0], # 样本 3 的 logits [1.2, 2.5, 0.6]])# 样本 4 的 logits # 创建 CrossEntropyLoss 实例 cross_entropy_loss = nn.CrossEntropyLoss() # 计算损失值 (直接使用 logits 作为输入) loss = cross_entropy_loss(logits, y_true) print(f"CrossEntropy Loss: {loss.item()}") # 输出 CrossEntropy Loss: 1.0555 # 使用函数式 API import torch.nn.functional as F loss_functional = F.cross_entropy(logits, y_true) print(f"Functional CrossEntropy Loss: {loss_functional.item()}") # 输出 Functional CrossEntropy Loss: 1.0555

2.4.4.4 负对数似然损失 (Negative Log Likelihood Loss, NLLLoss)

特点:

  • 通常与 LogSoftmax 配合使用: NLLLoss 本身不包含 Softmax 激活函数,通常需要先使用 nn.LogSoftmaxF.log_softmax 对模型输出进行处理,得到对数概率,再输入到 NLLLoss 中。

  • 理解 CrossEntropyLoss 的组成: CrossEntropyLoss 其实是 LogSoftmaxNLLLoss 的组合。

适用场景: 多分类问题,当需要更精细地控制概率计算过程时,可以先使用 LogSoftmax 获取对数概率,再使用 NLLLoss 计算损失。 但通常情况下,直接使用 CrossEntropyLoss 更为方便。

PyTorch 代码示例:

import torch import torch.nn as nn import torch.nn.functional as F # 假设真实标签和模型输出 (logits) - 与 CrossEntropyLoss 示例相同 y_true = torch.tensor([1, 0, 2, 1]) logits = torch.tensor([[2.0, 1.0, 0.1], [0.5, 3.0, 0.2], [0.3, 0.8, 4.0], [1.2, 2.5, 0.6]]) # 使用 LogSoftmax 获取对数概率 log_probs = F.log_softmax(logits, dim=1) # dim=1 表示对每个样本的类别维度进行 Softmax # 创建 NLLLoss 实例 nll_loss = nn.NLLLoss() # 计算损失值 (输入对数概率) loss = nll_loss(log_probs, y_true) print(f"NLL Loss: {loss.item()}") # 输出 NLL Loss: 1.0555 # 可以看到,NLLLoss 的结果与 CrossEntropyLoss 相同,因为 CrossEntropyLoss 内部就包含了 LogSoftmax 和 NLLLoss

2.4.5 其他常用的损失函数 (简要介绍)

除了上述常用的回归和分类损失函数外,PyTorch 还提供了许多其他损失函数,用于解决更特定的问题,例如:

  • KL 散度损失 (KLDivLoss): 用于衡量两个概率分布之间的差异,常用于生成模型、知识蒸馏等任务。

  • Margin Ranking Loss (MarginRankingLoss): 用于排序学习任务,例如学习物品排序、推荐系统等。

  • Triplet Margin Loss (TripletMarginLoss): 用于度量学习任务,例如人脸识别、图像检索等,目标是拉近相似样本的距离,推远不相似样本的距离。

  • CTC Loss (CTCLoss): 用于序列到序列学习任务,例如语音识别、手写文字识别等,处理输入序列和输出序列长度不一致的情况。

这些损失函数在特定领域有着重要的应用,您可以根据具体的任务需求选择合适的损失函数。

2.4.6 如何选择合适的损失函数

选择合适的损失函数是构建有效模型的关键步骤。 以下是一些选择损失函数的通用原则:

  • 任务类型: 首先要明确任务类型是回归还是分类。 回归问题通常选择回归损失 (MSELoss, L1Loss, SmoothL1Loss),分类问题通常选择分类损失 (BCELoss, CrossEntropyLoss)。

  • 输出层激活函数: 损失函数的选择通常与模型输出层的激活函数相关。 例如,二分类问题使用 Sigmoid 激活函数时,通常搭配 BCELoss 或 BCEWithLogitsLoss; 多分类问题使用 Softmax 激活函数 (或使用 CrossEntropyLoss 内置的 Softmax) 时,通常搭配 CrossEntropyLoss 或 NLLLoss。

  • 数据特性: 考虑数据集中是否存在异常值。 如果存在较多异常值,L1Loss 或 SmoothL1Loss 可能比 MSELoss 更稳健。

  • 优化难度: 不同损失函数的梯度特性会影响优化难度。 例如,MSELoss 的梯度比较平滑,容易收敛; L1Loss 的梯度不平滑,可能导致震荡。

  • 具体任务目标: 根据具体任务的目标选择最合适的损失函数。 例如,在目标检测任务中,bounding box 回归通常使用 SmoothL1Loss,分类任务使用 CrossEntropyLoss。

可以使用以下 mermaid 图来辅助选择损失函数:

图 2.4.6 损失函数选择流程图

2.4.7 自定义损失函数

除了 PyTorch 提供的预定义损失函数外,您还可以根据需要自定义损失函数。 自定义损失函数可以通过以下两种方式实现:

  1. 继承 nn.Module 类: 创建一个新的类,继承 nn.Module,并在 forward 方法中实现损失函数的计算逻辑。

  2. 使用函数式 API: 直接使用 PyTorch 的张量操作和函数式 API (例如 torch.mean, torch.sum, torch.log, torch.exp 等) 来实现损失函数的计算逻辑。

自定义损失函数示例 (继承 nn.Module):

import torch import torch.nn as nn class MyCustomLoss(nn.Module): def __init__(self): super(MyCustomLoss, self).__init__() def forward(self, y_pred, y_true): # 自定义损失计算逻辑,例如这里简单地使用 MSELoss loss = torch.mean((y_pred - y_true)**2) # 注意这里是 torch.mean 而不是 nn.MSELoss return loss # 使用自定义损失函数 y_true = torch.tensor([3.0, -0.5, 2.0, 7.0]) y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0]) custom_loss_func = MyCustomLoss() loss = custom_loss_func(y_pred, y_true) print(f"Custom Loss: {loss.item()}") # 输出 Custom Loss: 0.375

自定义损失函数示例 (函数式 API):

import torch import torch.nn.functional as F def my_functional_loss(y_pred, y_true): # 自定义损失计算逻辑,例如这里简单地使用 L1Loss loss = torch.mean(torch.abs(y_pred - y_true)) # 注意这里使用 torch.mean 和 torch.abs return loss # 使用自定义损失函数 y_true = torch.tensor([3.0, -0.5, 2.0, 7.0]) y_pred = torch.tensor([2.5, 0.0, 2.0, 8.0]) loss = my_functional_loss(y_pred, y_true) print(f"Functional Custom Loss: {loss.item()}") # 输出 Functional Custom Loss: 0.375

自定义损失函数提供了极大的灵活性,您可以根据具体的任务需求设计更合适的损失函数,从而更好地优化模型性能。

2.4.8 总结

损失函数是 PyTorch 模型构建中不可或缺的一部分。 本文详细介绍了损失函数的概念、重要性、常用类型以及代码实践。 我们学习了:

  • 损失函数的作用是量化模型预测与真实值之间的误差,并指导模型优化。

  • PyTorch 提供了丰富的预定义损失函数,包括回归损失 (MSELoss, L1Loss, SmoothL1Loss) 和分类损失 (BCELoss, BCEWithLogitsLoss, CrossEntropyLoss, NLLLoss)。

  • 不同的损失函数适用于不同的任务类型和数据特性。

  • 可以根据需要自定义损失函数,以满足特定的任务需求。

理解和掌握损失函数的选择和使用,是成为一名优秀的深度学习工程师的关键一步。 希望本文能够帮助您更好地理解 PyTorch 中的损失函数,并在模型构建的道路上取得更大的进步。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U