在深度學(xué)習(xí)中要用到各種各樣的損失函數(shù)(loss function)锌奴,這些損失函數(shù)可看作是一種特殊的 layer ,PyTorch也將這些損失函數(shù)實(shí)現(xiàn)為 nn.Module 的子類(lèi)椅挣。然而在實(shí)際使用中通常將這些 loss function 專(zhuān)門(mén)提取出來(lái)项戴,和主模型互相獨(dú)立三痰。
我們所說(shuō)的優(yōu)化腰懂,即優(yōu)化網(wǎng)絡(luò)權(quán)值使得損失函數(shù)值變小。但是隅津,損失函數(shù)值變小是否能代表模型的分類(lèi)/回歸精度變高呢诬垂?那么多種損失函數(shù),應(yīng)該如何選擇呢伦仍?要解答這些就首先要了解Pytorch中的損失函數(shù)都有哪些和他們的機(jī)制结窘,來(lái)看一下吧。
值得注意的是呢铆,很多的 loss 函數(shù)都有 size_average 和 reduce 兩個(gè)布爾類(lèi)型的參數(shù)晦鞋,需要解釋一下。因?yàn)橐话銚p失函數(shù)都是直接計(jì)算 batch 的數(shù)據(jù)棺克,因此返回的 loss 結(jié)果都是維度為 (batch_size, ) 的張量悠垛。
如果 reduce = False,那么 size_average 參數(shù)失效娜谊,直接返回張量形式的 loss确买;(與下文中的
size_average=None, reduce=None, reduction='none'
一致)如果 reduce = True,那么 loss 返回的是標(biāo)量;
1)如果 size_average = True纱皆,返回 loss.mean(); (與下文中的size_average=None, reduce=None, reduction='mean'
一致)
2)如果 size_average = False湾趾,返回 loss.sum();(與下文中的size_average=None, reduce=None, reduction='sum'
一致)
1、L1范數(shù)損失 L1Loss
class torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
reduction
有三個(gè)取值派草,分別為'none'
搀缠、'mean'
、'sum'
近迁;
reduction
取'none'
艺普,則返回 與預(yù)測(cè)值或者真實(shí)值形狀一致的 張量;
reduction
不取'mean'
或者'sum'
,則返回 一個(gè)標(biāo)量值歧譬。size_average
和reduce
都取None
岸浑,不作更改,這是推薦的方式瑰步;
size_average
和reduce
兩者中有一個(gè)不為None
矢洲,則會(huì)重寫(xiě)reduction
。總之缩焦,推薦的方式是:1)
size_average
和reduce
都取None
读虏;2)同時(shí),更改reduction
的值以達(dá)到不同的目標(biāo)
1.1 功能
計(jì)算預(yù)測(cè)值 x
and 真實(shí)值 y
之間的平均絕對(duì)值誤差(MAE)(mean absolute error).
1.2 公式
1)當(dāng)reduction = 'none'
:
其中舌界,為批量大小batch size掘譬。
2)當(dāng)reduction = 'mean'
或者reduction = 'sum'
:
3)默認(rèn)情況(即reduction='mean'
):
1.3 代碼
input_ = torch.empty(2, 3, dtype=torch.float).random_(0, 4)
target = torch.empty(2, 3, dtype=torch.float).random_(0, 4)
print(input_); print(target);
print(input_.size(), target.size())
print('=== mean ===')
loss_fn = torch.nn.L1Loss(reduce=None, size_average=None, reduction='mean')
loss = loss_fn(input_, target)
print(loss)
print(loss.size())
print('=== sum ===')
loss_fn = torch.nn.L1Loss(reduce=None, size_average=None, reduction='sum')
loss = loss_fn(input_, target)
print(loss)
print(loss.size())
print('=== none ===')
loss_fn = torch.nn.L1Loss(reduce=None, size_average=None, reduction='none')
loss = loss_fn(input_, target)
print(loss)
print(loss.size())
輸出結(jié)果如下:
tensor([[3., 0., 3.],
[3., 0., 3.]])
tensor([[2., 0., 3.],
[2., 0., 0.]])
torch.Size([2, 3]) torch.Size([2, 3])
=== mean ===
tensor(0.8333)
torch.Size([])
=== sum ===
tensor(5.)
torch.Size([])
=== none ===
tensor([[1., 0., 0.],
[1., 0., 3.]])
torch.Size([2, 3])
- mean和sum方式下泰演,loss值為一個(gè)標(biāo)量呻拌;
- none方式下,loss值為張量睦焕,形狀與input_或者target一致藐握;
- 以mean為例計(jì)算,(|3-2| + |0-0| + |3-3| + |3-2| + |0-0| + |3-0|) / 6 = 5 / 6 = 0.8333
2垃喊、均方誤差損失 MSELoss
class torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
reduction
有三個(gè)取值猾普,分別為'none'
、'mean'
本谜、'sum'
初家;
reduction
取'none'
,則返回 與預(yù)測(cè)值或者真實(shí)值形狀一致的 張量乌助;
reduction
不取'mean'
或者'sum'
溜在,則返回 一個(gè)標(biāo)量值。size_average
和reduce
都取None
他托,不作更改掖肋,這是推薦的方式;
size_average
和reduce
兩者中有一個(gè)不為None
赏参,則會(huì)重寫(xiě)reduction
志笼。總之,推薦的方式是:1)
size_average
和reduce
都取None
把篓;2)同時(shí)纫溃,更改reduction
的值以達(dá)到不同的目標(biāo)
2.1 功能
計(jì)算預(yù)測(cè)值 x
and 真實(shí)值 y
之間的均方誤差(MSE)(mean squared error (squared L2 norm)).
2.2 公式
1)當(dāng)reduction = 'none'
:
其中,為批量大小batch size韧掩。
2)當(dāng)reduction = 'mean'
或者reduction = 'sum'
:
3)默認(rèn)情況(即reduction='mean'
):
2.3 代碼
input_ = torch.empty(2, 3, dtype=torch.float).random_(0, 4)
target = torch.empty(2, 3, dtype=torch.float).random_(0, 4)
print(input_); print(target);
print(input_.size(), target.size())
print('=== mean ===')
loss_fn = torch.nn.MSELoss(reduce=None, size_average=None, reduction='mean')
loss = loss_fn(input_, target)
print(loss)
print(loss.size())
print('=== sum ===')
loss_fn = torch.nn.MSELoss(reduce=None, size_average=None, reduction='sum')
loss = loss_fn(input_, target)
print(loss)
print(loss.size())
print('=== none ===')
loss_fn = torch.nn.MSELoss(reduce=None, size_average=None, reduction='none')
loss = loss_fn(input_, target)
print(loss)
print(loss.size())
輸出結(jié)果如下:
tensor([[2., 0., 3.],
[3., 0., 1.]])
tensor([[2., 0., 0.],
[0., 3., 0.]])
torch.Size([2, 3]) torch.Size([2, 3])
=== mean ===
tensor(4.6667)
torch.Size([])
=== sum ===
tensor(28.)
torch.Size([])
=== none ===
tensor([[0., 0., 9.],
[9., 9., 1.]])
torch.Size([2, 3])
- mean和sum方式下紊浩,loss值為一個(gè)標(biāo)量;
- none方式下,loss值為張量郎楼,形狀與input_或者target一致万伤;
- 以none為例計(jì)算,
3呜袁、交叉熵?fù)p失 CrossEntropyLoss
class torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
3.1 功能
該方法將nn.LogSoftmax()
和nn.NLLLoss()
進(jìn)行了結(jié)合敌买。嚴(yán)格意義上的交叉熵?fù)p失函數(shù)應(yīng)該是nn.NLLLoss()。在類(lèi)別分布不平衡的數(shù)據(jù)集中尤其有用阶界。
其中虹钮,nn.LogSoftmax()
公式如下:
nn.NLLLoss()
公式參見(jiàn)下文。
3.2 公式
1)當(dāng)不指明權(quán)重時(shí):
2)當(dāng)指明權(quán)重時(shí):
3.3 代碼
import torch
import torch.nn as nn
import numpy as np
import math
# ----------------------------------- CrossEntropy loss: base
loss_f = nn.CrossEntropyLoss(weight=None, size_average=True, reduce=False)
# 生成網(wǎng)絡(luò)輸出 以及 目標(biāo)輸出
output = torch.ones(2, 3, requires_grad=True) * 0.5 # 假設(shè)一個(gè)三分類(lèi)任務(wù)膘融,batchsize=2芙粱,假設(shè)每個(gè)神經(jīng)元輸出都為0.5
target = torch.from_numpy(np.array([0, 1])).type(torch.LongTensor)
loss = loss_f(output, target)
print('--------------------------------------------------- CrossEntropy loss: base')
print('loss: ', loss)
print('由于reduce=False,所以可以看到每一個(gè)樣本的loss氧映,輸出為[1.0986, 1.0986]')
# 熟悉計(jì)算公式春畔,手動(dòng)計(jì)算第一個(gè)樣本
output = output[0].detach().numpy()
output_1 = output[0] # 第一個(gè)樣本的輸出值
target_1 = target[0].numpy()
# 第一項(xiàng)
x_class = output[target_1]
# 第二項(xiàng)
exp = math.e
sigma_exp_x = pow(exp, output[0]) + pow(exp, output[1]) + pow(exp, output[2])
log_sigma_exp_x = math.log(sigma_exp_x)
# 兩項(xiàng)相加
loss_1 = -x_class + log_sigma_exp_x
print('--------------------------------------------------- 手動(dòng)計(jì)算')
print('第一個(gè)樣本的loss:', loss_1)
# ----------------------------------- CrossEntropy loss: weight
weight = torch.from_numpy(np.array([0.6, 0.2, 0.2])).float()
loss_f = nn.CrossEntropyLoss(weight=weight, size_average=True, reduce=False)
output = torch.ones(2, 3, requires_grad=True) * 0.5 # 假設(shè)一個(gè)三分類(lèi)任務(wù),batchsize為2個(gè)岛都,假設(shè)每個(gè)神經(jīng)元輸出都為0.5
target = torch.from_numpy(np.array([0, 1])).type(torch.LongTensor)
loss = loss_f(output, target)
print('\n\n--------------------------------------------------- CrossEntropy loss: weight')
print('loss: ', loss) #
print('原始loss值為1.0986, 第一個(gè)樣本是第0類(lèi)律姨,weight=0.6,所以輸出為1.0986*0.6 =', 1.0986*0.6)
# ----------------------------------- CrossEntropy loss: ignore_index
loss_f_1 = nn.CrossEntropyLoss(weight=None, size_average=False, reduce=False, ignore_index=1)
loss_f_2 = nn.CrossEntropyLoss(weight=None, size_average=False, reduce=False, ignore_index=2)
output = torch.ones(3, 3, requires_grad=True) * 0.5 # 假設(shè)一個(gè)三分類(lèi)任務(wù),batchsize為2個(gè)臼疫,假設(shè)每個(gè)神經(jīng)元輸出都為0.5
target = torch.from_numpy(np.array([0, 1, 2])).type(torch.LongTensor)
loss_1 = loss_f_1(output, target)
loss_2 = loss_f_2(output, target)
print('\n\n--------------------------------------------------- CrossEntropy loss: ignore_index')
print('ignore_index = 1: ', loss_1) # 類(lèi)別為1的樣本的loss為0
print('ignore_index = 2: ', loss_2) # 類(lèi)別為2的樣本的loss為0
輸出結(jié)果:
--------------------------------------------------- CrossEntropy loss: base
loss: tensor([1.0986, 1.0986], grad_fn=<NllLossBackward>)
由于reduce=False择份,所以可以看到每一個(gè)樣本的loss,輸出為[1.0986, 1.0986]
--------------------------------------------------- 手動(dòng)計(jì)算
第一個(gè)樣本的loss: 1.0986122886681098
--------------------------------------------------- CrossEntropy loss: weight
loss: tensor([0.6592, 0.2197], grad_fn=<NllLossBackward>)
原始loss值為1.0986, 第一個(gè)樣本是第0類(lèi)烫堤,weight=0.6,所以輸出為1.0986*0.6 = 0.65916
--------------------------------------------------- CrossEntropy loss: ignore_index
ignore_index = 1: tensor([1.0986, 0.0000, 1.0986], grad_fn=<NllLossBackward>)
ignore_index = 2: tensor([1.0986, 1.0986, 0.0000], grad_fn=<NllLossBackward>)
3.4 其他
交叉熵?fù)p失(cross-entropy Loss) 又稱(chēng)為對(duì)數(shù)似然損失(Log-likelihood Loss)荣赶、對(duì)數(shù)損失;二分類(lèi)時(shí)還可稱(chēng)之為邏輯斯諦回歸損失(Logistic Loss)鸽斟。pytroch這里不是嚴(yán)格意義上的交叉熵?fù)p失函數(shù)拔创,而是先將input經(jīng)過(guò)softmax激活函數(shù),將向量“歸一化”成概率形式湾盗,然后再與target計(jì)算嚴(yán)格意義上交叉熵?fù)p失伏蚊。 在多分類(lèi)任務(wù)中,經(jīng)常采用softmax激活函數(shù)+交叉熵?fù)p失函數(shù)格粪,因?yàn)榻徊骒孛枋隽藘蓚€(gè)概率分布的差異躏吊,然而神經(jīng)網(wǎng)絡(luò)輸出的是向量,并不是概率分布的形式帐萎。所以需要softmax激活函數(shù)將一個(gè)向量進(jìn)行“歸一化”成概率分布的形式比伏,再采用交叉熵?fù)p失函數(shù)計(jì)算loss。 再回顧PyTorch的CrossEntropyLoss()疆导,官方文檔中提到時(shí)將nn.LogSoftmax()和 nn.NLLLoss()進(jìn)行了結(jié)合赁项,nn.LogSoftmax() 相當(dāng)于激活函數(shù) , nn.NLLLoss()是損失函數(shù),將其結(jié)合悠菜,完整的是否可以叫做softmax+交叉熵?fù)p失函數(shù)呢舰攒?
4、負(fù)對(duì)數(shù)似然損失 NLLLoss
class torch.nn.NLLLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
-
size_average
悔醋、reduce
摩窃、reduction
這個(gè)三個(gè)參數(shù)不再贅述,與L1Loss芬骄、MSELoss一致猾愿; -
weight
: 如果提供,則應(yīng)該是 由每個(gè)類(lèi)別的權(quán)重組成的一維張量账阻,例如有三個(gè)類(lèi)別淘太,則weight可為weight=[0.6, 0.3, 0.1]姻僧,其中weight的長(zhǎng)度與類(lèi)別數(shù)相等;如果沒(méi)有提供琴儿,所有權(quán)重置為1段化;
-
ignore_index
:忽略某一類(lèi)別,不計(jì)算其loss造成,其loss會(huì)為0,并且雄嚣,在采用size_average時(shí)晒屎,不會(huì)計(jì)算那一類(lèi)的loss,除的時(shí)候的分母也不會(huì)統(tǒng)計(jì)那一類(lèi)的樣本缓升; - NLLLoss的輸入是每一類(lèi)別的log-probabilities鼓鲁,該log-probabilities可以通過(guò)
LogSoftmax
網(wǎng)絡(luò)層獲得,如果不想讓網(wǎng)絡(luò)的最后一層是 log_softmax 層的話(huà)港谊,就可以采用 CrossEntropyLoss 完全代替此函數(shù)骇吭,因?yàn)镃rossEntropyLoss 中就有這些步驟。
4.1 功能
在類(lèi)別分布不平衡的數(shù)據(jù)集中尤其有用歧寺。
4.2 公式
1)當(dāng)reduction = 'none'
:
其中燥狰,為批量大小batch size;
表示一個(gè)批量中的第
個(gè)樣本斜筐;
表示第
個(gè)樣本的損失值龙致;
表示第
個(gè)樣本的真實(shí)標(biāo)簽(例如是類(lèi)別2,則
)顷链;
表示類(lèi)別
的權(quán)重目代。
2)當(dāng)reduction = 'mean'
或者reduction = 'sum'
:
4.3 shape
Input:
(N, C)
whereC = number of classes
or(N, C, d_1, d_2, ..., d_K)
within the case of
K
- dimensional loss.Target:
(N)
where each value isor
(N, d_1, d_2, ..., d_K)
within the case of
K
- dimensional loss.Output: scalar.
Ifreduction
is'none'
, then the same size as the target:(N)
or(N, d_1, d_2, ..., d_K)
within the case of
K
- dimensional loss.會(huì)發(fā)現(xiàn)Target 與 Output的形狀一致,但I(xiàn)nput要比前兩者多一個(gè)類(lèi)別通道
(C)
4.4 理解
以三分類(lèi)任務(wù)為例,類(lèi)別標(biāo)號(hào)為0榛了、1在讶、2。NLLLoss 的輸入input=[-1.233, 2.657, 0.534]霜大, 真實(shí)標(biāo)簽為2(class=2)真朗,則 = -0.534(有點(diǎn)列表切片的意思)。
4.5 舉例
1) 當(dāng)reduction = 'none'
僧诚,
- 理論計(jì)算
使用如下公式:
以三分類(lèi)任務(wù)為例遮婶,類(lèi)別標(biāo)號(hào)為0、1湖笨、2旗扑。NLLLoss 的輸入input為[[0.6, 0.2, 0.2], [0.4, 1.2, 0.4]](即兩個(gè)樣本分別在三個(gè)類(lèi)別的預(yù)測(cè)概率值),真實(shí)標(biāo)簽target= [0, 1]慈省,不指定權(quán)重(則權(quán)重全為1)臀防。
這個(gè)批量有兩個(gè)樣本(),所以
边败;
真實(shí)標(biāo)簽列表為target= [0, 1]袱衷,即第一個(gè)樣本的真實(shí)標(biāo)簽為類(lèi)別0,第二個(gè)樣本的真實(shí)標(biāo)簽為類(lèi)別1笑窜,所以致燥、
;
權(quán)重沒(méi)有指定(則weight = [1, 1, 1])排截,所以嫌蚤,同理,
断傲;
表示第一個(gè)樣本中
對(duì)應(yīng)的值脱吱,即
,同理认罩,
;
所以 箱蝠,同理,
因此垦垂,宦搬。
- 代碼實(shí)現(xiàn):
import torch
import torch.nn as nn
import numpy as np
# ----------------------------------- log likelihood loss
# 生成網(wǎng)絡(luò)輸出 以及 目標(biāo)輸出
output = torch.from_numpy(np.array([[0.6, 0.2, 0.2], [0.4, 1.2, 0.4]])).float()
output.requires_grad = True
target = torch.from_numpy(np.array([0, 1])).type(torch.LongTensor)
loss_f = nn.NLLLoss(weight=None, size_average=None, reduce=None, reduction='none')
loss = loss_f(output, target)
print('\nloss: \n', loss)
print('\n第一個(gè)樣本是0類(lèi),loss = -(1*0.6)={}'.format(loss[0]))
print('\n第二個(gè)樣本是1類(lèi)乔外,loss = -(1*1.2)={}'.format(loss[1]))
輸出結(jié)果:
loss:
tensor([-0.6000, -1.2000], grad_fn=<NllLossBackward>)
第一個(gè)樣本是0類(lèi)床三,loss = -(1*0.6)=-0.6000000238418579
第二個(gè)樣本是1類(lèi),loss = -(1*1.2)=-1.2000000476837158
2)當(dāng)reduction = 'mean'
杨幼,且?guī)蠙?quán)重
- 理論計(jì)算
以三分類(lèi)任務(wù)為例撇簿,類(lèi)別標(biāo)號(hào)為0聂渊、1、2四瘫。NLLLoss 的輸入input為[[0.6, 0.2, 0.2], [0.4, 1.2, 0.4]](即兩個(gè)樣本分別在三個(gè)類(lèi)別的預(yù)測(cè)概率值)汉嗽,真實(shí)標(biāo)簽target= [0, 1],指定權(quán)值weight = [0.6, 0.2, 0.2]找蜜。
則有饼暑,
;
因?yàn)?code>reduction = 'mean'洗做,所以弓叛。
- 代碼實(shí)現(xiàn)
import torch
import torch.nn as nn
import numpy as np
# ----------------------------------- log likelihood loss
# 各類(lèi)別權(quán)重
weight = torch.from_numpy(np.array([0.6, 0.2, 0.2])).float()
# 生成網(wǎng)絡(luò)輸出 以及 目標(biāo)輸出
output = torch.from_numpy(np.array([[0.6, 0.2, 0.2], [0.4, 1.2, 0.4]])).float()
output.requires_grad = True
target = torch.from_numpy(np.array([0, 1])).type(torch.LongTensor)
loss_f = nn.NLLLoss(weight=weight, size_average=None, reduce=None, reduction='mean')
loss = loss_f(output, target)
print('\nloss: \n', loss)
輸出結(jié)果:
loss:
tensor(-0.7500, grad_fn=<NllLossBackward>)
5、目標(biāo)值為泊松分布的負(fù)對(duì)數(shù)似然損失PoissonNLLLoss
class torch.nn.PoissonNLLLoss(log_input=True, full=False, size_average=None, eps=1e-08, reduce=None, reduction='mean')
-
log_input (bool, optional):
如果為T(mén)rue诚纸,使用撰筷;如果為False,使用
-
full (bool, optional):
是否計(jì)算全部的loss畦徘。例如毕籽,當(dāng)采用斯特林公式近似階乘項(xiàng)時(shí),階乘項(xiàng)近似為
-
eps(float)
: 當(dāng)log_input = False時(shí)井辆,用來(lái)防止計(jì)算log(0)关筒,而增加的一個(gè)修正項(xiàng),即 loss(input,target)=input - target * log(input+eps)杯缺。默認(rèn)為
5.1 功能
目標(biāo)值為泊松分布的負(fù)對(duì)數(shù)似然損失
5.2 公式
其中蒸播,上式的最后一項(xiàng)能被省略或者使用Stirling formula近似。當(dāng)target的值大于1時(shí)夺谁,使用該近似廉赔;當(dāng)小于或等于1時(shí),將該最后一項(xiàng)加到損失中匾鸥,不近似。
5.3 代碼
import torch
import torch.nn as nn
import numpy as np
# ----------------------------------- Poisson NLLLoss
# 生成網(wǎng)絡(luò)輸出 以及 目標(biāo)輸出
log_input = torch.randn(5, 2, requires_grad=True)
target = torch.randn(5, 2)
loss_f = nn.PoissonNLLLoss()
loss = loss_f(log_input, target)
print('\nloss: \n', loss)
輸出結(jié)果:
loss:
tensor(1.1533, grad_fn=<MeanBackward0>)
6碉纳、KL 散度損失 KLDivLoss
class torch.nn.KLDivLoss(size_average=None, reduce=None, reduction='mean')
注意:
-
reduction
的選項(xiàng)增加了batchmean
勿负; -
reduction
='mean'
doesn't return the true kl divergence value;除 loss總個(gè)數(shù)劳曹; -
reduction
='batchmean'
which aligns with KL math definition.(目前是這樣的奴愉,后續(xù)版本可能會(huì)改進(jìn));除 batch size 铁孵。
6.1 功能
計(jì)算input和target之間的KL散度( Kullback–Leibler divergence) 锭硼。 KL 散度,又叫做相對(duì)熵蜕劝,算的是兩個(gè)分布之間的距離檀头,越相似則越接近零轰异。KL散度是連續(xù)分布的有用距離度量,在對(duì)連續(xù)輸出分布的空間進(jìn)行直接回歸時(shí)通常很有用暑始。
6.2 公式
1)當(dāng)reduction = 'none'
2)當(dāng)reduction
不為 'none'
搭独,默認(rèn)為'mean'
6.3 代碼
import torch
import torch.nn as nn
import numpy as np
# ----------------------------------- KLDiv loss
loss_f = nn.KLDivLoss(size_average=False, reduce=False)
loss_f_mean = nn.KLDivLoss(size_average=True, reduce=True)
loss_f_mean_1 = nn.KLDivLoss(reduction='mean')
loss_f_mean_2 = nn.KLDivLoss(reduction='batchmean')
# 生成網(wǎng)絡(luò)輸出 以及 目標(biāo)輸出
output = torch.from_numpy(np.array([[0.1132, 0.5477, 0.3390], [0.1132, 0.5477, 0.3390]])).float()
output.requires_grad = True
target = torch.from_numpy(np.array([[0.8541, 0.0511, 0.0947], [0.1132, 0.5477, 0.3390]])).float()
loss_1 = loss_f(output, target)
loss_mean = loss_f_mean(output, target)
loss_mean_1 = loss_f_mean_1(output, target)
loss_mean_2 = loss_f_mean_2(output, target)
print('\nloss: ', loss_1)
print('\nloss_mean: ', loss_mean)
print('\nloss_mean_1: ', loss_mean_1) # 除 總損失個(gè)數(shù)
print('\nloss_mean_2: ', loss_mean_2) # 這是真正數(shù)學(xué)上KL散度的定義,除 batch size
print(torch.sum(loss_1) / 6) # 所以 與 loss_mean_1相等
print(torch.sum(loss_1) / 2) # 所以 與 loss_mean_2相等
# 熟悉計(jì)算公式廊镜,手動(dòng)計(jì)算樣本的第一個(gè)元素的loss牙肝,注意這里只有一個(gè)樣本,是 element-wise計(jì)算的
output = output[0].detach().numpy()
output_1 = output[0] # 第一個(gè)樣本的第一個(gè)元素
target_1 = target[0][0].numpy()
loss_1 = target_1 * (np.log(target_1) - output_1)
print('\n第一個(gè)樣本第一個(gè)元素的loss:', loss_1)
輸出結(jié)果:
loss: tensor([[-0.2314, -0.1800, -0.2553],
[-0.2594, -0.6297, -0.4816]], grad_fn=<KlDivBackward>)
loss_mean: tensor(-0.3396, grad_fn=<KlDivBackward>)
loss_mean_1: tensor(-0.3396, grad_fn=<KlDivBackward>)
loss_mean_2: tensor(-1.0187, grad_fn=<DivBackward0>)
tensor(-0.3396, grad_fn=<DivBackward0>)
tensor(-1.0187, grad_fn=<DivBackward0>)
第一個(gè)樣本第一個(gè)元素的loss: -0.23138165
7嗤朴、二進(jìn)制交叉熵?fù)p失 BCELoss
class torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
7.1 功能
二分類(lèi)任務(wù)時(shí)的交叉熵計(jì)算函數(shù)配椭。此函數(shù)可以認(rèn)為是 nn.CrossEntropyLoss
函數(shù)的特例。其分類(lèi)限定為二分類(lèi)雹姊,必須是{0,1}股缸。還需要注意的是,input 應(yīng)該為概率分布的形式容为,這樣才符合交叉熵的應(yīng)用乓序。所以在 BCELoss 之前,input一般為 sigmoid 激活層的輸出坎背,官方例子也是這樣給的替劈。該損失函數(shù)在自編碼器中常用。
7.2 公式
1)當(dāng)reduction = 'none'
2)當(dāng)reduction
不為 'none'
得滤,默認(rèn)為'mean'
7.3 代碼
import torch.nn.functional as F
loss_fn = torch.nn.BCELoss(reduce=False, size_average=False)
input = torch.autograd.Variable(torch.randn(3, 4))
target = torch.autograd.Variable(torch.FloatTensor(3, 4).random_(2))
loss = loss_fn(F.sigmoid(input), target)
print(input); print(target); print(loss)
輸出結(jié)果
tensor([[-1.8626, -0.1685, 1.3190, 0.4265],
[ 0.3094, -1.2203, -0.4972, -0.4424],
[-0.1279, 0.4547, 0.7306, 0.0625]])
tensor([[0., 0., 1., 1.],
[0., 0., 1., 0.],
[1., 0., 1., 1.]])
tensor([[0.1443, 0.6125, 0.2370, 0.5025],
[0.8597, 0.2586, 0.9723, 0.4962],
[0.7591, 0.9461, 0.3931, 0.6624]])
8陨献、BCEWithLogitsLoss
class torch.nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean', pos_weight=None)
8.1 功能
- 將
Sigmoid
層和BCELoss
組合成一個(gè)層; - This version is more numerically stable than using a plain
Sigmoid
followed by aBCELoss
懂更; - 將兩個(gè)操作組合成一個(gè)層眨业, we take advantage of the log-sum-exp trick for numerical stability.。
8.2 公式
1)當(dāng)reduction = 'none'
2)當(dāng)reduction
不為 'none'
,默認(rèn)為'mean'
8.3 代碼
loss = nn.BCEWithLogitsLoss(reduction='none')
input = torch.randn(3, requires_grad=True)
target = torch.empty(3).random_(2)
output = loss(input, target)
print(input); print(target); print(output)
輸出結(jié)果:
tensor([ 0.1099, 1.3278, -0.2820], requires_grad=True)
tensor([0., 0., 0.])
tensor([0.7496, 1.5629, 0.5620],
grad_fn=<BinaryCrossEntropyWithLogitsBackward>)
9躏啰、MarginRankingLoss
class torch.nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
計(jì)算兩個(gè)向量之間的相似度赞厕,當(dāng)兩個(gè)向量之間的距離大于margin,則loss為正聘殖,小于margin,loss為0
9.1 公式
- inputs :
x1
,x2
, two 1D mini-batchTensors
行瑞; -
y
: a label 1D mini-batch tensor (containing 1 or -1)奸腺; -
margin
:默認(rèn)為0; - 當(dāng)
時(shí)血久,
要比
大突照,且
,才不會(huì)有損失氧吐;
- 當(dāng)
時(shí)讹蘑,
要比
大末盔,且
,才不會(huì)有損失衔肢。
9.2 代碼
loss = nn.MarginRankingLoss(reduction='none')
input1 = torch.randn(3, requires_grad=True)
input2 = torch.randn(3, requires_grad=True) + 0.5
target = torch.empty(3).random_(2)
output = loss(input1, input2, target)
print(input1); print(input2); print(target); print(output)
輸出結(jié)果:
tensor([ 0.2112, -0.0281, 0.5583], requires_grad=True)
tensor([ 1.8994, -0.6425, 0.9355], grad_fn=<AddBackward0>)
tensor([1., 0., 1.])
tensor([1.6882, 0.0000, 0.3772], grad_fn=<ClampMinBackward>)
10庄岖、MultiMarginLoss
多分類(lèi)(multi-class)的 Hinge 損失,
其中角骤,表示標(biāo)簽隅忿,
默認(rèn)取1,margin默認(rèn)取1邦尊,也可以取別的值背桐。
注意:
-
為向量,
為標(biāo)量值蝉揍。
代碼:
loss = nn.MultiMarginLoss()
x = torch.FloatTensor([[0.1, 0.2, 0.4, 0.8]])
y = torch.LongTensor([3])
# 0.25 * ((1 - 0.8 + 0.1) + (1 - 0.8 + 0.2) + (1 - 0.8 + 0.4)) = 0.325
loss(x, y)
11链峭、MultiLabelMarginLoss
class torch.nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction='mean')
多類(lèi)別(multi-class)多分類(lèi)(multi-classification)的 Hinge 損失,是上面 MultiMarginLoss 在多類(lèi)別上的拓展又沾。同時(shí)限定 p = 1弊仪,margin = 1.
這個(gè)接口有點(diǎn)坑,是直接從 Torch 那里抄過(guò)來(lái)的杖刷,見(jiàn) MultiLabelMarginCriterion 的描述励饵。而 Lua 的下標(biāo)和 Python 不一樣,前者的數(shù)組下標(biāo)是從 1 開(kāi)始的滑燃,所以用 0 表示占位符役听。有幾個(gè)坑需要注意:
- 這里的
都是大小為 N 的向量,如果
不是向量而是標(biāo)量表窘,后面的
就沒(méi)有了典予,因此就退化成上面的MultiMarginLoss;
- 限制
的大小為 N乐严,是為了處理多標(biāo)簽中標(biāo)簽個(gè)數(shù)不同的情況瘤袖,用 0 表示占位,該位置和后面的數(shù)字都會(huì)被認(rèn)為不是正確的類(lèi)昂验。如
那么就會(huì)被認(rèn)為是屬于類(lèi)別 5 和 3孽椰,而 4 因?yàn)樵诹愫竺妫虼藭?huì)被忽略凛篙。
- 上面的公式和說(shuō)明只是為了和文檔保持一致,其實(shí)在調(diào)用接口的時(shí)候栏渺,用的是 -1 做占位符呛梆,而 0 是第一個(gè)類(lèi)別。
11.1 公式解析
-
形狀相同的向量磕诊,為了維持一致填物,使用-1填充
纹腌;
- 對(duì)于真實(shí)標(biāo)簽
,不考慮
中-1之后的值滞磺;
-
是
的索引升薯,從0開(kāi)始直到
,也就是取
中-1之前的值击困。例如
涎劈,則
可取0、1阅茶;
可取3蛛枚、0;
-
是
的索引,
的取值為{0, 1, ..., n-1}中不等于
的值脸哀。
代碼:
loss = nn.MultiLabelMarginLoss()
x = torch.FloatTensor([[0.1, 0.2, 0.4, 0.8]])
# for target y, only consider labels 3 and 0, not after label -1
y = torch.LongTensor([[3, 0, -1, 1]])
# 0.25 * ((1-(0.1-0.2)) + (1-(0.1-0.4)) + (1-(0.8-0.2)) + (1-(0.8-0.4)))
loss(x, y)
代碼中公式:
12蹦浦、SoftMarginLoss
class nn.SoftMarginLoss(size_average=None, reduce=None, reduction='mean')
多標(biāo)簽二分類(lèi)問(wèn)題,這 N 項(xiàng)都是二分類(lèi)問(wèn)題撞蜂,其實(shí)就是把 N 個(gè)二分類(lèi)的 loss 加起來(lái)盲镶,化簡(jiǎn)一下。其中 只能取 1,?1 兩種蝌诡,代表正類(lèi)和負(fù)類(lèi)溉贿。和下面的其實(shí)是等價(jià)的,只是
的形式不同送漠。
13顽照、MultiLabelSoftMarginLoss
class nn.MultiLabelSoftMarginLoss(
weight=None,
size_average=None,
reduce=None,
reduction='mean',
)
根據(jù)最大熵的多標(biāo)簽 one-versue-all 損失。
其中闽寡, 代兵;
14、CosineEmbeddingLoss
class nn.CosineEmbeddingLoss(
margin=0.0,
size_average=None,
reduce=None,
reduction='mean',
)
余弦相似度的損失爷狈,目的是讓兩個(gè)向量盡量相近植影。注意這兩個(gè)向量都是有梯度的。
margin 可以取 [?1,1]涎永,但是比較建議取 0-0.5 較好思币。
15、HingeEmbeddingLoss
class nn.HingeEmbeddingLoss(
margin=1.0,
size_average=None,
reduce=None,
reduction='mean',
)
This is usually used for measuring whether two inputs are similar or
dissimilar, e.g. using the L1 pairwise distance as x
, and is typically
used for learning nonlinear embeddings or semi-supervised learning.
16羡微、TripleMarginLoss
class nn.TripletMarginLoss(
margin=1.0,
p=2.0,
eps=1e-06,
swap=False,
size_average=None,
reduce=None,
reduction='mean',
)
triplet_loss = nn.TripletMarginLoss(margin=1.0, p=2)
anchor = torch.randn(100, 128, requires_grad=True)
positive = torch.randn(100, 128, requires_grad=True)
negative = torch.randn(100, 128, requires_grad=True)
output = triplet_loss(anchor, positive, negative)
output
17谷饿、SmoothL1Loss
class nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean')
18、CTCLoss
class nn.CTCLoss(blank=0, reduction='mean', zero_infinity=False)
還不要清楚妈倔。博投。。
T = 50 # Input sequence length
C = 20 # Number of classes (including blank)
N = 16 # Batch size
S = 30 # Target sequence length of longest target in batch
S_min = 10 # Minimum target length, for demonstration purposes
# Initialize random batch of input vectors, for *size = (T,N,C)
input = torch.randn(T, N, C).log_softmax(2).detach().requires_grad_()
# Initialize random batch of targets (0 = blank, 1:C = classes)
target = torch.randint(low=1, high=C, size=(N, S), dtype=torch.long)
input_lengths = torch.full(size=(N,), fill_value=T, dtype=torch.long)
target_lengths = torch.randint(low=S_min, high=S, size=(N,), dtype=torch.long)
ctc_loss = nn.CTCLoss()
loss = ctc_loss(input, target, input_lengths, target_lengths)
loss.backward()