当前位置: 首页 > 知识库问答 >
问题:

为什么binary_crossentropy和categorical_crossentropy对同一个问题给出不同的表现?

楮星鹏
2023-03-14

我正在尝试训练CNN按主题对文本进行分类。当我使用二进制交叉熵时,我可以获得大约80%的准确率,而使用分类交叉熵,我可以获得大约50%的准确率。

我不明白为什么会这样。这是一个多类的问题,难道这不意味着我必须使用分类交叉熵,而二元交叉熵的结果是没有意义的吗?

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'])

直觉上,我想使用分类交叉熵是有道理的,我不明白为什么我用二进制得到的结果好,而用分类得到的结果差。

共有3个答案

马沛
2023-03-14

我遇到了一个“颠倒”的问题-我在分类交叉熵(2个类)方面得到了很好的结果,而在二进制交叉熵方面却很差。似乎问题出在错误的激活功能上。正确的设置为:

  • 对于binary\u crossentropy:sigmoid激活,标量目标
郎欣然
2023-03-14

这完全取决于您处理的分类问题的类型。主要有三类

  • 二进制分类(两个目标类),
  • 多类别分类(两个以上的独占目标),
  • 多标签分类(两个以上的非独占目标),其中可以同时启用多个目标类

在第一种情况下,应使用二进制交叉熵,并将目标编码为一个热向量。

在第二种情况下,应使用分类交叉熵,目标应编码为one-Hot向量。

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

二元交叉熵定义

分类交叉熵定义为

其中c是运行在类C数量上的索引。

钱经业
2023-03-14

分类之间明显的性能差异的原因

当使用具有两个以上标签的二进制交叉熵时,使用Keras方法计算的精度显然是错误的

我想对此进行更多的阐述,展示实际的根本问题,解释它,并提供补救措施。

这种行为不是bug;根本原因是一个相当微妙的

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

有效,您的第二个:

model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

不会产生您期望的结果,但原因不是使用二进制交叉熵(至少在原则上,这是一个绝对有效的损失函数)。

为什么会这样?如果检查度量源代码,Keras不会定义单个精度度量,而是定义几个不同的度量,其中包括二进制精度和分类精度。在幕后发生的事情是,由于您选择了二进制交叉熵作为损失函数,并且没有指定特定的精度度量,Keras(错误地…)推断您对二进制精度感兴趣,这就是它返回的结果,而实际上您对分类精度感兴趣。

让我们使用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    

为了解决这一问题,即使用二进制交叉熵作为损失函数(如我所说,至少在原则上,这没有错),同时仍能获得当前问题所需的分类精度,您应该在模型编译中明确要求分类精度,如下所示:

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

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

 类似资料:
  • 问题内容: 查看以下代码,并请解释为什么该方法和函数给出两个不同的输出。 输出: 问题答案: 计算子字符串的非重叠出现次数: 返回substring sub 的不重叠出现的次数。 在字符串中恰好有一个这样的子字符串出现的位置:就在开头。因此计数 应该 返回。 一般来说,空字符串将匹配给定字符串中的 所有位置 ,包括开始和结束处的正确 位置 ,因此计数应 始终 为长度加1: 这是因为空字符串被认为存

  • Java 中的字节长度为 8 位。一个

  • 类别:账户余额 我已经把这两个类都放在Balance.java和Account tBalance.java.这两个文件都在E:/程序/MyPack. Balance.java编译没有错误但是当我编译Account tBalance.java它给出错误:找不到符号"平衡". 我无法弄清楚为什么当两个类都在同一个包中声明时? 我正在使用javac B从MyPack编译alance.javajavac

  • 问题内容: 我以为运算符检查对象的相等性。但事实并非如此: 问题答案: *Python *将相同的内存 位置用于方法和,这是*两个对象,它们的生命周期不重叠,因此对它们返回相同的标识。请参阅下面的详细说明。 从is运算符的文档中: 运算符是否测试对象标识:并且仅当x和y是同一对象时,x is y才是true。 从ID的文档中 返回对象的“身份”。这是一个整数(或长整数),在此对象的生存期内,此整数

  • 问题内容: public class Test { public static void main(String[] args) { int i = 10; i = i++; System.out.println(“value of i is : ” + i); } } 输出为: 当我在执行类似的代码时,输出为。 问题答案: 关于此问题,这是未定义的行为,因为您试图在同一行的同一序列点内多次修改同

  • <代码>car\U gear字段在数据库中填写为“stick”(斗杆)。在图像标记后,输出更改为“自动” 为什么结果是$car\u result1-