pytorch中多分类问题中最常用的损失函数应该就是CrossEntropyLoss了,传闻该函数结合了LogSoftmax和NLLLoss两个函数,那么这个函数到底是什么来头呢?本文来一探究竟。
交叉熵的定义
交叉熵刻画的是实际输出的分布与期望分布的距离:如果模型输出的结果的分布和期望的分布越相似,那么交叉熵就越小。交叉熵的定义公式为,其中表示的维度;更一般地,假设为期望分布,为输出的分布,则和的交叉熵就是:。
举个栗子,假设在一个三分类问题中,一条数据的真实标签为,而模型A的预测概率为,模型B的预测概率为那么模型A的输出对应的交叉熵为,而模型B的输出的交叉熵为,可见即使两个模型都能够预测正确,但是模型A的交叉熵更小,而更小的交叉熵也意味着模型A的预测概率分布与真实的概率分布更相似。
Pytorch中的CrossEntropyLoss
根据定义,Pytorch中的CrossEntropyLoss是将Softmax-log-NLLoss合并到一块的结果,也就是说CrossEntropyLoss就是我们前面所讨论的交叉熵,为什么这么说呢?看下面的推导:
假设一个10分类的神经网络,那么在输出层就应该有10个节点。在Pytorch中如果使用CrossEntropyLoss,这10个节点的后面将不再需要softmax,因为CrossEntropyLoss已经包含了softmax这一部分。softmax的公式为,其能够将十个节点的输出进行处理使得,也就是对10个节点的输出做了一个变换,且使得其大小关系不变。
在得到softmax的结果之后再将概率值取log,并且执行后面剩下的求和计算。也就是说NLLLOSS的结果就是把取log得到的结果和真实的标签相乘并求和,之后再求均值并取反(求当前mini-batch的均值)。
结合Pytorch文档测试
来看看Pytorch文档中对CrossEntropyLoss的定义:
运行下面的代码:
import torch
import torch.nn as nn
import numpy as np
loss = nn.CrossEntropyLoss()
x_input = torch.randn(3, 3)
y = torch.tensor([1,2,0])
print('x_input = ', x_input)
print('y = ', y)
softmax_func = nn.Softmax(dim=1)
softmax_output = softmax_func(x_input)
print('softmax_output = ', softmax_output)
log_output = torch.log(softmax_output)
print('log_output = ', log_output)
nllloss = nn.NLLLoss()
nllloss_output = nllloss(log_output, y)
print('nlloss_output = ', nllloss_output)
print('crossentropyloss = ', loss(x_input, y))
得到输出为:
x_input = tensor([[ 0.1650, 1.7421, 0.1270],
[-0.1254, -0.7676, -0.1373],
[ 1.3294, -0.0838, -1.2392]])
y = tensor([1, 2, 0])
softmax_output = tensor([[0.1470, 0.7115, 0.1415],
[0.3977, 0.2093, 0.3930],
[0.7576, 0.1844, 0.0581]])
log_output = tensor([[-1.9174, -0.3404, -1.9554],
[-0.9220, -1.5642, -0.9339],
[-0.2777, -1.6908, -2.8462]])
nlloss_output = tensor(0.5173)
crossentropyloss = tensor(0.5173)
可以看到使用softmax-log-nllloss得到的结果和直接使用CrossEntropyLoss得到的结果是完全一样的。
再去看一下NLLLoss损失函数的官方定义:
参考资料: