欢迎交流,禁止转载!!
前言
机器学习(ML)算法生产的模型的质量直接取决于训练数据的质量,但现实世界的数据集通常包含一定量的噪音,训练数据集里的标签通常不会都是正确的,比如图像分类,如果有人错误地把猫标记成狗,将会对训练结果造成不良的影响。当然错误的标签也分明显的错误和不明显的错误,针对这个问题可以采用Bi -Tempered Logistic Loss来缓解错误标签带来的影响。
论文:《Robust Bi-Tempered Logistic Loss Based on Bregman Divergences》
问题
通常我们用来训练图像分类的是逻辑损失函数(Logistic loss),如下图所示:
但是它存在两大缺点,导致在处理带噪声的数据时存在以下不足:
- 左侧靠近原点部分,曲线陡峭,且没有上界。往往明显标记错误的标签样本都会靠近左侧y轴,此时的损失值会变得很大。这就导致异常大的错误数值会拉伸决策边界,对训练的结果造成不良影响,并且会牺牲掉其他的正确样本所带来的贡献。即远离的异常值会支配总体的损失
- 再看看靠近虚线部分,神经网络的输出是一个矢量激活值,一般对于分类问题,我们使用的是softmax,将激活值表示为分别属于每个分类的概率,如下图所示,输出值若是接近于0,则会快速衰减,最终损失函数尾部靠近虚线部分也会以指数方式快速衰减。不明显的错误标签样本,会接近此处,此时决策边界会靠近错误样本,因为正样本的贡献很小,靠错误样本来弥补。即错误的标签的影响会扩展到分类的边界上
方法
谷歌通过引入两个可调参数的双稳态逻辑损失函数(Bi-Tempered Logistic Loss)来解决上述两个问题。这两个参数分别是“温度”(temperature)t1和尾部重量(tail-heaviness)t2。尾部重量其实就是指传递函数尾部下降的速率。
在线demo
如此调整之后,正确的样本会有更大的区分度和贡献,错误的样本所带来的影响会受到约束。
1)在无噪声情况下,两种损失都能产生良好的决策边界,从而成功地将这两种类别分开。
2)小边距噪声,即噪声数据接近于决策边界。可以看出,由于softmax尾部快速衰减的原因,逻辑损失会将边界拉伸到更接近噪声点,以补偿它们的低概率。而双稳态损失函数有较重的尾部,保持边界远离噪声样本。
3)大边距噪声,即噪声数据远离决策边界。由于双稳态损失函数的有界性,可以防止这些远离边界的噪声点将决策边界拉开。
4)随机噪声,噪声点随机分布在矢量空间中。逻辑损失受到噪声样本的高度干扰,无法收敛到一个良好的决策边界。而双稳态损失可以收敛到与无噪声情况几乎相同的结果上
效果
计算方式
tf代码:https://github.com/google/bi-tempered-loss.
pytorch代码:
import torch
def log_t(u, t):
if t==1.0:
return torch.log(u)
else:
return (u**(1.0-t)-1.0)/(1.0-t)
def exp(u, t):
if t==1.0:
return torch.exp(u)
else:
return torch.relu(1.0+(1.0-t)*u)**(1.0/(1.0-t))
def compute_normalization_fixed_point(activations,t,num_iters=5):
mu=torch.max(activations,dim=-1).values.view(-1, 1)
normalized_activations_step_0=activations-mu
normalized_activations = normalized_activations_step_0
i=0
while i<num_iters:
i+=1
logt_partition=torch.sum(exp_t(normalized_activations,t),dim=-1).view(-1,1)
normalized_activations=normalized_activations_step_0*(logt_partition**(1.0-t))
logt_partition=torch.sum(exp_t(normalized_activations,t),dim=-1).view(-1,1)
return -log_t(1.0/logt_partition,t)+mu
def compute_normalization(activations,t,num_iters=5):
if t<1.0:
return None
else:
return compute_normalization_fixed_point(activations,t,num_iters)
def tempered_softmax(activations,t,num_iters=5):
if t==1.0:
normalization_constants=torch.log(torch.sum(torch.exp(activations),dim=-1))
else:
normalization_constants=compute_normalization(activations,t,num_iters)
return exp_t(activations-normalization_constants,t)
def bi_tempered_logistic_loss(activations,labels,t1,t2,label_smoothing=0.0,num_iters=5):
if label_smoothing>0.0:
num_classes=labels.shape[-1]
labels=(1-num_classes/(num_classes-1)*label_smoothing)*labels+
label_smoothing/(num_classes-1)
probabilities=tempered_softmax(activations,t2,num_iters)
temp1=(log_t(labels+1e-10,t1)-log_t(probabilities,t1))*labels
temp2=(1/(2-t1))*(torch.pow(labels,2-t1)-torch.pow(probabilities,2-t1))
loss_values=temp1-temp2
return torch.sum(loss_values,dim=-1)
欢迎交流,禁止转载!!