解决阶级失衡:缩减对损失的贡献和sgd

时间:2015-05-27 14:52:41

标签: c++ machine-learning neural-network deep-learning caffe

(已添加此问题的更新。)

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

最近我遇到了关于班级失衡的问题。我正在使用9216个训练样本,约5%标记为阳性(1),其余样品标记为阴性(0)。

我正在使用SigmoidCrossEntropyLoss图层来计算损失。在训练时,即使在几个时期之后,损失也会减少并且准确度非常高。这是由于不平衡:网络总是预测为负(0)。 (精确和召回均为零,支持此声明)

为了解决这个问题,我想根据预测 - 真值组合来衡量对损失的贡献(严厉惩罚假阴性)。我的导师/教练还建议我通过随机梯度下降(sgd)反向传播时使用比例因子:该因子将与批次中的不平衡相关联。仅包含负样本的批次根本不会更新权重。

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

任何人都可以帮助我或指导我如何调整SigmoidCrossEntropyLossSigmoid图层以适应以下更改:

  1. 根据预测 - 真值组合调整样本对总损失的贡献(真阳性,假阳性,真阴性,假阴性)。
  2. 根据批次中的不平衡(负数与正数)来衡量由随机梯度下降执行的权重更新。
  3. 提前致谢!

    更新

    我已根据InfogainLossLayer 的建议合并了 Shai。我还添加了另一个自定义图层,根据当前批次中的不平衡来构建基因矩阵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个时期后的结果)

    • 准确度约为。 ~70%(低于~97%);
    • 精度约为~20%(从0%起);
    • 召回约。 ~60%(从0%起)。

    这些数字在大约20个时期达到,之后没有显着变化。

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

2 个答案:

答案 0 :(得分:20)

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

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

[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时的错误之间的差异。

您可以在this thread找到如何为caffe定义矩阵H

关于样本权重,您可能会发现this post有趣:它显示了如何修改 SoftmaxWithLoss 图层以考虑样本权重。

最近,Tsung-Yi Lin, Priya Goyal, Ross Girshick, Kaiming He, Piotr Dollár Focal Loss for Dense Object Detection, (ICCV 2017)提出了对交叉熵损失的修改 焦点损失背后的想法是基于预测该示例的相对难度(而不是基于类大小等)为每个示例分配不同的权重。从我开始尝试这种损失的短暂时间来看,它感觉优于具有班级大小权重的"InfogainLoss"

答案 1 :(得分: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