交叉熵和torch.nn.CrossEntropyLoss() 学习笔记


前言

分开定义softmax运算和交叉熵损失函数可能会造成数值不稳定。因此,PyTorch提供了一个包括softmax运算和交叉熵损失计算的函数。它的数值稳定性更好。


一、什么是交叉熵?

交叉熵主要是用来判定实际的输出期望的输出的接近程度。
为什么这么说呢,举个例子:在做分类的训练的时候,如果一个样本属于第K类,那么这个类别所对应的的输出节点的输出值应该为1,而其他节点的输出都为0,即[0,0,1,0,….0,0],这个数组也就是样本的Label,是神经网络最期望的输出结果。也就是说用它来衡量网络的输出与标签的差异,利用这种差异经过反向传播去更新网络参数。

交叉熵原理

在说交叉熵之前,先说一下信息量

信息量:它是用来衡量一个事件的不确定性的;一个事件发生的概率越大,不确定性越小,则它所携带的信息量就越小。假设X是一个离散型随机变量,其取值集合为X,概率分布函数为
p ( x ) = P ( X = x ) , x ∈ X p(x) = P(X=x), x∈X p(x)=P(X=x),xX ,我们定义事件 X = x 0 X=x_0 X=x0的信息量为: I ( x 0 ) = − l o g ⁡ ( p ( x 0 ) ) I(x_0 )= -log⁡(p(x_0 )) I(x0)=log(p(x0)) p ( x 0 ) = 1 p(x_0) = 1 p(x0)=1时,熵将等于0,也就是说该事件的发生不会导致任何信息量的增加。

:它是用来衡量一个系统的混乱程度的,代表一个系统中信息量的总和;信息量总和越大,表明这个系统不确定性就越大。

举个例子:假如小明和小王去打靶,那么打靶结果其实是一个0-1分布,X的取值有{0:打中,1:打不中}。在打靶之前我们知道小明和小王打中的先验概率为10%,99.9%。根据上面的信息量的介绍,我们可以分别得到小明和小王打靶打中的信息量。但是如果我们想进一步度量小明打靶结果的不确定度,这就需要用到熵的概念了。那么如何度量呢,那就要采用期望了。我们对所有可能事件所带来的信息量求期望,其结果就能衡量小明打靶的不确定度:
H A ( x ) = − [ p ( x A ) l o g ( p ( x A ) ) + ( 1 − p ( x A ) ) l o g ( 1 − p ( x A ) ) ] = 0.4690 H_A (x)=-[p(x_A )log(p(x_A ))+(1-p(x_A ))log(1-p(x_A ))]=0.4690 HA(x)=[p(xA)log(p(xA))+(1p(xA))log(1p(xA))]=0.4690
与之对应的,小王的熵(打靶的不确定度)为:

H B ( x ) = − [ p ( x B ) l o g ( p ( x B ) ) + ( 1 − p ( x B ) ) l o g ( 1 − p ( x B ) ) ] = 0.0114 H_B (x)=-[p(x_B )log(p(x_B ))+(1-p(x_B ))log(1-p(x_B ))]=0.0114 HB(x)=[p(xB)log(p(xB))+(1p(xB))log(1p(xB))]=0.0114

小明打靶结果的不确定度为0.4690;小王打靶结果的不确定度为0.0114。不确定度越低,结果越确定。

交叉熵:它主要刻画的是实际输出(概率)与期望输出(概率)的距离,也就是交叉熵的值越小,两个概率分布就越接近。假设概率分布 p ( x ) p(x) p(x)为期望输出,概率分布 q ( x ) q(x) q(x)为实际输出, H ( p , q ) H(p,q) H(p,q) 为交叉熵,则
H ( p , q ) = − ∑ x [ p ( x ) l o g q ( x ) + ( 1 − p ( x ) ) l o g ( 1 − q ( x ) ) ] H(p,q)=-∑_x[p(x)logq(x)+(1-p(x))log(1-q(x))] H(p,q)=x[p(x)logq(x)+(1p(x))log(1q(x))]
那么该公式如何表示,举个例子,假设N=3,期望输出为 p = ( 1 , 0 , 0 ) p=(1,0,0) p=(1,0,0),实际输出 q 1 = ( 0.5 , 0.2 , 0.3 ) q_1=(0.5,0.2,0.3) q1=(0.5,0.2,0.3) q 2 = ( 0.8 , 0.1 , 0.1 ) q_2=(0.8,0.1,0.1) q2=(0.8,0.1,0.1)
那么:
H ( p , q 1 ) = − ( 1 l o g 0.5 + 0 l o g 0.2 + 0 l o g 0.3 + 0 l o g 0.5 + 1 l o g 0.8 + 1 l o g 0.7 ) = 0.55 H(p,q_1)=-(1log0.5+0log0.2+0log0.3+0log0.5+1log0.8+1log0.7)=0.55 H(p,q1)=(1log0.5+0log0.2+0log0.3+0log0.5+1log0.8+1log0.7)=0.55
H ( p , q 2 ) = − ( 1 l o g 0.8 + 0 l o g 0.1 + 0 l o g 0.1 + 0 l o g 0.2 + 1 l o g 0.9 + 1 l o g 0.9 ) = 0.19 H(p,q_2)=-(1log0.8+0log0.1+0log0.1+0log0.2+1log0.9+1log0.9)=0.19 H(p,q2)=(1log0.8+0log0.1+0log0.1+0log0.2+1log0.9+1log0.9)=0.19
通过上面可以看出, q 2 q_2 q2 p p p更为接近,它的交叉熵也更小。

二、Pytorch中的CrossEntropyLoss()函数

提前说一下,CrossEntropy()的作用和先进行LogSoftmax()再进行NLLLoss()是一样的,两者区别之处文章开头已经介绍。

1.Softmax()

将Softmax函数应用于一个n维输入张量,对其进行缩放,使n维输出张量的元素位于[0,1]范围内,总和为1。
Softmax定义为:
S o f t m a x ( x j )   =   e x p ( x i ) Σ j e x p ( x j ) Softmax(x_j)\ =\ \frac{exp(x_i)}{\Sigma_jexp(x_j)} Softmax(xj) = Σjexp(xj)exp(xi)

测试代码如下:

import torch
import torch.nn as nn

x_input=torch.Tensor([[1,2,3]]) 
print('x_input:\n',x_input) 

#计算输入softmax,此时可以看到每一行加到一起结果都是1
softmax_func=nn.Softmax(dim=1)
soft_output=softmax_func(x_input)
print('soft_output:\n',soft_output)

输出:

x_input:
 tensor([[1., 2., 3.]])
soft_output:
 tensor([[0.0900, 0.2447, 0.6652]])

2.LogSoftmax()

LogSoftmax是对Softmax取自然对数。
LogSoftmax定义为:
L o g S o f t m a x ( x j )   =   l o g ( e x p ( x i ) Σ j e x p ( x j ) ) LogSoftmax(x_j)\ =\ log\left(\frac{exp(x_i)}{\Sigma_jexp(x_j)}\right) LogSoftmax(xj) = log(Σjexp(xj)exp(xi))

测试代码如下:

#在softmax的基础上取log
log_output=torch.log(soft_output)
print('log_output:\n',log_output)

#对比softmax与log的结合与nn.LogSoftmaxloss(负对数似然损失)的输出结果,发现两者是一致的。
logsoftmax_func=nn.LogSoftmax(dim=1)
logsoftmax_output=logsoftmax_func(x_input)
print('logsoftmax_output:\n',logsoftmax_output)

输出:

log_output:
 tensor([[-2.4076, -1.4076, -0.4076]])
logsoftmax_output:
 tensor([[-2.4076, -1.4076, -0.4076]])

2.NLLLoss()

在这里插入图片描述
测试代码如下:

target = torch.Tensor([2]).long()
print('target:\n',target)

#pytorch中关于NLLLoss的默认参数配置为:reducetion=True、size_average=True
nllloss_func=nn.NLLLoss()
nlloss_output=nllloss_func(logsoftmax_output,target)
print('nlloss_output:\n',nlloss_output)

输出:

target:
 tensor([2])
nlloss_output:
 tensor(0.4076)

nn.NLLLoss()做的事情是取出logsoftmax_output中对应target位置的值并取负号,如target=0,就取logsoftmax_outputindex=0位置上的值再取负号为-1。

4.CrossEntropy()

在这里插入图片描述

#直接使用pytorch中的loss_func=nn.CrossEntropyLoss()看与经过NLLLoss的计算是不是一样
crossentropyloss=nn.CrossEntropyLoss()
crossentropyloss_output=crossentropyloss(x_input,target)
print('crossentropyloss_output:\n',crossentropyloss_output)
crossentropyloss_output:
 tensor(0.4076)

可见,CrossEntropy()输出结果和进行LogSoftmax()+NLLLoss()是一样的。


参考资料

[1]交叉熵解释
[2]代码分析

猜你喜欢

转载自blog.csdn.net/qq_27839923/article/details/122554692
今日推荐