通常,由于類別不均衡,需要使用weighted cross entropy loss平衡。
def inverse_freq(label):
"""
輸入label [N,1,H,W],1是channel數(shù)目
"""
den = label.sum() # 0
_,_,h,w= label.shape
num = h*w
alpha = den/num # 0
return torch.tensor([alpha, 1-alpha]).cuda()
# train
...
loss1 = F.cross_entropy(out1, label.squeeze(1).long(), weight=inverse_freq(label))
補充:Pytorch踩坑記之交叉熵(nn.CrossEntropy,nn.NLLLoss,nn.BCELoss的區(qū)別和使用)
在Pytorch中的交叉熵函數(shù)的血淚史要從nn.CrossEntropyLoss()這個損失函數(shù)開始講起。
從表面意義上看,這個函數(shù)好像是普通的交叉熵函數(shù),但是如果你看過一些Pytorch的資料,會告訴你這個函數(shù)其實是softmax()和交叉熵的結(jié)合體。
然而如果去官方看這個函數(shù)的定義你會發(fā)現(xiàn)是這樣子的:
![](/d/20211017/341e4462f83f6001d2e5d60213620070.gif)
哇,竟然是nn.LogSoftmax()和nn.NLLLoss()的結(jié)合體,這倆都是什么玩意兒啊。再看看你會發(fā)現(xiàn)甚至還有一個損失叫nn.Softmax()以及一個叫nn.nn.BCELoss()。
我們來探究下這幾個損失到底有何種關(guān)系。
nn.Softmax和nn.LogSoftmax
首先nn.Softmax()官網(wǎng)的定義是這樣的:
![](/d/20211017/f8c5c9391f08a9c9acccbc9d54ab6fe8.gif)
嗯...就是我們認(rèn)識的那個softmax。那nn.LogSoftmax()的定義也很直觀了:
![](/d/20211017/61bd761c93b43b64e98e5204fa36ce89.gif)
果不其然就是Softmax取了個log??梢詫憘€代碼測試一下:
import torch
import torch.nn as nn
a = torch.Tensor([1,2,3])
#定義Softmax
softmax = nn.Softmax()
sm_a = softmax=nn.Softmax()
print(sm)
#輸出:tensor([0.0900, 0.2447, 0.6652])
#定義LogSoftmax
logsoftmax = nn.LogSoftmax()
lsm_a = logsoftmax(a)
print(lsm_a)
#輸出tensor([-2.4076, -1.4076, -0.4076]),其中l(wèi)n(0.0900)=-2.4076
nn.NLLLoss
上面說過nn.CrossEntropy()是nn.LogSoftmax()和nn.NLLLoss的結(jié)合,nn.NLLLoss官網(wǎng)給的定義是這樣的:
The negative log likelihood loss. It is useful to train a classification problem with C classes
![](/d/20211017/b185af93d88e8bee130a7efa5d3024bc.gif)
負(fù)對數(shù)似然損失 ,看起來好像有點晦澀難懂,寫個代碼測試一下:
import torch
import torch.nn
a = torch.Tensor([[1,2,3]])
nll = nn.NLLLoss()
target1 = torch.Tensor([0]).long()
target2 = torch.Tensor([1]).long()
target3 = torch.Tensor([2]).long()
#測試
n1 = nll(a,target1)
#輸出:tensor(-1.)
n2 = nll(a,target2)
#輸出:tensor(-2.)
n3 = nll(a,target3)
#輸出:tensor(-3.)
看起來nn.NLLLoss做的事情是取出a中對應(yīng)target位置的值并取負(fù)號,比如target1=0,就取a中index=0位置上的值再取負(fù)號為-1,那這樣做有什么意義呢,要結(jié)合nn.CrossEntropy往下看。
nn.CrossEntropy
看下官網(wǎng)給的nn.CrossEntropy()的表達(dá)式:
![](/d/20211017/3e250d4384c90e11cbdff91c355f536f.gif)
看起來應(yīng)該是softmax之后取了個對數(shù),寫個簡單代碼測試一下:
import torch
import torch.nn as nn
a = torch.Tensor([[1,2,3]])
target = torch.Tensor([2]).long()
logsoftmax = nn.LogSoftmax()
ce = nn.CrossEntropyLoss()
nll = nn.NLLLoss()
#測試CrossEntropyLoss
cel = ce(a,target)
print(cel)
#輸出:tensor(0.4076)
#測試LogSoftmax+NLLLoss
lsm_a = logsoftmax(a)
nll_lsm_a = nll(lsm_a,target)
#輸出tensor(0.4076)
看來直接用nn.CrossEntropy和nn.LogSoftmax+nn.NLLLoss是一樣的結(jié)果。為什么這樣呢,回想下交叉熵的表達(dá)式:
![](https://private.codecogs.com/gif.latex?l%28x%2Cy%29%3D-%5Csum%20y*logx%3D%5Cleft%5C%7B%5Cbegin%7Bmatrix%7D%20-logx%20%2C%20y%3D1%26%20%5C%5C%200%2Cy%3D0%26%20%5Cend%7Bmatrix%7D%5Cright.)
其中y是label,x是prediction的結(jié)果,所以其實交叉熵?fù)p失就是負(fù)的target對應(yīng)位置的輸出結(jié)果x再取-log。這個計算過程剛好就是先LogSoftmax()再NLLLoss()。
所以我認(rèn)為nn.CrossEntropyLoss其實應(yīng)該叫做softmaxloss更為合理一些,這樣就不會誤解了。
nn.BCELoss
你以為這就完了嗎,其實并沒有。還有一類損失叫做BCELoss,寫全了的話就是Binary Cross Entropy Loss,就是交叉熵應(yīng)用于二分類時候的特殊形式,一般都和sigmoid一起用,表達(dá)式就是二分類交叉熵:
![](/d/20211017/18ccaf689f467410f240f0a6ef565ff2.gif)
直覺上和多酚類交叉熵的區(qū)別在于,不僅考慮了
的樣本,也考慮了
的樣本的損失。
總結(jié)
nn.LogSoftmax是在softmax的基礎(chǔ)上取自然對數(shù)
nn.NLLLoss是負(fù)的似然對數(shù)損失,但Pytorch的實現(xiàn)就是把對應(yīng)target上的數(shù)取出來再加個負(fù)號,要在CrossEntropy中結(jié)合LogSoftmax來用
BCELoss是二分類的交叉熵?fù)p失,Pytorch實現(xiàn)中和多分類有區(qū)別
Pytorch是個深坑,讓我們一起扎根使用手冊,結(jié)合實踐踏平這些坑吧暴風(fēng)哭泣。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
您可能感興趣的文章:- 在Pytorch中使用樣本權(quán)重(sample_weight)的正確方法
- pytorch中交叉熵?fù)p失(nn.CrossEntropyLoss())的計算過程詳解
- PyTorch的SoftMax交叉熵?fù)p失和梯度用法