文章來源於AI的那些事兒,作者黃鴻波
2018年我出版了《TensorFlow進階指南 基礎、算法與應用》這本書,今天我把這本書中關於常見的損失函數這一節的內容公開出來,希望能對大家有所幫助。
在深度學習分類任務中,我們經常會使用到損失函數,今天我們就來總結一下深度學習中常見的損失函數。
在分類問題中,可以使用函數的正負號來進行模式判斷,函數值本身大小並不重要,該函數衡量的是預測值與真實值的符號是否相同,具體形式如下所示:
其等價於下述函數:
由於0-1損失函數隻取決於正負號,是一個非凸的函數,在求解過程中,存在很多的不足,通常在實際應用中使用其替代函數。
Log損失函數是0-1損失函數的一種替代函數,其形式如下:
運用Log損失函數的典型分類器是logistic(邏輯)回歸算法。為什麼邏輯回歸不用平方損失呢?原因在於平方損失函數是線性回歸在假設樣本是高斯分布的條件下推導得到的(為什麼假設高斯分布?其實就是依據中心極限定理)。而邏輯回歸的推導中,它假設樣本服從於伯努利分布(0-1分布),然後求得滿足該分布的似然函數,接著求取對數等(Log損失函數中採用log就是因為求解過中使用了似然函數,為了求解方便而添加log,因為添加log並不改變其單調性)。但邏輯回歸併沒有極大化似然函數,而是轉變為最小化負的似然函數,因此有了上式。
已知邏輯函數(sigmoid函數)為:
可以得到邏輯回歸的Log損失函數:
上式的含義就是:如果y=1,我們鼓勵趨向於1,趨向於0,如果y=1,我們鼓勵也趨向於0,也趨向於0,即滿足損失函數的第二個條件,因為小於1,為了保證損失函數的非負性,即滿足第一個條件,所以添加負號。此時將其合併可得單個樣本的損失函數:
則全體樣本的經驗風險函數為:
該式就是sigmoid函數的交叉熵,這也是上文說的在分類問題上,交叉熵的實質是對數似然函數。在深度學習中更普遍的做法是將softmax作為最後一層,此時常用的仍是對數似然損失函數,如下所示:
其中為真時,否則為0。
該式其實是式(1)的推廣,正如softmax是sigmoid的多類別推廣一樣,在TensorFlow裡面根據最後分類函數softmax和sigmoid就分為softmax交叉熵以及sigmoid的交叉熵,並對這兩個功能進行統一封裝。
先看tf.nn.sigmoid_cross_entropy_with_logits(logits,targets)函數,它的實現和之前的交叉熵算法定義是一樣的,也是TensorFlow最早實現的交叉熵算法。這個函數的輸入是logits和targets,logits就是神經網絡模型中的W*X矩陣,注意不需要經過sigmoid,因為在函數中會對其進行sigmoid激活,而targets的shape和logtis相同,就是正確的label值。其計算過程大致如下:
tf.nn.softmax_cross_entropy_with_logits(logits,targets)同樣是將softmax和交叉熵計算放到一起了,但是需要注意的是,每個樣本只能屬於一個類別,即要求分類結果是互斥的,因此該函數隻適合單目標的二分類或多分類問題。補充一點,對於多分類問題,例如我們分為5類,並且將其人工編碼為0,1,2,3,4,因為輸出值是5維的特徵,因此需要人工做onehot enconding,即分別編碼為00001,00010,00100,01000,10000,才能作為該函數的輸入。理論上不做onehot encoding也可以,做成和為1的概率分布也可以,但需要保證和為1,否則TensorFlow會檢查這些參數,提醒用戶更改。
TensorFlow還提供了一個softmax_cross_entropy_with_logits的易用版本,tf.nn.sparse_softmax_cross_entropy_with_logits(),除了輸入參數不同,作用和算法實現都是一樣的。softmax_cross_entropy_with_logits的輸入必須是類似onehot encoding的多維特徵,但像CIFAR-10、ImageNet和大部分分類場景都只有一個分類目標,label值都是從0編碼的整數,每次轉成onehot encoding比較麻煩,TensorFlow為了簡化用戶操作,在該函數內部高效實現類似onehot encoding,第一個輸入函數和前面一樣,shape是[batch_size,num_classes],第二個參數以前必須也是[batch_size,num_classes]否則無法做交叉熵,而這裡將其改為[batch_size],但值必須是從0開始編碼的int32或int64,而且值的範圍是[0,num_class)。如果我們從1開始編碼或者步長大於1,則會導致某些label值超過範圍,代碼會直接報錯退出。其實如果用戶已經做了onehot encoding,那就可以不使用該函數。
還有一個函數tf.nn.weighted_cross_entropy_with_logits(),是sigmoid_cross_entropy_with_logits的拓展版,輸入和實現兩者類似,與後者相比,多支持一個pos_weight參數,目的是可以增加或減小正樣本在算交叉熵時的loss.其計算原理如下:
還有一個計算交叉熵的函數,sequence_loss_by_example (logits,targets,weights),用於計算所有examples(假設一句話有n個單詞,一個單詞及單詞所對應的label就是一個example,所有examples就是一句話中所有單詞)的加權交叉熵損失,logits的shape為[batch_size,num_decoder_symbols],返回值是一個1D float類型的tensor,尺寸為batch_size,其中每一個元素代表當前輸入序列example的交叉熵。另外,還有一個與之類似的函數sequence_loss,它對sequence_loss_by_example函數的返回結果進行了一個tf.reduce_sum運算。
值得一提的是,當最後分類函數是sigmoid和softmax時,不採用平方損失函數除上文中提到的樣本假設分布不同外,還有一個原因是如果採用平方損失函數,則模型權重更新非常慢,假設採用平方損失函數如下式所示:
採用梯度下降算法調整參數的話,則有
可知w和b的梯度跟激活函數的梯度成正比,但是因為sigmoid的性質,導致在z取大部分值時都會很小,這樣導致w和b更新非常慢,如圖所示。
而如果採用交叉熵或者說對數損失函數,則參數更新梯度變為:
可以看到,沒有這一項,權重的更新受誤差影響,誤差越大權重更新越快,誤差越小權重更新就慢,這是一個很好的性質。
為什麼一開始我們說log損失函數也是0-1損失函數的一種替代函數,因為log損失函數其實也等價於如下形式:
Hinge損失函數也是0-1函數的替代函數,具體形式如下:
對可能的輸出和分類器預測值, 預測值的損失就是上式。運用Hinge損失函數的典型分類器是SVM算法,。可以看出當和y同符號時,意味著hinge loss為0,但是如果它們的符號相反, 則會根據線性增加。
具體形式如下:
這也是0-1函數的一種替代函數,主要用於AdaBoost算法。
這也是0-1函數的一種替代函數,具體形式如下:
運用感知機損失的典型分類器是感知機算法,感知機算法只需對每個樣本判斷其是否分類正確,只記錄分類錯誤的樣本,類似hinge損失,不同之處在於,hinge損失對判定邊界附近的點的懲罰力度較高,而感知損失只要樣本的類別判定正確即可,而不需要其離判別邊界的距離,這樣的變化使得其比hinge損失簡單,但是泛化能力沒有hinge損失強。
這幾種損失函數形式如下,可以看出,除了0-1函數,其他函數都可認為是0-1函數的替代函數,目的在於使函數更平滑,提高計算性,如圖所示。
具體形式為:
平方損失函數較多應用於回歸任務,它假設樣本和噪聲都是服從高斯分布的,是連續的。它有幾個特點:計算簡單方便;歐式距離是一種很好的相似度度量標準;在不同的表示域變換後特徵性質不變。因此平方損失函數也是一種應用較多的形式。
在TensorFlow中計算平方損失,一般採用tf.pow(x,y),其返回值是x^y。舉例來說:
loss = tf.reduce_mean(tf.pow(y-y_, 2))
具體形式為:
絕對值損失函數與平方損失函數類似,不同之處在於平方損失函數更平滑,計算更簡便,因此實際應用中更多地使用平方損失函數。
以上主要講了損失函數的常見形式,在神經網絡中應用較多的是對數損失函數(交叉熵)和平方損失函數。可以看出,損失函數的選擇與模型是密切相關的,如果是square loss,就是最小二乘了,如果是hinge loss,就是SVM了;如果是exp-loss,那就是boosting了;如果是log loss,那就是logistic regression了,等等。不同的loss函數,具有不同的擬合特性,就需要具體問題具體分析。
Tensorflow不僅支持經典的損失函數,還可以優化任意的自定義損失函數。自定義的損失函數原則上滿足上文中講的兩個條件即可。TensorFlow提供了很多計算函數,基本可以滿足自定義損失函數可能會用到的計算操作。舉例來說,預測商品銷量時,假設商品成本為1元,銷售價為10,如果預測少一個,意味著少掙9元,但預測多一個,意味只損失1元,希望利潤最大化,因此損失函數不能採用均方誤差,需要自定義損失函數,定義如下:
在TensorFlow中可以這樣定義:其中tf.greater()用於比較輸入兩個張量每個元素的大小,並返回比較結果。Tf.select()會根據第一個輸入是否為true,來選擇第二個參數,還是第三個參數,類似三目運算符。
loss=tf.reduce_sum(tf.select(tf.greater(v1,v2),a*(v1-v2),b*(v2-v1)))本站qq群1003271085,加入微信群請回復「加群」
獲取一折本站知識星球優惠券,複製連結直接打開:
https://t.zsxq.com/yFQV7am
喜歡文章,點個在看