文章目录

  • 一、交叉熵公式推导
  • 二、 Pytorch 中交叉熵CrossEntropyLoss()
  • 参考资料


一、交叉熵公式推导

  分类问题中,交叉熵函数是比较常用也是比较基础的损失函数,原来就只是会用,但一直搞不懂他是怎么来的?为什么交叉熵能够表征真实样本标签和预测概率之间的差值?现在把这个概念好好学习一下。

  首先说起交叉熵,脑子里就会出现这个东西:

pytorch VAE kl散度_数组

  随后我们脑子里可能还会出现Sigmoid()这个函数:

pytorch VAE kl散度_神经网络_02

  那我们就先从sigmoid开始说起,我们知道sigmoid的作用其实是把前一层的输入映射到0~1这个区间上,可以认为上一层某个样本的输入数据越大,就代表这个样本标签属于1的概率就越大,反之,上一层某样本的输入数据越小,这个样本标签属于0的概率就越大,而且通过sigmoid函数的图像我们可以看出来,随着输入数值的增大,其对概率增大的作用效果是逐渐减弱的,反之同理,这就是非线性映射的一个好处,让模型对处于中间范围的输入数据更敏感。下面是sigmoid函数图:

  既然经过sigmoid之后的数据能表示样本所属某个标签的概率,那么举个例子,我们模型预测某个样本标签为1的概率是:

pytorch VAE kl散度_学习笔记_03

  那么,这个样本不为1的概率为:

pytorch VAE kl散度_数据_04

  极大似然的角度来看,就得到下式:

pytorch VAE kl散度_数组_05

  上式可以理解为,给定一个样本x,我们通过模型预测出其属于样本标签为y的概率,因为y是我们给的正确结果,所以我们当然希望上式越大越好。

  下一步我们要在 P(y|x) 的外面套上一层log函数,相当于进行了一次非线性的映射。log函数是不会改变单调性的,所以我们也希望 log( P( y | x ) ) 越大越好。

pytorch VAE kl散度_神经网络_06

  这样,就得到了我们一开始说的交叉熵的形式了,但是等一等,好像还差一个符号。

  因为一般来说我们相用上述公式做loss函数来使用,所以我们想要loss越小越好,这样符合我们的直观理解,所以我们只要 -log( P( y | x ) ) 就达到了我们的目的。

pytorch VAE kl散度_pytorch VAE kl散度_07

  上面是二分类问题的交叉熵,如果是有多分类,就对每个标签类别下的可能概率分别求相应的负log对数然后求和就好了:

pytorch VAE kl散度_学习笔记_08

二、 Pytorch 中交叉熵CrossEntropyLoss()

  下面要结合pytorch中的函数 CrossEntropyLoss() 来说一说具体怎么使用。

  举个小例子,假设我们有个一样本,他经过我们的神经网络后会输出一个5维的向量,分别代表这个样本分别属于这5种标签的数值(注意此时我们的5个数求和还并不等于1,需要先经过softmax处理,下面会说),我们还会从数据集中得到该样本的正确分类结果,下面我们要把经过神经网络的5维向量和正确的分类结果放到CrossEntropyLoss() 中,看看会发生什么:

import torch
import torch.nn as nn
import math

loss = nn.CrossEntropyLoss()
input = torch.randn(1,5, requires_grad=True)
target = torch.empty(1, dtype=torch.long).random_(5)

output = loss(input, target)

print("输入为5类:")
print(input)
print("要计算loss的类别:")
print(target)
print("要计算loss的结果:")
print(output)
输入为5类:
tensor([[ 1.2389,  0.3627,  0.1153, -0.2999, -1.8458]], requires_grad=True)
要计算loss的类别:
tensor([4])
要计算loss的结果:
tensor(3.7787, grad_fn=<NllLossBackward>)

pytorch VAE kl散度_pytorch VAE kl散度_09

pytorch VAE kl散度_神经网络_10

pytorch VAE kl散度_pytorch VAE kl散度_09的需要我们将输入的input向量进行softmax处理,softmax我就不多说了,应该比较简单,也是一种映射,使得input变成对应属于每个标签的概率值,对每个input[i]进行如下处理:

pytorch VAE kl散度_pytorch VAE kl散度_12

pytorch VAE kl散度_pytorch VAE kl散度_09.随后我们就可以把pytorch VAE kl散度_pytorch VAE kl散度_09带入公式了,下面我们还缺pytorch VAE kl散度_数据_15了,而奇怪的是我们输入的target是一个只有一个数的数组啊,而pytorch VAE kl散度_pytorch VAE kl散度_09是一个5维的向量,这什么情况?

  原来CrossEntropyLoss() 会把target变成ont-hot形式,我们现在例子的样本标签是[4](从0开始计算)。那么转换成one-hot编码就是[0,0,0,0,1],所以我们的最后也会变成一个5维的向量,并且不是该样本标签的数值为0,这样我们在计算交叉熵的时候只计算给定的那一项的sorce就好了,所以我们的公式最后变成了:

pytorch VAE kl散度_数组_17

  接下来,按照上面我们的推导来自己编写一下程序:

first = 0
for i in range(1):
    first -= input[i][target[i]]
second = 0
for i in range(1):
    for j in range(5):
        second += math.exp(input[i][j])

res = 0
res += first + math.log(second)
print("自己的计算结果:")
print(res)
自己的计算结果:
tensor(3.7787, grad_fn=<AddBackward0>)