为什么二元交叉熵和分类交叉熵在相同问题上表现不同?

我在尝试训练一个CNN来按主题对文本进行分类。当我使用二元交叉熵时,准确率约为80%,而使用分类交叉熵时,准确率约为50%。

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

model.add(embedding_layer)model.add(Dropout(0.25))# 卷积层model.add(Conv1D(nb_filter=32,                    filter_length=4,                    border_mode='valid',                    activation='relu'))model.add(MaxPooling1D(pool_length=2))# 全连接层model.add(Flatten())model.add(Dense(256))model.add(Dropout(0.25))model.add(Activation('relu'))# 输出层model.add(Dense(len(class_id_index)))model.add(Activation('softmax'))

然后我使用categorical_crossentropy作为损失函数来编译模型,如下所示:

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

或者

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

直觉上我明白为什么要使用分类交叉熵,但我无法理解为什么使用二元交叉熵能得到好的结果,而使用分类交叉熵却得到差的结果。


回答:

分类交叉熵和二元交叉熵之间表现差异的原因已经由用户xtof54在他的回答中报告过,即:

当使用二元交叉熵且标签超过两个时,Keras方法evaluate计算出的准确率是错误的

我想对此进行更详细的解释,展示实际的根本问题,解释它,并提供解决方案。

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

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

是有效的,但你的第二个选项:

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

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

为什么会这样?如果你查看metrics源代码,Keras没有定义一个单一的准确率指标,而是定义了几个不同的指标,其中包括binary_accuracycategorical_accuracy。在幕后发生的事情是,由于你选择了二元交叉熵作为损失函数,并且没有指定特定的准确率指标,Keras(错误地…)推断你对binary_accuracy感兴趣,并返回这个值——而实际上你对categorical_accuracy感兴趣。

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

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])  # 错误的方式model.fit(x_train, y_train,          batch_size=batch_size,          epochs=2,  # 仅2个epoch,仅用于演示          verbose=1,          validation_data=(x_test, y_test))# Keras报告的准确率:score = model.evaluate(x_test, y_test, verbose=0) score[1]# 0.9975801164627075# 手动计算的实际准确率:import numpy as npy_pred = model.predict(x_test)acc = sum([np.argmax(y_test[i])==np.argmax(y_pred[i]) for i in range(10000)])/10000acc# 0.98780000000000001score[1]==acc# False    

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

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

在MNIST示例中,训练后,评分并预测测试集如我上面所示,两个指标现在应该是一样的:

# Keras报告的准确率:score = model.evaluate(x_test, y_test, verbose=0) score[1]# 0.98580000000000001# 手动计算的实际准确率:y_pred = model.predict(x_test)acc = sum([np.argmax(y_test[i])==np.argmax(y_pred[i]) for i in range(10000)])/10000acc# 0.98580000000000001score[1]==acc# True    

系统设置:

Python版本 3.5.3Tensorflow版本 1.2.1Keras版本 2.0.4

更新:在我的帖子之后,我发现这个问题已经在这个回答中被识别出来。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注