翻譯自:https://stackoverflow.com/questions/34240703/whats-the-difference-between-softmax-and-softmax-cross-entropy-with-logits
問題:
在Tensorflow官方文檔中,他們使用一個關鍵詞,稱為logits。這個logits是什么?比如說在API文檔中有很多方法(methods),經常像下面這么寫:
tf.nn.softmax(logits, name=None)
另外一個問題是,有2個方法我不知道該怎么區分,它們是:
tf.nn.softmax(logits, name=None)
tf.nn.softmax_cross_entropy_with_logits(logits, labels, name=None)
它們之間的區別是什么?
回答:
簡短版本:
假設你有2個tensors,其中y_hat包含每個類預測的得分(比如說,從y = W*x +b計算得到),y_true包含one-hot編碼后的正確的label。
y_hat = ... # Predicted label, e.g. y = tf.matmul(X, W) + b y_true = ... # True label, one-hot encoded
如果你將y_hat的得分解釋為未歸一化的log概率,那么它們就是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))
完整版:
舉個例子,我們創建一個2x3大小的y_hat,其中行對應着訓練樣本,列對應類別。因此,這里有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函數,這個函數的輸入就是未歸一化的log概率(也稱為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屬於類別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。
那么,這些分類正確么?我們需要根據訓練樣本正確的標簽來衡量。你需要一個one-hot編碼的y_true數組,其中每一行表示訓練樣本實例,每一列表示類別。下面我將創建一個例子,y_true為one-hot編碼的數組,其中對於訓練樣本1正確的標簽是類別2,對於訓練樣本2正確的標簽是類別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匹配,而訓練樣本2的最高概率預測為類別1,與實際標簽(類別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])
我們想要的是訓練集上的所有的loss,因此我們需要將每個訓練樣本的loss加起來,如下:
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計算得到的結果是基本一樣的(有一點點小的差別)。然而,還是建議使用第二種方法,原因是1)代碼量更少,2)方法二的內部考慮到了一些邊界情況,不容易產生錯誤。