Pytorch:交叉熵损失(CrossEntropyLoss)以及标签平滑(LabelSmoothing)的实现

本文详细介绍了如何在Pytorch中自定义实现交叉熵损失函数CrossEntropyLoss,以及添加标签平滑功能的CrossEntropyLossWithLabelSmoothing。通过代码实例展示了两者的实现过程,并进行了实验验证,比较了标准交叉熵损失与带有标签平滑的损失在不同情况下的输出。标签平滑作为一种正则化手段,有助于防止过拟合并减轻错误标签的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0. 前言

一般情况下我们都是直接调用Pytorch自带的交叉熵损失函数计算loss,但涉及到魔改以及优化时,我们需要自己动手实现loss function,在这个过程中如果能对交叉熵损失的代码实现有一定的了解会帮助我们写出更优美的代码。

其次是标签平滑这个trick通常简单有效,只需要改改损失函数既可带来性能上的提升,通常与交叉熵配合食用。

因此,本文基于这两个出发点,介绍基于Pytorch框架下的交叉熵损失实现以及标签平滑的实现。

1. 浅谈CrossEntropyLoss

相信大家对于如何计算交叉熵已经非常熟悉,常规步骤是①计算softmax得到各类别置信度;②计算交叉熵损失。但其实从Pytorch的官方文档可以看出,还有更一步到位的方法,如下:

这避免了softmax的计算。

代码实现

很简单,根据公式写代码就好了

class CELoss(nn.Module):
    ''' Cross Entropy Loss'''
    def __init__(self):
        super().__init__()

    def forward(self, pred, target):
        ''' 
        Args:
            pred: prediction of model output    [N, M]
            target: ground truth of sampler [N]
        '''
        eps = 1e-12
      	# standard cross entropy loss
        loss = -1.*pred.gather(1, target.unsqueeze(-1)) + torch.log(torch.exp(pred+eps).sum(dim=1))

        return loss.mean()

2. 浅谈Label Smoothing

Label Smoothing也称之为标签平滑,其实是一种防止过拟合的正则化方法。传统的分类loss采用softmax loss,先对全连接层的输出计算softmax,视为各类别的置信度概率,再利用交叉熵计算损失。
在这里插入图片描述
在这里插入图片描述

在这个过程中尽可能使得各样本在正确类别上的输出概率为1,这要使得对应的z值为+∞,这拉大了其与其他类别间的距离。

现在假设一个多分类任务标签是[1,0,0],如果它本身的label的出现了问题,这对模型的伤害是非常大的,因为在训练的过程中强行学习一个非本类的样本,并且让其概率非常高,这会影响对后验概率的估计。并且有时候类与类之间的并不是毫无关联,如果鼓励输出的概率间相差过大,这会导致一定程度上的过拟合。

因此Label Smoothing的想法是让目标不再是one-hot标签,而是变为如下形式:

在这里插入图片描述

其中ε为一个较小的常数,这使得softmax损失中的概率优目标不再为1和0,同时z值的最优解也不再是正无穷大,而是一个具体的数值。这在一定程度上避免了过拟合,也缓解了错误标签带来的影响。

代码实现

基于上一节的交叉熵实现增加标签平滑功能,代码如下:

class CELoss(nn.Module):
    ''' Cross Entropy Loss with label smoothing '''
    def __init__(self, label_smooth=None, class_num=137):
        super().__init__()
        self.label_smooth = label_smooth
        self.class_num = class_num

    def forward(self, pred, target):
        ''' 
        Args:
            pred: prediction of model output    [N, M]
            target: ground truth of sampler [N]
        '''
        eps = 1e-12
        
        if self.label_smooth is not None:
            # cross entropy loss with label smoothing
            logprobs = F.log_softmax(pred, dim=1)	# softmax + log
            target = F.one_hot(target, self.class_num)	# 转换成one-hot
            
            # label smoothing
            # 实现 1
            # target = (1.0-self.label_smooth)*target + self.label_smooth/self.class_num 	
            # 实现 2
            # implement 2
            target = torch.clamp(target.float(), min=self.label_smooth/(self.class_num-1), max=1.0-self.label_smooth)
            loss = -1*torch.sum(target*logprobs, 1)
        
        else:
            # standard cross entropy loss
            loss = -1.*pred.gather(1, target.unsqueeze(-1)) + torch.log(torch.exp(pred+eps).sum(dim=1))

        return loss.mean()

实现1采用了(1.0-self.label_smooth)*target + self.label_smooth/self.class_num实现,与原始公式不太一样。

后续在了解到pytorch的clamp接口后,发现能够利用其能正确实现原公式,见实现2

3. 实验验证

① 交叉熵损失正确率,与标准的交叉熵比较:

	loss1 = nn.CrossEntropyLoss()
    loss2 = CELoss(label_smooth=None, class_num=3)

    x = torch.tensor([[1, 8, 1], [1, 1, 8]], dtype=torch.float)
    y = torch.tensor([1, 2])

    print(loss1(x, y), loss2(x, y))
	# tensor(0.0018) tensor(0.0018)

② 标签平滑结果展示:

	loss1 = nn.CrossEntropyLoss()
    loss2 = CELoss(label_smooth=0.05, class_num=3)

    x = torch.tensor([[1, 8, 1], [1, 1, 8]], dtype=torch.float)
    y = torch.tensor([1, 2])

    print(loss1(x, y), loss2(x, y))
	# tensor(0.0018) tensor(0.2352)

另一组结果:

	x = torch.tensor([[0.1, 8, 0.1], [0.1, 0.1, 8]], dtype=torch.float)
    y = torch.tensor([1, 2])

    print(loss1(x, y), loss2(x, y))
    # tensor(0.0007) tensor(0.2641)

分析:拉大模型输出数值间的差距后,原始的交叉熵会变小,而增加了标签平滑的反而变大。这也反映了标签平滑后,并不是概率越接近于1越好,而是接近某个小于1的值,这使得模型的输出不再是越高(+∞)越好。

### YOLO 训练中的 Label Smoothing 参数作用及设置方法 #### 什么是 Label Smoothing? Label Smoothing 是一种正则化技术,用于缓解模型过拟合以及提高泛化性能。它通过减少预测概率分布与真实标签之间的差异来实现这一点。具体来说,在标准分类任务中,真实的 one-hot 编码标签会被平滑处理为软标签 (soft labels),从而降低模型对单一类别过度自信的可能性。 在深度学习框架下,Label Smoothing 的主要目的是防止神经网络过于确信其预测结果,尤其是在训练过程中遇到噪声或者标注错误的情况下[^1]。 #### Label Smoothing 在 YOLO 中的应用 YOLO(You Only Look Once)是一种高效的实时目标检测算法。为了提升模型的鲁棒性和准确性,可以在损失函数部分引入 Label Smoothing 技术。这通常应用于交叉熵损失计算阶段,特别是在分类分支上。通过对背景和其他类别的边界进行模糊化处理,可以使模型更加关注于区分不同对象而非单纯追求高置信度。 例如,在 YOLOv5 实现中,默认情况下已经支持了 Label Smoothing 功能,并可以通过调整配置文件中的 `label_smoothing` 参数来进行控制。该参数取值范围一般介于0到1之间,当设为零时表示关闭此功能;而正值表示启用不同程度的平滑效果[^2]。 以下是关于如何设置 YOLO 训练期间使用的 Label Smoothing 参数的具体说明: #### 设置方法 要应用 Label Smoothing 到您的自定义数据集上的 YOLO 模型训练过程里,请按照如下方式操作: 1. **编辑 YAML 配置文件**: 打开您所使用的 `.yaml` 文件(位于项目根目录下的 `/data/your_dataset.yaml`),找到并修改相关字段。如果当前版本未显式暴露 `label_smoothing` 参数,则可能需要查看源代码确认其实现细节或手动添加相应选项。 假定存在这样的结构: ```yaml train: ../datasets/train/images/ val: ../datasets/val/images/ nc: 80 # number of classes names: ['person', 'bicycle', ...] hyp: lr0: 0.01 # initial learning rate (SGD=1E-2, Adam=1E-3) momentum: 0.937 # SGD momentum/Adam beta1 weight_decay: 0.0005 # optimizer weight decay label_smoothing: 0.01 # value between 0 and 1; default is often set as 0.01 or similar small values. ``` 2. **运行命令行脚本**: 使用更新后的配置启动训练流程即可自动生效新的设定项。比如执行以下指令开始一轮新实验: ```bash python train.py --img 640 --batch 16 --epochs 50 --data your_dataset.yaml --cfg yolov5s.yaml --weights '' --name my_experiment_name ``` 值得注意的是,虽然适当增加 Label Smoothing 能够改善某些场景的表现,但如果调节幅度过大会削弱监督信号强度进而影响最终精度表现。因此实际部署前应充分试验多种候选方案以选取最优组合[^3]。 ```python import torch.nn.functional as F def compute_loss_with_label_smoothing(pred, target, smoothing=0.0): """ Compute cross entropy loss with optional label smoothing. Args: pred (Tensor): Model predictions after softmax layer shape [N,C]. target (LongTensor): Ground truth indices tensor shape [N], where N=batch size & C=num_classes. smoothing (float): Factor for applying label smoothing regularization term within range [0..1]. Returns: Tensor scalar representing computed mean loss over batch samples. """ confidence = 1.0 - smoothing logprobs = F.log_softmax(pred, dim=-1) nll_loss = -logprobs.gather(dim=-1, index=target.unsqueeze(1)) smooth_loss = -logprobs.mean(dim=-1).unsqueeze(1) loss = confidence * nll_loss + smoothing * smooth_loss return loss.mean() ``` 上述代码片段展示了如何基于 PyTorch 库构建带 Label Smoothing 支持的标准 CrossEntropyLoss 函数替代品。您可以将其嵌入至任意兼容 Python 的机器学习管道之中作为基础组件调用[^4]。 ---
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值