我正在尝试培训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'))
然后
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
或
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
答案 0 :(得分:126)
分类与表现之间明显的性能差异的原因二元交叉熵是@ xtof54在他的答案中已经报道的,即:
使用Keras方法
evaluate
计算的准确度很简单 使用带有2个以上标签的binary_crossentropy时错误
我想详细说明这一点,展示实际的根本问题,解释它并提供补救措施。
此行为不是错误;根本原因是一个相当微妙的&在模型编译中只包含metrics=['accuracy']
时,Keras实际上猜测使用哪种精度的未记录问题,具体取决于您选择的损失函数。换句话说,当你的第一个编译选项
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
是有效的,你的第二个:
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
不会产生你所期望的,但原因不是使用二元交叉熵(至少在原理上,它是绝对有效的损失函数)。
为什么?如果您选中metrics source code,则Keras不会定义单个精度指标,而是定义几个不同的指标,其中包括binary_accuracy
和categorical_accuracy
。 under the hood会发生什么,因为您选择了二进制交叉熵作为损失函数并且没有指定特定的准确度度量,Keras(错误地......)推断您对binary_accuracy
感兴趣,这就是它返回的内容 - 实际上你对categorical_accuracy
感兴趣。
让我们使用Keras中的MNIST CNN example验证是否属于这种情况,并进行了以下修改:
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
更新:在我的帖子之后,我发现此问题已在this answer中确定。
答案 1 :(得分:24)
这是非常有趣的案例。实际上在您的设置中,以下陈述是正确的:
binary_crossentropy = len(class_id_index) * categorical_crossentropy
这意味着直到一个恒定的倍增因子,您的损失是相等的。您在训练阶段观察到的奇怪行为可能是以下现象的一个例子:
adam
时 - 学习率的值比训练开始时要小得多(这是因为这个优化器的性质)。它使训练速度变慢,并阻止您的网络留下较差的地方最低限度。这就是为什么这个常数因素可能对binary_crossentropy
有帮助的原因。在许多时期之后 - 学习率值大于categorical_crossentropy
案例。当我注意到这种行为或/并使用以下模式调整班级权重时,我通常会重新开始训练(和学习阶段)几次:
class_weight = 1 / class_frequency
这使得在训练开始时和优化过程的另一部分中平衡阶级损失影响的较不频繁的课程造成损失。
修改强>
实际上 - 我检查过,即使是数学:
binary_crossentropy = len(class_id_index) * categorical_crossentropy
应该保留 - 如果keras
不成立,因为keras
会自动将所有输出标准化为1
。这就是这种奇怪行为背后的真正原因,因为在多分类的情况下,这种规范化会损害培训。
答案 2 :(得分:22)
我遇到了倒立的#34;问题 - 我使用categorical_crossentropy(有2个类)得到了很好的结果,而使用binary_crossentropy得到了很好的结果。似乎问题是错误的激活功能。正确的设置是:
binary_crossentropy
:sigmoid activation,scalar target categorical_crossentropy
:softmax activation,one-hot encoded target 答案 3 :(得分:20)
这一切都取决于您正在处理的分类问题的类型。主要有三个类别;
在第一种情况下,应使用二进制交叉熵,并将目标编码为单热矢量。
在第二种情况下,应使用分类交叉熵,并将目标编码为单热矢量。
在最后一种情况下,应该使用二进制交叉熵,并且应该将目标编码为单热矢量。每个输出神经元(或单位)被视为一个单独的随机二进制变量,整个输出向量的损失是单个二进制变量丢失的乘积。因此,它是每个单个输出单元的二进制交叉熵的乘积。
二进制交叉熵定义如下: binary cross-entropy 分类交叉熵定义如下: categorical cross-entropy
答案 4 :(得分:13)
在评论@Marcin回答之后,我更仔细地检查了我的一个学生代码,在那里我发现了同样奇怪的行为,即使只有2个时代! (所以@ Marcin的解释在我的情况下不太可能。)
我发现答案实际上很简单:当使用带有2个以上标签的binary_crossentropy时,使用Keras方法evaluate
计算的准确性是完全错误的。您可以通过自己重新计算准确性来检查(首先调用Keras方法“预测”,然后计算预测返回的正确答案的数量):您得到的真实准确度远低于Keras“评估”的准确度。< / p>
答案 5 :(得分:4)
由于这是一个多类问题,你必须使用categorical_crossentropy,二进制交叉熵会产生虚假结果,很可能只会评估前两个类。
多类问题的50%可能非常好,具体取决于类的数量。如果你有n个类,则100 / n是输出随机类所能获得的最低性能。
答案 6 :(得分:4)
在多类设置下的一个简单示例进行说明
假设您有4个类(onehot编码),而下面只是一种预测
true_label = [0,1,0,0] 预测的标签= [0,0,1,0]
使用categorical_crossentropy时,精度仅为0,它只关心是否正确设置了相关的类。
但是,当使用binary_crossentropy时,将为所有类别计算准确性,该预测的准确性为50%。最终结果将是两种情况下个人准确度的平均值。
对于多类(类互斥)问题,建议使用categorical_crossentropy,对于多标签问题,建议使用binary_crossentropy。
答案 7 :(得分:1)
当使用categorical_crossentropy
丢失时,你的目标应该是分类格式(例如,如果你有10个类,每个样本的目标应该是一个全0的10维向量,除了1 at at对应于样本类的索引。)
答案 8 :(得分:1)
desernaut 的精彩侦探片令人满意地回答了要点。然而,有时 BCE(二元交叉熵)可能会产生与 CCE(分类交叉熵)不同的结果,并且可能是首选。虽然上面分享的拇指规则(选择哪个损失)适用于 99% 的情况,但我想在此讨论中添加一些新的维度。
OP 有一个 softmax 激活,这会抛出一个概率分布作为预测值。这是一个多类问题。首选损失是分类 CE。本质上,这归结为 -ln(p),其中“p”是样本中唯一正类的预测概率。这意味着负面预测在计算 CE 中不起作用。这是故意的。
在极少数情况下,可能需要让 -ve 声音计数。这可以通过将上述样本视为一系列二元预测来完成。因此,如果预期为 [1 0 0 0 0] 而预测为 [0.1 0.5 0.1 0.1 0.2],则进一步细分为:
expected = [1,0], [0,1], [0,1], [0,1], [0,1]
predicted = [0.1, 0.9], [.5, .5], [.1, .9], [.1, .9], [.2, .8]
现在我们继续计算 5 个不同的交叉熵 - 一个用于上述 5 个预期/预测组合中的每一个,并将它们相加。然后:
CE = -[ ln(.1) + ln(0.5) + ln(0.9) + ln(0.9) + ln(0.8)]
CE 具有不同的尺度,但仍然是对预期值和预测值之间差异的度量。唯一的区别是在这个方案中,-ve 值也与 +ve 值一起受到惩罚/奖励。如果您的问题是您将使用输出概率(+ve 和 -ves)而不是使用 max() 来预测 1 +ve 标签,那么您可能需要考虑这个版本的 CE。
多标签情况如何,预期 = [1 0 0 0 1]?传统的方法是对每个输出神经元使用一个 sigmoid,而不是整体的 softmax。这确保了输出概率彼此独立。所以我们得到类似的东西:
expected = [1 0 0 0 1]
predicted is = [0.1 0.5 0.1 0.1 0.9]
根据定义,CE 衡量 2 个概率分布之间的差异。但是上面两个列表并不是概率分布。概率分布应始终为 1。因此,传统的解决方案是使用与之前相同的损失方法 - 将预期值和预测值分成 5 个单独的概率分布,继续计算 5 个交叉熵并将它们相加。然后:
CE = -[ ln(.1) + ln(0.5) + ln(0.9) + ln(0.9) + ln(0.9)] = 3.3
当类的数量可能非常多时就会出现挑战 - 例如 1000 个,并且每个样本中可能只有几个。所以预期类似于:[1,0,0,0,0,0,1,0,0,0.....990 个零]。 预测的可能类似于:[.8, .1, .1, .1, .1, .1, .8, .1, .1, .1.....< em>990 0.1's]
在这种情况下,CE =
- [ ln(.8) + ln(.8) for the 2 +ve classes and 998 * ln(0.9) for the 998 -ve classes]
= 0.44 (for the +ve classes) + 105 (for the negative classes)
您可以看到 -ve 类在计算损失时如何开始创建一个令人讨厌的值。 +ve 样本的声音(这可能是我们关心的全部)被淹没了。我们做什么?我们不能使用分类 CE(在计算中只考虑 +ve 样本的版本)。这是因为,我们被迫将概率分布分解为多个二元概率分布,否则它首先就不是概率分布。一旦我们将其分解为多个二元概率分布,我们别无选择,只能使用二元 CE,这当然会给 -ve 类带来权重。
一种选择是通过乘数来淹没 -ve 类的声音。因此,我们将所有 -ve 损失乘以 gamma < 1 的值 gamma。在上述情况下,gamma 可以是 0.0001。现在损失来了:
= 0.44 (for the +ve classes) + 0.105 (for the negative classes)
滋扰值下降了。 2 年前,Facebook 做到了这一点,并且在他们提出的论文中还做了更多,他们还将 -ve 损失乘以 p 的 x 次方。 'p' 是输出为 a +ve 的概率,x 是一个常数 > 1。这会惩罚 -ve 损失更多尤其是模型非常自信的模型(其中 1-p 接近 1)。惩罚负类损失与对容易分类的案例(占 -ve 案例的大部分)更严厉惩罚的综合效果对 Facebook 来说效果很好,他们称之为焦点损失。
因此,对于 OP 的问题,即二进制 CE 在他的情况下是否有意义,答案是 - 视情况而定。在 99% 的情况下,传统的拇指规则有效,但有时这些规则可能会被弯曲甚至破坏以适应手头的问题。
答案 9 :(得分:0)
您正在传递目标形状数组(x-dim,y-dim),同时将其用作损耗categorical_crossentropy
。 categorical_crossentropy
期望目标是形状(样本,类)的二进制矩阵(1和0)。如果您的目标是整数类,则可以通过以下方式将它们转换为预期的格式:
from keras.utils import to_categorical
y_binary = to_categorical(y_int)
或者,您可以使用损失函数sparse_categorical_crossentropy
,它确实需要整数目标。
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
答案 10 :(得分:0)
看看方程式,您会发现binary cross entropy不仅惩罚那些标签= 1,预测= 0,而且还惩罚标签= 0,预测= 1。
然而categorical cross entropy只惩罚那些标签= 1但预测=1。这就是为什么我们假设只有一个标签为正的原因。
答案 11 :(得分:-1)
binary_crossentropy(y_target,y_predict)不需要应用于二进制分类问题。
在binary_crossentropy()的源代码中,实际上使用了nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)
TensorFlow函数。
并且,在documentation中表示:
测量离散分类任务中的概率误差,其中每个分类都是独立的并且不互斥。例如,可以执行多标签分类,其中一张图片可以同时包含一只大象和一只狗。