1、官方数据中的NLLLoss
前提:输出层的激活函数为softmax(存在问题)
分类问题可以使用MSE作为损失函数,但Pytorch官方提供了一种NLL(negative log likelihood)损失函数:
在上式中out为输出层的输出,假设为[[0.3, 0.7], #真实类别为1
[0.8, 0.2], #真实类别为0
[0.4, 0.6]];#真实类别为0
c表示为class_index 真实的类别编号0或1;N为batch_size大小(猜测),此处为3
out[c] = [[0.7],
[0.8],
[0.4]] 带入NLL计算公式即可得到损失NLL Loss = -( log(0.7) + log(0.8) + log(0.4) ) =1.4961
1.1 为什么要使用这个函数呢?
官方解释为:在损失计算中我们最关注的是预测为真实y的概率值,而并不需要关注其他类别的概率,即若类别为编号1,那我们只需要关注out[ 1 ]= 0.7这个值。在训练过程中,我们期望在预测正确的概率非常低的时候,Loss尽可能的大;在预测正确的概率非常高的时候,Loss尽可能的小。下图为NLL损失函数图像:
当使用softmax作为输出层的激活函数时,可能会出现目标标签所得概率接近于0的情况,这将会使得计算log非常大,所以我们 将采用nn.LogSoftmax(dim=1) 作为输出层的激活函数,使得计算出来的数值较为稳定 。NNL通过调用 nn.NLLLoss 类进行损失计算 。
1.2 对比交叉熵损失和MSE损失
由下图可以看出MSE在预测结果和目标偏移较多时,其梯度等于或接近于0非常不利于优化。而交叉熵损失却在异常处存在一定的梯度。最根本的原因是MSE的斜率太低、梯度太小无法补偿softmax错误预测的平坦度。所以MSE非常不适合做分类工作。
原文如下:
Ending our investigation of losses, we can look at how using cross-entropy loss improves over MSE. In figure 7.11(下图), we see that the cross-entropy loss has some slope when the prediction is off target (in the low-loss corner, the correct class is assigned a predicted probability of 99.97%), while the MSE we dismissed at the beginning saturates much earlier and—crucially—also for very wrong predictions. The underlying reason is that the slope of the MSE is too low to compensate for the flatness of the softmax function for wrong predictions. This is why the MSE for probabilities is not a good fit for classification work.
2、框架中的NLLLoss
2.0 来源
Pytorch官网文档:NLLLoss
2.1 公式
方法:torch.nn.NLLLoss(weight: Optional[torch.Tensor] = None, size_average=None, ignore_index: int = -100, reduce=None, reduction: str = 'mean')
reduction : str = 'mean'(默认参数)
其中:N为batch size,tag为真实类别为单个数字,w是输入每个类别的权重(针对分布不均匀的数据,默认全为1,比如二分类问题,类1的数据比类0占比大一倍,则可给类1施加 0.5权重),x为网络输入层输出out[tag]
reduction : str = 'sum'
reduction : str = 'none'
2.2 作用
可单独做一个损失函数或者别的。
3 问题发现的经过
使用ArcLoss追加Softmax输出函数,我使用的方法是torch.nn.Softmax(dim=1) + torch.nn.NLLLoss() 算出来的loss为负值,后调整使用第一个公式:
out_index = tag.data # 获取标签tag
softmax_out = torch.index_select(out.data[0], dim=0, index=out_index) #筛取out[tag]
NLL_out = -torch.sum(torch.log(softmax_out)) / batch_size
输出的NLL_out 结果为正,但只是查看了一下,未进行网络训练。
然后改为torch.nn.LogSoftmax(dim=1) + torch.nn.NLLLoss() 进行训练,以为是torch.nn.CrossEntropyLoss(),但其实不是。
最后,查看官网的文档,发现和Pytorch官方推出的Deep-learning-with-Pytorch书中写的不一样。