PyTorch提供了丰富的损失函数,而多分类任务用到最多的就是nn.CrossEntropyLoss和nn.NLLLoss了,不妨讨论一下。
nn.CrossEntropyLoss
CrossEntropy顾名思义就是交叉熵,概念来自香农的信息论,用于度量两个概率分布间的差异性信息,可以认为是在给定的真实分布下,使用非真实分布的策略消除系统的不确定性所需要付出的努力的大小。交叉熵越小,证明计算出的非真实分布越接近真实分布。
公式如下:
H(p,q)=−∑k=1N(pk∗logqk)H(p,q)=−∑k=1N(pk∗logqk)
在PyTroch的文档中明确指出它和nn.NLLLoss之间的关系,后面我们会进行测试。
This criterion combines nn.LogSoftmax() and nn.NLLLoss() in one single class.
nn.NLLLoss
全名是负对数似然损失函数(Negative Log Likelihood),在PyTorch的文档中有如下说明:
Obtaining log-probabilities in a neural network is easily achieved by adding a LogSoftmax layer in the last layer of your network. You may use CrossEntropyLoss instead, if you prefer not to add an extra layer.
简单来说,如果最后一层做了log softmax处理,那就直接使用nn.NLLLoss,我们动手试试吧。
-
首先我们构造一个网络的输出做为我们的预测值,再构造一个真实分布,一起做为损失函数的输入。
#预测值
inputs_tensor = torch.FloatTensor( [
[10, 0, 3,-1, 1],
[-1,-2, 0,-3,-4],
[-1, 1, 4, 8, 2]
])
#真实值
targets_tensor = torch.LongTensor([1,2,3])
-
计算预测值的log softmax,这里有先计算softmax再取log和直接调用log_softmax函数两种方式,结果相同。
#分步计算log softmax
softmax_result = F.softmax(inputs_variable)
logsoftmax_result = np.log(softmax_result.data)
#logsoftmax_result:
#-0.0011 -10.0011 -7.0011 -11.0011 -9.0011
#-1.4519 -2.4519 -0.4519 -3.4519 -4.4519
#-9.0216 -7.0216 -4.0216 -0.0216 -6.0216
#[torch.FloatTensor of size 3x5]
#直接调用log_softmax
log_softmax_result = F.log_softmax(inputs_variable)
#log_softmax_result:
#-0.0011 -10.0011 -7.0011 -11.0011 -9.0011
#-1.4519 -2.4519 -0.4519 -3.4519 -4.4519
#-9.0216 -7.0216 -4.0216 -0.0216 -6.0216
#[torch.FloatTensor of size 3x5]
计算每个样本的交叉熵再求平均,得到的结果和直接调用nn.CrossEntropyLoss函数相同。
loss = nn.CrossEntropyLoss()
output = loss(inputs_variable, targets_variable)
# 3.4915
# [torch.FloatTensor of size 1]
将前面求出的log softmax结果直接做为nn.NLLLoss的输入,得到的结果相同。
loss = nn.NLLLoss()
output = loss(log_softmax_result, targets_variable)
# 3.4915
# [torch.FloatTensor of size 1]
from torch import nn
import torch
# 重新封装的多标签损失函数
class WeightedMultilabel(nn.Module):
def __init__(self, weights: torch.Tensor):
super(WeightedMultilabel, self).__init__()
self.cerition = nn.BCEWithLogitsLoss(reduction='none')
self.weights = weights
def forward(self, outputs, targets):
loss = self.cerition(outputs, targets)
return (loss * self.weights).mean()
x=torch.randn(3,4 )
y= torch.randn(3,4 )
# 损失函数对应类别的权重
print(x,y)
w= torch.tensor([10,2 ,15,20],dtype=torch.float)
# 测试不同的损失函数
criterion_BCE= nn.BCEWithLogitsLoss(w)
criterion_mult= WeightedMultilabel(w)
criterion_mult2= nn.MultiLabelSoftMarginLoss(w)
loss1= criterion_BCE(x,y )
loss2= criterion_mult(x,y )
loss3= criterion_mult2(x,y )
print(loss1)
print(loss2)
print(loss3)