KL散度、交叉熵通常被用来衡量两个分布之间的差异程度,可以用于衡量模型的“损失”。
要深入理解KL散度和交叉熵,首先要从信息论中的基础概念入手。
假如有两条消息,一条内容是“今天太阳从东边升起”,另一条内容是“今天有日食”。
显然后一条消息的发生概率低,消息内容也更有信息量。
信息论的目标之一就是用最少的编码来传递信息。概率越低的事件,对应更高的信息量,概率越高的事件,对应更低的信息量。
所以在信息论中,把发生随机事件 X = x X=x X=x时的信息量进行量化,定义为自信息1:
I ( x ) = l o g ( 1 P ( x ) ) = − l o g ( P ( x ) ) I(x) = log(\frac{1}{P(x)})=-log(P(x)) I(x)=log(P(x)1)=−log(P(x))
如果log的底数时e, I ( x ) I(x) I(x)单位是奈特(nats);
如果底数是2,则 I ( x ) I(x) I(x)的单位是比特(bit)或香农(shannons)。
自信息反映的是单个随机事件的信息量,如果要反映整个随机变量分布的信息量,可以用数学期望来衡量这个分布的信息总量,这就是香农熵,又称为信息熵:
H ( x ) = E x ~ P [ I ( x ) ) ] = − E x ~ P [ l o g P ( x ) ] H(x)=\mathbb{E}_{\bold{x} \text{\textasciitilde} P}[I(x))]=-\mathbb{E}_{\bold{x} \text{\textasciitilde} P}[logP(x)] H(x)=Ex~P[I(x))]=−Ex~P[logP(x)]
信息熵给出了对分布为P的符号进行编码时的最优平均编码长度2。单位与自信息相同。
KL散度也叫KL距离或相对熵(Relative Entropy),用来表达以分布Q来近似分布P时的信息损失量。
D K L ( P , Q ) = E x ~ P ( l o g P ( x ) − l o g Q ( x ) ) D_{KL}(P,Q)=\mathbb{E}_{\bold{x} \text{\textasciitilde} P}{(log {P(x)}-log{Q(x)})} DKL(P,Q)=Ex~P(logP(x)−logQ(x))
在机器学习中,通常用KL散度来衡量训练数据上的经验分布 P ^ d a t a \hat{P}_{data} P^data和模型分布 P m o d e l P_{model} Pmodel之间的差异1。
D K L ( P ^ d a t a ∣ ∣ P m o d e l ) = E x ~ P ^ d a t a ( l o g P ^ d a t a ( x ) − l o g P m o d e l ( x ) ) D_{KL}(\hat{P}_{data}||P_{model})=\mathbb{E}_{\bold{x} \text{\textasciitilde} {\hat{P}_{data}}}{(log{\hat{P}_{data}{(\boldsymbol{x})}}-log{P_{model}{(\boldsymbol{x})}})} DKL(P^data∣∣Pmodel)=Ex~P^data(logP^data(x)−logPmodel(x))
交叉熵 H ( P , Q ) H(P,Q) H(P,Q)与KL散度相比,少一项 H ( P ) H(P) H(P):
H ( P , Q ) = − E x ~ P l o g Q ( x ) H(P,Q)=-\mathbb{E}_{\bold{x} \text{\textasciitilde} P}{log{Q(x)}} H(P,Q)=−Ex~PlogQ(x)
在给定分布P的情况下,如果分布Q和P越接近,交叉熵越小;如果分布Q和P越远,交叉熵就越大。
由于训练数据上的经验分布 P ^ d a t a \hat{P}_{data} P^data是不变的,所以最小化两个分布之间的差异性,就相当于最小化两个分布之间的交叉熵:
H ( P ^ d a t a , P m o d e l ) = − E x ~ P ^ d a t a l o g P m o d e l ( x ) ) H(\hat{P}_{data},P_{model})=-\mathbb{E}_{\bold{x} \text{\textasciitilde} {\hat{P}_{data}}}{log{P_{model}{(\boldsymbol{x})}})} H(P^data,Pmodel)=−Ex~P^datalogPmodel(x))
交叉熵可以衡量模型输出和标签之间的距离,所以机器学习、深度学习都常用交叉熵作为损失函数。
以分类任务为例,标签是 [ 0 , 0 , 1 ] T [0, 0, 1]^T [0,0,1]T,而模型的输出概率是 [ 0.3 , 0.3 , 0.4 ] T [0.3, 0.3, 0.4]^T [0.3,0.3,0.4]T,则交叉熵损失为 − ( 0 × l o g ( 0.3 ) + 0 × l o g ( 0.3 ) + 1 × l o g ( 0.4 ) ) = − l o g ( 0.4 ) -(0×log(0.3)+0×log(0.3)+1×log(0.4)) = -log(0.4) −(0×log(0.3)+0×log(0.3)+1×log(0.4))=−log(0.4)。2
为了减少模型输出和标签之间的距离,模型学习的准则就是找到使经验风险最小化的参数。
经验风险用训练集 D D D上的平均损失来表示:
R D ( θ ) = 1 N ∑ n = 1 N L ( y ( n ) , f ( x ( n ) ; θ ) ) = 1 N ∑ n = 1 N ( − l o g f y ( n ) ( x ( n ) ; θ ) ) R_D(θ) = \frac{1}{N}\sum_{\mathclap{n=1}}^NL(y^{(n)},f(\boldsymbol{x}^{(n)};\theta))=\frac{1}{N}\sum_{\mathclap{n=1}}^N(-log{f_{y^{(n)}}}(\boldsymbol{x}^{(n)};\theta)) RD(θ)=N1n=1∑NL(y(n),f(x(n);θ))=N1n=1∑N(−logfy(n)(x(n);θ))
pytorch中交叉熵损失对应的实现是torch.nn.CrossEntropyLoss3。
torch.nn.CrossEntropyLoss( weight=None , size_average=None , ignore_index=-100 , reduce=None , reduction=‘mean’ )
- 参数weight:(可选)是为每个类分配权重的一维张量,如果给定,必须是大小为C的张量,C表示类别数量(num of classes)。
前向计算:
(N, C)
,或(N, C, d_1, d_2, ..., d_K)
, K ≥ 1 K \geq 1 K≥1,C表示类别数量(num of classes),N表示minibatch(N)
或(N, d_1, d_2, ..., d_K)
,每个值满足: 0 ≤ targets [ i ] ≤ C − 1 0 \leq \text{targets}[i] \leq C-1 0≤targets[i]≤C−1(N)
,或(N, d_1, d_2, ..., d_K)
, K ≥ 1 K \geq 1 K≥1reduction
是'none'
,输出和target的形状相同。CrossEntropyLoss结合了LogSoftmax4 + NLLLoss5,通过在网络的最后一层添加LogSoftmax层,可以轻松获得神经网络中的对数概率 。如果不想添加额外的层,可以直接用CrossEntropyLoss。
以上一节的分类任务为例,标签是 [ 0 , 0 , 1 ] T [0, 0, 1]^T [0,0,1]T,而模型输出是 [ 0.3 , 0.3 , 0.4 ] T [0.3, 0.3, 0.4]^T [0.3,0.3,0.4]T,计算一下nn.CrossEntropyLoss的损失是多少:
import torch.nn as nn
criterion = nn.CrossEntropyLoss()
# 获取数据,前向计算,此处略... outputs = net(inputs)
outputs = torch.tensor([[0.3,0.3,0.4]])#shape:(N, C),C是类别数
labels = torch.tensor([1])#shape:(N)
loss = criterion(outputs, labels)
print(loss)
输出:0.9162907318741551,与前面计算的-log(0.4)的结果一致。
简单总结一下本文涉及的基本概念:
如果本文对你有帮助的话,欢迎一键三连支持下博主~
《深度学习》伊恩.古德费洛 ↩︎ ↩︎
《神经网络与深度学习》邱锡鹏 ↩︎ ↩︎
https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html#torch.nn.CrossEntropyLoss ↩︎
https://pytorch.org/docs/stable/generated/torch.nn.LogSoftmax.html#torch.nn.LogSoftmax ↩︎
https://pytorch.org/docs/stable/generated/torch.nn.NLLLoss.html#torch.nn.NLLLoss ↩︎