TensorFlow:交叉熵損失函數

基礎

softmax歸一化計算

什麼是logits?

        logits就是神經網絡模型中的 W * X矩陣,注意不需要經過sigmoid,即爲未通過激活函數的原始輸出。Tensorflow "with logit": The input_vector/logit is not normalized and can scale from [-inf, inf]. 

[What is the meaning of the word logits in TensorFlow?]

[如何理解深度學習源碼裏經常出現的logits?]

示例說明:計算multilabel時的BinaryCrossentropy

tf.keras.losses.BinaryCrossentropy原碼:

@tf_export('keras.backend.binary_crossentropy')
def binary_crossentropy(target, output, from_logits=False):
  """Binary crossentropy between an output tensor and a target tensor.

  Arguments:
      target: A tensor with the same shape as `output`.
      output: A tensor.
      from_logits: Whether `output` is expected to be a logits tensor.
          By default, we consider that `output`
          encodes a probability distribution.

  Returns:
      A tensor.
  """
  # Note: nn.sigmoid_cross_entropy_with_logits
  # expects logits, Keras expects probabilities.
  if not from_logits:
    # transform back to logits
    epsilon_ = _to_tensor(epsilon(), output.dtype.base_dtype)
    output = clip_ops.clip_by_value(output, epsilon_, 1 - epsilon_)
    output = math_ops.log(output / (1 - output))
  return nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)

tf.nn.sigmoid_cross_entropy_with_logits的原碼邏輯:

For brevity, let x = logits, z = labels. The logistic loss is
  z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))

所以

1 如果輸入是logits = tf.layers.dense(output, units=num_labels, activation=None),則

loss = tf.keras.losses.binary_crossentropy(labels=labels, logits=logits, from_logits=True)或者 loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=logits)。即Use an activation function and use the raw outputs (logits) for the sigmoid_crossentropy_with_logits function.

2 如果輸入是outputs = tf.layers.dense(output, units=num_labels, activation='sigmoid'),則loss = tf.keras.losses.binary_crossentropy(labels, outputs)。即內部邏輯爲先將outputs轉成logits,再使用loss = tf.nn.sigmoid_cross_entropy_with_logits(labels, logits)。所以爲了效率,還是用1來實現multilabel的BinaryCrossentropy計算吧。

-柚子皮-

 

 

TensorFlow交叉熵函數

        TensorFlow針對分類問題,實現了四個交叉熵函數,分別是tf.nn.sigmoid_cross_entropy_with_logitstf.nn.softmax_cross_entropy_with_logitstf.nn.sparse_softmax_cross_entropy_with_logitstf.nn.weighted_cross_entropy_with_logits

sigmoid_cross_entropy_with_logits詳解

        我們先看sigmoid_cross_entropy_with_logits[tf.nn.sigmoid_cross_entropy_with_logits],它的實現和交叉熵算法定義是一樣的。

tf.nn.sigmoid_cross_entropy_with_logits(
    _sentinel=None,
    labels=None,
    logits=None,
    name=None
)

        函數的輸入是logits和labels,logits就是神經網絡模型中的 W * X矩陣,注意不需要經過sigmoid,而labels的shape和logits相同,就是正確的label值,例如這個模型一次要判斷100張圖是否包含10種動物,這兩個輸入的shape都是[100, 10]。註釋中還提到這10個分類之間是獨立的、不要求是互斥,這種問題我們成爲多目標multi-label,例如判斷圖片中是否包含10種動物,label值可以包含多個1或0個1。

        還有一種問題是多分類問題multi-class,例如我們對年齡特徵分爲5段,只允許5個值有且只有1個值爲1,這種問題就不可以直接用這個函數。

        sigmoid_cross_entropy_with_logits的代碼實現邏輯:For brevity, let x = logitsz = labels. The logistic loss is

  z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))
= z * -log(1 / (1 + exp(-x))) + (1 - z) * -log(exp(-x) / (1 + exp(-x)))
= z * log(1 + exp(-x)) + (1 - z) * (-log(exp(-x)) + log(1 + exp(-x)))
= z * log(1 + exp(-x)) + (1 - z) * (x + log(1 + exp(-x))
= (1 - z) * x + log(1 + exp(-x))
= x - x * z + log(1 + exp(-x))

​        可以看到這就是標準的Cross Entropy算法實現,對W * X得到的值進行sigmoid激活,保證取值在0到1之間,然後放在交叉熵的函數中計算Loss。對於二分類問題這樣做沒問題,因爲對於多標籤分類問題,目標是將每個輸出標籤視作一個獨立伯努利分佈,而且我們需要獨立地懲罰每個輸出節點。

        但對於前面提到的多分類就不能使用,例如年輕取值範圍在0~4,目標值也在0~4,這裏如果經過sigmoid後預測值就限制在0到1之間,而且公式中的1 - z就會出現負數,仔細想一下0到4之間還不存在線性關係,如果直接把label值帶入計算肯定會有非常大的誤差。因此對於多分類問題是不能直接代入的,那其實我們可以靈活變通,把5個年齡段的預測用onehot encoding變成5維的label,訓練時當做5個不同的目標來訓練即可,但不保證只有一個爲1。對於這類問題TensorFlow又提供了基於Softmax的交叉熵函數。

softmax_cross_entropy_with_logits詳解

        將棄用,取代爲tf.nn.softmax_cross_entropy_with_logits_v2

tf.nn.softmax_cross_entropy_with_logits_v2(
    labels,
    logits,
    axis=None,
    name=None,
    dim=None
)

        Softmax本身的算法很簡單,就是把所有值用e的n次方計算出來,求和後算每個值佔的比率,保證總和爲1,一般我們可以認爲Softmax出來的就是confidence也就是概率,算法實現如下。

  This function performs the equivalent of
      softmax = tf.exp(logits) / tf.reduce_sum(tf.exp(logits), axis)

        ​softmax_cross_entropy_with_logits和sigmoid_cross_entropy_with_logits很不一樣,輸入是類似的logits和lables的shape一樣,但這裏要求分類的結果是互斥的,保證只有一個字段有值,例如CIFAR-10中圖片只能分一類而不像前面判斷是否包含多類動物。想一下問什麼會有這樣的限制?在函數頭的註釋中我們看到,這個函數傳入的logits是unscaled的,既不做sigmoid也不做softmax,因爲函數實現會在內部更高效得使用softmax,對於任意的輸入經過softmax都會變成和爲1的概率預測值,這個值就可以代入變形的Cross Entroy算法- y * ln(a) - (1 - y) * ln(1 - a)算法中,得到有意義的Loss值了。

        如果是多目標問題,經過softmax就不會得到多個和爲1的概率,而且label有多個1也無法計算交叉熵,因此這個函數只適合單目標的二分類或者多分類問題。並且它只計算了某個類別標籤爲1時的loss及梯度,而忽略了爲0時的loss,而每個輸出又相互獨立,不像softmax函數那樣有歸一化的限制。所以multi-label是一定不能使用CE作爲loss函數的

        ​再補充一點,對於多分類問題,例如我們的年齡分爲5類,並且人工編碼爲0、1、2、3、4,因爲輸出值是5維的特徵,因此我們需要人工做onehot encoding分別編碼爲00001、00010、00100、01000、10000,纔可以作爲這個函數的輸入。理論上我們不做onehot encoding也可以,做成和爲1的概率分佈也可以,但需要保證是和爲1,和不爲1的實際含義不明確,TensorFlow的C++代碼實現計劃檢查這些參數,可以提前提醒用戶避免誤用。

sparse_softmax_cross_entropy_with_logits詳解

        sparse_softmax_cross_entropy_with_logits是softmax_cross_entropy_with_logits的易用版本,除了輸入參數不同,作用和算法實現都是一樣的。

        前面提到softmax_cross_entropy_with_logits的輸入必須是類似onehot encoding的多維特徵,但CIFAR-10、ImageNet和大部分分類場景都只有一個分類目標(單分類),label值都是從0編碼的整數,每次轉成onehot encoding比較麻煩。 

        sparse_softmax_cross_entropy_with_logits,它的第一個參數logits和前面一樣,shape是[batch_size, num_classes];而第二個參數labels的shape改爲[batch_size],值必須是從0開始編碼的int32或int64,且值範圍是[0, num_class)。最後在內部高效實現類似的onehot encoding,這只是簡化用戶的輸入而已,當然如果用戶已經做了onehot encoding那可以直接使用不帶“sparse”的softmax_cross_entropy_with_logits函數。

-柚子皮-

 

 

keras中兩種交叉熵損失函數

categorical cross entropy [tf.keras.losses.CategoricalCrossentropy]和 binary cross entropy[tf.keras.losses.BinaryCrossentropy],以下簡稱CE和BCE. 關於這兩個函數的忠告就是:"CE用於多分類, BCE適用於二分類, 千萬別用混了."

CE:

CE(x)=-\sum_{i=1}^C y_i \log f_i(x) 。。。。。。(1)

其中, x表示輸入樣本, C爲待分類的類別總數, 這裏我們以手寫數字識別任務(MNIST-based)爲例, 其輸入出的類別數爲10, 對應的C=10. y_i 爲第i個類別對應的真實標籤, f_i(x) 爲對應的模型輸出值.

BCE:

BCE(x)_i = -[y_i \log f_i(x) + (1-y_i) \log (1-f_i(x))] .。。。。。。(2)

其中 i \in [1, C] , 即每個類別輸出節點都對應一個BCE值.

看到這裏, 大家會發現兩者的shape並不相同,對於單個樣本而言,CE是一個數值,而BCE是一個向量,其維度與輸出類別的個數相同,即爲C。但在Keras中,最終使用的是均值,即:

BCE(x) = \frac {\sum_{i=1}^C BCE(x)_i}{C}

那如果是batch的情況呢?Keras中的做法是對batch中所有樣本的loss求均值:

CE(x)_{final}=\frac {\sum_{b=1}^{N}CE(x^{(b)})}{N}

BCE(x)_{final}=\frac {\sum_{b=1}^{N}BCE(x^{(b)})}{N}

在tensorflow中則只提供原始的BCE(sigmoid_cross_entropy_with_logits)和CE(softmax_cross_entropy_with_logits_v2),這也給開發人員提供了更大的靈活性。

另外,再補充一點,keras使用tensorflow作爲backend時,默認情況下CE的實現調用的是自己內部實現的計算方法,而沒有像之前想象的那樣調用的tensorflow對應的函數。

[keras中兩種交叉熵損失函數的探討]

from:-柚子皮-

ref: [TensorFlow四種Cross Entropy算法實現和應用]

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章