我正在训练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'])

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


当前回答

binary_crossentropy(y_target, y_predict)不需要应用于二进制分类问题。

在binary_crossentropy()的源代码中,nn。实际上使用了tensorflow的Sigmoid_cross_entropy_with_logits (labels=target, logits=output)。

在文档中,它说:

度量离散分类任务中的概率误差,其中每个类是独立的,而不是互斥的。例如,可以执行多标签分类,其中一张图片可以同时包含大象和狗。

其他回答

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

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

这完全取决于你要处理的分类问题的类型。主要有三个类别

二元分类(两个目标类), 多类分类(两个以上专属目标), 多标签分类(两个以上的非排他目标),其中多个目标类别可以同时打开。

在第一种情况下,应该使用二进制交叉熵,目标应该被编码为单热向量。

在第二种情况下,应使用分类交叉熵,并将目标编码为单热向量。

在最后一种情况下,应该使用二进制交叉熵和目标应该编码为一个热向量。每个输出神经元(或单元)被视为一个单独的随机二进制变量,整个输出向量的损失是单个二进制变量损失的乘积。因此,它是每个单个输出单元的二进制交叉熵的乘积。

二元交叉熵定义为

分类交叉熵定义为

其中c是运行在c类数量上的索引。

分类交叉熵和二元交叉熵之间这种明显的性能差异的原因是用户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

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

您正在传递一个形状的目标数组(x-dim, y-dim),同时使用作为损失categorical_crossentropy。Categorical_crossentropy期望目标是形状(样本,类)的二进制矩阵(1和0)。如果你的目标是整数类,你可以通过以下方法将它们转换为预期的格式:

from keras.utils import to_categorical
y_binary = to_categorical(y_int)

或者,您也可以使用损失函数sparse_categorical_crossentropy,它需要整数目标。

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

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

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

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

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

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

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