(一文理解)信息量、熵、KL散度、交叉熵及CrossEntropyLoss

前言

本文首先重温了一些常见的对数函数运算法则,接着介绍了什么是信息量和熵,然后通过KL散度推出交叉熵损失函数的表达式,最后详解PyTorch常用的交叉熵损失函数CrossEntropyLoss()并进行代码实现。

对数函数运算法则

  • 乘法规则
    log ⁡ ( a ⋅ b ) = log ⁡ ( a ) + log ⁡ ( b ) \log(a \cdot b) = \log(a) + \log(b) log(ab)=log(a)+log(b)
  • 除法规则
    log ⁡ ( a b ) = log ⁡ ( a ) − log ⁡ ( b ) \log\left(\frac{a}{b}\right) = \log(a) - \log(b) log(ba)=log(a)log(b)
  • 幂次规则
    log ⁡ ( b a ) = a ⋅ log ⁡ ( b ) \log(b^a) = a \cdot \log(b) log(ba)=alog(b)
  • 对数的换底公式
    log ⁡ a ( N ) = log ⁡ b ( N ) log ⁡ b ( a ) = ln ⁡ ( N ) ln ⁡ ( a ) = lg ⁡ ( N ) lg ⁡ ( a ) \log_a(N) = \frac{\log_b(N)}{\log_b(a)} = \frac{\ln(N)}{\ln(a)} = \frac{\lg(N)}{\lg(a)} loga(N)=logb(a)logb(N)=ln(a)ln(N)=lg(a)lg(N)

自然对数 ln 是以常数 e 为底的对数,其中 e 是一个无理数,大约等于 2.71828。
对数 lg 是以常数 10 为底的对数。

信息量

  • 什么是信息量?
    信息量是对事件的不确定性的度量。

假设我们听到了两件事,分别如下:

事件A:巴西队进入了2018世界杯决赛圈。
事件B:中国队进入了2018世界杯决赛圈。


仅凭直觉来说,显而易见事件B的信息量比事件A的信息量要大。

究其原因,是因为事件A发生的概率很大,事件B发生的概率很小。

所以当越不可能的事件发生了,我们获取到的信息量就越大。越可能发生的事件发生了,我们获取到的信息量就越小。(事件发生的概率越小,信息量越大;事件发生的概率越大,信息量越小。)

具体而言,对于一个离散随机事件 x,其发生的概率为 p(x),则该事件所包含的信息量 l(x) 定义为:
l ( x ) = log ⁡ ( 1 p ( x ) ) = − log ⁡ ( p ( x ) ) l(x)= \log\left(\frac{1}{p(x)}\right) = -\log(p(x)) l(x)=log(p(x)1)=log(p(x))

在这里,对数函数可以采用任意底数,常见的有自然对数(以 e 为底)和常用对数(以 2为底)。选择不同的对数底数会影响信息量的单位,比如使用常用对数时,信息量的单位是比特(bit);使用自然对数时,单位是纳特(nat)。

  • 什么是熵?
    熵指的是随机变量的熵;熵是随机变量不确定度的度量。

设X是一个离散型随机变量,分布律为 p(x) = p(X = x) ,x ∈ X 为取值空间集合 ,则随机变量X的熵 H(X) 定义为: H ( X ) = − ∑ x ∈ X p ( x ) log ⁡ b p ( x ) \mathrm{H}(\mathrm{X})=-\sum_{\mathrm{x} \in \mathrm{X}} \mathrm{p}(\mathrm{x}) \log _{b} \mathrm{p}(\mathrm{x}) H(X)=xXp(x)logbp(x)

  • 熵的单位
    单位取决于定义用到对数的底。
    当b = 2,熵的单位是比特(bit);
    当b = e,熵的单位是纳特(nat)。

  • 例子

假设我们有一个离散随机变量 X,它有四个可能的结果:x1、x2、x3 和 x4,对应的概率分别为 0.1、0.2、0.3 和 0.4。我们要计算这个随机变量X的熵。

首先计算每个结果的信息量。根据信息量的定义,我们有:

I(x1) = -log(0.1)
I(x2) = -log(0.2)
I(x3) = -log(0.3)
I(x4) = -log(0.4)

然后,我们将这些信息量与对应的概率相乘,并将结果相加,得到随机变量 X 的熵:

H(X) = 0.1 * I(x1) + 0.2 * I(x2) + 0.3 * I(x3) + 0.4 * I(x4)

  • 信息量是事件的信息量,熵是随机变量的信息量;

相对熵(KL散度)

  • 什么是相对熵?
    相对熵又称KL散度,如果我们对于同一个随机变量 X 有两个单独的概率分布 P(x) 和 Q(x),我们可以使用 KL 散度(Kullback-Leibler (KL) divergence)来衡量这两个分布的差异。

  • KL散度的计算公式
    D K L ( p ∥ q ) = ∑ i = 1 n p ( x i ) log ⁡ ( p ( x i ) q ( x i ) ) D_{K L}(p \| q)=\sum_{i=1}^{n} p\left(x_{i}\right) \log \left(\frac{p\left(x_{i}\right)}{q\left(x_{i}\right)}\right) DKL(pq)=i=1np(xi)log(q(xi)p(xi))
    n代表的是概率分布的样本空间的大小,即随机变量可能的取值个数。
    D K L {D_{KL}} DKL的值越小,表示Q分布越接近P分布。

  • 在机器学习中的应用
    在机器学习中,P往往用来表示样本的真实分布,比如[1,0,0]表示当前样本属于第一类。Q用来表示模型所预测的分布,比如[0.7,0.2,0.1]。

交叉熵

  • 什么是交叉熵?
    交叉熵是一个衡量两个概率分布之间差异的很好的度量。

  • KL散度公式变形
    由于 log ⁡ ( q ( x i ) p ( x i ) ) = log ⁡ ( p ( x i ) ) − log ⁡ ( q ( x i ) ) \log \left(\frac{q(x_i)}{p(x_i)}\right) = \log(p(x_i)) - \log(q(x_i)) log(p(xi)q(xi))=log(p(xi))log(q(xi))
    我们对KL散度的公式进行变形,可以得到:
    D K L ( p ∥ q ) = ∑ i = 1 n p ( x i ) log ⁡ ( p ( x i ) ) − ∑ i = 1 n p ( x i ) log ⁡ ( q ( x i ) ) 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)) DKL(pq)=i=1np(xi)log(p(xi))i=1np(xi)log(q(xi))
    这里,第一个求和项是概率分布 p {p} p的熵(在前面加个“-”号)(entropy),记作 H ( p ) H(p) H(p)
    H ( p ) = ∑ i = 1 n p ( x i ) log ⁡ ( p ( x i ) ) = − H ( X ) H(p) = \sum_{i=1}^{n} p(x_i) \log(p(x_i))=-H(X) H(p)=i=1np(xi)log(p(xi))=H(X)
    第二个求和项则是 p {p} p 相对于 q {q} q 的交叉熵,记做 H ( p , q ) {H(p,q)} H(p,q)
    H ( p , q ) = − ∑ i = 1 n p ( x i ) log ⁡ ( q ( x i ) ) H(p, q) = -\sum_{i=1}^{n} p(x_i) \log(q(x_i)) H(p,q)=i=1np(xi)log(q(xi))
    因此,我们可以将KL散度重写为熵和交叉熵的差:
    D K L ( p ∥ q ) = H ( p ) − H ( p , q ) D_{KL}(p \| q) = H(p) - H(p, q) DKL(pq)=H(p)H(p,q)

  • 交叉熵损失函数
    由于 H ( p ) {H(p)} H(p),即熵,它描述了随机变量的不确定性。在机器学习中,训练数据的分布通常是固定的,因此 H ( p ) {H(p)} H(p)是一个常数,不影响模型的训练过程。我们的目标是使模型预测的概率分布 q {q} q尽可能接近真实分布
    p {p} p,就是最小化交叉熵 H ( p , q ) H(p, q) H(p,q),而不是去调整 H ( p ) {H(p)} H(p)
    所以我们把交叉熵损失函数定义为:
    H ( p , q ) = − ∑ i = 1 n p ( x i ) log ⁡ ( q ( x i ) ) H(p, q) = -\sum_{i=1}^{n} p(x_i) \log(q(x_i)) H(p,q)=i=1np(xi)log(q(xi))
    其中, n {n} n为类别数量, x i {x_i} xi代表第 i {i} i个类别的示例, p ( x i ) {p({x_i})} p(xi)是真实分布中属于第 i {i} i类的概率(对于二分类问题,如果示例属于正类,则 p ( x i ) = 1 {p({x_i})=1} p(xi)=1,否则 p ( x i ) = 0 {p({x_i})=0} p(xi)=0), q ( x i ) {q({x_i})} q(xi)是模型预测的第 i {i} i类的概率。

    通常情况下,真实标签label( p ( x i ) {p({x_i})} p(xi))是一个 1 ∗ n {1*n} 1n的向量,里面有 n {n} n个值,并且只有一个值是1,其他 n − 1 {n-1} n1个值都是0。(即真实标签对应位置的那个值是1,其他都是0),所以这个公式有一个更简单的形式:
    H ( p , q ) = − l o g ( q ( x i ) ) H(p, q) = -log(q(x_i)) H(p,q)=log(q(xi))
    由于真实标签label( p ( x i ) {p({x_i})} p(xi))中有且只有一个值是1,我们只需要计算真实值对应的概率熵值就可以了。

CrossEntropyLoss

  • 参考文章

PyTorch常用的交叉熵损失函数CrossEntropyLoss()详解

  • CrossEntropyLoss
    CrossEntropyLoss是PyTorch中一个常用的损失函数,主要用于处理多分类问题。它结合了Softmax函数、对数损失(Log Loss)和负对数似然损失(Negative Log Likelihood Loss),使得模型能够学习到更精确的概率分布,从而提高分类性能。
  • 具体来说
    Pytorch中CrossEntropyLoss()函数的主要是将softmax-log-NLLLoss合并到一块得到的结果。
    1、Softmax后的数值都在0~1之间,所以 ln 之后值域是负无穷到0。
    2、然后将Softmax之后的结果取log,将乘法改成加法减少计算量,同时保障函数的单调性 。
    3、NLLLoss 的结果就是把上面的输出与 Label 对应的那个值拿出来,去掉负号,再求均值。
  • 代码示例
    一个实例:直接使用pytorch中的 nn.CrossEntropyLoss() 计算得到的结果与 softmax-log-NLLLoss计算得到的结果是一致的。
import torch
import torch.nn as nn

x_input = torch.randn(3, 3)  # 随机生成输入
print('x_input:\n', x_input)
y_target = torch.tensor([1, 2, 0])  # 设置输出具体值 print('y_target\n',y_target)

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

# 在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)

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

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

代码运行结果:

x_input:
 tensor([[ 0.3321,  0.5043, -1.1833],
        [-2.1702, -1.8563,  1.0706],
        [ 1.7341,  0.2554, -1.3358]])
soft_output:
 tensor([[0.4153, 0.4934, 0.0913],
        [0.0358, 0.0490, 0.9152],
        [0.7847, 0.1789, 0.0364]])
log_output:
 tensor([[-0.8787, -0.7064, -2.3940],
        [-3.3295, -3.0155, -0.0886],
        [-0.2424, -1.7212, -3.3123]])
logsoftmax_output:
 tensor([[-0.8787, -0.7064, -2.3940],
        [-3.3295, -3.0155, -0.0886],
        [-0.2424, -1.7212, -3.3123]])
nlloss_output:
 tensor(0.3458)
crossentropyloss_output:
 tensor(0.3458)

binary_cross_entropy

  • 说明
    binary_cross_entropy 是二分类的交叉熵,实际是多分类 CrossEntropyLoss 的一种特殊情况,当多分类中,类别只有两类时,即 0 或者 1,即为二分类,二分类也是一个逻辑回归问题,也可以套用逻辑回归的损失函数。
  • 使用
    在PyTorch中,可以通过torch.nn.functional模块中的binary_cross_entropy函数来实现这一损失函数。
  • 代码示例
import torch
import torch.nn as nn
input = torch.rand(1, 3, 3)
target = torch.rand(1, 3, 3).random_(2)
print(input)
print(target)
input = torch.sigmoid(input)
output = torch.nn.functional.binary_cross_entropy(input, target)
print(output)

代码运行效果:

# input
tensor([[[0.0091, 0.8304, 0.5860],
         [0.8770, 0.7744, 0.5909],
         [0.3012, 0.0784, 0.7481]]])
# target
tensor([[[1., 1., 0.],
         [0., 0., 0.],
         [1., 0., 0.]]])
# output
tensor(0.8790)
  • 实际在计算中如果使用binary_cross_entropy_with_logits 就不需要先做softmax处理。

在机器学习中,binary_cross_entropy 和 binary_cross_entropy_with_logits 都是用于二分类问题的损失函数,但它们在处理模型输出方面有所不同。

binary_cross_entropy 通常用于处理已经通过 sigmoid 或其他激活函数转换过的模型输出,这些输出表示为概率值,范围在 0 到 1 之间。在这种情况下,损失函数直接比较模型预测的概率分布和真实标签。

然而,binary_cross_entropy_with_logits 适用于处理未经激活函数转换的原始模型输出,也称为 logits。在这种情况下,损失函数内部会自动应用 sigmoid 函数将 logits 转换为概率值,然后再计算损失。这意味着在使用 binary_cross_entropy_with_logits 时,开发者无需手动对模型输出进行 sigmoid 转换。

  • 代码示例
    不需要对输入进行sigmoid处理,因为binary_cross_entropy_with_logits函数会自动对输入进行sigmoid处理。以下是修改后的代码:
import torch
import torch.nn as nn
input = torch.rand(1, 3, 3)
target = torch.rand(1, 3, 3).random_(2)
print(input)
print(target)
output = torch.nn.functional.binary_cross_entropy_with_logits(input, target)
print(output)

代码运行效果:

tensor([[[0.7857, 0.6012, 0.4140],
         [0.0689, 0.9600, 0.7193],
         [0.9429, 0.6011, 0.7957]]])
tensor([[[0., 1., 1.],
         [1., 0., 1.],
         [1., 1., 1.]]])
tensor(0.6205)
  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值