解决类别不平衡问题:扩大损失和SGD的贡献规模

33

(此问题已进行更新。)

我是比利时根特大学的研究生,我的研究方向是使用深度卷积神经网络进行情感识别。我正在使用Caffe框架实现卷积神经网络。

最近我遇到了一个类别不平衡的问题。我使用了9216个训练样本,大约5%被标记为正(1),其余的样本都被标记为负(0)。

我正在使用SigmoidCrossEntropyLoss层来计算损失。在训练过程中,损失下降,准确率在几次迭代之后就非常高。这是由于数据不平衡造成的:网络总是预测负(0)。 (准确率和召回率都为零,支持此说法)

为了解决这个问题,我想根据预测结果和真实结果的组合,调整对损失的贡献度(重罚假阴性)。我的导师建议我在随机梯度下降(sgd)反向传播时使用缩放因子:该因子与批次中的数据不平衡有关。如果批次只包含负样本,则不更新权重。

我只向Caffe添加了一个自定义层,用于报告其他指标,如精度和召回率。我的Caffe代码经验有限,但我在编写C++代码方面有很多专业知识。


请问有人能帮助我或指导我如何调整SigmoidCrossEntropyLossSigmoid层以适应以下更改:

  1. 根据预测结果和真实结果的组合来调整每个样本对总损失的贡献度(真阳性、假阳性、真阴性、假阴性)。
  2. 根据批次中的正负样本不平衡来缩放随机梯度下降所执行的权重更新。

提前感谢!


更新

我已按照Shai的建议,加入了InfogainLossLayer。我还添加了另一个自定义层,基于当前批次中的不平衡性构建信息增益矩阵H

目前,矩阵的配置如下:

H(i, j) = 0          if i != j
H(i, j) = 1 - f(i)   if i == j (with f(i) = the frequency of class i in the batch)

我计划未来尝试不同的矩阵配置。

我已经在10:1的不平衡数据集上进行了测试。结果显示,网络现在正在学习有用的东西:(30个epoch之后的结果)

  • 准确率约为70%(从97%下降);
  • 精确度约为20%(从0%上升);
  • 召回率约为60%(从0%上升)。

这些数字在大约20个epoch时达到,并且此后没有显着变化。

!!以上结果仅是概念证明,是通过在10:1不平衡的数据集上训练简单的网络得到的。!!


干得好!你能详细说明一下你添加的自定义层是如何计算每批次的 H 的吗? - Shai
2
当然,这很简单。该层以一个blob作为输入:该批次的真实标签;并产生一个blob作为输出:infogain矩阵H。每个类别的频率是基于标签计算的,然后根据更新中提到的公式填充矩阵(我不声称那是唯一有效的公式,我计划尝试不同的值)。 - Maarten Bamelis
Maarten,你把这些层放到Github上了吗? - RockridgeKid
@MaartenBamelis 两个问题:1.你是否尝试过给样本不同的权重,而不是改变损失类型,就像这里所示(http://deepdish.io/2014/11/04/caffe-with-weighted-samples/)?2.看起来很明显,但为了确认,你没有必要为`H`矩阵计算层实现反向计算,对吗? - Autonomous
@ParagS.Chandakkar 感谢您的提问!回答如下:1. 我没有对样本进行加权;2. H矩阵层没有反向计算。 - Maarten Bamelis
显示剩余3条评论
2个回答

20
为什么不使用InfogainLoss层来弥补训练集中的不平衡问题?

Infogain损失是使用权重矩阵H(在您的情况下为2×2)定义的。它的条目含义为:

[cost of predicting 1 when gt is 0,    cost of predicting 0 when gt is 0
 cost of predicting 1 when gt is 1,    cost of predicting 0 when gt is 1]

因此,您可以设置H的条目以反映在预测0或1时的错误差异。

有关为caffe定义矩阵H的方法,请参见此线程

关于样本权重,您可能会发现这篇文章很有趣:它展示了如何修改SoftmaxWithLoss层以考虑样本权重。


最近,Tsung-Yi Lin, Priya Goyal, Ross Girshick, Kaiming He, Piotr Dollár在(ICCV 2017)中提出了对交叉熵损失的修改。
焦点损失背后的想法是根据相对于其他例子的难度为每个例子分配不同的权重,而不是基于类大小等因素。从我用这个损失进行过简短试验的时间来看,它似乎比使用类大小权重的"InfogainLoss"表现更好。


1
@MaartenBamelis 我自己在学习处理类别不平衡的模型时遇到了一些困难。如果您能更新您的进展以及您是如何克服这些困难的,我将不胜感激。谢谢! - Shai
1
我已经更新了问题,并应用了您的解决方案。InfogainLossLayer是一个很好的工具,可以帮助解决类别不平衡问题!如更新中所述,结果仅仅是概念验证;网络不再只是预测“0”,而是正在学习解决任务! - Maarten Bamelis
1
@Shai,您能否确认条目的含义与您所描述的一致。实现更符合Maarten Bamelis的逻辑,除非我漏掉了什么。 - ypx
1
@Alex 我仔细研究了关于InfoGainLossLayer的文档; 它指出,如果infogain矩阵H是单位矩阵,则损失层的行为类似于MultinomialLogisticLossLayer。因此据我所理解,这意味着H [1,1]适用于当预测为类别1时,实际标签也是类别1的情况。 - Maarten Bamelis
1
@MaartenBamelis:谢谢Maarten,我不得不查看源C++代码才能理解它的工作原理。我的真正困惑在于softmax概率数组的平铺和H[i,j]的乘法循环。一旦我理解了这一点,其余的就很容易了。再次感谢。 - Alex
显示剩余8条评论

0

我在分类任务中也遇到了类别不平衡的问题。目前我正在使用带有权重的CrossEntropyLoss(文档here),它运行良好。这个想法是给样本数量较少的类别更多的损失。

计算权重

每个类别的权重与该类别中图像数量成反比。以下是使用numpy计算所有类别权重的代码片段:

cls_num = []
# train_labels is a list of class labels for all training samples
# the labels are in range [0, n-1] (n classes in total)
train_labels = np.asarray(train_labels)
num_cls = np.unique(train_labels).size

for i in range(num_cls):
    cls_num.append(len(np.where(train_labels==i)[0]))

cls_num = np.array(cls_num)

cls_num = cls_num.max()/cls_num
x = 1.0/np.sum(cls_num)

# the weight is an array which contains weight to use in CrossEntropyLoss
# for each class.
weight = x*cls_num

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接