如何在TensorFlow中选择交叉熵损失函数?

101

分类问题,例如逻辑回归或多项式逻辑回归,优化 交叉熵 损失。通常,交叉熵层跟随 softmax 层,产生概率分布。

在tensorflow中,至少有十几种不同的 交叉熵损失函数:

  • tf.losses.softmax_cross_entropy
  • tf.losses.sparse_softmax_cross_entropy
  • tf.losses.sigmoid_cross_entropy
  • tf.contrib.losses.softmax_cross_entropy
  • tf.contrib.losses.sigmoid_cross_entropy
  • tf.nn.softmax_cross_entropy_with_logits
  • tf.nn.sigmoid_cross_entropy_with_logits
  • ...

哪个适用于仅二元分类,哪些适用于多类问题?何时应使用 sigmoid 而不是 softmaxsparse 函数与其他函数有何不同,为什么只有 softmax

相关(更加数学化)讨论:Keras 和 TensorFlow 中所有这些交叉熵损失之间的区别是什么?.


1
另外,我们有tf.losses.log_loss,实际上它只适用于二元交叉熵。此外,请参阅https://github.com/tensorflow/tensorflow/issues/2462。 - mrgloom
3个回答

151

初步事实

  • 从功能上讲,当类别数等于2时,sigmoid是softmax函数的一种特例。它们都执行相同的操作:将对数几率(见下文)转换为概率。

    在简单的二元分类中,两者没有太大区别,但在多项分类的情况下,sigmoid允许处理非排他标签(也称为多标签),而softmax处理排他类别(见下文)。

  • 对数几率(也称为分数)是与一个类别相关的原始未缩放值,在计算概率之前。在神经网络架构方面,这意味着对数几率是密集(全连接)层的输出。

    Tensorflow的命名有点奇怪:下面所有的函数都接受对数几率,而不是概率,并且自己应用转换(这更加高效)

Sigmoid函数族

  • tf.nn.sigmoid_cross_entropy_with_logits:使用sigmoid loss函数进行二分类。但是TensorFlow函数更加通用,可以进行多标签分类,当类别是独立的时候。换句话说,tf.nn.sigmoid_cross_entropy_with_logits可以同时解决N个二分类问题。
  • tf.nn.weighted_cross_entropy_with_logits:允许设置类权重(记住,分类是二元的),即使得正错误比负错误更大。这在训练数据不平衡时很有用。
  • tf.losses.sigmoid_cross_entropy:除了可以设置批量权重(即使一些例子比其他例子更重要)之外,还可以使用sigmoid cross entropy loss函数,需要注意的是,标签必须是独热编码或者可以包含软类概率。
  • tf.contrib.losses.sigmoid_cross_entropy (已弃用)

Softmax函数族

这些损失函数应该用于多项互斥分类,即从N个类中选择一个。当N = 2时也适用。

标签必须是独热编码或包含软类概率:一个特定的示例可以以50%的概率属于A类,以50%的概率属于B类。请注意,严格地说,这并不意味着它属于两个类,但可以这样解释概率。

就像在sigmoid族中一样,tf.losses.softmax_cross_entropy允许设置批量权重,即使一些示例比其他示例更重要。据我所知,截至tensorflow 1.3,没有内置的方法可以设置类别权重

[UPD] 在tensorflow 1.5中,引入了v2版本(被引入),原始的softmax_cross_entropy_with_logits损失已被弃用。它们之间唯一的区别是在较新的版本中,反向传播会发生在对数和标签中(这里有一个讨论为什么这可能有用)。

稀疏函数族

与普通的softmax一样,这些损失函数应该用于多项互斥分类,即从N个类中选择一个。区别在于标签编码:类被指定为整数(类索引),而不是one-hot向量。显然,这不允许软类别,但当有成千上万或数百万个类时,可以节省一些内存。但是,请注意,logits参数仍必须包含每个类的logits,因此它至少消耗[batch_size,classes]内存。
与上述相似,tf.losses版本有一个权重参数,允许设置批处理权重。
采样softmax函数族 这些函数提供了另一种处理大量类别的替代方案。它们不是计算和比较精确的概率分布,而是从随机样本中计算损失估计。
参数"weights"和"biases"指定一个单独的全连接层,用于计算所选样本的logits。
与上面一样,标签"labels"没有进行one-hot编码,但其形状为"[batch_size, num_true]".
采样函数仅适用于训练。在测试时间,建议使用标准的softmax loss(稀疏或one-hot)来获得实际分布。
另一种替代损失是"tf.nn.nce_loss",它执行噪声对比估计(如果您感兴趣,请参见非常详细的讨论)。我将此函数包含在softmax系列中,因为NCE保证在极限情况下逼近softmax。

我能请教一个关于sigmoid交叉熵(sigCE)的澄清点吗?如果它一次解决了N个二元分类任务,那么N = prod(output.shape),例如shape = [batch, examples, channels]; N = (batch * examples * channels)吗?如果tf.losses期望“logits”(网络输出),那么为了方便使用,我也应该返回概率吗?你可以看一下https://stackoverflow.com/questions/53612973/tensorflow-sigmoid-cross-entropy-with-logits-for-1d-data/53617271?noredirect=1#comment94097110_53617271吗? - SumNeuron

5

然而,在版本1.5中,必须使用softmax_cross_entropy_with_logits_v2,同时使用其参数argument key=...,例如:

softmax_cross_entropy_with_logits_v2(_sentinel=None, labels=y,
                                    logits=my_prediction, dim=-1, name=None)

4
尽管被接受的答案中包含了比问题要求更多的信息是很好的,但我认为分享一些通用的规则会使答案更紧凑,更易于理解:
  • 只有一个真正的损失函数。这个函数是交叉熵 (CE)。对于二元分类的特殊情况,该损失称为二元 CE (请注意公式不变),而对于非二进制或多类情况,则称之为分类 CE(CCE)。稀疏函数是分类 CE 的一种特殊情况,其中期望值不是独热编码,而是整数
  • 我们有一个针对多类情况的激活函数 softmax 公式。对于二元情况,同样的公式被赋予了一个特殊的名称 sigmoid 激活
  • 由于在处理对数函数时有时存在数值不稳定性 (极端值),TensorFlow 建议将激活层和损失层合并成一个单一函数。这个组合函数在数值上更加稳定。TensorFlow 提供了这些组合函数,并以 _with_logits 为后缀。

有了这些,现在让我们来看一些情况。假设有一个简单的二元分类问题 - 图像中是否存在猫?激活和损失函数的选择是什么?它将是 sigmoid 激活和 (二元) CE。因此可以使用 sigmoid_cross_entropy,或者更好的是 sigmoid_cross_entropy_with_logits。后者将激活和损失函数组合在一起,应该会更加稳定。

那么对于多类分类呢?假设我们想知道图像中是否存在猫、狗或驴子。激活和损失函数的选择是什么?它将是 softmax 激活和 (分类) CE。因此可以使用 softmax_cross_entropy,或者更好的是 softmax_cross_entropy_with_logits。我们假设期望值已进行了独热编码 (100 或 010 或 001)。如果 (由于某种奇怪的原因) 这不是这种情况,而期望值是一个整数 (1 或 2 或 3),则可以使用上述函数的“稀疏”版本。

可能会有第三种情况,即我们可能会面临多标签分类。因此,同一张图像中可能会有狗和猫。我们该如何处理呢?这里的技巧是将这种情况视为多个二元分类问题 - 基本上是猫或没有猫/狗或没有狗和驴子或没有驴子。找出每个二元分类的损失,然后将它们加起来。因此,本质上可以使用sigmoid_cross_entropy_with_logits损失函数。
这回答了您提出的3个具体问题。上面分享的函数就是所需的全部内容。您可以忽略tf.contrib系列,这已被弃用,不应使用。

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