我在尝试训练一个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_accuracy
和categorical_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
更新:在我的帖子之后,我发现这个问题已经在这个回答中被识别出来。