在常见的多分类问题中,先经过softmax处理后进行交叉熵计算,原理很简单可以将计算loss理解为,为了使得网络对测试集预测的概率分布和其真实分布接近,常用的做法是使用one-hot对真实标签进行编码,然后用预测概率去拟合one-hot的真实概率。但是这样会带来两个问题:
无法保证模型的泛化能力,使网络过于自信会导致过拟合;
全概率和0概率鼓励所属类别和其他类别之间的差距尽可能加大,而由梯度有界可知,这种情况很难adapt。会造成模型过于相信预测的类别。
标签平滑可以缓解这个问题,可以有两个角度理解这件事。
角度一
软化这种one-hot编码方式。
等号左侧:是一种新的预测的分布
等号右侧:前半部分是对原分布乘一个权重, ϵ \epsilonϵ 是一个超参,需要自己设定,取值在0到1范围内。后半部分u是一个均匀分布,k表示模型的类别数。
由以上公式可以看出,这种方式使label有 ϵ \epsilonϵ 概率来自于均匀分布, 1 − ϵ 1-\epsilon1−ϵ 概率来自于原分布。这就相当于在原label上增加噪声,让模型的预测值不要过度集中于概率较高的类别,把一些概率放在概率较低的类别。
因此,交叉熵可以替换为:
可以理解为:loss为对“预测的分布与真实分布”及“预测分布与先验分布(均匀分布)”的惩罚。
代码实现如下:
class LabelSmoothingCrossEntropy(nn.Module):
def __init__(self, eps=0.1, reduction='mean', ignore_index=-100):
super(LabelSmoothingCrossEntropy, self).__init__()
self.eps = eps
self.reduction = reduction
self.ignore_index = ignore_index
def forward(self, output, target):
c = output.size()[-1]
log_pred = torch.log_softmax(output, dim=-1)
if self.reduction == 'sum':
loss = -log_pred.sum()
else:
loss = -log_pred.sum(dim=-1)
if self.reduction == 'mean':
loss = loss.mean()
return loss * self.eps / c + (1 - self.eps) * torch.nn.functional.nll_loss(log_pred, target, reduction=self.reduction, ignore_index=self.ignore_index)
角度二
对于以Dirac函数分布的真实标签,我们将它变成分为两部分获得(替换):
- 第一部分:将原本Dirac分布的标签变量替换为(1 - ϵ)的Dirac函数;
- 第二部分:以概率 ϵ ,在u(k)u(k) 中份分布的随机变量。
def label_smoothing(inputs, epsilon=0.1): K = inputs.get_shape().as_list()[-1] # number of channels return ((1-epsilon) * inputs) + (epsilon / K)
代码的第一行是取Y的channel数也就是类别数,第二行就是对应公式了。
下面用一个例子理解一下:假设我做一个蛋白质二级结构分类,是三分类,那么K=3;假如一个真实标签是[0, 0, 1],取epsilon = 0.1,
新标签就变成了 (1 - 0.1)× [0, 0, 1] + (0.1 / 3) = [0, 0, 0.9] + [0.0333, 0.0333, 0.0333]= [0.0333, 0.0333, 0.9333]
实际上分了一点概率给其他两类(均匀分),让标签没有那么绝对化,留给学习一点泛化的空间。
从而能够提升整体的效果。
torch版本
首先,让我们使用一个辅助函数来计算两个值之间的线性组合:
def linear_combination(x, y, epsilon):
return epsilon*x + (1-epsilon)*y
接下来,我们使用 PyTorch nn.Module实现一个新的损失函数
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 例子。
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)
Tensorflow中使用方法时候只要在损失函数中加上label_smoothing的值即可,如下:
tf.losses.softmax_cross_entropy(
onehot_labels,
logits,
weights=1.0,
label_smoothing=0,
scope=None,
loss_collection=tf.GraphKeys.LOSSES,
reduction=Reduction.SUM_BY_NONZERO_WEIGHTS
)
-
————————————————
原文链接:https://blog.csdn.net/qq_40176087/article/details/121519888文章来源:https://www.toymoban.com/news/detail-427750.html -
标签平滑Label Smoothing_奔跑的小仙女的博客-CSDN博客_label smoothing文章来源地址https://www.toymoban.com/news/detail-427750.html
到了这里,关于标签平滑(label smoothing) torch和tensorflow的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!