[损失函数]——交叉熵

在了解交叉熵之前我们需要关于熵的一些基本知识,可以参考我的上一篇博客[1]

1.信息熵

信息熵的定义为离散随机事件的出现概率[2]。当一个事件出现的概率更高的时候,我们认为该事件会传播的更广,因此可以使用信息熵来衡量信息的价值。
当一个信源具有多种不同的结果,记为:U1,U2,...,Un,每个事件相互独立,对应的概率记为:P1,P2,...,Pn。信息熵为各个事件方式概率的期望,公式为:
H(U)=E[-\log p_{i}]=-\sum_{i=1}^{n}p_{i}\log p_{i}

对于二分类问题,当一种事件发生的概率为p时,另一种事件发生的概率就为(1-p),因此,对于二分类问题的信息熵计算公式为:


2.相对熵(KL散度)

相对熵(relative entropy),又被称为Kullback-Leibler散度(Kullback-leibler divergence),是两个概率分布间差异的一种度量[3]。在信息论中,相对熵等于两个概率分布的信息熵的差值。
相对熵的计算公式为:
\begin{align} \text{KL}(P||Q) & = \sum_{i = 1}^{n} [p(x_{i})\log p(x_{i})-p(x_{i})\log q(x_{i})] \\ & = \sum_{i = 1}^{n}p(x_{i})\log \frac{p(x_{i})}{q(x_{i})} \end{align}
其中p(x)代表事件的真实概率,q(x)代表事件的预测概率。例如三分类问题的标签为(1,0,0),预测标签为(0.7,0.1,0.2)

因此该公式的字面上含义就是真实事件的信息熵与理论拟合的事件的香农信息量与真实事件的概率的乘积的差的累加。[4]

当p(x)和q(x)相等时相对熵为0,其它情况下大于0。证明如下:


KL散度在Pytorch中的使用方法为:

torch.nn.KLDivLoss(size_average=None, reduce=None, reduction='mean', log_target=False)

在使用过程中,reduction一般设置为batchmean这样才符合数学公式而不是mean,在以后的版本中mean会被替换掉。
此外,还要注意log_target参数,因为在计算的过程中我们往往使用的是log softmax函数而不是softmax函数来避免underflow和overflow问题,因此我们要提前了解target是否经过了log运算。

torch.nn.KLDivLoss()会传入两个参数(input, target), input是模型的预测输出,target是样本的观测标签。

kl_loss = nn.KLDivLoss(reduction="batchmean")
output = kl_loss(input, target)

下面我们用一个例子来看看torch.nn.KLDivLoss()是如何使用的:

import torch
import torch.nn as nn
import torch.nn.functional as F

input = torch.randn(3, 5, requires_grad=True)
input = F.log_softmax(input, dim=1)  # dim=1 每一行为一个样本

target = torch.rand(3,5)

# target使用softmax
kl_loss = nn.KLDivLoss(reduction="batchmean", log_target=False)
output = kl_loss(input, F.softmax(target, dim=1))
print(output)

# target使用log_softmax
kl_loss_log = nn.KLDivLoss(reduction="batchmean", log_target=True)
output = kl_loss_log(input, F.log_softmax(target, dim=1))
print(output)

输出结果如下:

tensor(0.3026, grad_fn=<DivBackward0>)
tensor(0.3026, grad_fn=<DivBackward0>)

3.交叉熵

相对熵可以写成如下形式:
D_{KL}(p||q)=\sum_{i=1}^{n}p(x_{i})\log p(x_{i})-\sum_{i=1}^{n}p(x_{i})\log q(x_{i})=-H(p(x)) +[-\sum_{i=1}^{n}p(x_{i})\log q(x_{i})]
等式的前一项为真实事件的熵,后一部分为交叉熵[4]
H(p,q)=-\sum_{i=1}^{n}p(x_{i})\log q(x_{i})
在机器学习中,使用KL散度就可以评价真实标签与预测标签间的差异,但由于KL散度的第一项是个定值,故在优化过程中只关注交叉熵就可以了。一般大多数机器学习算法会选择交叉熵作为损失函数。

交叉熵在pytorch中可以调用如下函数实现:

torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')

其计算方法如下所示[5]
假设batch size为4,待分类标签有3个,隐藏层的输出为:

input = torch.tensor([[ 0.8082,  1.3686, -0.6107],
                      [ 1.2787,  0.1579,  0.6178],
                      [-0.6033, -1.1306,  0.0672],
                      [-0.7814,  0.1185, -0.2945]])

经过softmax激活函数之后得到预测值:

output = nn.Softmax(dim=1)(input)
output:
tensor([[0.3341, 0.5851, 0.0808],
        [0.5428, 0.1770, 0.2803],
        [0.2821, 0.1665, 0.5515],
        [0.1966, 0.4835, 0.3199]])

softmax函数的输出结果每一行相加为1。
假设这一个mini batch的标签为

[1,0,2,1]

根据交叉熵的公式:
H(p,q)=-\sum_{i=1}^{n}p(x_{i})\log q(x_{i})
p(x_{i})代表真实标签,在真实标签中,除了对应类别其它类别的概率都为0,实际上,交叉熵可以简写为:
H(p,q)=-\log q(x_{class})
所以该mini batch的loss的计算公式为(别忘了除以batch size,我们最后求得的是mini batch的平均loss):
Loss = - [log(0.5851) + log(0.5428) + log(0.5515) + log(0.4835)] / 4
因此,我们还需要计算一次对数:

output_log = torch.log(output)
output_log

计算结果为:

tensor([[-1.0964, -0.5360, -2.5153],
        [-0.6111, -1.7319, -1.2720],
        [-1.2657, -1.7930, -0.5952],
        [-1.6266, -0.7267, -1.1397]])

根据交叉熵的计算公式,loss的最终计算等式为:
loss = - (-0.5360 - 0.6111 - 0.5952 - 0.7267) / 4 = 0.61725
运算结果和pytorch内置的交叉熵函数相同:


import torch
import torch.nn as nn

input = torch.tensor([[ 0.8082,  1.3686, -0.6107],
        [ 1.2787,  0.1579,  0.6178],
        [-0.6033, -1.1306,  0.0672],
        [-0.7814,  0.1185, -0.2945]])
target = torch.tensor([1,0,2,1])

loss = nn.CrossEntropyLoss()
output = loss(input, target)
output.backward()

结果为:

tensor(0.6172)

除了torch.nn.CrosEntropyLoss()函数外还有一个计算交叉熵的函数torch.nn.BCELoss()。与前者不同,该函数是用来计算二项分布(0-1分布)的交叉熵,因此输出层只有一个神经元(只能输出0或者1)。其公式为:
loss = -[y·logx+(1-y)·log(1-x)]
在pytorch中的函数为:

torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')

用一个实例来看看如何使用该函数:

input = torch.tensor([-0.7001, -0.7231, -0.2049])
target = torch.tensor([0,0,1]).float()
m = nn.Sigmoid()
loss = nn.BCELoss()
output = loss(m(input), target)
output.backward()

输出结果为:

tensor([0.5332])

它是如何计算的呢,我们接下来一步步分析:

首先输入是:

input = [-0.7001, -0.7231, -0.2049]

需要经过sigmoid函数得到一个输出

output_mid = m(input)

输出结果为:

[0.3318, 0.3267, 0.4490]

然后我们根据二项分布交叉熵的公式:
loss = -[y·logx+(1-y)·log(1-x)]
得到loss的如下计算公式:

loss = - [1*\log (1-0.3318) + 1*\log (1-0.3267) + 1*\log (0.4490)]/3=0.5312

和pytorch的内置函数计算结果相同。
另外,需要注意的是,当使用交叉熵作为损失函数的时候,标签不能为onehot形式,只能是一维的向量,例如,当batch size是5时,这一个batch的标签只能时[0,1,4,2,6]这样的形式。


  1. 什么是熵,如何计算?

  2. 百度百科-信息熵

  3. 百度百科-相对熵

  4. 一文搞懂交叉熵在机器学习中的使用,透彻理解交叉熵背后的直觉

  5. NLL_Loss & CrossEntropyLoss(交叉熵)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容