短版:
假设您有两个张量,其中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_1 和 total_loss_2 产生的结果基本相同,但最后的数字有一些细微差别。但是,您不妨使用第二种方法:它需要更少的代码行并累积更少的数值错误,因为 softmax 是在 softmax_cross_entropy_with_logits() 内部为您完成的。