Keras 中具有类权重的多标签分类
Posted
技术标签:
【中文标题】Keras 中具有类权重的多标签分类【英文标题】:Multi-label classification with class weights in Keras 【发布时间】:2018-07-07 05:45:45 【问题描述】:我在网络中有 1000 个类,它们具有多标签输出。对于每个训练示例,正输出的数量相同(即 10),但它们可以分配给 1000 个类中的任何一个。所以 10 个类有输出 1,其余 990 个有输出 0。
对于多标签分类,我使用“二元交叉熵”作为成本函数,使用“sigmoid”作为激活函数。当我尝试将 0.5 这条规则作为 1 或 0 的截止值时。它们都是 0。我知道这是一个类别不平衡问题。从这个link,我了解到,我可能需要创建额外的输出标签。不幸的是,我无法弄清楚如何将其合并到 keras 中的简单神经网络中。
nclasses = 1000
# if we wanted to maximize an imbalance problem!
#class_weight = k: len(Y_train)/(nclasses*(Y_train==k).sum()) for k in range(nclasses)
inp = Input(shape=[X_train.shape[1]])
x = Dense(5000, activation='relu')(inp)
x = Dense(4000, activation='relu')(x)
x = Dense(3000, activation='relu')(x)
x = Dense(2000, activation='relu')(x)
x = Dense(nclasses, activation='sigmoid')(x)
model = Model(inputs=[inp], outputs=[x])
adam=keras.optimizers.adam(lr=0.00001)
model.compile('adam', 'binary_crossentropy')
history = model.fit(
X_train, Y_train, batch_size=32, epochs=50,verbose=0,shuffle=False)
谁能帮我解决这里的代码,如果您能针对这个问题提出一个好的“准确度”指标,我也将不胜感激?
非常感谢:) :)
【问题讨论】:
尝试将1
s 分配给前 20 个分数,而不是使用阈值。
我应该如何考虑班级不平衡?您能否解释一下如果我使用前 10/20 的分数将如何处理? :)
【参考方案1】:
我有一个类似的问题,很遗憾大部分问题都没有答案。尤其是类不平衡问题。
就指标而言,有几种可能性:在我的例子中,我使用前 1/2/3/4/5 个结果并检查其中一个是否正确。因为在您的情况下,您始终拥有相同数量的标签 = 1,您可以获取前 10 个结果,看看其中有多少是正确的,然后在您的批量大小上平均该结果。我没有找到将这个算法作为 keras 度量的可能性。相反,我编写了一个回调,它在我的验证数据集上计算 epoch 结束时的指标。
此外,如果您在测试数据集上预测前 n 个结果,请查看每个类别的预测次数。 Counter Class 非常方便。
编辑:如果找到一种方法来包含类权重而不拆分输出。 您需要一个 numpy 二维数组,其中包含形状为 [要预测的数字类别,2(背景和信号)] 的权重。 这样的数组可以用这个函数来计算:
def calculating_class_weights(y_true):
from sklearn.utils.class_weight import compute_class_weight
number_dim = np.shape(y_true)[1]
weights = np.empty([number_dim, 2])
for i in range(number_dim):
weights[i] = compute_class_weight('balanced', [0.,1.], y_true[:, i])
return weights
现在的解决方案是构建您自己的二元交叉熵损失函数,您可以在其中自己乘以权重:
def get_weighted_loss(weights):
def weighted_loss(y_true, y_pred):
return K.mean((weights[:,0]**(1-y_true))*(weights[:,1]**(y_true))*K.binary_crossentropy(y_true, y_pred), axis=-1)
return weighted_loss
weights[:,0] 是一个包含所有背景权重的数组,而 weights[:,1] 包含所有信号权重。
剩下的就是将这个损失包含在编译函数中:
model.compile(optimizer=Adam(), loss=get_weighted_loss(class_weights))
【讨论】:
我真的很喜欢这个答案!顺便说一句,万一其他人遇到这个问题:如果您保存使用此自定义损失函数训练的模型并想再次加载它,您将收到“未知损失函数”错误。这可以通过设置“custom_objects”参数来克服,例如 model = load_model("path/to/model.hd5f", custom_objects="weighted_loss": get_weighted_loss(weights) 有人能解释一下权重公式K.mean((weights[:,0]**(1-y_true))*(weights[:,1]**(y_true))*K.binary_crossentropy(y_true, y_pred), axis=-1)
是如何构建的吗?谢谢。
我在上一篇文章 github.com/keras-team/keras/issues/2592#issuecomment-387579022 中对此进行了解释。基本上一切都是 K.mean 内的形状向量(number_samples,number_outputs)。新的是构建的权重因子,其中一个是一个,另一个是相应的权重。
只是一个更正(不能编辑 cmets?)。应该是 load_model("path/to/model.h5", ...) 而不是 .hd5f
一个小补充:如果您保存模型(添加自定义对象)并加载模型仅用于推理,则不再需要该功能。只需使用另一个 keras 损失函数:custom_objects="weighted_loss": some_other_loss_function。没关系,因为无论如何您都不会使用它。因此,您不必在推理代码中复制损失函数的代码。以上是关于Keras 中具有类权重的多标签分类的主要内容,如果未能解决你的问题,请参考以下文章