解决 Class 不平衡:缩放对损失和 sgd 的贡献
Tackling Class Imbalance: scaling contribution to loss and sgd
(已添加对此问题的更新。)
我是比利时根特大学的研究生;我的研究是关于深度卷积神经网络的情绪识别。我正在使用 Caffe 框架来实现 CNN。
最近我 运行 遇到了一个关于 class 失衡的问题。我正在使用大约 9216 个训练样本。 5% 被标记为正面 (1),其余样本被标记为负面 (0)。
我正在使用 SigmoidCrossEntropyLoss 层来计算损失。训练时,损失会减少,甚至在几个 epoch 后准确率会非常高。这是由于不平衡造成的:网络总是简单地预测负数 (0)。 (准确率和召回率均为零,支持这一说法)
为了解决这个问题,我想根据预测-真实组合缩放对损失的贡献(严厉惩罚假阴性)。我的 mentor/coach 还建议我 在通过随机梯度下降 (sgd) 反向传播 时使用比例因子:该因子将与批处理中的不平衡相关。仅包含负样本的批次根本不会更新权重。
我只向 Caffe 添加了一个定制层:报告其他指标,例如精度和召回率。我使用 Caffe 代码的经验有限,但我在编写 C++ 代码方面有很多专业知识。
任何人都可以帮助我或指出正确的方向,了解如何调整 SigmoidCrossEntropyLoss 和 Sigmoid 层以适应以下变化:
- 根据预测-真实组合(真阳性、假阳性、真阴性、假阴性)调整样本对总损失的贡献。
- 根据批处理中的不平衡(负数与正数)缩放由随机梯度下降执行的权重更新。
提前致谢!
更新
我合并了InfogainLossLayer as suggested by 。我还添加了另一个自定义层,它根据当前批次中的不平衡构建信息增益矩阵 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 个 epoch 时达到,之后没有显着变化。
!!上述结果仅是概念证明,它们是通过在 10:1 不平衡数据集上训练简单网络获得的。 !!
为什么不使用 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 的错误之间的差异。
您可以在 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)提出了对交叉熵损失的修改。
focal-loss 背后的想法是根据预测该示例的相对难度(而不是基于 class 大小等)为每个示例分配不同的权重。从我尝试这种损失的短暂时间来看,它感觉优于 "InfogainLoss"
和 class 大小的权重。
我在 class 化任务中也遇到过这个 class 不平衡问题。现在我正在使用带有权重的 CrossEntropyLoss(文档 here)并且它工作正常。这个想法是在 classes 中用较少的图像给样本更多的损失。
正在计算权重
每个class的权重与本class中的图像数量成反比。这是使用 numpy
计算所有 class 权重的片段
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
(已添加对此问题的更新。)
我是比利时根特大学的研究生;我的研究是关于深度卷积神经网络的情绪识别。我正在使用 Caffe 框架来实现 CNN。
最近我 运行 遇到了一个关于 class 失衡的问题。我正在使用大约 9216 个训练样本。 5% 被标记为正面 (1),其余样本被标记为负面 (0)。
我正在使用 SigmoidCrossEntropyLoss 层来计算损失。训练时,损失会减少,甚至在几个 epoch 后准确率会非常高。这是由于不平衡造成的:网络总是简单地预测负数 (0)。 (准确率和召回率均为零,支持这一说法)
为了解决这个问题,我想根据预测-真实组合缩放对损失的贡献(严厉惩罚假阴性)。我的 mentor/coach 还建议我 在通过随机梯度下降 (sgd) 反向传播 时使用比例因子:该因子将与批处理中的不平衡相关。仅包含负样本的批次根本不会更新权重。
我只向 Caffe 添加了一个定制层:报告其他指标,例如精度和召回率。我使用 Caffe 代码的经验有限,但我在编写 C++ 代码方面有很多专业知识。
任何人都可以帮助我或指出正确的方向,了解如何调整 SigmoidCrossEntropyLoss 和 Sigmoid 层以适应以下变化:
- 根据预测-真实组合(真阳性、假阳性、真阴性、假阴性)调整样本对总损失的贡献。
- 根据批处理中的不平衡(负数与正数)缩放由随机梯度下降执行的权重更新。
提前致谢!
更新
我合并了InfogainLossLayer as suggested by 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 个 epoch 时达到,之后没有显着变化。
!!上述结果仅是概念证明,它们是通过在 10:1 不平衡数据集上训练简单网络获得的。 !!
为什么不使用 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 的错误之间的差异。
您可以在 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)提出了对交叉熵损失的修改。
focal-loss 背后的想法是根据预测该示例的相对难度(而不是基于 class 大小等)为每个示例分配不同的权重。从我尝试这种损失的短暂时间来看,它感觉优于 "InfogainLoss"
和 class 大小的权重。
我在 class 化任务中也遇到过这个 class 不平衡问题。现在我正在使用带有权重的 CrossEntropyLoss(文档 here)并且它工作正常。这个想法是在 classes 中用较少的图像给样本更多的损失。
正在计算权重
每个class的权重与本class中的图像数量成反比。这是使用 numpy
计算所有 class 权重的片段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