二元交叉熵和分类交叉熵在同一问题中表现不同的原因

二元交叉熵和分类交叉熵在同一问题中表现不同的原因

技术背景

在机器学习和深度学习中,交叉熵是常用的损失函数,用于衡量模型预测结果与真实标签之间的差异。二元交叉熵(binary_crossentropy)和分类交叉熵(categorical_crossentropy)是两种常见的交叉熵损失函数。然而,在某些情况下,使用这两种损失函数会得到不同的模型性能,这引发了人们的疑问。

实现步骤

问题描述

在训练一个用于文本主题分类的卷积神经网络(CNN)时,使用二元交叉熵得到了约80%的准确率,而使用分类交叉熵仅得到约50%的准确率。模型结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
model.add(embedding_layer)
model.add(Dropout(0.25))
# 卷积层
model.add(Conv1D(nb_filter=32,
filter_length=4,
border_mode='valid',
activation='relu'))
model.add(MaxPooling1D(pool_length=2))
# 全连接层
model.add(Flatten())
model.add(Dense(256))
model.add(Dropout(0.25))
model.add(Activation('relu'))
# 输出层
model.add(Dense(len(class_id_index)))
model.add(Activation('softmax'))

模型编译

使用分类交叉熵作为损失函数进行编译:

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

使用二元交叉熵作为损失函数进行编译:

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

核心代码

验证Keras评估准确率的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import numpy as np
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Flatten, Conv2D, MaxPooling2D
from keras.utils import to_categorical
from keras.metrics import categorical_accuracy

# 加载MNIST数据集
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape(-1, 28, 28, 1).astype('float32') / 255
x_test = x_test.reshape(-1, 28, 28, 1).astype('float32') / 255
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)

# 构建模型
model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(10, activation='softmax'))

# 错误的编译方式
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

model.fit(x_train, y_train,
batch_size=64,
epochs=2,
verbose=1,
validation_data=(x_test, y_test))

# Keras报告的准确率
score = model.evaluate(x_test, y_test, verbose=0)
print("Keras报告的准确率:", score[1])

# 手动计算的准确率
y_pred = model.predict(x_test)
acc = sum([np.argmax(y_test[i]) == np.argmax(y_pred[i]) for i in range(10000)]) / 10000
print("手动计算的准确率:", acc)

# 正确的编译方式
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=[categorical_accuracy])

model.fit(x_train, y_train,
batch_size=64,
epochs=2,
verbose=1,
validation_data=(x_test, y_test))

# Keras报告的准确率
score = model.evaluate(x_test, y_test, verbose=0)
print("Keras报告的准确率:", score[1])

# 手动计算的准确率
y_pred = model.predict(x_test)
acc = sum([np.argmax(y_test[i]) == np.argmax(y_pred[i]) for i in range(10000)]) / 10000
print("手动计算的准确率:", acc)

最佳实践

  • 分类问题类型选择合适的损失函数
    • 二元分类问题:使用二元交叉熵,输出层使用sigmoid激活函数,目标标签可以是标量或单热编码。
    • 多类分类问题(类别互斥):使用分类交叉熵,输出层使用softmax激活函数,目标标签需要进行单热编码。
    • 多标签分类问题:使用二元交叉熵,输出层每个神经元使用sigmoid激活函数,目标标签可以是单热编码。
  • 明确指定准确率指标:当使用二元交叉熵进行多类分类时,为了得到正确的分类准确率,应在模型编译时明确指定categorical_accuracy

常见问题

准确率计算错误

当使用二元交叉熵进行多类分类且在模型编译时仅指定metrics=['accuracy']时,Keras会错误地推断使用binary_accuracy,导致报告的准确率与实际分类准确率不符。解决方法是明确指定categorical_accuracy

激活函数与损失函数不匹配

不同的损失函数需要搭配合适的激活函数。例如,二元交叉熵通常与sigmoid激活函数搭配,分类交叉熵通常与softmax激活函数搭配。如果搭配错误,可能会导致模型性能不佳。

标签编码问题

使用分类交叉熵时,目标标签需要是单热编码的形式;而使用二元交叉熵时,标签编码形式取决于具体问题类型。如果标签编码不符合要求,会影响模型的训练和评估结果。


二元交叉熵和分类交叉熵在同一问题中表现不同的原因
https://119291.xyz/posts/2025-04-22.reason-different-performances-binary-categorical-crossentropy/
作者
ww
发布于
2025年4月22日
许可协议