技术背景
在机器学习和深度学习中,交叉熵是常用的损失函数,用于衡量模型预测结果与真实标签之间的差异。二元交叉熵(binary_crossentropy)和分类交叉熵(categorical_crossentropy)是两种常见的交叉熵损失函数。然而,在某些情况下,使用这两种损失函数会得到不同的模型性能,这引发了人们的疑问。
实现步骤
问题描述
在训练一个用于文本主题分类的卷积神经网络(CNN)时,使用二元交叉熵得到了约80%的准确率,而使用分类交叉熵仅得到约50%的准确率。模型结构如下:
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'))
模型编译
使用分类交叉熵作为损失函数进行编译:
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
使用二元交叉熵作为损失函数进行编译:
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
核心代码
验证Keras评估准确率的问题
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激活函数搭配。如果搭配错误,可能会导致模型性能不佳。
标签编码问题
使用分类交叉熵时,目标标签需要是单热编码的形式;而使用二元交叉熵时,标签编码形式取决于具体问题类型。如果标签编码不符合要求,会影响模型的训练和评估结果。