我正在训练一个文本分类模型,输入数据由4096个词频-逆文档频率组成。
我的输出有416个可能的类别。每个数据有3个类别,所以在一个包含413个零的数组中会有3个一(独热编码)。
我的模型如下所示:
model = Sequential()model.add(Dense(2048, activation="relu", input_dim=X.shape[1]))model.add(Dense(512, activation="relu"))model.add(Dense(416, activation="sigmoid"))
当我使用binary_crossentropy
损失函数进行训练时,经过一个epoch后损失为0.185,准确率为96%。经过5个epoch后,损失降至0.037,准确率达到99.3%。我猜测这是错误的,因为我的标签中有很多零,它正确地分类了这些零。
当我使用categorical_crossentropy
损失函数进行训练时,在最初几个epoch中损失为15.0,准确率低于5%,之后在经过多个(超过50个)epoch后,损失稳定在5.0,准确率为12%。
在我的情况下(大型独热编码且有多个1),使用哪种损失函数是正确的?这些分数告诉我什么信息?
编辑:这是model.compile()
语句:
model.compile(loss='categorical_crossentropy', optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
和
model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
回答:
简而言之:当你使用loss='binary_crossentropy'
时报告的(高)准确率不是正确的,如你所猜测。对于你的问题,推荐的损失函数是categorical_crossentropy
。
详细说明:
这种行为背后的原因是一个相当微妙且未记录的问题,关于Keras如何根据你选择的损失函数来猜测使用哪种准确率,当你在模型编译时简单地使用metrics=['accuracy']
,如你所做的那样。换句话说,虽然你的第一个编译选项
model.compile(loss='categorical_crossentropy', optimizer=keras.optimizers.Adam(), metrics=['accuracy']
是有效的,但你的第二个选项:
model.compile(loss='binary_crossentropy', optimizer=keras.optimizers.Adam(), metrics=['accuracy'])
不会产生你期望的结果,但原因不是使用了二元交叉熵(至少在原则上,这是一个绝对有效的损失函数)。
为什么会这样呢?如果你查看度量源代码,Keras并没有定义单一的准确率度量,而是定义了多个不同的度量,其中包括binary_accuracy
和categorical_accuracy
。在幕后,由于你选择了loss='binary_crossentropy'
并且没有指定特定的准确率度量,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 version 3.5.3Tensorflow version 1.2.1Keras version 2.0.4