正则化技巧:标签平滑(Label Smoothing)以及在 PyTorch 中的实现

2021-11-16 11:26:41 浏览数 (1)

过拟合和概率校准是训练深度学习模型时出现的两个问题。深度学习中有很多正则化技术可以解决过拟合问题;权重衰减、早停机制和dropout是都是最常见的方式。Platt缩放和保序回归可以用于模型校准。

但是有没有一种方法可以同时对抗过度拟合和过度自信呢?

标签平滑是一种正则化技术,它扰动目标变量,使模型对其预测的确定性降低。它被视为一种正则化技术,因为它限制了softmax 函数的最大概率使最大概率不会比其他标签大得多(过度自信)。

在本文中,我们将解释标签平滑的原理,实现了一个使用这种技术的交叉熵损失函数,并评估了它的性能。

标签平滑

我们有一个多类分类问题。在此类问题中,目标变量通常是一个one-hot向量,其中正确类别的位置为1,其他位置为0。这是与二元分类不同的任务因为在二分类中只有两个可能的类,但是在多标签分类中,一个数据点中可以有多个正确的类。因此,多标签分类问题的需要检测图像中存在的每个对象。

标签平滑将目标向量改变少量 ε。因此,我们不是要求我们的模型为正确的类别预测 1,而是要求它为正确的类别预测 1-ε,并将所有其他类别预测为 ε。

带有标签平滑的交叉熵损失函数转化为下面的公式。

在这个公式中,ce(x) 表示 x 的标准交叉熵损失(例如 -log(p(x))),ε 是一个小的正数,i 是正确的类,N 是类的数量。

直观地说,标签平滑将正确类的概率值限制为更接近其他类的概率值。通过这种方式,它被用作正则化技术和对抗模型过度自信的方法。

PyTorch 实现

在 PyTorch 中实现标签平滑交叉熵损失函数非常简单。在这个例子中,我们使用 fast.ai 课程的一部分代码。

首先,让我们使用一个辅助函数来计算两个值之间的线性组合:

代码语言:javascript复制
def linear_combination(x, y, epsilon): 
    return epsilon*x   (1-epsilon)*y

接下来,我们使用 PyTorch nn.Module实现一个新的损失函数

代码语言:javascript复制
import torch.nn.functional as F


def reduce_loss(loss, reduction='mean'):
    return loss.mean() if reduction=='mean' else loss.sum() if reduction=='sum' else loss


class LabelSmoothingCrossEntropy(nn.Module):
    def __init__(self, epsilon:float=0.1, reduction='mean'):
        super().__init__()
        self.epsilon = epsilon
        self.reduction = reduction
    
    def forward(self, preds, target):
        n = preds.size()[-1]
        log_preds = F.log_softmax(preds, dim=-1)
        loss = reduce_loss(-log_preds.sum(dim=-1), self.reduction)
        nll = F.nll_loss(log_preds, target, reduction=self.reduction)
        return linear_combination(loss/n, nll, self.epsilon)

我们现在可以在我们的代码中使用这个类。对于这个例子,我们使用标准的 fast.ai pets 例子。

代码语言:javascript复制
from fastai.vision import *
from fastai.metrics import error_rate

# prepare the data
path = untar_data(URLs.PETS)
path_img = path/'images'
fnames = get_image_files(path_img)

bs = 64
np.random.seed(2)
pat = r'/([^/] )_d .jpg$'
data = ImageDataBunch.from_name_re(path_img, fnames, pat, ds_tfms=get_transforms(), size=224, bs=bs) 
                     .normalize(imagenet_stats)

# train the model
learn = cnn_learner(data, models.resnet34, metrics=error_rate)
learn.loss_func = LabelSmoothingCrossEntropy()
learn.fit_one_cycle(4)

我们将数据转换为可供模型使用的格式,并使用 ResNet ,我们自定义的类作为损失。经过四轮训练后,结果总结如下。

我们得到了只有 7.5% 的错误率,这对于十行左右的代码来说是可以接受的,因为我们使用的都是默认设置。

我们可以调整很多东西来使我们的模型表现得更好。不同的优化器、超参数、模型架构等。

总结

在这篇文章中,我们研究了标签平滑,这是一种试图对抗过度拟合和过度自信的技术。我们看到了何时使用它以及如何在 PyTorch 中实现它。然后,我们训练了一个计算机视觉模型,用十行代码识别不同品种的猫和狗。

模型正则化和校准是两个重要的概念。更好地理解这些概念可以帮你成为一个更好的深度学习实践者。

作者:Dimitris Poulopoulos

0 人点赞