Keras:binary_crossentropy 和 categorical_crossentropy 混淆

Posted

技术标签:

【中文标题】Keras:binary_crossentropy 和 categorical_crossentropy 混淆【英文标题】:Keras: binary_crossentropy & categorical_crossentropy confusion 【发布时间】:2018-06-01 07:18:51 【问题描述】:

在使用 TensorFlow 一段时间后,我阅读了一些 Keras 教程并实现了一些示例。我找到了几个使用 keras.losses.binary_crossentropy 作为损失函数的卷积自动编码器教程。

我认为binary_crossentropy应该是一个多类损失函数并且很可能使用二进制标签,但实际上Keras(TF Python后端)调用tf.nn.sigmoid_cross_entropy_with_logits,这实际上是有意的用于具有多个互斥的独立类的分类任务。

另一方面,我对 categorical_crossentropy 的期望是用于多类分类,其中目标类相互依赖,但不一定是 one-hot 编码。

但是,Keras 文档指出:

(...) 当使用 categorical_crossentropy 损失时,你的目标应该是分类格式(例如,如果你有 10 个类,每个样本的目标应该是一个 10 维向量,期望全零对应于样本类别的索引处为 1)。

如果我没记错的话,这只是 one-hot 编码分类任务的特例,但底层的交叉熵损失也适用于概率分布(“多类”,依赖标签)?

此外,Keras 使用tf.nn.softmax_cross_entropy_with_logits(TF python 后端)来实现,它本身是states:

注意:虽然类是互斥的,但它们的概率不一定是。所需要的只是每一行标签都是一个有效的概率分布。如果不是,则梯度的计算将不正确。

如果我错了,请纠正我,但在我看来,Keras 文档 - 至少 - 不是很“详细”?!

那么,Keras 命名损失函数背后的想法是什么?文档是否正确?如果二进制交叉熵真的依赖于二进制标签,它应该适用于自动编码器,对吧?! 同样,分类交叉熵:如果文档正确,则仅适用于 one-hot 编码标签?!

【问题讨论】:

【参考方案1】:

文档没有提到BinaryCrossentropy 可用于多标签分类,这可能会造成混淆。但它也可以用于二元分类器(当我们只有 2 个专有类,如猫和狗时) - 请参阅经典的 example。但是在这种情况下我们必须设置n_classes=1:

tf.keras.layers.Dense(units=1)

BinaryCrossentropytf.keras.losses.binary_crossentropy 也有不同的行为。

让我们看一下文档中的例子,证明它实际上是用于多标签分类的。

y_true = tf.convert_to_tensor([[0, 1], [0, 0]])
y_pred = tf.convert_to_tensor([[0.6, 0.4], [0.4, 0.6]])

bce = tf.keras.losses.BinaryCrossentropy()
loss1 = bce(y_true=y_true, y_pred=y_pred)
# <tf.Tensor: shape=(), dtype=float32, numpy=0.81492424>

loss2 = tf.keras.losses.binary_crossentropy(y_true, y_pred)
# <tf.Tensor: shape=(2,), dtype=float32, numpy=array([0.9162905 , 0.71355796], dtype=float32)>

np.mean(loss2.numpy())
# 0.81492424

scce = tf.keras.losses.SparseCategoricalCrossentropy()
y_true = tf.convert_to_tensor([0, 0])
scce(y_true, y_pred)
# <tf.Tensor: shape=(), dtype=float32, numpy=0.71355814>
y_true = tf.convert_to_tensor([1, 0])
scce(y_true, y_pred)
# <tf.Tensor: shape=(), dtype=float32, numpy=0.9162907>

【讨论】:

【参考方案2】:

您正确地定义了这些损失中的每一个都适用的区域:

binary_crossentropy(和底层的tf.nn.sigmoid_cross_entropy_with_logits)用于二元多标签分类(标签是独立的)。 categorical_crossentropy(和底层的tf.nn.softmax_cross_entropy_with_logits)用于多类分类(类是专有的)。

详细分析见this question。

我不确定你指的是什么教程,所以无法评论 binary_crossentropy 是自动编码器的好还是坏选择。

至于命名,绝对正确合理。还是您认为 sigmoidsoftmax 的名字听起来更好?

因此,您问题中唯一的困惑是categorical_crossentropy 文档。请注意,所陈述的一切都是正确的:损失支持 one-hot 表示。在 tensorflow 后端的情况下,此函数确实适用于标签的任何概率分布(除了 one-hot 向量),并且它可以包含在文档中,但这并不对我来说并不重要。此外,需要检查其他后端,theano 和 CNTK 是否支持软类。请记住,keras 试图简约并针对大多数流行的用例,所以我可以理解这里的逻辑。

【讨论】:

【参考方案3】:

不确定这是否能回答您的问题,但对于 softmax 损失,输出层需要是概率分布(即总和为 1),而对于二元交叉熵损失则不需要。就那么简单。 (二进制并不意味着只有2个输出类,它只是意味着每个输出都是二进制的。)

【讨论】:

是的(很抱歉造成混淆):我的意思是,对于 n 输出神经元,根据命名 & keras 文档(binary_crossentropy)。但是(再次,如果我没记错的话),这是错误的:Keras(TF python 后端)使用tf.nn.sigmoid_cross_entropy_with_logits,它旨在用于多类、独立、非互斥的分类问题。这意味着对于 n 个输出神经元,它们中的每一个都可以在 [0.0, 1.0] 区间(sigmoid-activation)中具有一个值(很可能是 float32)。 当您使用网络进行评分时,网络的输出将是浮点值,但您在训练时需要使用二进制标签;如果有帮助,您可以将最后一层视为倒数第二层输出上的多个逻辑回归模型 这就是人们对binary_crossentropy 的期望,对吧?但同样,如果情况确实如此,那么 (1) 自动编码器不应该与 binary_crossentropy 一起使用,并且 (2) 使用 tf.nn.sigmoid_cross_entropy_with_logits 将是错误的,因为它是针对独立的、多类问题而不是互斥标签。 而且categorical_crossentropy也应该是错误的,因为在这种情况下它使用tf.nn.softmax_cross_entropy_with_logits和交叉熵本身,就像TF实现本身一样,不依赖于one-hot的特殊情况编码标签(即全为零,除了真正的类,它是 1)。如果您提供任何概率分布作为标签,它也可以工作(在数学上并由 TF 文档说明)。 这就是为什么我对 Keras 对损失函数的命名及其文档感到困惑。要么他们有一些额外的实现,要么我错过了一些东西。否则他们的命名和文档不是很详细,部分错误?!

以上是关于Keras:binary_crossentropy 和 categorical_crossentropy 混淆的主要内容,如果未能解决你的问题,请参考以下文章

为啥对于 Keras 中的多类分类, binary_crossentropy 比 categorical_crossentropy 更准确?

多标签分类 Keras 指标

为啥 Keras/tensorflow 的 sigmoid 和交叉熵精度低?

如何在 keras 模型中实现汉明损失作为自定义指标

每个示例具有不同权重的 Keras 自定义损失函数

具有 F1 分数的 Keras 多标签图像分类