Keras binary_crossentropy vs categorical_crossentropy性能?

Dan*_*ias 130 machine-learning neural-network deep-learning conv-neural-network keras

我正在尝试培训CNN按主题对文本进行分类.当我使用binary_crossentropy时,我得到~80%acc,而categorical_crossentrop我得到~50%acc.

我不明白为什么会这样.这是一个多类问题,这是否意味着我必须使用分类,二进制结果是没有意义的?

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'))
Run Code Online (Sandbox Code Playgroud)

然后

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
Run Code Online (Sandbox Code Playgroud)

要么

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
Run Code Online (Sandbox Code Playgroud)

des*_*aut 164

分类和二元交叉熵之间明显的性能差异的原因是@ xtof54在他的答案中已经报道了,即:

evaluate当使用带有2个以上标签的binary_crossentropy时,使用Keras方法计算的精度是完全错误的

我想详细说明这一点,展示实际的根本问题,解释它,并提供补救措施.

这种行为不是一个bug; 根本原因是Keras实际上猜测使用哪种准确度是一个相当微妙和无证的问题,这取决于你选择的损失函数,当你简单地包含metrics=['accuracy']在你的模型编译中时.换句话说,当你的第一个编译选项

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
Run Code Online (Sandbox Code Playgroud)

是有效的,你的第二个:

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
Run Code Online (Sandbox Code Playgroud)

不会产生你所期望的,但原因不是使用二元交叉熵(至少在原理上,这是一个绝对有效的损失函数).

这是为什么?如果您检查指标的源代码,Keras没有定义一个准确的度量,但有几个不同的,其中binary_accuracycategorical_accuracy.发生什么事情,因为你选择了二进制交叉熵作为你的损失函数并且没有指定一个特定的精度度量,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    
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,即使用确实二进制交叉熵作为损失函数(正如我所说的,没有错,至少在原则上),同时还获得了绝对的手头上的问题所需的精度,你应该问明确了categorical_accuracy在模型编译如下:

from keras.metrics import categorical_accuracy
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=[categorical_accuracy])
Run Code Online (Sandbox Code Playgroud)

在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    
Run Code Online (Sandbox Code Playgroud)

系统设置:

Python version 3.5.3
Tensorflow version 1.2.1
Keras version 2.0.4
Run Code Online (Sandbox Code Playgroud)

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


Why*_*ote 37

这一切都取决于您正在处理的分类问题的类型.主要有三类;

  • 二元分类(两个目标类)
  • 多级分类(超过两个独家目标)
  • 多标签分类(超过两个非独占目标),其中多个目标类可以同时打开

在第一种情况下,应该使用二进制交叉熵,并且应该将目标编码为单热矢量.

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

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

二元交叉熵定义如下: 二元交叉熵 和分类交叉熵定义如下: 分类交叉熵


Ale*_*kin 33

我遇到了一个"倒置"的问题 - 我使用categorical_crossentropy(有2个类)并且使用binary_crossentropy很差.似乎问题是错误的激活功能.正确的设置是:

  • for binary_crossentropy:sigmoid激活,标量目标
  • for categorical_crossentropy:softmax激活,单热编码目标

  • 您确定关于binary_crossentropy的标量目标吗?看来您应该使用“很多”编码目标(例如[0 1 0 0 1 1])。 (3认同)
  • 当然。请参阅https://keras.io/losses/#usage-of-loss-functions,其中说:“使用categorical_crossentropy损失时,您的目标应采用分类格式(例如,如果您有10个类别,则每个样本的目标应该是一个10维向量,在与样本类别相对应的索引处期望为1的全零。”“ (3认同)

Mar*_*jko 25

这是非常有趣的案例.实际上在您的设置中,以下陈述是正确的:

binary_crossentropy = len(class_id_index) * categorical_crossentropy
Run Code Online (Sandbox Code Playgroud)

这意味着最多一个乘法因子,您的损失是相等的.您在训练阶段观察到的奇怪行为可能是以下现象的一个例子:

  1. 一开始,最频繁的课程主导着失败 - 所以网络正在学习为每个例子预测这个课程.
  2. 在学习了最频繁的模式之后,它开始区分不太频繁的类.但是当你使用时adam- 学习率的值比训练开始时要小得多(这是因为这个优化器的性质).它使训练变得更慢,并且可以防止您的网络更糟糕地保留较差的局部最小值.

这就是为什么这个常数因素可能有用的原因binary_crossentropy.在许多时期之后 - 学习率值大于categorical_crossentropy案例.当我注意到这种行为或/和使用以下模式调整类权重时,我通常会重新开始训练(和学习阶段)几次:

class_weight = 1 / class_frequency
Run Code Online (Sandbox Code Playgroud)

这使得来自较​​不频繁的类的损失在训练开始时和在优化过程的另一部分中平衡主要类损失的影响.

编辑:

实际上 - 我检查过,即使在数学的情况下:

binary_crossentropy = len(class_id_index) * categorical_crossentropy
Run Code Online (Sandbox Code Playgroud)

应该保持 - 如果keras它不是真的,因为keras自动归一化所有输出总结1.这就是这种奇怪行为背后的真正原因,因为在多分类的情况下,这种规范化会损害培训.

  • 这是一个非常合理的解释。但我不确定这真的是主要原因。因为我还观察到我的 * 几个 * 学生在应用 binary-X-ent 而不是 cat-X-ent 时会出现这种奇怪的行为(这是一个错误)。即使只训练了 2 个 epoch,也是如此!使用具有逆类先验的 class_weight 没有帮助。可能对学习率进行严格的调整会有所帮助,但默认值似乎有利于 bin-X-ent。我认为这个问题值得更多调查...... (2认同)
  • 等等,抱歉,我没有得到你的更新:softmax 总是使输出总和为 1,所以我们不在乎?为什么这会损害训练,只要我们每个示例只有一个正确的黄金类? (2认同)

xto*_*f54 15

在评论@Marcin回答后,我更仔细地检查了我的一个学生代码,在那里我发现了同样奇怪的行为,即使只有2个时代!(所以@ Marcin的解释在我的情况下不太可能).

我发现答案实际上非常简单:evaluate当使用带有2个以上标签的binary_crossentropy时,使用Keras方法计算的准确性是完全错误的.您可以通过自己重新计算准确性来检查(首先调用Keras方法"预测",然后计算预测返回的正确答案的数量):您获得的真实准确度远低于Keras"评估"的准确度.

  • 我在第一次迭代中也看到了类似的行为。 (2认同)

小智 6

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

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

true_label = [0,1,0,0]预测_label = [0,0,1,0]

当使用categorical_crossentropy时,精度仅为0,它只在乎您是否正确设置了相关的类。

但是,当使用binary_crossentropy时,将为所有类别计算精度,该预测的准确度将为50%。最终结果将是两种情况下个人准确度的平均值。

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


Mat*_*gro 5

由于它是一个多类问题,因此您必须使用categorical_crossentropy,二元交叉熵会产生假结果,很可能仅会评估前两个类。

多类问题的50%可能很好,具体取决于类的数量。如果您有n个类别,则通过输出随机类别可以获得100 / n的最低性能。