三大视觉任务的loss

机器学习算法的设计使其可以从错误中“学习”并使用我们提供给他们的训练数据来“更新”自己。但是他们如何量化这些错误呢?这是通过使用“损失函数”来完成的,该函数可以帮助算法了解与基本事实相比,其预测的错误程度。选择合适的损失函数很重要,因为它会影响算法尽快生成最佳结果的能力。

Image

 

 

01 基本的损失函数:

 

1.1 L2 LOSS

  • 这是可用的最基本的损失,也称为MSE Loss。这依赖于两个向量[预测和真实标签]之间的Euclidean距离。

  • L2 Loss 对异常值非常敏感,因为误差是平方的。

Pytorch 使用范例: 

import torchimport numpy as np
a = torch.tensor([[1, 2], [3, 4]], dtype=torch.float)b = torch.tensor([[3, 5], [8, 6]], dtype=torch.float)
loss_fn1 = torch.nn.MSELoss()loss1 = loss_fn1(a.float(), b.float())

 

1.2 Softmax 函数

分类的损失函数一般都要求算法的每个标量输出输入概率  且和为1。但是预测值并非总是如此,我们可以使用Softmax 函数(非线性函数)将预测值变为 概率 且和为1。因此Softmax 函数也称为归一化指数函数,其公式如下:

  ​​​​​​

import torchimport torch.nn.functional as Fx= torch.Tensor( [ [1,2,3,4],[1,2,3,4],[1,2,3,4]])y1= F.softmax(x, dim = 0) #对每一列进行softmaxy2 = F.softmax(x,dim =1) #对每一行进行softmax

02

图像分类中的损失函数

 

在图像分类中,经常使用softmax+交叉熵作为损失函数

CROSS-ENTROPY LOSS

  • Cross Entropy Loss Function交叉熵损失函数是使用对数(loge)的更高级的损失函数。与L2 Loss 相比,这有助于加快对神经网络的训练。

  • 在二分类的情况下交叉熵损失函数被称为BCE Loss,模型最后需要预测的结果只有两种情况,对于每个类别我们的预测得到的概率为     和    。此时表达式为:

其中:
-    —— 表示样本i的label,正类为1,负类为0
-    —— 表示样本i预测为正的概率
 

  • 交叉熵(多类别误差)是对二分类的扩展的公式如下。也称为分类交叉熵。

    其中:
    -    ——类别的数量;
    -    ——指示变量(0或1),如果该类别和样本i的类别相同就是1,否则是0;
    -    ——对于观测样本i属于类别  的预测概率。

    

pytorch 二分类交叉熵 BCE LOSS:

 

import torchloss = torch.nn.BCEWithLogitsLoss()#preds预测值 如:[0.123,  0.3255, -0.5321]preds= torch.randn(3, requires_grad=True)#target输入是onthot向量,target是真实标签,,可以是[0,0,1,1,0...]多个值为1target = torch.empty(3).random_(2)output = loss(pred, target)

 

pytorch 多分类交叉熵 CrossEntropy LOSS:

#五分类范例loss = torch.nn.CrossEntropyLoss()preds= torch.randn(3, 5, requires_grad=True)#target输入不是onthot向量,target是真实标签,是稀疏的标签值;tensor([4, 2, 4])target = torch.empty(3, dtype=torch.long).random_(5)output = loss(predsinputs, target)

 

Label Smoothing

谷歌在交叉熵的基础上,提出了label smoothing(标签平滑)防止模型在训练时过于自信地预测标签,改善泛化能力差的问题。

 

论文:When Does Label Smoothing Help?

 

论文链接:https://arxiv.org/pdf/1906.02629.pdf

详细介绍:label smoothing 解读

 

label smoothing:

label smoothing 是图像分类问题经常会用到的trick,通过对 label 进行 weighted sum,用更新的标签向量  来替换传统的ont-hot编码的标签向量  能够取得更好的效果:  其中K为多分类的类别总个数,  为label smoothing引入的超参数,从上式可以看出,实际上 label smoothing 正如其名称一样,将 label 由原来极端的 one hot 形式转化为较平滑的形式。

class LabelSmoothing(nn.Module):    """NLL loss with label smoothing.    """    def __init__(self, smoothing=0.0):        """Constructor for the LabelSmoothing module.        :param smoothing: label smoothing factor        """        super(LabelSmoothing, self).__init__()        self.confidence = 1.0 - smoothing        self.smoothing = smoothing        # 此处的self.smoothing即我们的epsilon平滑参数。
    def forward(self, x, target):        # 此处x的shape应该是(batch size * class数量),所以这里在class数量那个维度做了logsoftmax。        logprobs = torch.nn.functional.log_softmax(x, dim=-1)        # 此处的target的shape是(batch size), 应该就是每个training data的数字标签。        nll_loss = -logprobs.gather(dim=-1, index=target.unsqueeze(1))        # 把输出的shape变回(batch size)        nll_loss = nll_loss.squeeze(1)        # 在第二个维度取均值的话,应该就是对每个x,所有类的logprobs取了平均值。        smooth_loss = -logprobs.mean(dim=-1)        loss = self.confidence * nll_loss + self.smoothing * smooth_loss        return loss.mean()

03

目标检测中的损失函数

目标检测的损失函数一般由classification loss和bounding box regression loss组成,这部分我们以YOLOv5为例介绍目标检测中的损失函数因为它包括了多数情况。详细介绍可以看我们之前的教程:YOLOv5从入门到部署之:网络和损失函数

分类损失

首先是分类损失:YOLOv5 采用了BECLogits 损失函数计算objectness score的损失,class probability score采用了交叉熵损失函数(BCEclsloss),这两种loss 和 上述介绍的图像分类交叉熵损失基本一致,区别在于目标检测中需要有一个二分类区分是否为背景的loss,以及判断目标的类别多分类损失。 

objectness loss:

class loss: 

Focal Loss

在分类损失中YOLOV5还提供 Focal Loss 来缓解样本的不平衡,何凯明团队在他们的论文Focal Loss for Dense Object Detection 提出来的损失函数,主要是为了解决one-stage目标检测中正负样本比例严重失衡的问题。该损失函数降低了大量简单负样本在训练中所占的权重,也可理解为一种困难样本挖掘。

 

Focal Loss是在交叉熵损失上进行改进,普通的交叉熵对于正样本而言,输出概率越大损失越小。对于负样本而言,输出概率越小则损失越小。此时的损失函数在大量简单样本的迭代过程中比较缓慢且可能无法优化至最优。那么Focal loss是怎么改进的呢?  首先在原有的基础上加了一个因子,其中gamma>0使得减少易分类样本的损失。使得更关注于困难的、错分的样本。此外,加入平衡因子alpha,用来平衡正负样本本身的比例不均:文中alpha取0.25,即正样本要比负样本占比小,这是因为负例易分。

class FocalLoss(nn.Module):    # Wraps focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5)    def __init__(self, loss_fcn, gamma=1.5, alpha=0.25):        super(FocalLoss, self).__init__()        self.loss_fcn = loss_fcn  # must be nn.BCEWithLogitsLoss()        self.gamma = gamma        self.alpha = alpha        self.reduction = loss_fcn.reduction        self.loss_fcn.reduction = 'none'  # required to apply FL to each element
    def forward(self, pred, true):        loss = self.loss_fcn(pred, true)        # p_t = torch.exp(-loss)        # loss *= self.alpha * (1.000001 - p_t) ** self.gamma  # non-zero power for gradient stability
        # TF implementation https://github.com/tensorflow/addons/blob/v0.7.1/tensorflow_addons/losses/focal_loss.py        pred_prob = torch.sigmoid(pred)  # prob from logits        p_t = true * pred_prob + (1 - true) * (1 - pred_prob)        alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha)        modulating_factor = (1.0 - p_t) ** self.gamma        loss *= alpha_factor * modulating_factor
        if self.reduction == 'mean':            return loss.mean()        elif self.reduction == 'sum':            return loss.sum()        else:  # 'none'            return loss

 

回归损失

接下来是最重要的回归损失,YOLOv1采用的回归框loss更像是MSE,YOLOv1是  ,而对w和h分别取平方根做差,再求平方,用以消除一些物体大小不均带来的不利。YOLOv2和YOLOv3则利用  , 将框大小的影响放在前面作为系数,连x和y部分也一块考虑了进去。

YOLOv4作者认为YOLOv1-v3建立的类MSE损失是不合理的。因为MSE的四者是需要解耦独⽴的,但实际上这四者并不独⽴,应该需要⼀个损失函数能捕捉到它们之间的相互关系。因此引入了IOU系列。经过其验证,GIOU,DIOU, CIOU,最终作者发现CIOU效果最好,另外YOLOv5采用 GIOU。

IOU 

IoU就是我们所说的交并比,就是两个图形面积的交集和并集的比值

 Image

GIOU Loss

IOU的缺陷在于预测的检测框如果和真实物体的检测框没有重叠(没有交集)的话,我们从IoU的公式可以看出,IoU始终为0且无法优化,也就是如果算法一开始检测出来的框很离谱,根本没有和真实物体发生交集的话,算法无法优化。因此GIOU被提出:

论文:Generalized Intersection over Union: A Metric and A Loss for Bounding Box Regression

论文链接:https://arxiv.org/pdf/1902.09630.pdf

GIOU:

上面公式的意思将两个任意框A,B,我们找到一个最小的封闭形状C,让C可以把A,B包含在内,接着计算C中没有覆盖A和B的面积占C总面积的比值,然后用A与B的IoU减去这个比值。与IoU类似,GIoU也可以作为一个距离,loss可以用  

  GIoU代码:YOLOv5 的 general.py  bbox_iou 函数  返回值是GIoU值iou = inter / union # iou inter是A和B的相交面积,union是A和B的面积之和 ,计算A和B的IOU

if GIoU or DIoU or CIoU:  # 可以选择三种IOU ,yolov5默认采用GIOU    cw = torch.max(b1_x2, b2_x2) - torch.min(b1_x1, b2_x1)  # convex (smallest enclosing box) width      ch = torch.max(b1_y2, b2_y2) - torch.min(b1_y1, b2_y1)  # convex height      if GIoU:  # Generalized IoU https://arxiv.org/pdf/1902.09630.pdf          c_area = cw * ch + 1e-16  # convex area  计算公式里的C 就是能够包含A和B的最小矩形面积          return iou - (c_area - union) / c_area  # GIoU


GIoU Loss代码 : 在compute_loss 函数​​​​​​​

# Regression  pxy = ps[:, :2].sigmoid() * 2. - 0.5      #预测框的中心点  pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i]   #预测框的hw  pbox = torch.cat((pxy, pwh), 1).to(device)  # predicted box  iou = bbox_iou(pbox.T, tbox[i], x1y1x2y2=False, CIoU=True)  # iou(prediction, target)  lbox += (1.0 - iou).mean()  # iou loss       giou_loss = 1- giou

 

Classloss和Obj loss 代码:在compute_loss 函数  obj loss 采用BEC Logits loss,class loss 采用BCEcls loss二分类交叉熵损失。​​​​​​​

    # Objectness      tobj[b, a, gj, gi] = (1.0 - model.gr) + model.gr * iou.detach().clamp(0).type(tobj.dtype)  # iou ratio  
    # Classification      if model.nc > 1:  # cls loss (only if multiple classes)          t = torch.full_like(ps[:, 5:], cn, device=device)  # targets          t[range(n), tcls[i]] = cp          lcls += BCEcls(ps[:, 5:], t)  # BCE  
    # Append targets to text file      # with open('targets.txt', 'a') as file:      #     [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)]  
lobj += BCEobj(pi[..., 4], tobj) * balance[i]  # obj loss

04

图像分割中的损失函数

 

图像分割可以定义为像素级别的分类任务。图像由各种像素组成,这些像素组合在一起定义了图像中的不同元素,因此将这些像素分类为一类元素的方法称为语义图像分割。

大家可以看这篇关于图像分割的损失函数综述

论文:A survey of loss functions for semantic segmentation

论文地址:https://arxiv.org/pdf/2006.14822.pdf

代码地址:https://github.com/shruti-jadon/Semantic-Segmentation-Loss-Functions

 

在文中,总结了15种基于图像分割的损失函数。被证明可以在不同领域提供最新技术成果。这些损失函数可大致分为4类:基于分布的损失函数,基于区域的损失函数,基于边界的损失函数和基于复合的损失函数,如下表所示:

Image

 

基于分布的损失函数:

基于分布的损失函数,主要有Binary Cross-Entropy, Weighted Cross-Entropy, Balanced Cross-Entropy Focal Loss, Distance map derived loss, penalty term。其中Binary Cross-Entropy二进制交叉熵损失和Focal Loss我们在上文中介绍了,我们主要介绍Weighted Cross-Entropy加权交叉熵以及损失平衡交叉熵损失函数。

Weighted Cross-Entropy 加权交叉熵损失函数

加权交叉熵损失函数只是在交叉熵Loss的基础上为每一个类别添加了一个权重参数为正样本加权。设置  减少假阴性,设置  减少假阳性。这样相比于原始的交叉熵Loss,在样本数量不均衡的情况下可以获得更好的效果。

公式如下所示:

 pytroch 代码:​​​​​​​

   class WeightedCrossEntropyLoss(torch.nn.CrossEntropyLoss):   """   Network has to have NO NONLINEARITY!   """   def __init__(self, weight=None):       super(WeightedCrossEntropyLoss, self).__init__()       self.weight = weight
   def forward(self, inp, target):       target = target.long()       num_classes = inp.size()[1]
       i0 = 1       i1 = 2
       while i1 < len(inp.shape): # this is ugly but torch only allows to transpose two axes at once           inp = inp.transpose(i0, i1)           i0 += 1           i1 += 1
       inp = inp.contiguous()       inp = inp.view(-1, num_classes)
       target = target.view(-1,)       wce_loss = torch.nn.CrossEntropyLoss(weight=self.weight)
       return wce_loss(inp, target)

 

Balanced Cross-Entropy 平衡交叉熵损失函数

平衡交叉熵损失函数与加权交叉熵损失函数类似,但平衡交叉熵损失函数对负样本也进行加权。公式如下: 

基于区域的损失函数:

基于区域的loss旨在最大程度地减少失配或最大化ground truth G与预测的分割S之间的重叠区域。主要的损失函数是Dice Loss。

Dice系数是计算机视觉界广泛使用的度量标准,用于计算两个图像之间的相似度。在2016年的时候,它也被改编为损失函数,称为Dice Loss,公式如下: 此处,在分子和分母中添加1以确保函数在诸如y = 0的极端情况下的确定性。Dice Loss使用与样本极度不均衡的情况,如果一般情况下使用Dice Loss会回反向传播有不利的影响,使得训练不稳定。 

class SoftDiceLoss(nn.Module):   def __init__(self, apply_nonlin=None, batch_dice=False, do_bg=True, smooth=1.,                square=False):       """       paper: https://arxiv.org/pdf/1606.04797.pdf       """       super(SoftDiceLoss, self).__init__()
       self.square = square       self.do_bg = do_bg       self.batch_dice = batch_dice       self.apply_nonlin = apply_nonlin       self.smooth = smooth
   def forward(self, x, y, loss_mask=None):       shp_x = x.shape
       if self.batch_dice:           axes = [0] + list(range(2, len(shp_x)))       else:           axes = list(range(2, len(shp_x)))
       if self.apply_nonlin is not None:           x = self.apply_nonlin(x)
       tp, fp, fn = get_tp_fp_fn(x, y, axes, loss_mask, self.square)
       dc = (2 * tp + self.smooth) / (2 * tp + fp + fn + self.smooth)
       if not self.do_bg:           if self.batch_dice:               dc = dc[1:]           else:               dc = dc[:, 1:]       dc = dc.mean()
       return -dc

基于边界的损失函数

基于边界的损失是一种新型的损失函数,旨在最小化地面真实情况和预测的分段之间的距离。

Shape-aware Loss

顾名思义,Shape-aware Loss考虑了形状。通常,所有损失函数都在像素级起作用,Shape-aware Loss会计算平均点到曲线的欧几里得距离,即预测分割到ground truth的曲线周围点之间的欧式距离,并将其用作交叉熵损失函数的系数,具体定义如下:(CE指交叉熵损失函数)

class DistBinaryDiceLoss(nn.Module):   """   Distance map penalized Dice loss   Motivated by: https://openreview.net/forum?id=B1eIcvS45V   Distance Map Loss Penalty Term for Semantic Segmentation           """   def __init__(self, smooth=1e-5):       super(DistBinaryDiceLoss, self).__init__()       self.smooth = smooth
   def forward(self, net_output, gt):       """       net_output: (batch_size, 2, x,y,z)       target: ground truth, shape: (batch_size, 1, x,y,z)       """       net_output = softmax_helper(net_output)       # one hot code for gt       with torch.no_grad():           if len(net_output.shape) != len(gt.shape):               gt = gt.view((gt.shape[0], 1, *gt.shape[1:]))
           if all([i == j for i, j in zip(net_output.shape, gt.shape)]):               # if this is the case then gt is probably already a one hot encoding               y_onehot = gt           else:               gt = gt.long()               y_onehot = torch.zeros(net_output.shape)               if net_output.device.type == "cuda":                   y_onehot = y_onehot.cuda(net_output.device.index)               y_onehot.scatter_(1, gt, 1)              gt_temp = gt[:,0, ...].type(torch.float32)       with torch.no_grad():           dist = compute_edts_forPenalizedLoss(gt_temp.cpu().numpy()>0.5) + 1.0       # print('dist.shape: ', dist.shape)       dist = torch.from_numpy(dist)
       if dist.device != net_output.device:           dist = dist.to(net_output.device).type(torch.float32)              tp = net_output * y_onehot       tp = torch.sum(tp[:,1,...] * dist, (1,2,3))              dc = (2 * tp + self.smooth) / (torch.sum(net_output[:,1,...], (1,2,3)) + torch.sum(y_onehot[:,1,...], (1,2,3)) + self.smooth)
       dc = dc.mean()
       return -dc

 

基于复合的损失函数

基于复合的损失函数是指前面三种的多种损失函数的组合,比如Compound loss,Combo loss是直接的weighted CE和Dice loss的结合,用超参数alpha和1-alpha结合。

  

05

总结

 

本文总结了计算机视觉三大任务(图像分类,目标检测。图像分割)的主要损失函数的原理和代码,以便于大家选择适合自己任务的损失函数。

原文​​​​​​​

### 动量对比学习在视觉任务中的应用 动量对比学习(Momentum Contrast, MoCo)是一种用于无监督特征学习的方法,它通过构建动态词典来进行实例级别的区分[^5]。MoCo 的核心思想在于维护一个队列结构以存储历史负样本,并利用动量更新机制保持编码器参数的一致性。 #### 方法概述 MoCo 使用两个神经网络:一个是查询编码器 \( q \),另一个是键编码器 \( k \)。查询编码器负责处理当前批次的数据并生成嵌入向量;而键编码器则用来生成历史数据的嵌入向量。为了减少计算开销,键编码器的权重不是直接优化得到,而是通过动量更新的方式从查询编码器继承: \[ k_{t} = m \cdot k_{t-1} + (1-m) \cdot q_t, \] 其中 \( m \in [0, 1] \) 是动量系数。这种设计使得键编码器能够平滑地跟踪查询编码器的变化,从而提高模型稳定性。 #### 对比损失函数 MoCo 中使用的对比损失函数通常采用 InfoNCE 形式,其定义如下: \[ L = -\log \frac{\exp(\text{sim}(q, k^+)/\tau)}{\sum_{i=0}^{K}\exp(\text{sim}(q, k_i)/\tau)}, \] 这里 \( q \) 表示查询向量,\( k^+ \) 是正样本对应的键向量,\( k_i \) 则是从队列中采样的负样本集合成员[\(^5]\)]。相似度函数 \( \text{sim}() \) 可能是最简单的点积操作或者经过归一化后的余弦距离。 #### 实验设置与性能提升 实验表明,在规模图像分类任务上预训练过的 MoCo 模型可以通过线性评估协议展示出优异的表现[^3]。具体来说,当与其他自监督方法比较时,MoCo 不仅能够在较小批量小下工作良好,而且还能随着批尺寸增进一步改善效果。此外,增加网络宽度和深度同样有助于获得更好的表征质量。 ```python import torch.nn.functional as F def info_nce_loss(query, key_positive, keys_negative, temperature=0.1): """ Compute the contrastive loss using InfoNCE. Args: query: Tensor of shape (B, D), where B is batch size and D is embedding dimension. key_positive: Tensor of shape (B, D). keys_negative: Tensor of shape (K, D), K being number of negatives per positive pair. temperature: Scalar value controlling sharpness of distribution. Returns: Loss scalar tensor. """ logits = torch.cat([F.cosine_similarity(query.unsqueeze(1), key_positive.unsqueeze(2)), F.cosine_similarity(query.unsqueeze(1).unsqueeze(2), keys_negative.T)], dim=-1) labels = torch.zeros(len(query)).long().cuda() return F.cross_entropy(logits / temperature, labels) ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值