pytorch loss函数整理
温馨提示:这篇文章已超过384天没有更新,请注意相关的内容是否还可用!
变量名解释
logits:未经过normalize(未经过激活函数处理)的原始预测值,例如一个mlp将特征映射到num_target_class维的输出tensor就是logits。
probs:probabilities的简写,例如logits经过sigmoid函数,就变成了分布在0-1之间的概率值probs。
Binary Cross-Entropy Loss
Binary Cross-Entropy Loss,简称为BCE loss,即二元交叉熵损失。
二元交叉熵损失是一种用于二分类问题的损失函数。它衡量的是模型预测的概率分布与真实标签的概率分布之间的差异。在二分类问题中,每个样本的标签只有两种可能的状态,通常表示为 0(负类)和 1(正类)。
其公式为:
L B C E = − 1 N ∑ i = 1 N [ y i log ( p i ) + ( 1 − y i ) log ( 1 − p i ) ] L_{BCE}=-\frac{1}{N} \sum_{i=1}^N\left[y_i \log \left(p_i\right)+\left(1-y_i\right) \log \left(1-p_i\right)\right] LBCE=−N1i=1∑N[yilog(pi)+(1−yi)log(1−pi)]
其中:
- N N N是数据集的样本数量。
- y i y_i yi是第 i {i} i个样本的真实标签,取值为 0 或 1,即第 i {i} i个样本要么属于类别 0(负类),要么属于类别 1(正类)。
- p i p_i pi是第 i {i} i个样本属于类别 1(正类)的预测概率
-
log
\log
log是是自然对数。
当真实标签 y i = 1 y_i=1 yi=1 时,损失函数的第一部分 y i log ( p i ) y_i \log \left(p_i\right) yilog(pi) 起作用,第二部分为 0 。此时, 如果预测概率 p i p_i pi 接近 1 (接近真实标签 y i = 1 y_i=1 yi=1), 那么 log ( p i ) \log \left(p_i\right) log(pi) 接近 0 , 损失较小;如果 p i p_i pi 接近 0 (即模型预测错误),那么 log ( p i ) \log \left(p_i\right) log(pi) 会变得成绝对值很大的负数,导致取反后loss很大。
当真实标签 y i = 0 y_i=0 yi=0 时,损失函数的第二部分 ( 1 − y i ) log ( 1 − p i ) \left(1-y_i\right) \log \left(1-p_i\right) (1−yi)log(1−pi)起作用,第一部分为 0。此时,预测概率 p i p_i pi越接近于 0,整体loss越小。
Pytorch手动实现
import torch import torch.nn.functional as F def manual_binary_cross_entropy_with_logits(logits, targets): # 使用 Sigmoid 函数将 logits 转换为概率 probs = torch.sigmoid(logits) # 计算二元交叉熵损失 loss = - torch.mean(targets * torch.log(probs) + (1 - targets) * torch.log(1 - probs)) return loss # logits和targets可以是任意shape的tensor,只要两者shape相同即可 logits = torch.tensor([0.2, -0.4, 1.2, 0.8]) targets = torch.tensor([0., 1., 1., 0.]) assert logits.shape == targets.shape # 使用 PyTorch 的 F.binary_cross_entropy_with_logits 函数计算损失 loss_pytorch = F.binary_cross_entropy_with_logits(logits, targets) # 使用手动实现的函数计算损失 loss_manual = manual_binary_cross_entropy_with_logits(logits, targets) print(f'Loss (PyTorch): {loss_pytorch.item()}') print(f'Loss (Manual): {loss_manual.item()}')F.binary_cross_entropy 与 F.binary_cross_entropy_with_logits的区别
F.binary_cross_entropy的输入是probs
F.binary_cross_entropy_with_logits的输入是logits
Dice Loss
Dice Loss 来自文章《V-Net: Fully Convolutional Neural Networks for Volumetric Medical Image Segmentation》,它能够有效地处理图像分割任务中正负样本不平衡问题。例如,当一张图片中真实分割区域占比很小,即gt_label里的正样本像素很少。这时候如果用BCE Loss,图片里的每个像素都要去进行二分类,而绝大部分的像素都是负样本,所以会存在正负样本不平衡的问题。
介绍Dice Loss前,需要先了解Dice系数。
Dice系数是一种用于评估两个集合的相似性的度量函数,取值范围在0到1之间,取值越大表示越相似。定义如下:
dice coefficient = 2 × ∣ X ∩ Y ∣ ∣ X ∣ + ∣ Y ∣ \text { dice coefficient }=\frac{2 \times|X \cap Y|}{|X|+|Y|} dice coefficient =∣X∣+∣Y∣2×∣X∩Y∣
其中 X X X 和 Y Y Y 分别是两个样本集合, X ∩ Y X \cap Y X∩Y 表示它们交集的大小, ∣ X ∣ |X| ∣X∣ 和 ∣ Y ∣ |Y| ∣Y∣分别表示 X X X 和 Y Y Y中的样本个数。由于分子乘上了 2,Dice 系数的值在 0 到 1 之间,值越大表示相似度越高。
而对于Dice Loss,我们的训练目的是让两个样本越来越相似,所以当 X X X 和 Y Y Y越相似,loss值应该越小,而Dice系数的值在 0 到 1 之间,我们可以直接用1减去Dice系数来作为Dice Loss,这样就能达成两个样本越相似loss值越小的目的:
L d i c e = 1 − dice coefficient L_{dice}= 1 - \text { dice coefficient } Ldice=1− dice coefficient
在图像分割任务中,Dice 系数计算预测分割区域和真实分割区域之间的重叠度。上述公式中的 X X X 和 Y Y Y则分别代表预测分割区域和真实分割区域。
I = ∑ 1 N p i y i U = ∑ 1 N ( p i + y i ) = ∑ 1 N p i + ∑ 1 N y i \begin{gathered} I=\sum_1^N p_i y_i \\ U=\sum_1^N\left(p_i+y_i\right)=\sum_1^N p_i+\sum_1^N y_i \end{gathered} I=1∑NpiyiU=1∑N(pi+yi)=1∑Npi+1∑Nyi
其中:
- N N N是一张图片里的像素数量。
- y i y_i yi是第 i {i} i个像素的真实标签,取值为 0 或 1,即第 i {i} i个像素要么属于类别 0(非分割区域),要么属于类别 1(分割区域)。
- p i p_i pi是第 i {i} i个像素属于类别 1(分割区域)的预测概率。
- I I I是图像的分割区域中,每个像素的预测概率之和。 I I I代表上述公式中的 ∣ X ∩ Y ∣ |X \cap Y| ∣X∩Y∣。
-
U
U
U是图像每个像素的真实分割概率之和(即真实分割区域的像素总数) + 每个像素的预测分割概率之和。
U
U
U代表上述公式中的
∣
X
∣
+
∣
Y
∣
|X|+|Y|
∣X∣+∣Y∣。
Dice Loss 为 Dice 系数的补数,即 1 减去 Dice 系数。在实际应用中,为了避免除以零的情况,通常会在分子和分母中添加一个小的平滑项 ϵ \epsilon ϵ。
L dice = 1 − 2 I + ε U + ε L_{\text {dice }}=1-\frac{2 I+\varepsilon}{U+\varepsilon} Ldice =1−U+ε2I+ε
在许多实现中,将预测值 logits 和目标 targets 在计算Dice Loss的过程中除以一个缩放因子(如 1000 或 10000),通常是为了数值稳定性。当处理大型图像或大量数据时,像素点的总数可能非常大,这意味着求和操作可能产生非常大的值。这可能导致计算过程中出现数值不稳定性,尤其是在梯度下降和反向传播过程中。通过引入一个缩放因子,可以将这些值缩小到一个更合理的范围内,从而减少数值不稳定性的风险。
Pytorch手动实现
import torch import torch.nn.functional as F def dice_loss( logits: torch.Tensor, # shape为[bs, h, w] targets: torch.Tensor, # shape为[bs, h, w] scale=1000, eps=1e-6, ): probs = logits.sigmoid() # 只对h,w这两维求和,保留bs这一维,这样每个sample都可以得到一个dice loss numerator = 2 * (probs / scale * targets).sum(dim=(-2, -1)) denominator = (probs / scale).sum(dim=(-2, -1)) + (targets / scale).sum(dim=(-2, -1)) loss = 1 - (numerator + eps) / (denominator + eps) return loss # 随机生成shape为(4, 480, 640)的预测值 logits 和目标 targets # targets要么是 0(非分割区域),要么是 1(分割区域) logits = torch.randn(4, 480, 640) targets = torch.randint(0, 2, (4, 480, 640)) loss = dice_loss(logits, targets) # shape为[bs, ] print(f"Dics Loss: {loss}")GIOU Loss
Focal Loss
Cross-Entropy Loss
KLDivLoss
