Keras:二元交叉熵和分类交叉熵混淆

11

使用 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 维向量,在该向量中只有一个索引对应于样本的类别,其他都是零)。

如果我没有误解,这仅是一个 one-hot 编码分类任务的特殊情况,但底层的交叉熵损失也可以处理概率分布(“多类别”,有依赖关系的标签)吗?

此外,Keras 使用 tf.nn.softmax_cross_entropy_with_logits(TF Python 后端)进行实现,它本身 states

请纠正我如果我错了,但是在我看来Keras的文档说明至少不是很“详细”?!
那么,Keras对损失函数命名背后的想法是什么?文档是否正确?如果二元交叉熵确实依赖于二元标签,那它应该对自动编码器无法起作用,对吗?同样的,如果文档正确,分类交叉熵:只能适用于独热编码标签!
3个回答

8

您是对的,通过定义适用于每种损失的区域:

  • 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 试图保持简约,并针对最流行的用例,因此我可以理解这里的逻辑。

1

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


是的(抱歉造成混淆):我实际上是指对于每个输出神经元,根据命名和Keras文档(binary_crossentropy),它们应该是01。然而(再次,如果我没有弄错的话),这是错误的:Keras(TF Python后端)使用tf.nn.sigmoid_cross_entropy_with_logits,这是用于多类、独立、非互斥分类问题的。这意味着对于n个输出神经元,每个神经元可以在区间[0.0, 1.0]内具有一个值(最可能是float32)(sigmoid激活)。 - daniel451
当您使用网络进行评分时,网络的输出将是浮点值,但在训练时需要使用二进制标签;如果有帮助的话,您可以将最后一层视为对倒数第二层的输出执行多个逻辑回归模型。 - maxymoo
这就是人们对 binary_crossentropy 的期望,对吗?但是如果真的是这种情况,那么(1)自编码器不应使用 binary_crossentropy,(2)使用 tf.nn.sigmoid_cross_entropy_with_logits 是错误的,因为它是用于具有非相互排斥标签的独立多类问题的。 - daniel451
好的,针对CNN自编码器,其输出的解释是不同的,它的输出是像素强度而非概率。这并不是问题,只是意味着你需要为损失函数进行不同的解释。 - maxymoo
二进制并不意味着只有两个输出类,它只是意味着每个输出都是二进制的。我之前不知道,谢谢。 - ozgur
显示剩余2条评论

0
文档没有提到BinaryCrossentropy可以用于多标签分类,这可能会让人感到困惑。但它也可以用于二元分类器(当我们只有两个互斥类别,如猫和狗)- 参见经典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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接