Keras:大规模的独热编码:二元交叉熵还是分类交叉熵

4

我正在训练一个文本分类模型,其输入数据由4096个术语频率-逆文档频率组成。

我的输出有416个可能的类别。每个数据有3个类别,因此在一个由413个零(独热编码)和3个1组成的数组中有3个1。

我的模型如下:

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 loss进行训练时,经过一次epoch后,它的损失为0.185,准确率为96%。经过5个epoch后,损失降至0.037,准确率达到99.3%。但我猜测这是错误的,因为我的标签中有很多0,它正确地对其进行了分类。
当我使用categorical_crossentropy loss进行训练时,在前几次epoch中,损失大约在15.0左右,准确率低于5%,几十个epoch后,损失稳定在5.0左右,准确率为12%。
对于我的情况(具有多个1的大型one-hot编码),哪种方法更适合?这些分数告诉我什么?
编辑:此处是model.compile()语句。
model.compile(loss='categorical_crossentropy',
              optimizer=keras.optimizers.Adam(),
              metrics=['accuracy'])

并且

model.compile(loss='binary_crossentropy',
              optimizer=keras.optimizers.Adam(),
              metrics=['accuracy'])

对于任何审阅这篇帖子的人:这是一个与Keras相关的编码问题,而不是与统计/机器学习理论或实践相关的问题;因此,它适合在这里讨论,而不是在交叉验证中。 - desertnaut
只是好奇,为什么你在最后一层使用'sigmoid'而不是首选的'softmax'激活函数? - Naveen
1个回答

9
简而言之,当您使用 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_accuracycategorical_accuracy。在底层实现中发生了什么是这样的,由于您选择了loss='binary_crossentropy'并且没有指定一个特定的准确率度量,Keras(错误地……)推断出您对binary_accuracy感兴趣,并返回该值——而实际上您想要的是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

非常感谢您的回答!有人指出对我来说categorical_crossentropy并不是最好的选择,因为我总是需要预测3个1,而categorical_crossentropy则期望一个分布。这是正确的吗?如果是这样,那么使用binary_crossentropycategorical_accuracy是否值得呢? - Tutanchamunon
正如我所说,当你有多个类别时,“规范化”损失函数是categorical_crossentropy; 但在(理论上非常贫乏的)深度学习领域中,实际上最终的评判标准是实验!因此,您可以尝试一下(使用我上面建议的补救措施),看看结果如何(顺便说一句,您也可以接受答案 - 谢谢)。 - desertnaut

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接