LESSON 10.110.210.3 SSE与二分类交叉熵损失函数二分类交叉熵损失函数的pytorch实现多分类交叉熵损失函数
在之前的課程中,我們已經完成了從0建立深層神經網絡,并完成正向傳播的全過程。本節課開始,我們將以分類深層神經網絡為例,為大家展示神經網絡的學習和訓練過程。在介紹PyTorch的基本工具AutoGrad庫時,我們系統地介紹過數學中的優化問題和優化思想,我們介紹了最小二乘法以及梯度下降法這兩個入門級優化算法的具體操作,并使用AutoGrad庫實現了他們。接下來,我們將從梯度下降法向外拓展,介紹神經網絡的損失函數、常用優化算法等信息,實現神經網絡的學習和迭代。本節主要講解神經網絡常用的損失函數,并在PyTorch中實現這些函數。
一、機器學習中的優化思想
在之前的學習中,我們建立神經網絡時總是先設定好與的值(或者由我們調用的PyTorch類幫助我們隨機生成權重向量),接著通過加和求出zzz,再在zzz上嵌套sigmoid或者softmax函數,最終獲得神經網絡的輸出。我們的代碼及計算流程,總是從神經網絡的左側向右側計算的。之前我們提到過,這是神經網絡的正向傳播過程。但很明顯,這并不是神經網絡算法的全流程,這個流程雖然可以輸出預測結果,但卻無法保證神經網絡的輸出結果與真實值接近。
在講解線性回歸時,我們提起過,線性回歸的任務就是構造一個預測函數來映射輸入的特征矩陣XXX和標簽值yyy的線性關系。構造預測函數核心就是找出模型的權重向量www,并令線性回歸的輸出結果與真實值相近,也就是求解線性方程組中的www和bbb。對神經網絡而言也是如此,我們的核心任務是求解一組最適合的www和bbb,令神經網絡的輸出結果與真實值接近。找尋這個www和bbb的過程就是“學習”,也叫做“訓練”或者“建模”。
那我們如何評價和是否合適呢?我們又如何衡量我們的輸出結果與真實值之間的差異大小呢?此時,我們就需要使用機器學習中通用的優化流程了。在講解autograd的時候,其實我們已經提過這個優化流程,在這里我們稍微復習一下:
1)提出基本模型,明確目標
我們的基本模型就是我們自建的神經網絡架構,我們需要求解的就是神經網絡架構中的權重向量www。
2)確定損失函數/目標函數
我們需要定義某個評估指標,用以衡量模型權重為www的情況下,預測結果與真實結果的差異。當真實值與預測值差異越大時,我們就認為神經網絡學習過程中丟失了許多信息,丟失的這部分被形象地稱為”損失“,因此評估真實值與預測值差異的函數被我們稱為“損失函數”。
我們希望損失函數越小越好,以此,我們將問題轉變為求解函數L(w)L(w)L(w)的最小值所對應的自變量www。但是,損失函數往往不是一個簡單的函數,求解復雜函數就需要復雜的數學工具。在這里,我們使用的數學工具可能有兩部分:
- 將損失函數L(w)L(w)L(w)轉變成凸函數的數學方法,常見的有拉格朗日變換等
- 在凸函數上求解L(w)L(w)L(w)的最小值對應的www的方法,也就是以梯度下降為代表的優化算法
3)確定適合的優化算法
4)利用優化算法,最小化損失函數,求解最佳權重(訓練)
之前我們在線性回歸上走過這個全流程。對線性回歸,我們的損失函數是SSE,優化算法是最小二乘法和梯度下降法,兩者都是對機器學習來說非常重要的優化算法。但遺憾的是,最小二乘法作為入門級優化算法,有較多的假設和先決條件,不足以應對神經網絡需要被應用的各種復雜環境。梯度下降法應用廣泛,不過也有很多問題需要改進。接下來,我將主要以分類深層神經網絡為例來介紹神經網絡中所使用的入門級損失函數及優化算法。
二、回歸:誤差平方和SSE
對于回歸類神經網絡而言,最常見的損失函數是SSE(Sum of the Squared Errors),現在已經是我們第三次見到SSE的公式了:
SSE=∑i=1m(zi?z^i)2S S E=\sum_{i=1}^{m}\left(z_{i}-\hat{z}_{i}\right)^{2} SSE=i=1∑m?(zi??z^i?)2
其中ziz_{i}zi?是樣本iii的真實值,而z^i\hat{z}_{i}z^i?是樣本iii的預測值。對于全部樣本的平均損失,則可以寫作:
MSE=1m∑i=1m(zi?z^i)2M S E=\frac{1}{m} \sum_{i=1}^{m}\left(z_{i}-\hat{z}_{i}\right)^{2} MSE=m1?i=1∑m?(zi??z^i?)2
在PyTorch中,我們可以簡單通過以下代碼調用MSE:
三、二分類交叉熵損失函數
在這一節中,我們將介紹二分類神經網絡的損失函數:二分類交叉熵損失函數(Binary Cross Entropy Loss),也叫做對數損失(log loss)。這個損失函數被廣泛地使用在任何輸出結果是二分類的神經網絡中,即不止限于單層神經網絡,還可被拓展到多分類中,因此理解二分類交叉熵損失是非常重要的一環。大多數時候,除非特殊聲明為二分類,否則提到交叉熵損失,我們會默認算法的分類目標是多分類。
二分類交叉熵損失函數是由極大似然估計推導出來的,對于有m個樣本的數據集而言,在全部樣本上的平均損失寫作:
L(w)=?∑i=1m(yi?ln?(σi)+(1?yi)?ln?(1?σi))L(w)=-\sum_{i=1}^{m}\left(y_{i} * \ln \left(\sigma_{i}\right)+\left(1-y_{i}\right) * \ln \left(1-\sigma_{i}\right)\right) L(w)=?i=1∑m?(yi??ln(σi?)+(1?yi?)?ln(1?σi?))
在單個樣本的損失寫作:
L(w)i=?(yi?ln?(σi)+(1?yi)?ln?(1?σi))L(w)_{i}=-\left(y_{i} * \ln \left(\sigma_{i}\right)+\left(1-y_{i}\right) * \ln \left(1-\sigma_{i}\right)\right) L(w)i?=?(yi??ln(σi?)+(1?yi?)?ln(1?σi?))
其中,ln是以自然底數eee為底的對數函數,www表示求解出來的一組權重(在等號的右側,www在σ\sigmaσ里),m是樣本的個數,yiy_{i}yi?是樣本i上真實的標簽,σi\sigma_{i}σi?是樣本i上,基于參數www計算出來的sigmoid函數的返回值,xix_{i}xi?是樣本i各個特征的取值。我們的目標,就是求解出使L(w)L(w)L(w)最小的www取值。注意,在神經網絡中,特征張量XXX是自變量,權重是www。但在損失函數中,權重www是損失函數的自變量,特征x和真實標簽y都是已知的數據,相當于是常數。不同的函數中,自變量和參數各有不同,因此大家需要在數學計算中,尤其是求導的時候避免混淆。
1 極大似然估計求解二分類交叉熵損失
二分類交叉熵損失函數是怎么來的呢?為什么這個函數就能夠代表二分類的時候,真實值與預測值的差異呢?
在這里,我們基于極大似然法來推導交叉熵損失,這個推導過程能夠幫助我們充分了解交叉熵損失的含義,以及為什么的最小化能夠實現模型在數據集上的擬合最好。
在二分類的例子中,我們的“任意事件”就是每個樣本的分類都正確,對數似然函數的負數就是我們的損失函數。接下來,我們來看看邏輯回歸的對數似然函數是怎樣構筑的。
- 構筑對數似然函數
二分類神經網絡的標簽是[0,1],此標簽服從伯努利分布(即0-1分布),因此可得:
樣本i在由特征向量xix_{i}xi?和權重向量www組成的預測函數中,樣本標簽被預測為1的概率為:
對二分類而言,σ\sigmaσ就是sigmoid函數的結果。
樣本i在由特征向量xix_{i}xi?和權重向量www組成的預測函數中,樣本標簽被預測為0的概率為:
當P1P_{1}P1?值為1的時候,代表樣本i的標簽被預測為1,當P0P_{0}P0?的值為1的時候,代表樣本i的標簽被預測為0。P0P_{0}P0?與P1P_{1}P1?相加是一定等于1的。
假設樣本i的真實標簽yiy_{i}yi?為1,并且P1P_{1}P1?也為1的話,那就說明我們將iii的標簽預測為1的概率很大,與真實值一致,那模型的預測就是準確的,擬合程度很高,信息損失很少。相反,如果真實標簽為1,我們的P1P_{1}P1?卻很接近0,這就說明我們將iii的標簽預測為1的概率很小,即與真實值一致的概率很小,那模型的預測就是失敗的,擬合程度很低,信息損失很多。當yiy_{i}yi?為0時,也是同樣的道理。所以,當yiy_{i}yi?為1的時候,我們希望P1P_{1}P1?非常接近1,當yiy_{i}yi?為0的時候,我們希望P0P_{0}P0?非常接近1,這樣,模型的效果就很好,信息損失就很少。
將兩種取值的概率整合,我們可以定義如下等式:
P(y^i∣xi,w)=P1yi?P01?yiP\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)=P_{1}^{y_{i}} * P_{0}^{1-y_{i}} P(y^?i?∣xi?,w)=P1yi???P01?yi??
這個等式代表同時代表了P1P_{1}P1?和P0P_{0}P0?,在數學上,它被叫做邏輯回歸的假設函數。
當樣本i的真實標簽yiy_{i}yi?為1的時候,1 - yiy_{i}yi?就等于0,P0P_{0}P0?的0次方就是1,所以P(y^i∣xi,w)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)P(y^?i?∣xi?,w)就等于P1P_{1}P1?,這個時候,如果P1P_{1}P1?為1,模型的效果就很好,損失就很小。
同理,當yiy_{i}yi?為0的時候,P(y^i∣xi,w)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)P(y^?i?∣xi?,w)就等于P0P_{0}P0?,此時如果P0P_{0}P0?非常接近1,模型的效果就很好,損失就很小。
所以,為了達成讓模型擬合好,損失小的目的,我們每時每刻都希望P(y^i∣xi,w)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)P(y^?i?∣xi?,w)的值等于1。而P(y^i∣xi,w)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)P(y^?i?∣xi?,w)的本質是樣本i由特征向量xix_{i}xi?和權重www組成的預測函數中,預測出所有可能的y^i\hat{y}_{i}y^?i?的概率,因此1是它的最大值。也就是說,每時每刻,我們都在追求P(y^i∣xi,w)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)P(y^?i?∣xi?,w)的最大值。而尋找相應的參數www,使得每次得到的預測概率最大,正是極大似然估計的基本方法,不過P(y^i∣xi,w)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)P(y^?i?∣xi?,w)是對單個樣本而言的,因此我們還需要將其拓展到多個樣本上。
P(y^i∣xi,w)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)P(y^?i?∣xi?,w)是對單個樣本i而言的函數,對一個訓練集的m個樣本來說,我們可以定義如下等式來表達所有樣本在特征張量XXX和權重向量www組成的預測函數中,預測出所有可能的y^i\hat{y}_{i}y^?i?的概率P為:
P=∏i=1mP(y^i∣xi,w)=∏i=1m(P1yi?P01?yi)=∏i=1m(σiyi?(1?σi)1?yi)\begin{aligned} \boldsymbol{P} &=\prod_{i=1}^{m} P\left(\hat{y}_{i} \mid x_{i}, w\right) \\ &=\prod_{i=1}^{m}\left(P_{1}^{y_{i}} * P_{0}^{1-y_{i}}\right) \\ &=\prod_{i=1}^{m}\left(\sigma_{i}^{y_{i}} *\left(1-\sigma_{i}\right)^{1-y_{i}}\right) \end{aligned} P?=i=1∏m?P(y^?i?∣xi?,w)=i=1∏m?(P1yi???P01?yi??)=i=1∏m?(σiyi???(1?σi?)1?yi?)?
這個函數就是邏輯回歸的似然函數。對該概率P取以e為底的對數,再由log?(A?B)=log?A+log?B\log (A * B)=\log A+\log Blog(A?B)=logA+logB和log?AB=Blog?A\log A^{B}=B \log AlogAB=BlogA可得到邏輯回歸的對數似然函數:
ln?P=ln?∏i=1m(σiyi?(1?σi)1?yi)=∑i=1mln?(σiyi?(1?σi)1?yi)=∑i=1m(ln?σiyi+ln?(1?σi)1?yi)=∑i=1m(yi?ln?(σi)+(1?yi)?ln?(1?σi))\begin{aligned} \ln \boldsymbol{P} &=\ln \prod_{i=1}^{m}\left(\sigma_{i}^{y_{i}} *\left(1-\sigma_{i}\right)^{1-y_{i}}\right) \\ &=\sum_{i=1}^{m} \ln \left(\sigma_{i}^{y_{i}} *\left(1-\sigma_{i}\right)^{1-y_{i}}\right) \\ &=\sum_{i=1}^{m}\left(\ln \sigma_{i}^{y_{i}}+\ln \left(1-\sigma_{i}\right)^{1-y_{i}}\right) \\ &=\sum_{i=1}^{m}\left(y_{i} * \ln \left(\sigma_{i}\right)+\left(1-y_{i}\right) * \ln \left(1-\sigma_{i}\right)\right) \end{aligned} lnP?=lni=1∏m?(σiyi???(1?σi?)1?yi?)=i=1∑m?ln(σiyi???(1?σi?)1?yi?)=i=1∑m?(lnσiyi??+ln(1?σi?)1?yi?)=i=1∑m?(yi??ln(σi?)+(1?yi?)?ln(1?σi?))?
這就是我們的二分類交叉熵函數。為了數學上的便利以及更好地定義”損失”的含義,我們希望將極大值問題轉換為極小值問題,因此我們對lnPlnPlnP取負,并且讓權重www作為函數的自變量,就得到了我們的損失函數 :
L(w)=?∑i=1m(yi?ln?(σi)+(1?yi)?ln?(1?σi))L(w)=-\sum_{i=1}^{m}\left(y_{i} * \ln \left(\sigma_{i}\right)+\left(1-y_{i}\right) * \ln \left(1-\sigma_{i}\right)\right) L(w)=?i=1∑m?(yi??ln(σi?)+(1?yi?)?ln(1?σi?))
現在,我們已經將模型擬合中的“最小化損失”問題,轉換成了對函數求解極值的問題。這就是一個,基于邏輯回歸的返回值σi\sigma_{i}σi?的概率性質以及極大似然估計得出的損失函數。在這個函數上,我們只要追求最小值,就能讓模型在訓練數據上的擬合效果最好,損失最低。
在極大似然估計中,我們只要在對數似然函數上對權重www求導,再令導數為0,就可以求解出最合適的www,但是對于像交叉熵這樣復雜的損失函數,加上神經網絡中復雜的權重組合,令所有權重的導數為0并一個個求解方程的難度很大。因此我們要使用優化算法,這部分我們下一章展開來聊。
2 用tensor實現二分類交叉熵損失
現在,讓我們在PyTorch中來實現二分類交叉熵損失函數。首先使用基本的tensor方法來試試看,以加深我們對二分類交叉熵損失的印象:
import torch import time N = 3*pow(10,3) torch.random.manual_seed(420) X = torch.rand((N,4),dtype=torch.float32) w = torch.rand((4,1),dtype=torch.float32,requires_grad=True) y = torch.randint(low=0,high=2,size=(N,1),dtype=torch.float32) #high取不到 zhat = torch.mm(X,w) sigma = torch.sigmoid(zhat) Loss = -(1/N)*torch.sum((1-y)*torch.log(1-sigma)+y*torch.log(sigma)) #底數默認為e Loss #tensor(0.7962, grad_fn=<MulBackward0>)注意,在寫損失函數這樣的復雜函數時,除了普通的加減乘除以外的全部計算,都要使用torch中的函數,因為tensor的運算速度是遠遠超過普通Python代碼,甚至是NumPy的。你可以試著比較在樣本量為300W時,以下兩行代碼運行的時間差異:
#你可以試著比較在樣本量為300W時,以下兩行代碼運行的時間差異。這段代碼不需要GPU。 #如果你的電腦內存或計算資源有限,可以試著將樣本量調小為30W或3W N = 3*pow(10,6) torch.random.manual_seed(420) X = torch.rand((N,4),dtype=torch.float32) w = torch.rand((4,1),dtype=torch.float32,requires_grad=True) y = torch.randint(low=0,high=2,size=(N,1),dtype=torch.float32) zhat = torch.mm(X,w) sigma = torch.sigmoid(zhat)start = time.time() L1 = -(1/N)*torch.sum((1-y)*torch.log(1-sigma)+y*torch.log(sigma)) now = time.time() #seconds print(now - start) #0.03389596939086914start = time.time() L2 = -(1/N)*sum((1-y)*torch.log(1-sigma)+y*torch.log(sigma)) now = time.time() #seconds print(now - start) #11.579372882843018從運行結果來看,除了加減乘除,我們應該盡量避免使用任何Python原生的計算方法。如果可能的話,讓PyTorch處理一切。
3 用PyTorch中的類實現二分類交叉熵損失
在PyTorch當中,我們有多種方式可以調用二分類交叉熵損失函數。
對于二分類交叉熵損失,nn提供了兩個類:BCEWithLogitsLoss以及BCELoss。雖然PyTorch官方沒有直接明確,但實際上兩個函數所需要輸入的參數不同。
BCEWithLogitsLoss內置了sigmoid函數與交叉熵函數,它會自動計算輸入值的sigmoid值,因此需要輸入zhat與真實標簽,且順序不能變化,zhat必須在前。
相對的,BCELoss中只有交叉熵函數,沒有sigmoid層,因此需要輸入sigma與真實標簽,且順序不能變化。
同時,這兩個函數都要求預測值與真實標簽的數據類型以及結構(shape)必須相同,否則運行就會報錯。
接下來,我們來看看這兩個類是如何使用的:
import torch.nn as nn #調用nn模塊下的類 criterion = nn.BCELoss() #實例化 loss = criterion(sigma,y) #真實標簽在后 loss #tensor(0.8685, grad_fn=<BinaryCrossEntropyBackward0>)criterion2 = nn.BCEWithLogitsLoss() #實例化 loss = criterion2(zhat,y) #真實標簽在后 loss #tensor(0.8685, grad_fn=<BinaryCrossEntropyWithLogitsBackward0>)可以看出,兩個類的結果是一致的。根據PyTorch官方的公告,他們更推薦使用BCEWithLogitsLoss這個內置了sigmoid函數的類。內置的sigmoid函數可以讓精度問題被縮小(因為將指數運算包含在了內部),以維持算法運行時的穩定性,即是說當數據量變大、數據本身也變大時,BCELoss類產生的結果可能有精度問題。所以,當我們的輸出層使用sigmoid函數時,我們就可以使用BCEWithLogitsLoss作為損失函數。
與MSELoss相同,二分類交叉熵的類們也有參數reduction,默認是”mean“,表示求解所有樣本平均的損失,也可換為”sum”,要求輸出整體的損失。以及,還可以使用選項“none”,表示不對損失結果做任何聚合運算,直接輸出每個樣本對應的損失矩陣。
criterion2 = nn.BCEWithLogitsLoss(reduction = "mean") loss = criterion2(zhat,y) loss #tensor(0.8685, grad_fn=<BinaryCrossEntropyWithLogitsBackward0>)criterion2 = nn.BCEWithLogitsLoss(reduction = "sum") loss = criterion2(zhat,y) loss #tensor(2605616.5000, grad_fn=<BinaryCrossEntropyWithLogitsBackward0>)criterion2 = nn.BCEWithLogitsLoss(reduction = "none") loss = criterion2(zhat,y) loss #tensor([[1.3102], # [0.3155], # [0.4247], # ..., # [0.1727], # [0.1716], # [0.1673]], grad_fn=<BinaryCrossEntropyWithLogitsBackward0>)第二種方法很少用,我們了解一下即可:
和nn中的類們相似,名稱中帶有Logits的是內置了sigmoid功能的函數,沒有帶Logits的,是只包含交叉熵損失的函數。對于含有sigmoid功能的函數,我們需要的輸入是zhat與標簽,不含sigmoid的函數我們則需要輸入sigma與標簽。同樣的,這兩個函數對輸入有嚴格的要求,輸入的預測值必須與標簽結構一致、數據類型一致。我們來看看他們的運行結果:
在這里,兩個函數的運行結果是一致的。同樣的,PyTorch官方推薦的是內置sigmoid功能的函數binary_cross_entropy_with_logits。通常來說,我們都使用類,不使用函數。雖然代碼會因此變得稍稍有點復雜,但為了代碼的穩定性與日后維護,使用類是更好的選擇。當然,在進行代碼演示和快速測算的時候,使用函數或者類都沒有問題。
四、多分類交叉熵損失函數
1 由二分類推廣到多分類
二分類交叉熵損失可以被推廣到多分類上,但在實際處理時,二分類與多分類卻有一些關鍵的區別。依然使用極大似然估計的推導流程,首先我們來確定單一樣本概率最大化后的似然函數。
對于多分類的狀況而言,標簽不再服從伯努利分布(0-1分布),因此我們可以定義,樣本i在由特征向量xix_{i}xi?和權重向量www組成的預測函數中,樣本標簽被預測為類別k的概率為:
Pk=P(y^i=k∣xi,w)=σP_{k}=P\left(\hat{y}_{i}=k \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)=\sigma Pk?=P(y^?i?=k∣xi?,w)=σ
對于多分類算法而言, 就是softmax函數返回的對應類別的值。
假設一種最簡單的情況:我們現在有三分類[1, 2, 3],則樣本i被預測為三個類別的概率分別為:
假設樣本的真實標簽為1,我們就希望P1P_{1}P1?最大,同理,如果樣本的真實標簽為其他值,我們就希望其他值所對應的概率最大。在二分類中,我們將y和1-y作為概率PPP的指數,以此來融合真實標簽為0和為1的兩種狀況。但在多分類中,我們的真實標簽可能是任意整數,無法使用y和1-y這樣的結構來構建似然函數。所以我們認為,如果多分類的標簽也可以使用0和1來表示就好了,這樣我們就可以繼續使用真實標簽作為指數的方式。
因此,我們對多分類的標簽做出了如下變化:
原本的真實標簽y是含有[1, 2, 3]三個分類的列向量,現在我們把它變成了標簽矩陣,每個樣本對應一個向量。(如果你熟悉機器學習或統計學,你能夠一眼看出這其實就是獨熱編碼one-hot)。在矩陣中,每一行依舊對應樣本,但卻由三分類衍生出了三個新的列,分別代表:真實標簽是否等于1、等于2以及等于3。在矩陣中,我們使用“1”標注出樣本的真實標簽的位置,使用0表示樣本的真實標簽不是這個標簽。不難注意到,這個標簽矩陣的結構其實是和softmax函數輸出的概率矩陣的結構一致,并且一一對應的。
回顧下二分類的似然函數:
P(y^i∣xi,w)=P1yi?P01?yiP\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)=P_{1}^{y_{i}} * P_{0}^{1-y_{i}} P(y^?i?∣xi?,w)=P1yi???P01?yi??
當我們把標簽整合為標簽矩陣后,我們就可以將單個樣本在總共K個分類情況整合為以下的似然函數:
P(y^i∣xi,w)=P1yi(k=1)?P2yi(k=2)?P3yi(k=3)?…?PKyi(k=K)P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)=P_{1}^{y_{i(k=1)}} * P_{2}^{y_{i(k=2)}} * P_{3}^{y_{i(k=3)}} * \ldots * P_{K}^{y_{i(k=K)}} P(y^?i?∣xi?,w)=P1yi(k=1)???P2yi(k=2)???P3yi(k=3)???…?PKyi(k=K)??
其中P就是樣本標簽被預測為某個具體值的概率,而右上角的指數就是標簽矩陣中對應的值,即這個樣本的真實標簽是否為當前標簽的判斷(是就是1,否就是0)。
更具體的,小k代表y的真實取值,K代表總共有K個分類(此處不是非常嚴謹,按道理說若K代表總共有K個類別,則不應該再使用K代表某個具體類別,但在這里,由于我們使用的類別編號與類別本身相同,所以為了公式的簡化,使用了這樣不嚴謹的表示方式)。雖然是連乘,但對于一個樣本,除了自己所在的真實類別指數會是1之外,其他類別的指數都為0,所以被分類為其他類別的概率在這個式子里就都為0。所以我們可以將式子簡寫為:
P(y^i∣xi,w)=Pjyi(k=j),j為樣本?i所對應的真實標簽的編號?P\left(\hat{y}_{i} \mid \boldsymbol{x}_{i}, \boldsymbol{w}\right)=P_{j}^{y_{i(k=j)}}, j \text { 為樣本 } i \text { 所對應的真實標簽的編號 } P(y^?i?∣xi?,w)=Pjyi(k=j)??,j?為樣本?i?所對應的真實標簽的編號?
對一個訓練集的m個樣本來說,我們可以定義如下等式來表達所有樣本在特征張量XXX和權重向量www組成的預測函數中,預測出所有可能的y^\hat{y}y^?的概率P為:
P=∏i=1mP(y^i∣xi,w)=∏i=1mPjyi(k=j)=∏i=1mσjyi(k=j)\begin{aligned} \boldsymbol{P} &=\prod_{i=1}^{m} P\left(\hat{y}_{i} \mid x_{i}, w\right) \\ &=\prod_{i=1}^{m} P_{j}^{y_{i(k=j)}} \\ &=\prod_{i=1}^{m} \sigma_{j}^{y_{i(k=j)}} \end{aligned} P?=i=1∏m?P(y^?i?∣xi?,w)=i=1∏m?Pjyi(k=j)??=i=1∏m?σjyi(k=j)???
這就是多分類狀況下的似然函數。與二分類一致,似然函數解出來后,我們需要對似然函數求對數:
ln?P=ln?∏i=1mσjyi(k?j)=∑i=1mln?(σjyi(k=j))=∑i=1myi(k=j)ln?σi\begin{aligned} \ln \boldsymbol{P} &=\ln \prod_{i=1}^{m} \sigma_{j}^{y_{i(k-j)}} \\ &=\sum_{i=1}^{m} \ln \left(\sigma_{j}^{y_{i(k=j)}}\right) \\ &=\sum_{i=1}^{m} y_{i(k=j)} \ln \sigma_{i} \end{aligned} lnP?=lni=1∏m?σjyi(k?j)??=i=1∑m?ln(σjyi(k=j)??)=i=1∑m?yi(k=j)?lnσi??
這個函數就是我們之前提到過的交叉熵函數。不難看出,二分類的交叉熵函數其實是多分類的一種特殊情況。
交叉熵函數十分特殊,雖然我們求解過程中,取對數的操作是在確定了似然函數后才進行的,但從計算結果來看,對數操作其實只對softmax函數的結果σ\sigmaσ起效。因此在實際操作中,我們把ln?(softmax?(z))\ln (\operatorname{softmax}(z))ln(softmax(z))這樣的函數單獨定義了一個功能做logsoftmax,PyTorch中可以直接通過nn.logsoftmax類調用這個功能。同時,我們把對數之外的,乘以標簽、加和、取負等等過程打包起來,稱之為負對數似然函數(Negative Log Likelihood function),在PyTorch中可以使用nn.NLLLoss來進行調用。也就是說,在計算損失函數時,我們不再需要使用單獨的softmax函數了。
2 用PyTorch實現多分類交叉熵損失
在PyTorch中實現交叉熵函數的時候,有兩種辦法:
- 調用logsoftmax和NLLLoss實現
更加簡便的方法是:
- 直接調用CrossEntropyLoss
可以發現,兩種輸出方法得到的損失函數結果是一致的。與其他損失函數一致,CrossEntropyLoss也有參數reduction,可以設置為mean、sum以及None,大家可以自行嘗試其代碼并查看返回結果。
無論時二分類還是多分類,PyTorch都提供了包含輸出層激活函數和不包含輸出層激活函數的類兩種選擇。在實際神經網絡建模中,類可以被放入定義好的Model類中去構建神經網絡的結構,因此是否包含激活函數,就需要由用戶來自行選擇。
- 重視展示網絡結構和靈活性,應該使用不包含輸出層激活函數的類
通常在Model類中,__init__中層的數量與forward函數中對應的激活函數的數量是一致的,如果我們使用內置sigmoid/logsoftmax功能的類來計算損失函數,forward函數在定義時就會少一層(輸出層),網絡結構展示就不夠簡單明了,對于結構復雜的網絡而言,結構清晰就更為重要。同時,如果激活函數是單獨寫的,要修改激活函數就變得很容易,如果混在損失函數中,要修改激活函數時就得改掉整個損失函數的代碼,不利于維護。
- 重視穩定性和運算精度,使用包含輸出層激活函數的類
如果在一個Model中,很長時間我們都不會修改輸出層的激活函數,并且模型的穩定運行更為要緊,我們就使用內置了激活函數的類來計算損失函數。同時,就像之前提到的,內置激活函數可以幫助我們推升運算的精度。
因此,選擇哪種損失函數的實現方式,最終還要看我們的需求。
有了損失函數,我們終于要開始進行求解了。下一部分我們來講解神經網絡的入門級優化算法:小批量隨機梯度下降。
總結
以上是生活随笔為你收集整理的LESSON 10.110.210.3 SSE与二分类交叉熵损失函数二分类交叉熵损失函数的pytorch实现多分类交叉熵损失函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 9.5 从0实现多层神经网
- 下一篇: XGBoost核心讲解笔记(贪心学院)