我正在训练CNN按主题对文本进行分类。当我使用二进制交叉熵时,我得到~80%的准确率,使用分类交叉熵时,我得到~50%的准确率。

我不明白为什么会这样。这是一个多类问题,这是不是意味着我必须使用分类交叉熵而二元交叉熵的结果是没有意义的?

model.add(embedding_layer)
model.add(Dropout(0.25))
# convolution layers
model.add(Conv1D(nb_filter=32,
                    filter_length=4,
                    border_mode='valid',
                    activation='relu'))
model.add(MaxPooling1D(pool_length=2))
# dense layers
model.add(Flatten())
model.add(Dense(256))
model.add(Dropout(0.25))
model.add(Activation('relu'))
# output layer
model.add(Dense(len(class_id_index)))
model.add(Activation('softmax'))

然后我用categorical_crossentropy作为损失函数编译它:

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

or

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

直观地说,我为什么要使用分类交叉熵,我不明白为什么我用二进制得到好的结果,而用分类得到的结果很差。


当前回答

由于这是一个多类问题,你必须使用categorical_crossentropy,二元交叉熵会产生虚假的结果,很可能只会评估前两个类。

对于一个多类问题,50%的概率是相当不错的,这取决于类的数量。如果您有n个类,那么100/n是通过输出一个随机类可以获得的最小性能。

其他回答

我遇到了一个“倒置”的问题-我用categorical_crossentropy(2个类)得到了很好的结果,而用binary_crossentropy得到了很差的结果。看来问题出在激活函数错了。正确的设置是:

对于binary_crossentropy: sigmoid激活,标量目标 对于categorical_crossentropy: softmax激活,单热编码目标

一个简单的例子下一个多类设置来说明

假设您有4个类(其中一个是编码的),下面只有一个预测

True_label = [0,1,0,0] Predicted_label = [0,0,1,0]

当使用categorical_crossentropy时,准确率仅为0,它只关心你是否得到了相关的类。

然而,当使用binary_crossentropy时,对所有类都计算精度,这个预测的准确率为50%。最终结果将是两种情况下个体准确度的平均值。

对于多类(类是互斥的)问题,建议使用categorical_crossentropy;对于多标签问题,建议使用binary_crossentropy。

这本书的主要观点得到了出色的侦探作品《沙漠》(desernaut)的圆满回答。然而,在某些情况下,BCE(二元交叉熵)可能会产生与CCE(分类交叉熵)不同的结果,并且可能是首选。虽然上面分享的经验法则(选择哪种损失)适用于99%的情况,但我想在这个讨论中添加一些新的维度。

OP有一个软最大激活,这将抛出一个概率分布作为预测值。这是一个多阶层的问题。首选的损失是分类CE。本质上,这可以归结为-ln(p),其中“p”是样本中唯一正类的预测概率。这意味着负面预测在计算CE时没有作用。这是有意为之。

在极少数情况下,它可能需要让-ve的声音发挥作用。这可以通过将上述样本视为一系列二进制预测来实现。因此,如果预期为[1 0 0 0 0],预测为[0.1 0.5 0.1 0.1 0.2],则进一步分解为:

expected = [1,0], [0,1], [0,1], [0,1], [0,1]
predicted = [0.1, 0.9], [.5, .5], [.1, .9], [.1, .9], [.2, .8]

现在我们继续计算5个不同的交叉熵——分别对应上述5个预期/预测组合,并将它们相加。然后:

CE = -[ ln(.1) + ln(0.5) + ln(0.9) + ln(0.9) + ln(0.8)]

CE有一个不同的尺度,但仍然是预期值和预测值之间差异的衡量标准。唯一的区别是,在这个方案中,-ve值也与+ve值一起受到惩罚/奖励。如果您的问题是要使用输出概率(+ve和-ves)而不是使用max()来预测1 +ve标签,那么您可能需要考虑这个版本的CE。

多标签的情况下,期望= [1 0 0 0 1]?传统的方法是每个输出神经元使用一个sigmoid,而不是一个整体的softmax。这确保了输出概率是相互独立的。所以我们会得到这样的结果:

expected = [1 0 0 0 1]
predicted is = [0.1 0.5 0.1 0.1 0.9]

根据定义,CE度量两个概率分布之间的差值。但上面两个列表不是概率分布。概率分布的总和应该总是1。因此,传统的解决方法是使用与之前相同的损失方法,将期望值和预测值分解为5个单独的概率分布,继续计算5个交叉熵并将它们相加。然后:

CE = -[ ln(.1) + ln(0.5) + ln(0.9) + ln(0.9) + ln(0.9)] = 3.3

当类的数量可能非常多时——比如1000个,而每个样本中可能只有几个类时,就会出现挑战。所以期望是这样的:[1,0,0,0,0,0,0,1,0,0,0.....990 0]。预测结果可能是这样的:[。8, 1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1, 1.....990年0.1的)

在本例中,CE =

- [ ln(.8) + ln(.8) for the 2 +ve classes and 998 * ln(0.9) for the 998 -ve classes]

= 0.44 (for the +ve classes) +  105 (for the negative classes)

You can see how the -ve classes are beginning to create a nuisance value when calculating the loss. The voice of the +ve samples (which may be all that we care about) is getting drowned out. What do we do? We can't use categorical CE (the version where only +ve samples are considered in calculation). This is because, we are forced to break up the probability distributions into multiple binary probability distributions because otherwise it would not be a probability distribution in the first place. Once we break it into multiple binary probability distributions, we have no choice but to use binary CE and this of course gives weightage to -ve classes.

一种选择是用一个倍增器淹没-ve类的声音。所以我们将所有-ve损失乘以一个值,其中gamma < 1。在上面的例子中,gamma可以是。0001。现在损失是:

= 0.44 (for the +ve classes) +  0.105 (for the negative classes)

The nuisance value has come down. 2 years back Facebook did that and much more in a paper they came up with where they also multiplied the -ve losses by p to the power of x. 'p' is the probability of the output being a +ve and x is a constant>1. This penalized -ve losses even further especially the ones where the model is pretty confident (where 1-p is close to 1). This combined effect of punishing negative class losses combined with harsher punishment for the easily classified cases (which accounted for majority of the -ve cases) worked beautifully for Facebook and they called it focal loss.

所以在回答OP关于二进制CE在他的情况下是否有任何意义的问题时,答案是——这要看情况。在99%的情况下,传统的拇指规则是有效的,但有时这些规则可能会被弯曲甚至破坏,以适应手头的问题。

要了解更深入的治疗方法,请访问:https://towardsdatascience.com/cross-entropy-classification-losses-no-math-few-stories-lots-of-intuition-d56f8c7f06b0

在评论@Marcin的答案后,我更仔细地检查了我的一个学生的代码,在那里我发现了同样奇怪的行为,即使只有2个纪元!(所以@Marcin的解释在我的情况下不太可能)。

我发现答案其实很简单:当使用超过2个标签的binary_crossentropy时,用Keras方法evaluate计算的准确性完全错误。你可以自己重新计算准确率(首先调用Keras方法“predict”,然后计算由predict返回的正确答案的数量):你得到了真正的准确率,这比Keras的“evaluate”要低得多。

分类交叉熵和二元交叉熵之间这种明显的性能差异的原因是用户xtof54已经在他的回答中报告的,即:

用Keras方法计算的精度很简单 当使用超过2个标签的binary_crossentropy时错误

我想对此进行更详细的阐述,展示实际的潜在问题,解释它,并提供补救措施。

这种行为不是bug;潜在的原因是一个相当微妙且未被记录的问题,即当你在模型编译中简单地包含metrics=['accuracy']时,Keras实际上是如何根据你所选择的损失函数猜测使用哪个精度的。换句话说,当您的第一个编译选项

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

是有效的,第二个

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

不会产生你期望的结果,但原因不是二元交叉熵的使用(至少在原则上,这是一个绝对有效的损失函数)。

Why is that? If you check the metrics source code, Keras does not define a single accuracy metric, but several different ones, among them binary_accuracy and categorical_accuracy. What happens under the hood is that, since you have selected binary cross entropy as your loss function and have not specified a particular accuracy metric, Keras (wrongly...) infers that you are interested in the binary_accuracy, and this is what it returns - while in fact you are interested in the categorical_accuracy.

让我们来验证一下,使用Keras中的MNIST CNN示例,并进行以下修改:

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])  # WRONG way

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=2,  # only 2 epochs, for demonstration purposes
          verbose=1,
          validation_data=(x_test, y_test))

# Keras reported accuracy:
score = model.evaluate(x_test, y_test, verbose=0) 
score[1]
# 0.9975801164627075

# Actual accuracy calculated manually:
import numpy as np
y_pred = model.predict(x_test)
acc = sum([np.argmax(y_test[i])==np.argmax(y_pred[i]) for i in range(10000)])/10000
acc
# 0.98780000000000001

score[1]==acc
# False    

为了解决这个问题,即使用二进制交叉熵作为你的损失函数(正如我所说的,这没有错,至少在原则上),同时仍然得到手头问题所需的分类精度,你应该在模型编译中明确要求categorical_accuracy,如下所示:

from keras.metrics import categorical_accuracy
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=[categorical_accuracy])

在MNIST的例子中,在我上面展示的训练、评分和预测测试集之后,两个指标现在是相同的,因为它们应该是:

# Keras reported accuracy:
score = model.evaluate(x_test, y_test, verbose=0) 
score[1]
# 0.98580000000000001

# Actual accuracy calculated manually:
y_pred = model.predict(x_test)
acc = sum([np.argmax(y_test[i])==np.argmax(y_pred[i]) for i in range(10000)])/10000
acc
# 0.98580000000000001

score[1]==acc
# True    

系统设置:

Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4

更新:在我的帖子发布后,我发现这个问题已经在这个答案中被确定了。