【问题标题】:What are logits? What is the difference between softmax and softmax_cross_entropy_with_logits?什么是逻辑? softmax 和 softmax_cross_entropy_with_logits 有什么区别?
【发布时间】:2016-03-18 09:06:33
【问题描述】:

tensorflow API docs 中,他们使用了一个名为logits 的关键字。它是什么?很多方法都是这样写的:

tf.nn.softmax(logits, name=None)

如果logits 只是一个通用的Tensor 输入,为什么将其命名为logits


其次,下面两种方法有什么区别?

tf.nn.softmax(logits, name=None)
tf.nn.softmax_cross_entropy_with_logits(logits, labels, name=None)

我知道tf.nn.softmax 做了什么,但不知道另一个。举个例子会很有帮助。

【问题讨论】:

标签: python machine-learning tensorflow


【解决方案1】:

softmax+logits 仅仅意味着该函数对早期层的未缩放输出进行操作,并且理解单位的相对比例是线性的。特别是,这意味着输入的总和可能不等于 1,即这些值是 not 概率(您的输入可能为 5)。在内部,它首先将 softmax 应用于未缩放的输出,然后计算这些值与它们“应该”由标签定义的交叉熵。

tf.nn.softmax 产生将softmax function 应用于输入张量的结果。 softmax 将输入“挤压”成sum(input) = 1,它通过将输入解释为对数概率(logits)然后将它们转换回 0 和 1 之间的原始概率来进行映射。softmax 的输出形状是与输入相同:

a = tf.constant(np.array([[.1, .3, .5, .9]]))
print s.run(tf.nn.softmax(a))
[[ 0.16838508  0.205666    0.25120102  0.37474789]]

请参阅this answer,了解有关为什么在 DNN 中广泛使用 softmax 的更多信息。

tf.nn.softmax_cross_entropy_with_logits 将 softmax 步骤与应用 softmax 函数后的交叉熵损失的计算结合起来,但它以一种更加数学谨慎的方式将这一切结合在一起。结果类似于:

sm = tf.nn.softmax(x)
ce = cross_entropy(sm)

交叉熵是一个汇总度量:它对元素求和。 tf.nn.softmax_cross_entropy_with_logits 在形状 [2,5] 张量上的输出是形状 [2,1](第一个维度被视为批次)。

如果您想进行优化以最小化交叉熵 并且您在最后一层之后进行 softmaxing,您应该使用 tf.nn.softmax_cross_entropy_with_logits 而不是自己做,因为它涵盖了数值不稳定的极端情况以数学上正确的方式。否则,你最终会通过到处添加小 epsilon 来破解它。

于 2016 年 2 月 7 日编辑: 如果您有单类标签,其中一个对象只能属于一个类,您现在可以考虑使用tf.nn.sparse_softmax_cross_entropy_with_logits,这样您就不必将标签转换为密集的 one-hot 数组。此功能是在 0.6.0 版本之后添加的。

【讨论】:

  • 关于softmax_cross_entropy_with_logits,不知道用对了没有。结果在我的代码中不是那么稳定。相同的代码运行两次,总准确率从 0.6 变为 0.8。 cross_entropy = tf.nn.softmax_cross_entropy_with_logits(tf.nn.softmax(tf.add(tf.matmul(x,W),b)),y) cost=tf.reduce_mean(cross_entropy)。但是当我使用另一种方式时,pred=tf.nn.softmax(tf.add(tf.matmul(x,W),b)) cost =tf.reduce_mean(-tf.reduce_sum(y*tf.log(pred),reduction_indices=1)) 结果稳定且更好。
  • 您在第一行中使用了双重软最大化。 softmax_cross_entropy_with_logits 期望未缩放的 logits,而不是 tf.nn.softmax 的输出。您只需要 tf.nn.softmax_cross_entropy_with_logits(tf.add(tf.matmul(x, W, b)) 在您的情况下。
  • @dga 我认为您的代码中有错字,b 需要在括号之外,tf.nn.softmax_cross_entropy_with_logits(tf.add(tf.matmul(x, W), b)
  • 什么是“理解单位的相对比例是线性的”。你第一句话的一部分是什么意思?
  • 赞成——但是当你说“[t]softmax 的输出形状与输入相同——它只是将值标准化”时,你的回答有点不正确。 Softmax 不只是“压缩”这些值,使它们的总和等于 1。它还重新分配它们,这可能是使用它的主要原因。请参阅stackoverflow.com/questions/17187507/…,尤其是 Piotr Czapla 的回答。
【解决方案2】:

短版:

假设您有两个张量,其中y_hat 包含每个类的计算分数(例如,来自 y = W*x +b),y_true 包含 one-hot 编码的真实标签。

y_hat  = ... # Predicted label, e.g. y = tf.matmul(X, W) + b
y_true = ... # True label, one-hot encoded

如果您将y_hat 中的分数解释为非标准化的对数概率,那么它们就是logits

此外,以这种方式计算的总交叉熵损失:

y_hat_softmax = tf.nn.softmax(y_hat)
total_loss = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), [1]))

本质上相当于用函数softmax_cross_entropy_with_logits()计算的总交叉熵损失:

total_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))

加长版:

在神经网络的输出层中,您可能会计算一个数组,其中包含每个训练实例的类分数,例如来自计算 y_hat = W*x + b。作为示例,下面我创建了一个 y_hat 作为 2 x 3 数组,其中行对应于训练实例,列对应于类。所以这里有 2 个训练实例和 3 个类。

import tensorflow as tf
import numpy as np

sess = tf.Session()

# Create example y_hat.
y_hat = tf.convert_to_tensor(np.array([[0.5, 1.5, 0.1],[2.2, 1.3, 1.7]]))
sess.run(y_hat)
# array([[ 0.5,  1.5,  0.1],
#        [ 2.2,  1.3,  1.7]])

请注意,这些值未标准化(即行加起来不等于 1)。为了对它们进行归一化,我们可以应用 softmax 函数,它将输入解释为非归一化的对数概率(又名 logits)并输出归一化的线性概率。

y_hat_softmax = tf.nn.softmax(y_hat)
sess.run(y_hat_softmax)
# array([[ 0.227863  ,  0.61939586,  0.15274114],
#        [ 0.49674623,  0.20196195,  0.30129182]])

充分理解 softmax 输出的含义很重要。下面我展示了一个更清楚地代表上述输出的表格。可以看出,例如,训练实例 1 为“Class 2”的概率为 0.619。每个训练实例的类概率都进行了归一化,因此每行的总和为 1.0。

                      Pr(Class 1)  Pr(Class 2)  Pr(Class 3)
                    ,--------------------------------------
Training instance 1 | 0.227863   | 0.61939586 | 0.15274114
Training instance 2 | 0.49674623 | 0.20196195 | 0.30129182

所以现在我们有了每个训练实例的类概率,我们可以在其中获取每一行的 argmax() 来生成最终分类。从上面,我们可以生成训练实例 1 属于“类 2”,训练实例 2 属于“类 1”。

这些分类是否正确?我们需要根据训练集中的真实标签进行衡量。您将需要一个单热编码的y_true 数组,其中行再次是训练实例,列是类。下面我创建了一个示例y_true one-hot 数组,其中训练实例 1 的真实标签是“Class 2”,训练实例 2 的真实标签是“Class 3”。

y_true = tf.convert_to_tensor(np.array([[0.0, 1.0, 0.0],[0.0, 0.0, 1.0]]))
sess.run(y_true)
# array([[ 0.,  1.,  0.],
#        [ 0.,  0.,  1.]])

y_hat_softmax 中的概率分布是否接近y_true 中的概率分布?我们可以使用cross-entropy loss来衡量误差。

我们可以逐行计算交叉熵损失并查看结果。下面我们可以看到,训练实例 1 的损失为 0.479,而训练实例 2 的损失更高,为 1.200。这个结果是有道理的,因为在我们上面的例子中,y_hat_softmax 表明训练实例 1 的最高概率是“类 2”,它与 y_true 中的训练实例 1 匹配;但是,训练实例 2 的预测显示“Class 1”的概率最高,这与真实的“Class 3”类不匹配。

loss_per_instance_1 = -tf.reduce_sum(y_true * tf.log(y_hat_softmax), reduction_indices=[1])
sess.run(loss_per_instance_1)
# array([ 0.4790107 ,  1.19967598])

我们真正想要的是所有训练实例的总损失。所以我们可以计算:

total_loss_1 = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), reduction_indices=[1]))
sess.run(total_loss_1)
# 0.83934333897877944

使用 softmax_cross_entropy_with_logits()

我们可以使用tf.nn.softmax_cross_entropy_with_logits() 函数来计算总交叉熵损失,如下所示。

loss_per_instance_2 = tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true)
sess.run(loss_per_instance_2)
# array([ 0.4790107 ,  1.19967598])

total_loss_2 = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))
sess.run(total_loss_2)
# 0.83934333897877922

请注意,total_loss_1total_loss_2 产生的结果基本相同,但最后的数字有一些细微差别。但是,您不妨使用第二种方法:它需要更少的代码行并累积更少的数值错误,因为 softmax 是在 softmax_cross_entropy_with_logits() 内部为您完成的。

【讨论】:

  • 我确认以上所有内容。简单代码:M = tf.random.uniform([100, 10], minval=-1.0, maxval=1.0); labels = tf.one_hot(tf.random.uniform([100], minval=0, maxval=10 , dtype='int32'), 10); tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=M) - tf.reduce_sum(-tf.nn.log_softmax(M)*tf.one_hot(labels, 10), -1) 处处返回接近于零
  • 抱歉,这个问题很简单。我不明白从训练实例 1 中获得损失“0.479”。 instance-1 的真实标签是“2”。如果我申请 -1xlog2(0.619) 我得到 0.691。
  • 编辑:损失是使用 log 'e' base 计算的,好吧。
【解决方案3】:

tf.nn.softmax 通过 softmax 层计算前向传播。当您计算模型输出的概率时,您可以在模型的评估期间使用它。

tf.nn.softmax_cross_entropy_with_logits 计算 softmax 层的成本。它仅在训练期间使用。

logits 是模型输出的未归一化的对数概率(在对其应用 softmax 归一化之前输出的值)。

【讨论】:

  • 我明白了。为什么不调用函数 tf.nn.softmax_cross_entropy_sans_normalization?
  • @auro 因为它在交叉熵计算期间对值(内部)进行了归一化。 tf.nn.softmax_cross_entropy_with_logits 的重点是评估模型偏离黄金标签的程度,而不是提供标准化输出。
  • 在使用 tf.nn.sparse_softmax_cross_entropy_with_logits() 计算稀疏 softmax 层的成本的情况下,因此只能在训练期间使用针对新数据运行模型时的替代方法,是否有可能从中获得概率。
  • @SerialDev,不可能从tf.nn.sparse_softmax_cross_entropy_with_logits 获得概率。要获得概率,请使用tf.nn.softmax
  • 它们不是对数概率,而是log odds
【解决方案4】:

术语的数学动机

当我们希望将输出限制在 0 和 1 之间,但我们的模型架构输出不受限制的值时,我们可以添加一个归一化层来强制执行此操作。

一个常见的选择是sigmoid 函数。1在二元分类中,这通常是逻辑函数,而在多类任务中,多项逻辑函数(又名 softmax).2

如果我们想将新最后一层的输出解释为“概率”,那么(暗示)我们的 sigmoid 的无约束输入必须是inverse-sigmoid(概率)。在逻辑案例中,这相当于我们概率的 log-odds(即 odds 的对数)a.k.a. logit

这就是为什么 softmax 的参数在 Tensorflow 中被称为 logits - 因为假设 softmax 是模型中的最后一层,并且输出 p 被解释为一个概率,这一层的输入 x 可以解释为 logit:

广义术语

在机器学习中,倾向于概括从数学/统计/计算机科学借来的术语,因此在 Tensorflow 中,logit(通过类推)被用作许多归一化函数输入的同义词。


  1. 虽然它具有很好的特性,例如易于区分,以及前面提到的概率解释,但它有点arbitrary
  2. softmax 可能更准确地称为 softargmax,因为它是 smooth approximation of the argmax function

【讨论】:

    【解决方案5】:

    以上答案对所提出的问题有足够的描述。

    除此之外,Tensorflow 还优化了应用激活函数的操作,然后使用自己的激活计算成本,然后是成本函数。因此,最好使用:tf.nn.softmax_cross_entropy() over tf.nn.softmax(); tf.nn.cross_entropy()

    您可以在资源密集型模型中发现它们之间的显着差异。

    【讨论】:

    • 上面的答案显然没有读懂问题。。他们都说同样的话,这是已知的,但不回答问题本身
    • @abhish 你的意思是,tf.nn.softmax 后跟 tf.losses.softmax_cross_entropy
    【解决方案6】:

    Tensorflow 2.0 Compatible Answerdgastackoverflowuser2010的解释非常详细,关于Logits和相关的功能。

    所有这些功能,在 Tensorflow 1.x 中使用时都可以正常工作,但是如果您将代码从 1.x (1.14, 1.15, etc) 迁移到 2.x (2.0, 2.1, etc..),使用这些函数会导致错误。

    因此,如果我们从 1.x to 2.x 迁移,则为我们上面讨论的所有函数指定 2.0 兼容调用,以造福社区。​​p>

    1.x 中的功能

    1. tf.nn.softmax
    2. tf.nn.softmax_cross_entropy_with_logits
    3. tf.nn.sparse_softmax_cross_entropy_with_logits

    从 1.x 迁移到 2.x 时的各个功能

    1. tf.compat.v2.nn.softmax
    2. tf.compat.v2.nn.softmax_cross_entropy_with_logits
    3. tf.compat.v2.nn.sparse_softmax_cross_entropy_with_logits

    有关从 1.x 迁移到 2.x 的更多信息,请参阅Migration Guide

    【讨论】:

      【解决方案7】:

      我肯定要强调的另一件事是 logit 只是一个原始输出,通常是最后一层的输出。这也可以是负值。如果我们将其用于“交叉熵”评估,如下所述:

      -tf.reduce_sum(y_true * tf.log(logits))
      

      然后它不会工作。由于未定义 -ve 的日志。 所以使用 o softmax 激活,就可以解决这个问题。

      这是我的理解,如有错误请指正。

      【讨论】:

        猜你喜欢
        • 2016-09-15
        • 1970-01-01
        • 2016-05-16
        • 2020-09-07
        • 1970-01-01
        • 1970-01-01
        • 2021-03-11
        • 2019-06-08
        • 1970-01-01
        相关资源
        最近更新 更多