常见loss函数归类
- 二分类任务
- BCELoss
- BCEWithLogitsLoss
- Focal Loss
- 多分类任务
- CrossEntropyLoss
- NLLLoss
- Label Smoothing Loss
- 检测任务
- FocalLoss
- SSD Loss
- SmoothL1Loss
- 分割任务
- Cross Entropy Loss
- DiceLoss
- BCEWithLogitsLoss
- 回归任务
- MSELoss
- SmoothL1Loss
PyTorch中的loss函数源码通常可以在torch.nn.functional模块中找到。这些函数接受模型的输出和真实标签作为输入,并计算出模型预测的误差,从而帮助优化器调整模型参数。下面是一些常见的loss函数及其对应的任务类型:
- 二分类任务(Binary Classification):
二分类任务只有两个类别,如判断一张图片是猫还是狗。常用的损失函数有:
- (1)BCELoss(Binary Cross Entropy Loss):二分类交叉熵损失函数,适合输出为概率值的情况。
- (2)BCEWithLogitsLoss:结合sigmoid函数和二分类交叉熵损失函数的一种损失函数,适合输出的是未经过sigmoid函数处理的原始值的情况。
- (3)Focal Loss:对于类别不平衡问题较严重的二分类任务,可以采用Focal Loss,来关注分类错误样本中难分类的样本,加大难分类样本的损失权重,从而提升准确率。
- 多分类任务(Multi-class Classification):
多分类任务有多个互斥的类别,如图像分类任务中的1000个类别。常用的损失函数有:
- (1)CrossEntropyLoss:交叉熵损失函数,适合输出为类别概率分布的情况。
- (2)NLLLoss(Negative Log Likelihood Loss):负对数似然损失函数,适合输出为类别标签的情况。
- 目标检测任务(Object Detection):
目标检测任务常用的损失函数是Focal Loss和SSD Loss。Focal Loss是针对目标检测任务中的类别不平衡问题而设计的一种损失函数,SSD Loss则是一种结合置信度和位置回归的损失函数。
- (1)Focal Loss: 针对目标检测任务中的类别不平衡问题而设计的一种损失函数。
- (2)SSD Loss则是一种结合置信度和位置回归的损失函数。
- 图像分割任务(Image Segmentation):
图像分割任务常用的损失函数是交叉熵损失函和Dice Loss,其中Dice Loss是一种适合于像素级别的分割任务的损失函数。
- (1)CrossEntropyLoss :多分类交叉熵损失函数(用于像素级别的分类任务)。
- (2)Dice Loss是一种适合于像素级别的分割任务的损失函数。
- 回归任务(Regression):
回归任务常用的损失函数包括:
- (1)MSELoss(Mean Square Error Loss):均方误差损失函数。
- (2)L1Loss:平均绝对误差损失函数。
- (3)SmoothL1Loss:平滑L1损失函数,对离群值更加稳健。
注意:以上列出的loss函数不是绝对的分类,对于不同的任务类型,也可以使用其他的loss函数。
二分类任务
BCELoss
BCELoss(Binary Cross Entropy Loss)是二分类任务中最常用的loss函数之一,适用于输出只有两个类别的模型。它的公式如下:
其中表示模型经过sigmoid
的输出,表示真实标签(计算中需要转为float
类型),表示样本数。BCELoss可以看作是标准交叉熵损失的一个特例。
在Pytorch中,可以使用nn.BCELoss()
来调用该函数。常用的参数如下:
-
weight
:权重参数,用于对样本进行加权,可以根据数据集中各类别的比例来设定,比例较小的类别通常给予较高的权重。可选参数默认是None。
下面是一个使用nn.BCELoss()
的示例代码:
import torch.nn as nn
import torch.nn.functional as F
loss_func = nn.BCELoss(weight=torch.tensor([0.2, 0.8])) # 2类别样本比例为0.2和0.8
output = model(data) # 模型经过sigmoid的输出
loss = loss_func(output, target.to(torch.float32)) # 计算损失
# 或者
output = torch.sigmoid(output)
loss = f.binary_cross_entropy(output, labels.float(), weight=torch.tensor([0.2, 0.8]))
BCEWithLogitsLoss
BCEWithLogitsLoss是对BCELoss的一种改进,它将sigmoid函数和BCELoss二元交叉熵合并在一起,可以提高数值稳定性。在Pytorch中,可以使用nn.BCEWithLogitsLoss()
来调用该函数。常用的参数如下:
其中,表示模型的预测值,表示目标变量(二分类标签),是平衡正负样本数量的参数,是权重。
-
weight
:权重参数,同上。
下面是一个使用nn.BCEWithLogitsLoss()
的示例代码:
import torch.nn as nn
import torch.nn.functional as F
loss_func = nn.BCEWithLogitsLoss(weight=torch.tensor([0.2, 0.8])) # 2类别样本比例为0.2和0.8
output = model(data) # 模型输出
loss = loss_func(output, target) # 计算损失
# output [B,C] labels [B,C]
loss = f.binary_cross_entropy_with_logits(output, labels.float(), weight=torch.tensor([0.2, 0.8]))
Focal Loss
Focal Loss是一种解决类别不平衡问题的损失函数,特别适用于正负样本极其不平衡的情况下。在传统的交叉熵损失函数(Cross-Entropy Loss)中,分类错误的样本会被等同对待,这对于正负样本极其不平衡的情况显然不合适。Focal Loss通过动态调节每个样本的权重来缓解这个问题,使得模型更关注于那些难以分类的样本(即“难样本”),更加有效地进行训练。
Focal Loss的公式推导如下:
假设有个样本,其中表示第个样本的输入,为它的真实标签,为模型预测其为正样本的概率,为样本实际为正样本的概率(即当,当),为平衡因子,为难样本调节因子,那么Focal Loss的公式可以表示为:
其中,为交叉熵损失函数,为难样本调节因子,为平衡因子,用来平衡正负样本的数量。
Focal Loss的应用场景包括但不限于:
- 目标检测
- 分割任务
- 人脸识别
- 不平衡数据集的分类任务
下面是使用PyTorch实现Focal Loss的代码示例:
import torch
import torch.nn as nn
class FocalLoss(nn.Module):
def __init__(self, alpha=1, gamma=2, reduction='mean'):
super(FocalLoss, self).__init__()
self.alpha = alpha
self.gamma = gamma
self.reduction = reduction
def forward(self, input, target):
ce_loss = nn.CrossEntropyLoss(reduction='none')(input, target) # 计算交叉熵损失
pt = torch.exp(-ce_loss) # 计算概率
focal_loss = self.alpha * (1 - pt) ** self.gamma * ce_loss # 计算Focal Loss
if self.reduction == 'mean':
return focal_loss.mean()
elif self.reduction == 'sum':
return focal_loss.sum()
else:
return focal_loss
其中,alpha
为平衡因子,gamma
为难样本调节因子,一般取值为2,reduction
为损失函数的求和/求平均方式,默认为mean
。
多分类任务
CrossEntropyLoss
CrossEntropyLoss是多分类任务中最常用的loss函数之一,适用于输出多个类别的模型。它的公式如下:
其中表示模型的输出,表示真实标签,表示类别数,表示样本数。在Pytorch中,可以使用nn.CrossEntropyLoss()
来调用该函数。常用的参数如下:
weight
:权重参数,用于对样本进行加权,可以根据数据集中各类别的比例来设定,比例较小的类别通常给予较高的权重。ignore_index
:忽略某一类别,在分割任务中常用。
下面是一个使用nn.CrossEntropyLoss()
的示例代码:
import torch.nn as nn
loss_func = nn.CrossEntropyLoss(weight=torch.tensor([0.2, 0.3, 0.5])) # 3类别样本比例为0.2、0.3和0.5
output = model(data) # 模型输出
loss = loss_func(output, target) # 计算损失
NLLLoss
NLLLoss(Negative Log Likelihood Loss)是CrossEntropyLoss的一种改进,在处理预测概率时更加灵活,可以用于带权重的多分类问题。它的公式如下:
其中表示模型的输出,表示真实标签,表示样本数,为样本的权重。在Pytorch中,可以使用nn.NLLLoss()
来调用该函数。常用的参数如下:
-
weight
:权重参数,用于对样本进行加权,可以根据数据集中各类别的比例来设定,比例较小的类别通常给予较高的权重。
下面是一个使用nn.NLLLoss()
的示例代码:
import torch.nn as nn
loss_func = nn.NLLLoss(weight=torch.tensor([0.2, 0.3, 0.5])) # 3类别样本比例为0.2、0.3和0.5
output = model(data) # 模型输出
loss = loss_func(output, target) # 计算损失
Label Smoothing Loss
Label Smoothing Loss是一种用于减轻分类问题中过拟合现象的损失函数,其基本思想是将真实标签的概率分布进行平滑化处理,使得模型在训练时不会过于自信地预测某种类别,从而提高其泛化能力。
具体来说,Label Smoothing Loss将原本的one-hot标签转化为如下的平滑标签:
其中为类别数,为平滑参数,为真实类别的标签。
根据交叉熵的定义,可以得到Label Smoothing Loss的公式:
其中为模型预测的第个类别的概率。
Label Smoothing Loss可以应用于各种分类任务场景,但通常在存在过拟合风险的情况下效果更为显著。
以下是使用PyTorch实现Label Smoothing Loss的代码示例:
import torch.nn.functional as F
class LabelSmoothingLoss(nn.Module):
def __init__(self, epsilon=0.1, reduction='mean'):
super(LabelSmoothingLoss, self).__init__()
self.epsilon = epsilon
self.reduction = reduction
def forward(self, logits, targets):
K = logits.size(-1)
log_probs = F.log_softmax(logits, dim=-1)
targets = F.one_hot(targets, num_classes=K)
targets = (1 - self.epsilon) * targets + self.epsilon / K
loss = (-targets * log_probs).sum(dim=-1)
if self.reduction == 'none':
return loss
elif self.reduction == 'mean':
return loss.mean()
elif self.reduction == 'sum':
return loss.sum()
在使用时,只需要将LabelSmoothingLoss作为损失函数进行传参即可。
criterion = LabelSmoothingLoss(epsilon=0.1, reduction='mean')
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for epoch in range(num_epochs):
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
pytorch代码示例二:
import torch.nn as nn
class LabelSmoothingLoss(nn.Module):
def __init__(self, smoothing=0.1):
super(LabelSmoothingLoss, self).__init__()
self.smoothing = smoothing
def forward(self, predict, target):
n_class = predict.size(1)
log_pred = F.log_softmax(predict, dim=1)
smooth_loss = -log_pred.mean(dim=1)
nll_loss = F.nll_loss(log_pred, target)
return (1 - self.smoothing) * nll_loss + self.smoothing * smooth_loss.mean()
其中,predict是模型的输出,target是真实标签,smoothing是平滑系数,默认为0.1。在forward方法中,我们首先计算log_softmax值,然后分别计算平滑损失和NLL loss,并将它们加权平均得到最终的loss。
检测任务
FocalLoss
Focal Loss(焦点损失函数)是一种针对不平衡数据集的损失函数,它在处理分类问题时能够提高较少样本的分类准确率。Focal Loss是由Lin等人在2017年提出的。在此之前,交叉熵损失函数是处理分类问题的最常见的损失函数。在不平衡数据集上,交叉熵损失函数容易受到多数类的干扰,从而降低较少类的分类准确率。
Focal Loss通过引入一个可调整的指数项来减轻多数类的影响。指数项对于容易分类的样本减少损失的贡献,同时对于难分类的样本增加损失的贡献,从而使网络更加关注难分类的样本。Focal Loss的公式如下:
其中,表示网络预测的概率,是一个可调整的指数项,调节难分类样本的权重。当时,Focal Loss等价于交叉熵损失;当时,容易分类的样本的权重减少,难分类的样本的权重增加。
在pytorch中,实现Focal Loss的代码如下:
import torch
import torch.nn as nn
import torch.nn.functional as F
class FocalLoss(nn.Module):
def __init__(self, gamma=2, alpha=None, size_average=True):
super(FocalLoss, self).__init__()
self.gamma = gamma
self.alpha = alpha
if isinstance(alpha, (float, int)): self.alpha = torch.Tensor([alpha, 1 - alpha])
if isinstance(alpha, list): self.alpha = torch.Tensor(alpha)
self.size_average = size_average
def forward(self, input, target):
if input.dim() > 2:
input = input.view(input.size(0), input.size(1), -1) # N,C,H,W => N,C,H*W
input = input.transpose(1, 2) # N,C,H*W => N,H*W,C
input = input.contiguous().view(-1, input.size(2)) # N,H*W,C => N*H*W,C
target = target.view(-1, 1)
logpt = F.log_softmax(input)
logpt = logpt.gather(1, target)
logpt = logpt.view(-1)
pt = logpt.data.exp()
if self.alpha is not None:
if self.alpha.type() != input.data.type():
self.alpha = self.alpha.type_as(input.data)
at = self.alpha.gather(0, target.data.view(-1))
logpt = logpt * at
loss = -1 * (1 - pt) ** self.gamma * logpt
if self.size_average:
return loss.mean()
else:
return loss.sum()
其中,gamma是指数项,alpha是类别权重。如果不需要设置alpha,可以传入None。size_average表示是否对batch维度求平均值。
至此,Focal Loss的原理、公式和pytorch代码实现已经介绍完毕。
SSD Loss
SSD Loss是一种用于物体检测任务的损失函数,用于衡量模型对物体位置和类别的预测与真实值之间的差距。其原理及公式推导如下:
- Ground Truth Box与Default Box的匹配
在SSD中,每个预测框(Default Box)都与一个Ground Truth Box进行匹配,这样可以确定每个预测框应该预测哪个物体的位置和类别。匹配的过程分为两个步骤:
- 首先,计算所有Default Box与Ground Truth Box的IoU值(交并比),确定每个Ground Truth Box应该与哪个Default Box匹配。具体来说,对于一个Ground Truth Box,我们选择与它IoU值最大的Default Box进行匹配(前提是这个IoU值大于一定阈值,比如0.5)。
- 接下来,对于每个Default Box,如果它的IoU值与所有Ground Truth Box都小于一定阈值(比如0.5),那么就认为它不匹配任何Ground Truth Box,此时它的分类标签应该设为背景。
- 损失函数公式
通过上述匹配方法,可以得到每个Default Box的匹配Ground Truth Box的位置和类别。对于位置,我们可以使用Smooth L1 Loss(一种类似于MSE的损失函数)来计算位置误差:
其中,是匹配的Default Box总数,是匹配的Positive Default Box(即匹配成功并且不是背景的Default Box)的集合,是对应的Ground Truth Box的位置信息,是模型对应的预测值,为Smooth L1 Loss函数,公式如下:
对于类别,我们可以使用Cross Entropy Loss来计算:
其中,是类别集合,表示Default Box 是否属于类别(即Ground Truth Box的类别是否为),表示模型对于Default Box 属于类别的预测概率。
最终的SSD Loss由这两个部分组成:
是一个用于平衡位置误差和类别误差的超参数,通常设置为1。
- Pytorch代码示例
以下是使用Pytorch实现SSD Loss的代码示例:
import torch.nn as nn
import torch.nn.functional as F
class SSDLoss(nn.Module):
def __init__(self, num_classes, alpha=1.0):
super(SSDLoss, self).__init__()
self.num_classes = num_classes
self.alpha = alpha
def forward(self, loc_preds, conf_preds, loc_targets, conf_targets):
# loc_preds: [batch_size, num_default_boxes, 4]
# conf_preds: [batch_size, num_default_boxes, num_classes]
# loc_targets: [batch_size, num_default_boxes, 4]
# conf_targets: [batch_size, num_default_boxes, num_classes]
pos_mask = conf_targets > 0 # 正样本的掩码
num_pos = pos_mask.sum(dim=1, keepdim=True) # 每个Default Box匹配的Positive Ground Truth Box数量
smooth_l1 = nn.SmoothL1Loss(reduction='none')
loss_loc = smooth_l1(loc_preds, loc_targets)
loss_loc = (loss_loc * pos_mask).sum() / num_pos.sum()
loss_conf = F.cross_entropy(conf_preds.view(-1, self.num_classes),
conf_targets.view(-1).long(),
reduction='none')
loss_conf = loss_conf.view(conf_targets.size())
loss_conf_pos = loss_conf[pos_mask]
num_pos_per_img = num_pos.sum(dim=2, keepdim=True).float()
loss_conf_pos_sum = loss_conf_pos.sum(dim=1)
loss_conf_neg = loss_conf.sum(dim=1) - loss_conf_pos_sum
num_neg = torch.clamp(num_pos_per_img * 3, max=pos_mask.size(1)-num_pos_per_img)
loss_conf_neg, _ = torch.topk(loss_conf_neg, k=num_neg.int().item(), dim=1, largest=False)
loss_conf = (loss_conf_pos_sum + loss_conf_neg.sum()) / num_pos_per_img.sum()
return loss_loc + self.alpha * loss_conf
其中,loc_preds表示模型预测的位置信息,conf_preds表示模型预测的类别信息,loc_targets表示Ground Truth Box的位置信息,conf_targets表示Ground Truth Box的类别信息。在forward方法中,首先计算位置误差损失loss_loc,然后计算类别误差损失loss_conf。在计算loss_conf时,我们对所有Default Box计算Cross Entropy Loss,并根据Positive和Negative样本计算不同的损失。具体来说,我们将所有Positive样本的损失相加,并从Negative样本中选取与Positive样本数目相同的样本加入损失。选取的方法是取出所有Negative样本的loss,从小到大排序,保留最小的num_pos_per_img * 3个样本,num_pos_per_img表示每个图片中Positive样本的数量。最后将Positive样本和Negative样本的损失相加,除以Positive样本数量和图片数量的乘积作为最终的loss_conf。
SmoothL1Loss
SmoothL1Loss用于目标检测任务中的回归问题,其计算公式为:
其中,表示模型的预测值,表示目标变量。
分割任务
Cross Entropy Loss
语义分割任务中,常用的loss函数是交叉熵损失函数(Cross Entropy Loss),计算公式如下:
其中,表示样本总数,表示类别总数,表示第i个样本的真实标签是否为类别j,表示模型对第i个样本是否属于类别j的预测结果。
源码示例:
import torch.nn.functional as F
criterion = F.cross_entropy(output, target, weight=torch.tensor([1, 2, 3, 4]))
Cross Entropy Loss可以通过weight参数调整不同类别的权重。
DiceLoss
除了Cross Entropy Loss之外,还常用的一个loss函数是Dice Loss,Dice Loss是一种常见的非sigmoid型的损失函数,也适用于多分类任务。Dice Loss通过计算模型预测结果与真实标签的交集与并集之间的差异来衡量模型的性能。Dice Loss的计算公式如下:
其中,表示模型的预测结果,表示真实标签。
源码示例:
class DiceLoss(nn.Module):
def __init__(self, weight=None, size_average=True):
super(DiceLoss, self).__init__()
self.size_average = size_average
self.register_buffer('weight', weight)
def forward(self, inputs, targets):
self.num_classes = inputs.size(1)
inputs_soft = F.softmax(inputs, dim=1)
if self.weight is not None and targets.dim() == 4:
weights = self.weight.repeat(targets.size(0), 1, targets.size(2), targets.size(3))
inputs_soft = inputs_soft * weights
dice = 0
for i in range(1, self.num_classes):
input_i = inputs_soft[:, i]
target_i = (targets == i).float()
intersection = (input_i * target_i).sum(dim=(2, 3))
union = input_i.sum(dim=(2, 3)) + target_i.sum(dim=(2, 3))
score_i = (2.0 * intersection / (union + 1e-7)).mean()
dice += score_i
loss = 1 - dice / float(self.num_classes - 1)
return loss
Dice Loss可以通过weight参数调整不同类别的权重。
BCEWithLogitsLoss
BCEWithLogitsLoss也可以用于图像分割任务,其计算公式和二分类任务中的相同。
回归任务
MSELoss
MSELoss(Mean Squared Error Loss)是回归任务中最常用的loss函数之一。其计算公式如下:
其中,表示模型的预测值,表示目标变量(回归值),表示样本数量。
import torch.nn.functional as F
criterion = F.mse_loss(pred, target, reduction='sum')
SmoothL1Loss
SmoothL1Loss也可以用于回归任务,其计算公式和检测任务中的相同。
总结:
本文介绍了常见的loss函数,并按照任务类型进行了分类,并给出了源码示例和参数解析。在实际使用过程中,需要根据任务类型和数据情况选用合适的loss函数,并合理设置参数以达到最佳的训练效果。