深度学习中的损失函数详解
1. 二元交叉熵损失(Binary Cross-Entropy Loss)
在深度学习中,交叉熵损失是一种常用的损失函数。根据特定公式,第 $i$ 个训练数据实例的交叉熵损失(CE Loss)可以表示为:
[
L^{(i)}(y^{(i)}, \overline{y}^{(i)}) = -\overline{y}^{(i)} \log(y^{(i)}) - (1 - \overline{y}^{(i)}) \log(1 - y^{(i)})
]
在实际应用中,这种特殊情况的公式也被称为交叉熵损失或二元交叉熵损失。以下是使用 PyTorch 计算二元交叉熵损失的代码示例:
from torch.nn.functional import binary_cross_entropy
# 导入二元交叉熵损失函数
y_pred = torch.tensor([0.8])
# 类别 0 的输出概率 - y0
# 由于 y1 = 1 - y0,一个值就足够了
y_gt = torch.tensor([1.])
# 真实标签,只能是 0 或 1
loss = binary_cross_entropy(y_pred, y_gt)
# 计算交叉熵损失
这个代码片段展示了如何使用 PyTorch 中的
binary_cross_entropy
函数来计算二元交叉熵损失。
2. 像素交叉熵损失(Pixel Cross-Entropy Loss)
二元交叉熵损失的一种变体常用于估计一对向量或图像之间的不匹配程度,我们称之为像素交叉熵损失。它不仅可以用于图像,还可以用于任何一对向量的不匹配度量。在使用时,两个向量都需要进行归一化,即将其值限制在 0 到 1 之间。对于图像,通常像素值在 0 到 255 之间,我们可以通过将每个像素值除以 255 来进行归一化。
在图像自编码器中,这种对两个图像的比较非常有用。自编码器以图像为输入,从图像中创建低维描述符(通常称为图像嵌入),并尝试从嵌入中重建输入图像。自编码器网络的损失就是输入图像和重建图像之间的不匹配程度。像素交叉熵损失通常用于量化这种不匹配,其定义为:
[
L^{(i)}(\overline{y}^{(i)}, \vec{y}^{(i)}) = -\sum_{j=0}^{N - 1} (\overline{y}
{j}^{(i)} \log(y
{j}^{(i)}) + (1 - \overline{y}
{j}^{(i)}) \log(1 - y
{j}^{(i)}))
]
这里的 $N$ 是图像中的像素数量,而不是之前的类别数量。求和是针对图像中的像素进行的。需要注意的是,真实向量 $\overline{y}^{(i)}$ 不是一个独热向量,而是输入图像。尽管有这些差异,但这个公式的基本思想与二元交叉熵损失的公式是相同的。
可能有人会疑惑,当两个输入匹配时,像素交叉熵损失的值是多少呢?直观上,我们期望这个损失为零,但实际上,即使两个值完全相等,像素交叉熵损失也可能不为零。例如,当第 $i$ 个训练数据实例的第 $j$ 个像素 $\overline{y}
{j}^{(i)} = y
{j}^{(i)} = 0.25$ 时:
[
L^{(i)}(\overline{y}
{j}^{(i)}, \vec{y}
{j}^{(i)})|
{\overline{y}
{j}=\vec{y}
{j}^{(i)}=0.25} = -0.25\log(0.25) - 0.75\log(0.75) = 0.56 \neq 0
]
实际上,像素交叉熵损失只有在特殊情况下(如 $y
{j}^{(i)} = \overline{y}_{j}^{(i)} = 1$)才会为零。那么,为什么我们认为最小化这个损失会使 $\vec{y}$ 与 $\overline{y}$ 匹配呢?答案是,损失的绝对值并不重要,我们只关心在理想输出时损失达到最小值。可以证明,当输入与真实值匹配时,像素交叉熵损失确实达到最小值。以下是简要的证明过程:
设 $-L = \overline{y} \log(y) + (1 - \overline{y}) \log(1 - y)$,在最小值处,$\frac{\partial L}{\partial y} = 0$,这意味着:
[
-\frac{\partial L}{\partial y} = \frac{\overline{y}}{y} - \frac{1 - \overline{y}}{1 - y} = 0
]
由此可得 $y = \overline{y}$。因此,当网络输出与真实值匹配时,像素交叉熵损失达到最小值。
3. SoftMax 函数
在构建分类器时,我们的分类器可能会输出一个得分向量 $\vec{s}$,向量的第 $i$ 个元素对应第 $i$ 个类别。通常,我们会取得分向量的最大值作为神经网络对图像的预测标签。然而,这些得分是无界的,而神经网络在处理有界数值时通常表现更好,训练收敛更快,推理更准确。因此,我们希望将这些得分转换为概率,即取值范围在 $[0, 1]$ 之间且向量元素之和为 1 的数值。
SoftMax 函数可以将无界得分转换为概率。给定得分向量 $\vec{s} = [s_0, s_1, s_2, \cdots, s_{N - 1}]$,对应的 SoftMax 向量为:
[
\text{softmax}(\vec{s}) =
\begin{bmatrix}
\frac{e^{s_0}}{\sum_{k = 0}^{N - 1} e^{s_k}} \
\frac{e^{s_1}}{\sum_{k = 0}^{N - 1} e^{s_k}} \
\frac{e^{s_2}}{\sum_{k = 0}^{N - 1} e^{s_k}} \
\cdots \
\frac{e^{s_{N - 1}}}{\sum_{k = 0}^{N - 1} e^{s_k}}
\end{bmatrix}
]
SoftMax 向量具有以下特点:
- 向量的元素数量与可能的类别数量相同。
- 向量中所有元素的和为 1。
- 向量的第 $j$ 个元素表示第 $j$ 类的预测概率。
- 该公式可以处理任意得分,包括负数。
例如,对于一个包含 4 个类别的分类问题(猫、狗、飞机、汽车),得分向量 $\vec{s} = [9.99, 10, 0.01, -10]$ 经过 SoftMax 函数处理后得到的向量为 $[0.497, 0.502, 2.30\times10^{-5}, 1.04\times10^{-9}]$。这表明分类器预测图像为狗的概率略高于猫,而预测为飞机和汽车的概率非常低。
SoftMax 函数之所以被称为 SoftMax,是因为它是对 argmaxonehot 函数的平滑(可微)近似。argmaxonehot 函数会输出一个与最大得分索引对应的独热向量,它是不连续的。而 SoftMax 函数是连续的,当得分接近时,SoftMax 概率也会接近。以下是使用 PyTorch 计算 SoftMax 的代码示例:
from torch.nn.functional import softmax
# 导入 SoftMax 函数
scores = torch.tensor([9.99, 10, 0.01, -10])
# 得分通常是神经网络的原始未归一化输出
output = softmax(scores, dim=0)
# 计算 SoftMax
4. SoftMax 交叉熵损失(SoftMax Cross-Entropy Loss)
从前面的讨论可以看出,将分类器神经网络的最后一层设置为 SoftMax 层是很有必要的。这样,给定输入,网络会输出每个类别的概率。在训练过程中,我们需要根据这些概率与已知的真实概率来评估损失,这可以通过交叉熵损失来实现。因此,在分类器训练中,SoftMax 函数通常会接着使用交叉熵损失。
在许多深度学习框架(如 PyTorch)中,SoftMax 交叉熵损失被作为一个单独的操作提供,这既方便又在数值计算上更优。下面通过一个例子来说明 SoftMax 交叉熵损失如何随着输出预测的变化而变化。
考虑一个图像分类问题,我们要判断图像中包含猫(类别 0)、狗(类别 1)、飞机(类别 2)还是汽车(类别 3)。假设真实图像中包含狗,真实标签用独热向量 $[0, 1, 0, 0]$ 表示。如果分类器预测向量为 $[0.498, 0.502, 0, 0]$,这意味着它对猫和狗的预测概率几乎相等,这是一个糟糕的预测,对应的交叉熵损失较高(0.688)。相反,如果分类器预测向量为 $[0.003, 0.997, 0, 0]$,表明它非常确定图像包含狗,这是一个好的预测,交叉熵损失较低(0.0032)。
以下是使用 PyTorch 计算 SoftMax 交叉熵损失的代码示例:
from torch.nn.functional import cross_entropy
# 导入交叉熵损失函数
scores = torch.tensor([[9.99, 10, 0.01, -10]])
y_gt = torch.tensor([1])
# 真实类别索引,范围从 0 到类别数 - 1
loss = cross_entropy(scores, y_gt)
# 计算 SoftMax 交叉熵损失
5. Focal Loss
在训练过程中,当存在数据不平衡问题,即某些类别的训练数据实例数量明显少于其他类别时,我们需要明智地使用训练数据。直观上,我们应该关注那些网络表现不佳的训练数据实例(即“难”样本),而不是在网络表现良好的“易”样本上过度优化。
为了实现这一点,我们可以对远离真实值的训练数据实例的损失赋予更大的权重,对接近真实值的训练数据实例的损失赋予较小的权重。以二元交叉熵损失为例,第 $i$ 个训练实例的损失可以重写为:
[
L^{(i)}(y^{(i)}, \overline{y}^{(i)}) =
\begin{cases}
-\log(y^{(i)}) & \text{如果真实类别为 1,即 } \overline{y}^{(i)} = 1 \
-\log(1 - y^{(i)}) & \text{如果真实类别为 0,即 } \overline{y}^{(i)} = 0
\end{cases}
]
当真实类别为 1 时,$(1 - y)$ 衡量了预测值与真实值的偏离程度,我们可以将损失乘以 $(1 - y)^{\gamma}$($\gamma$ 是一个超参数,例如 $\gamma = 2$)来降低好预测的损失权重,增加坏预测的损失权重。同理,当真实类别为 0 时,我们将损失乘以 $y^{\gamma}$。最终的损失可以简化为:
[
L(\vec{y}, \overline{y}) = -(1 - y_t)^{\gamma} \log(y_t)
]
其中
[
y_t =
\begin{cases}
y & \text{如果真实类别为 1,即 } \overline{y} = 1 \
1 - y & \text{如果真实类别为 0,即 } \overline{y} = 0
\end{cases}
]
这就是 Focal Loss 的常用表达式。Focal Loss 通常在数据不平衡的情况下使用,例如在计算机视觉中,当背景数据远多于前景数据时,使用 Focal Loss 可以提高分类器的准确性。以下是使用 PyTorch 实现 Focal Loss 的代码示例:
import torch
def focal_loss(y, y_gt, gamma):
y_t = (y_gt * y) + ((1 - y_gt) * (1 - y))
# 如果 y_gt 为 1,y_t 为 y;如果 y_gt 为 0,y_t 为 1 - y
loss = -1 * torch.pow((1 - y_t), gamma) * torch.log(y_t)
return loss
6. Hinge Loss
SoftMax 交叉熵损失只有在理想情况下(正确类别得分无穷大,其他类别得分负无穷大)才会变为零。因此,这种损失会持续推动网络进行改进,但在实际中永远无法达到理想状态。有时,我们只希望在正确类别得分最大时停止改变网络,而不再关注正确和错误得分之间的距离。这就是 Hinge Loss 发挥作用的地方。
Hinge Loss 类似于一扇只能朝一个方向打开的门,当某个良好标准不满足时,损失函数会增加;当标准满足时,损失变为零且不再减小。在多类别分类问题中,常用的 Hinge Loss 是多类别 SVM 损失(Multiclass SVM Loss)。
考虑一个图像分类器,它预测图像中包含猫(类别 0)、狗(类别 1)、飞机(类别 2)或汽车(类别 3)。给定一个(训练数据实例,真实标签)对 $(\vec{x}, c)$(即输入 $\vec{x}$ 对应的真实类别为 $c$),多类别 SVM 损失定义为:
[
\sum_{j = 0, j \neq c}^{N - 1} \max(0, y_j - y_c + m)
]
其中 $m$ 是一个边界值(通常 $m = 1$)。为了更好地理解这个公式,我们先考虑没有边界值的情况:
[
\sum_{j = 0, j \neq c}^{N - 1} \max(0, y_j - y_c)
]
在这个公式中,我们只对除真实类别之外的所有类别进行求和。对于这些错误类别,我们希望其得分 $y_j$ 小于正确类别的得分 $y_c$。有两种情况:
-
好的输出
:如果 $y_j - y_c < 0$,则 $\max(0, y_j - y_c) = 0$,这意味着该类别对损失的贡献为零。
-
坏的输出
:如果 $y_j > y_c$,则 $\max(0, y_j - y_c) = y_j - y_c$,该类别对损失的贡献为正。
在实际应用中,设置边界值 $m$ 是为了惩罚那些正确类别得分仅略高于错误类别得分的预测,从而迫使分类器更有信心地预测正确类别。以下是一个简单的流程图,展示了 Hinge Loss 的计算过程:
graph TD
A[输入得分向量 y 和真实类别 c] --> B[初始化损失 L = 0]
B --> C{遍历所有类别 j}
C -- j != c --> D[计算 y_j - y_c + m]
D --> E{y_j - y_c + m > 0?}
E -- 是 --> F[L = L + (y_j - y_c + m)]
E -- 否 --> C
C -- j == c --> C
C -- 遍历结束 --> G[返回损失 L]
综上所述,不同的损失函数在不同的场景下有各自的优势。在实际应用中,我们需要根据具体问题和数据特点选择合适的损失函数,以提高模型的性能。
深度学习中的损失函数详解(下半部分)
7. 损失函数对比总结
为了更清晰地了解各种损失函数的特点和适用场景,我们将前面介绍的几种损失函数进行对比总结,如下表所示:
| 损失函数名称 | 公式 | 特点 | 适用场景 |
| — | — | — | — |
| 二元交叉熵损失(Binary Cross-Entropy Loss) | (L^{(i)}(y^{(i)}, \overline{y}^{(i)}) = -\overline{y}^{(i)} \log(y^{(i)}) - (1 - \overline{y}^{(i)}) \log(1 - y^{(i)})) | 常用于二分类问题,衡量预测概率与真实标签之间的差异 | 简单二分类任务,如判断图片是否包含特定物体 |
| 像素交叉熵损失(Pixel Cross-Entropy Loss) | (L^{(i)}(\overline{y}^{(i)}, \vec{y}^{(i)}) = -\sum_{j=0}^{N - 1} (\overline{y}
{j}^{(i)} \log(y
{j}^{(i)}) + (1 - \overline{y}
{j}^{(i)}) \log(1 - y
{j}^{(i)}))) | 可用于衡量向量或图像之间的不匹配程度 | 图像自编码器等需要比较图像相似度的任务 |
| SoftMax 函数 | (\text{softmax}(\vec{s}) = \begin{bmatrix}\frac{e^{s_0}}{\sum_{k = 0}^{N - 1} e^{s_k}} \\frac{e^{s_1}}{\sum_{k = 0}^{N - 1} e^{s_k}} \\frac{e^{s_2}}{\sum_{k = 0}^{N - 1} e^{s_k}} \\cdots \\frac{e^{s_{N - 1}}}{\sum_{k = 0}^{N - 1} e^{s_k}}\end{bmatrix}) | 将无界得分转换为概率分布 | 多分类问题中,将模型输出转换为概率 |
| SoftMax 交叉熵损失(SoftMax Cross-Entropy Loss) | 结合 SoftMax 函数和交叉熵损失 | 常用于多分类任务的训练,能有效衡量预测概率与真实标签的差异 | 图像分类、文本分类等多分类任务 |
| Focal Loss | (L(\vec{y}, \overline{y}) = -(1 - y_t)^{\gamma} \log(y_t)),其中 (y_t = \begin{cases}y & \text{如果真实类别为 1,即 } \overline{y} = 1 \1 - y & \text{如果真实类别为 0,即 } \overline{y} = 0\end{cases}) | 对难样本赋予更高权重,解决数据不平衡问题 | 数据不平衡的分类任务,如医疗图像中罕见病的检测 |
| 多类别 SVM 损失(Multiclass SVM Loss,Hinge Loss) | (\sum_{j = 0, j \neq c}^{N - 1} \max(0, y_j - y_c + m)) | 当正确类别得分最大时停止优化,关注正确类别与错误类别得分的边界 | 希望模型在正确分类后不再过度优化得分差距的多分类任务 |
8. 损失函数选择流程
在实际应用中,选择合适的损失函数是一个关键步骤。以下是一个简单的流程图,帮助你根据问题的特点选择合适的损失函数:
graph LR
A[问题类型] --> B{是否为二分类问题}
B -- 是 --> C{是否存在数据不平衡}
C -- 是 --> D[Focal Loss]
C -- 否 --> E[二元交叉熵损失]
B -- 否 --> F{是否需要比较向量或图像相似度}
F -- 是 --> G[像素交叉熵损失]
F -- 否 --> H{是否关注正确与错误得分差距}
H -- 是 --> I[多类别 SVM 损失(Hinge Loss)]
H -- 否 --> J[SoftMax 交叉熵损失]
9. 损失函数的使用建议
- 数据平衡情况 :如果数据平衡,二元交叉熵损失和 SoftMax 交叉熵损失通常是不错的选择。但如果存在数据不平衡问题,Focal Loss 可以帮助模型更关注少数类样本,提高整体性能。
- 任务类型 :对于图像自编码器等需要比较图像相似度的任务,像素交叉熵损失是合适的。而对于分类任务,根据是否需要控制得分差距,可以选择 SoftMax 交叉熵损失或多类别 SVM 损失。
- 模型训练阶段 :在训练初期,可以使用 SoftMax 交叉熵损失等通用损失函数让模型快速收敛。在训练后期,如果发现模型对某些样本的分类效果不佳,可以考虑引入 Focal Loss 等针对性的损失函数进行微调。
10. 总结
深度学习中的损失函数是模型训练的核心组成部分,不同的损失函数适用于不同的场景。二元交叉熵损失和 SoftMax 交叉熵损失是最常用的损失函数,适用于大多数分类任务。像素交叉熵损失用于衡量向量或图像之间的相似度,在图像自编码器等任务中发挥重要作用。Focal Loss 解决了数据不平衡问题,让模型更关注难样本。而 Hinge Loss 则在某些情况下可以避免模型过度优化得分差距。
在实际应用中,我们需要根据问题的特点、数据的分布以及模型的训练阶段等因素综合考虑,选择合适的损失函数。同时,也可以尝试不同损失函数的组合或进行适当的调整,以达到最佳的模型性能。希望通过本文的介绍,你对深度学习中的损失函数有了更深入的理解,能够在实际项目中灵活运用这些知识。
超级会员免费看
1530

被折叠的 条评论
为什么被折叠?



