CrossEntropyLoss
公式
J l o s s = − ∑ i = 1 N ∑ k = 1 K y i k ∗ l o g ( h θ ( x i ) k ) J_{loss} = -\sum_{i = 1}^{N} \sum_{k = 1}^{K} y_{ik}*log(h_{\theta}(x_{i})_{k}) Jloss=−∑i=1N∑k=1Kyik∗log(hθ(xi)k)
h θ ( x i ) j = e x p ( x i ) j ∑ k = 1 K e x p ( x i ) k h_{\theta}(x_{i})_{j} = \frac{exp(x_{i})_{j}}{\sum_{k = 1}^{K}exp(x_{i})_{k}} hθ(xi)j=∑k=1Kexp(xi)kexp(xi)j
其中
- N N N : 样本点的数量
- K K K : 类别的数量
- y i c y_{ic} yic : 样本目标的 o n e − h o t one-hot one−hot 编码。如果样本 x i x_{i} xi 的真实类别等于 k k k 取1,否则取0。
- h θ ( x i ) k h_{\theta}(x_{i})_{k} hθ(xi)k : 观测样本 x i x_{i} xi 属于类别 k k k 的预测概率。
样例
下面以一个图片分类问题为例,理解上面的公式。这个例子,根据图片中动物的轮廓、颜色等特征,来预测动物的类别(猫、狗、马)。
类别名称 | 类别编号 | one-hot |
---|---|---|
猫 | 0 | [1, 0, 0] |
狗 | 1 | [0, 1, 0] |
马 | 2 | [0, 0, 1] |
有一张马的图片,它的真实类别编号为 2, one-hot 编码为 [ 0 , 0 , 1 ] [0, 0, 1] [0,0,1] 。假定模型的预测概率为 [ 0.1 , 0.12 , 0.78 ] [0.1, 0.12, 0.78] [0.1,0.12,0.78],如下为计算交叉熵的过程。
类别名称 | 预测概率 | 类别的one-hot | 符号函数值 yicyic | 单项交叉熵计算过程 | 单项交叉熵结果 |
---|---|---|---|---|---|
猫 | 0.1 | [1, 0, 0] | 0 | -0 * log(0.1) | 0 |
狗 | 0.12 | [0, 1, 0] | 0 | -0 * log(0.12) | 0 |
马 | 0.78 | [0, 0, 1] | 1 | -1 * log(0.78) | 0.2485 |
总的交叉熵为0.2485。
Pytorch 中的CrossEntropy的形式
pytorch中torch.nn模块和torch.nn.functional中均有对应的交叉熵的模块,但是两者是相通的。不过torch.nn.functional中可以理解为是一个接口,而torch.nn是具体类的定义。
- torch.nn.functional.cross_entropy
def cross_entropy(input, target, weight=None, size_average=None, ignore_index=-100,
reduce=None, reduction='mean'):
# type: (Tensor, Tensor, Optional[Tensor], Optional[bool], int, Optional[bool], str) -> Tensor
if size_average is not None or reduce is not None:
reduction = _Reduction.legacy_get_string(size_average, reduce)
return nll_loss(log_softmax(input, 1), target, weight, None, ignore_index, None, reduction)
看上面代码也能知道input和target是必选项,并且是Tensor类型的。最后一行说明functional.cross_entropy实际计算过程就是先计算Tensor的log_softmax,然后再计算nll_loss。
那么问题来了,log_softmax是怎么计算的,干了些什么,用上面表格的数据来举例来说:
Tensor的log_softmax函数和functional的函数作用一样,都是先对数据进行softmax,然后进行log函数,这里的log以e为底,即ln。log_softmax和softmax中的数字表示按照什么维度计算。0代表按列计算,softmax函数计算后的数据按列加起来为1;1代表按行计算,softmax函数计算后的数据按行加起来为1。
import torch.nn.functional as F
import torch
truth = torch.tensor([[1, 0, 0]], dtype=torch.float)
predicted1 = torch.tensor([[0.5, 0.4, 0.1]], dtype=torch.float)
print(truth.softmax(0))
print(truth.softmax(1))
print(F.log_softmax(predicted1, 1))
print(truth.log_softmax(-1))
上面代码的输出结果为:
tensor([[1., 1., 1.]])
tensor([[0.5017, 0.2491, 0.2491]])
tensor([[-0.9459, -1.0459, -1.3459]])
tensor([[-0.6897, -1.3897, -1.3897]])
第一行的输出结果可以理解,按照列计算只有一个数据,所以计算后每列的只有1个1。
第二行的输出结果可以通过计算得到:
e 1 e 1 + e 0 + e 0 = e e + 2 = 0.5017 \frac{e^{1}}{e^{1}+e^{0}+e^{0}}=\frac{e}{e+2}=0.5017 e1+e0+e