關於categorical cross entropy 和 binary cross entropy的比較,差異一般體現在不同的分類(二分類、多分類等)任務目標,可以參考文章keras中兩種交叉熵損失函數的探討,其結合keras的API討論了兩者的計算原理和應用原理。
本文主要是介紹TF中的接口調用方式。
一、二分類交叉熵
對應的是網絡輸出單個節點,這個節點將被sigmoid處理,使用閾值分類為0或者1的問題。此類問題logits和labels必須具有相同的type和shape。
原理介紹
設x = logits
, z = labels
.
logistic loss 計算式為: 其中交叉熵(cross entripy)基本函數式
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))
對於x<0時,為了避免計算exp(-x)時溢出,我們使用以下這種形式表示
x - x * z + log(1 + exp(-x))
= log(exp(x)) - x * z + log(1 + exp(-x))
= - x * z + log(1 + exp(x))
綜合x>0和x<0的情況,並防止溢出我們使用如下公式,
max(x, 0) - x *z + log(1 + exp(-abs(x)))
接口介紹
import numpy as np import tensorflow as tf input_data = tf.Variable(np.random.rand(1, 3), dtype=tf.float32) # np.random.rand()傳入一個shape,返回一個在[0,1)區間符合均勻分布的array output = tf.nn.sigmoid_cross_entropy_with_logits(logits=input_data, labels=[[1.0, 0.0, 0.0]]) with tf.Session() as sess: init = tf.global_variables_initializer() sess.run(init) print(sess.run(output)) # [[ 0.5583781 1.06925142 1.08170223]]
二、多分類交叉熵
對應的是網絡輸出多個節點,每個節點表示1個class的得分,使用Softmax最終處理的分類問題。
原理介紹
cross_entropy = -tf.reduce_mean(y * tf.log(tf.clip_by_value(y_pre, 1e-10, 1.0))
調用一下:
import tensorflow as tf input_data = tf.Variable([[0.2, 0.1, 0.9], [0.3, 0.4, 0.6]], dtype=tf.float32) labels=tf.constant([[1,0,0], [0,1,0]], dtype=tf.float32) cross_entropy = -tf.reduce_mean(labels * tf.log(tf.clip_by_value(input_data, 1e-10, 1.0))) with tf.Session() as sess: init = tf.global_variables_initializer() sess.run(init) print(sess.run(output))
接口介紹
softmax之后,計算輸出層全部節點各自的交叉熵(輸出向量而非標量)
cross_entropy_mean = tf.reduce_mean( tf.nn.sparse_softmax_cross_entropy_with_logits( labels=tf.argmax(labels,1), logits=logits), name='cross_entropy') cross_entropy_mean = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( logits=logits, labels=labels), name='cross_entropy')
tf.nn.softmax_cross_entropy_with_logits()
函數的參數label是稀疏表示的,比如表示一個3分類的一個樣本的標簽,稀疏表示的形式為[0,0,1]這個表示這個樣本為第3個分類,而非稀疏表示就表示為2,同理[0,1,0]就表示樣本屬於第2個分類,而其非稀疏表示為1。
import tensorflow as tf input_data = tf.Variable([[0.2, 0.1, 0.9], [0.3, 0.4, 0.6]], dtype=tf.float32) output = tf.nn.softmax_cross_entropy_with_logits(logits=input_data, labels=[[1,0,0], [0,1,0]]) with tf.Session() as sess: init = tf.global_variables_initializer() sess.run(init) print(sess.run(output))
tf.nn.sparse_softmax_cross_entropy_with_logits()
此函數大致與tf.nn.softmax_cross_entropy_with_logits的計算方式相同,
適用於每個類別相互獨立且排斥的情況,一幅圖只能屬於一類,而不能同時包含一條狗和一只大象
但是在對於labels的處理上有不同之處,labels從shape來說此函數要求shape為[batch_size],
labels[i]是[0,num_classes)的一個索引, type為int32或int64,即labels限定了是一個一階tensor,
並且取值范圍只能在分類數之內,表示一個對象只能屬於一個類別
import tensorflow as tf input_data = tf.Variable([[0.2, 0.1, 0.9], [0.3, 0.4, 0.6]], dtype=tf.float32) output = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=input_data, labels=[0, 2]) with tf.Session() as sess: init = tf.global_variables_initializer() sess.run(init) print(sess.run(output)) # [ 1.36573195 0.93983102]
比tf.nn.softmax_cross_entropy_with_logits多了一步將labels稀疏化的操作。因為深度學習中,圖片一般是用非稀疏的標簽的,所以tf.nn.sparse_softmax_cross_entropy_with_logits()的頻率比tf.nn.softmax_cross_entropy_with_logits高。
不過兩者輸出尺寸等於輸入shape去掉最后一維(上面輸入[2*3],輸出[2]),所以均常和tf.reduce_mean()連用。