CrossEntropyLoss(交叉熵损失)与NLLLoss(negative log likelihood,负对数似然损失)都是适用于分类问题,基于log似然损失,即交叉熵损失函数的实现方式,其体现了两个分布的近似程度: L ( p , q ) = − ∑ i p i log q i L(p,q)=-\sum_ip_i\log q_i L(p,q)=−i∑pilogqi对于分类问题,其只在真实所属类别 k k k上的编码为1,其他均为0(即one-hot编码),所以该损失函数可进一步写为: L = − C k log q k L=-C_k\log q_k L=−Cklogqk
CrossEntropyLoss和NLLLoss的区别在于:
(1)CrossEntropyLoss对输出层结果自动进行softmax概率归一化运算以及对数处理,然后再基于
L
=
−
C
k
log
q
k
L=-C_k\log q_k
L=−Cklogqk进行计算;
(2)NLLLoss直接计算
L
=
−
C
k
log
q
k
L=-C_k\log q_k
L=−Cklogqk,因此为了保证计算的正确,必须接在logsoftmax
函数后面。
也就是说,在pytorch中:CrossEntropyLoss()=logsoftmax()+NLLLoss()
下面以一个例子来验证该结论:
import torch
import torch.nn as nn
output = torch.rand(3, 5) # 随机输出的3*5未归一化结果
target = torch.tensor([0, 1, 4]) # 对应3个样本的真实分类
criterion1 = nn.NLLLoss()
criterion2 = nn.CrossEntropyLoss()
# 方案一:直接采用CrossEntropyLoss()
print(criterion2(output, target))
# 方案二:在output后接入logsoftmax(),再计算NLLLoss()
logsoftmax = nn.LogSoftmax(dim=1)
output_logsoftmax = logsoftmax(output)
print(criterion1(output_logsoftmax, target))
# 方案三:手动计算交叉熵损失函数的平均值
target_onehot = torch.zeros_like(output)
for i,j in enumerate(target):
target_onehot[i][j] = 1
crossentropy = - (target_onehot * output_logsoftmax).sum()/len(target)
print(crossentropy)
结果表明,上述三种方法计算的结果是完全相同的。
此外,还有一个注意值得的细节为:作为Target的分类编码要从0开始计起。