<p><strong>短版本:</strong></p>
<p>假设有两个张量,其中<code>y_hat</code>包含每个类的计算分数(例如,来自y=W*x+b),而<code>y_true</code>包含一个热编码的真标签。</p>
<pre><code>y_hat = ... # Predicted label, e.g. y = tf.matmul(X, W) + b
y_true = ... # True label, one-hot encoded
</code></pre>
<p>如果您将<code>y_hat</code>中的分数解释为不规范的日志概率,则它们是<strong>logits</strong>。</p>
<p>此外,以这种方式计算的总交叉熵损失:</p>
<pre><code>y_hat_softmax = tf.nn.softmax(y_hat)
total_loss = tf.reduce_mean(-tf.reduce_sum(y_true * tf.log(y_hat_softmax), [1]))
</code></pre>
<p>基本上等于用<code>softmax_cross_entropy_with_logits()</code>函数计算的总交叉熵损失:</p>
<pre><code>total_loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(y_hat, y_true))
</code></pre>
<p><strong>长版本:</strong></p>
<p>在神经网络的输出层中,您可能会计算一个数组,该数组包含每个训练实例的类分数,例如通过计算<code>y_hat = W*x + b</code>。作为一个例子,下面我创建了一个<code>y_hat</code>作为2 x 3数组,其中的行对应于训练实例,而列对应于类。这里有2个训练实例和3个课程。</p>
<pre><code>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]])
</code></pre>
<p>请注意,这些值不是标准化的(即,行加起来不等于1)。为了规范化它们,我们可以应用softmax函数,该函数将输入解释为非规范化的对数概率(aka<strong>logits</strong>),并输出规范化的线性概率。</p>
<pre><code>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]])
</code></pre>
<p>充分理解softmax输出的意思很重要。下面我展示了一个更清楚地表示上面输出的表。可以看出,例如,训练实例1成为“类2”的概率为0.619。每个训练实例的类概率是标准化的,因此每行的总和为1.0。</p>
<pre><code> 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
</code></pre>
<p>现在我们有了每个训练实例的类概率,在这里我们可以取每一行的argmax()来生成最终的分类。由此,我们可以生成训练实例1属于“类2”,训练实例2属于“类1”。</p>
<p>这些分类正确吗?我们需要根据训练场的真实标签来衡量。您将需要一个单热编码的<code>y_true</code>数组,其中的行是训练实例,列是类。下面我创建了一个示例<code>y_true</code>一个热数组,其中训练实例1的真正标签是“类2”,训练实例2的真正标签是“类3”。</p>
<pre><code>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.]])
</code></pre>
<p><code>y_hat_softmax</code>中的概率分布是否与<code>y_true</code>中的概率分布接近?我们可以用<a href="https://en.wikipedia.org/wiki/Cross_entropy">cross-entropy loss</a>来测量误差。</p>
<p><a href="https://i.stack.imgur.com/rODko.png"><img src="https://i.stack.imgur.com/rODko.png" alt="Formula for cross-entropy loss"/></a></p>
<p>我们可以按行计算交叉熵损失,并看到结果。下面我们可以看到训练实例1的损失为0.479,而训练实例2的损失更高,为1.200。这个结果是有意义的,因为在我们上面的例子中,<code>y_hat_softmax</code>显示训练实例1的最高概率是“类2”,它与<code>y_true</code>中的训练实例1匹配;然而,对训练实例2的预测显示了“类1”的最高概率,它与真正的类“类3”不匹配。</p>
<pre><code>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])
</code></pre>
<p>我们真正想要的是所有训练实例的全部损失。所以我们可以计算:</p>
<pre><code>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
</code></pre>
<p><strong>使用softmax_cross_entropy_with_logits()</strong></p>
<p>相反,我们可以使用<code>tf.nn.softmax_cross_entropy_with_logits()</code>函数计算总交叉熵损失,如下所示。</p>
<pre><code>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
</code></pre>
<p>注意<code>total_loss_1</code>和<code>total_loss_2</code>产生的结果基本上是等价的,但在最后几位数字上有一些小的差异。不过,您也可以使用第二种方法:它只需要少一行代码,并且累积的数字错误更少,因为softmax是在<code>softmax_cross_entropy_with_logits()</code>内为您完成的。</p>