熵、交叉熵、KL散度、Softmax 与分类损失函数
2026/6/12 19:18:58 网站建设 项目流程

熵、交叉熵、KL散度、Softmax 与分类损失函数

0. 今天学习的主线

今天主要学习了以下几个概念之间的关系:

信息量 ↓ 熵 Entropy ↓ 交叉熵 Cross Entropy ↓ 相对熵 / KL 散度 ↓ 神经网络分类问题中的损失函数 ↓ Softmax + CrossEntropyLoss ↓ Sigmoid + BCEWithLogitsLoss

我一开始的疑惑是:

熵到底是什么?为什么它和期望放在一起?
交叉熵为什么可以作为神经网络分类任务的损失函数?
KL 散度和交叉熵到底是什么关系?
CrossEntropyLoss 和 BCEWithLogitsLoss 有什么区别?
Softmax 为什么经常和交叉熵绑定在一起?

通过今天的学习,我逐渐把这些概念串起来了。


1. 信息量

1.1 信息量的直观理解

一个事件发生的概率越小,它一旦发生,带来的信息量就越大。

比如:

  • “明天太阳升起”概率极大,所以信息量很小;

  • “明天东京下金条雨”概率极小,所以如果真的发生,信息量很大。

所以有一个核心直觉:

概率越大,信息量越小; 概率越小,信息量越大。

信息量定义为:

I(x)=−log⁡p(x)I(x) = -\log p(x)

其中:

  • p(x)表示事件x发生的概率;

  • log用来把独立事件联合发生时的概率乘法变成信息量加法;

  • 负号是因为当0 < p(x) <= 1时,log p(x) <= 0,加负号后信息量变成非负数。


1.2 为什么用 log?

如果两个事件 A 和 B 独立,那么:

P(A,B)=P(A)P(B)P(A,B) = P(A)P(B)

我们希望两个独立事件同时发生的信息量满足:

I(A,B)=I(A)+I(B)I(A,B) = I(A) + I(B)

如果定义:

I(x)=−log⁡p(x)I(x) = -\log p(x)

那么:

I(A,B)=−log⁡(P(A)P(B))I(A,B) = -\log(P(A)P(B))

根据对数性质:

−log⁡(P(A)P(B))=−log⁡P(A)−log⁡P(B)-\log(P(A)P(B)) = -\log P(A) - \log P(B)

所以:

I(A,B)=I(A)+I(B)I(A,B) = I(A) + I(B)

因此,log的一个重要作用就是:

把概率的乘法结构转化为信息量的加法结构。

2. 熵 Entropy

2.1 熵的直观理解

熵衡量的是一个随机系统的平均信息量,也可以理解为平均不确定性。

我的理解:

熵就是系统自己描述自己。

更准确地说:

熵是一个分布自身的不确定性。

熵的公式是:

H(P)=−∑xP(x)log⁡P(x)H(P) = -\sum_x P(x)\log P(x)

或者写成期望形式:

H(X)=E[−log⁡p(X)]H(X) = E[-\log p(X)]

这说明:

熵本质上是一种期望。

它不是某一个事件的信息量,而是所有可能事件的信息量,按照各自发生概率加权求平均。


2.2 熵为什么和期望放在一起?

普通期望是:

E[X]=∑xxp(x)E[X] = \sum_x x p(x)

如果我们关心的不是X本身,而是X的某个函数g(X),那么:

E[g(X)]=∑xg(x)p(x)E[g(X)] = \sum_x g(x)p(x)

熵里面取:

g(X)=−log⁡p(X)g(X) = -\log p(X)

所以:

H(X)=E[−log⁡p(X)]H(X) = E[-\log p(X)]

也就是说:

熵 = 信息量的期望 = 平均信息量。

2.3 熵的大小怎么理解?

例如两个系统:

PA=[0.5,0.5]P_A = [0.5, 0.5] PB=[0.99,0.01]P_B = [0.99, 0.01]

系统 A 的熵更大。

因为系统 A 的两个结果一样可能,最不确定。

系统 B 几乎一定出现第一个结果,不确定性很小。

所以:

分布越均匀,熵越大; 分布越集中,熵越小。

3. 交叉熵 Cross Entropy

3.1 交叉熵的直观理解

熵是:

系统自己描述自己。

交叉熵是:

用另一个系统来描述这个系统。

假设真实分布是P,模型分布是Q

交叉熵公式是:

H(P,Q)=−∑xP(x)log⁡Q(x)H(P,Q) = -\sum_x P(x)\log Q(x)

它的含义是:

真实事件按照 P 分布发生,但我用 Q 分布去描述它,平均需要付出多少信息代价。

注意:

H(P)=−∑xP(x)log⁡P(x)H(P) = -\sum_x P(x)\log P(x) H(P,Q)=−∑xP(x)log⁡Q(x)H(P,Q) = -\sum_x P(x)\log Q(x)

区别在于:

  • 熵:前面是P,log 里面也是P

  • 交叉熵:前面是P,log 里面是Q

所以可以记成:

熵:P 自己描述 P; 交叉熵:用 Q 描述 P。

3.2 交叉熵为什么可以衡量两个分布的差异?

如果Q很接近P,说明用Q描述P不会额外浪费太多信息,交叉熵会较小。

如果QP差很多,说明用Q描述P会很费劲,交叉熵会变大。

所以交叉熵可以用于衡量:

模型预测分布 Q 和真实分布 P 的差异。

4. KL 散度 / 相对熵

4.1 KL 散度的直观理解

KL 散度也叫相对熵,用来衡量两个分布之间的差异。

我的理解:

KL 散度就是说: 我用另外一个系统 Q 描述系统 P, 相比我用 P 自己描述 P, 到底多困难。

KL 散度公式是:

DKL(P∥Q)=∑xP(x)log⁡P(x)Q(x)D_{KL}(P \| Q) = \sum_x P(x)\log \frac{P(x)}{Q(x)}

它可以展开成:

DKL(P∥Q)=H(P,Q)−H(P)D_{KL}(P \| Q) = H(P,Q) - H(P)

也就是:

KL散度=交叉熵−熵KL散度 = 交叉熵 - 熵

更准确地说:

DKL(P∥Q)=H(P,Q)−H(P)D_{KL}(P \| Q) = H(P,Q) - H(P)

其中:

  • H(P,Q)是用Q描述P的总代价;

  • H(P)是用P自己描述自己的最优代价;

  • 两者相减,就是额外多出来的信息代价。

所以可以记成:

熵:自己描述自己; 交叉熵:用别人描述自己; KL 散度:用别人描述自己,比自己描述自己多出来的代价。

4.2 KL 散度是不是距离?

KL 散度经常被口语化地说成“两个分布之间的距离”,但严格来说它不是数学意义上的距离。

原因之一是:

DKL(P∥Q)≠DKL(Q∥P)D_{KL}(P \| Q) \neq D_{KL}(Q \| P)

也就是说,KL 散度是非对称的。

但真正的距离通常要求对称性:

d(P,Q)=d(Q,P)d(P,Q) = d(Q,P)

所以更严谨的说法是:

KL 散度是衡量两个概率分布差异的一种非对称信息代价。

5. 熵、交叉熵、KL 散度的记忆方式

5.1 看 log 里面是谁

H(P)=−∑xP(x)log⁡P(x)H(P) = -\sum_x P(x)\log P(x)

前面是P,log 里面也是P

所以:

P 自己描述自己。

交叉熵

H(P,Q)=−∑xP(x)log⁡Q(x)H(P,Q) = -\sum_x P(x)\log Q(x)

前面是P,log 里面是Q

所以:

真实事件按 P 发生,但用 Q 去描述。

KL 散度

DKL(P∥Q)=∑xP(x)log⁡P(x)Q(x)D_{KL}(P \| Q) = \sum_x P(x)\log \frac{P(x)}{Q(x)}

它比较的是:

P(x)Q(x)\frac{P(x)}{Q(x)}

所以:

KL 散度衡量的是 Q 和 P 的差异。

但要注意方向:

DKL(P∥Q)D_{KL}(P \| Q)

表示站在P的角度,用Q去近似P


5.2 三个概念一句话总结

熵 H(P):真实分布 P 自己的不确定性。 交叉熵 H(P,Q):真实分布是 P,但用模型分布 Q 去描述它的总代价。 KL 散度 D_KL(P||Q):用 Q 描述 P 时,比用 P 自己描述自己多出来的额外代价。

6. 为什么机器学习里经常最小化交叉熵?

因为:

DKL(P∥Q)=H(P,Q)−H(P)D_{KL}(P \| Q) = H(P,Q) - H(P)

在监督学习中:

  • P是真实标签分布;

  • Q是模型预测分布。

训练模型时,真实标签P是固定的,模型只能改变预测分布Q

所以:

H(P)H(P)

只和真实标签有关,是常数。

因此:

min⁡QDKL(P∥Q)\min_Q D_{KL}(P \| Q)

等价于:

min⁡QH(P,Q)\min_Q H(P,Q)

也就是说:

最小化交叉熵,本质上也在最小化真实分布 P 和模型分布 Q 之间的 KL 散度。

7. 分类问题中的交叉熵

7.1 多分类问题中的真实标签

在单标签多分类任务中,一个样本只属于一个类别。

例如类别为:

[猫, 狗, 兔子]

如果真实答案是狗,那么真实分布可以写成 one-hot:

P=[0,1,0]P = [0,1,0]

模型预测分布可能是:

Q=[0.2,0.6,0.2]Q = [0.2,0.6,0.2]

交叉熵公式:

H(P,Q)=−∑iPilog⁡QiH(P,Q) = -\sum_i P_i \log Q_i

代入:

H(P,Q)=−(0⋅log⁡0.2+1⋅log⁡0.6+0⋅log⁡0.2)H(P,Q) = -(0\cdot \log 0.2 + 1\cdot \log 0.6 + 0\cdot \log 0.2)

所以:

H(P,Q)=−log⁡0.6H(P,Q) = -\log 0.6

这说明:

在 one-hot 标签下,交叉熵只关心模型给真实类别分配了多少概率。

7.2 分类交叉熵的本质

如果真实类别是第k类,那么:

CE=−log⁡QkCE = -\log Q_k

其中Q_k是模型给真实类别的预测概率。

所以:

真实类别的预测概率越大,交叉熵越小; 真实类别的预测概率越小,交叉熵越大。

例如:

Q真实类别=0.9Q_{\text{真实类别}} = 0.9

则:

−log⁡0.9-\log 0.9

很小。

如果:

Q真实类别=0.01Q_{\text{真实类别}} = 0.01

则:

−log⁡0.01-\log 0.01

很大。

所以分类训练可以理解成:

让模型越来越相信正确答案。

8. 最大似然估计和交叉熵的关系

8.1 最大似然的思想

最大似然估计的思想是:

既然这些数据已经发生了,那就选择一组参数,使得这些数据发生的可能性最大。

分类模型输出的是:

Pθ(y∣x)P_\theta(y|x)

表示在输入x的条件下,模型认为标签是y的概率。

训练数据为:

(x1,y1),(x2,y2),...,(xn,yn)(x_1,y_1),(x_2,y_2),...,(x_n,y_n)

整批数据的似然为:

L(θ)=∏i=1nPθ(yi∣xi)L(\theta)=\prod_{i=1}^{n}P_\theta(y_i|x_i)

最大似然就是:

max⁡θ∏i=1nPθ(yi∣xi)\max_\theta \prod_{i=1}^{n}P_\theta(y_i|x_i)


8.2 从最大似然到交叉熵

对似然取 log:

log⁡L(θ)=∑i=1nlog⁡Pθ(yi∣xi)\log L(\theta)=\sum_{i=1}^{n}\log P_\theta(y_i|x_i)

最大化 log 似然等价于最小化负 log 似然:

min⁡θ−∑i=1nlog⁡Pθ(yi∣xi)\min_\theta -\sum_{i=1}^{n}\log P_\theta(y_i|x_i)

而对于 one-hot 标签,单个样本的交叉熵就是:

−log⁡Pθ(yi∣xi)-\log P_\theta(y_i|x_i)

所以:

最小化交叉熵 = 最大化真实标签的似然。

也就是说:

交叉熵不是凭空来的,它可以从最大似然估计自然推导出来。

9. CrossEntropyLoss 和 BCEWithLogitsLoss 的区别

9.1 CrossEntropyLoss

CrossEntropyLoss 通常用于:

单标签多分类任务。

特点是:

多个类别互斥,只能选一个。

例如:

判断一张图片是猫、狗、兔子中的哪一个。

真实标签是 one-hot:

[0,1,0][0,1,0]

但在 PyTorch 中通常直接用类别编号:

target = torch.tensor([1])

模型输出 logits:

logits.shape = [batch_size, num_classes]

使用:

loss_fn = torch.nn.CrossEntropyLoss() loss = loss_fn(logits, target)

注意:

PyTorch 的 CrossEntropyLoss 直接接收 logits, 不需要手动 softmax。

因为它内部已经包含:

LogSoftmax + NLLLoss

9.2 BCEWithLogitsLoss

BCEWithLogitsLoss 通常用于:

二分类任务或多标签分类任务。

它的关键不是“整个任务只有两个类别”,而是:

每个标签都是一个独立的是/否判断。

例如多标签任务:

判断一张图片里是否有猫、是否有狗、是否有草地、是否有人。

标签可以是:

[1,0,1,1][1,0,1,1]

表示:

  • 有猫;

  • 没有狗;

  • 有草地;

  • 有人。

这不是 one-hot,因为可以有多个 1。

此时每一维都是一个二元判断:

是不是猫? 是不是狗? 是不是草地? 是不是人?

使用:

loss_fn = torch.nn.BCEWithLogitsLoss() loss = loss_fn(logits, target.float())

其中 logits 的形状通常是:

logits.shape = [batch_size, num_labels]

target 的形状也是:

target.shape = [batch_size, num_labels]

9.3 CE 和 BCE 的核心区别

任务类型类别关系输出方式损失函数
单标签多分类类别互斥,只能选一个SoftmaxCrossEntropyLoss
二分类是/不是SigmoidBCEWithLogitsLoss
多标签分类每个标签独立判断是/否SigmoidBCEWithLogitsLoss

记忆方式:

CrossEntropyLoss:单选题。 BCEWithLogitsLoss:一堆判断题。

或者:

CE:多个类别里选一个。 BCE:每个类别分别判断有没有。

10. Softmax 为什么经常和交叉熵绑定在一起?

10.1 神经网络先输出 logits

神经网络最后一层通常先输出 logits。

例如三分类:

z=[2.0,1.0,0.1]z = [2.0,1.0,0.1]

这些 logits 是原始分数,不是概率。

它们可能:

  • 有负数;

  • 不在 0 到 1 之间;

  • 加起来不等于 1。

所以不能直接拿来当概率。


10.2 Softmax 把 logits 转换成概率分布

Softmax 公式:

qi=ezi∑jezjq_i = \frac{e^{z_i}}{\sum_j e^{z_j}}

它把 logits 转成概率分布:

q=[0.66,0.24,0.10]q = [0.66,0.24,0.10]

满足:

q1+q2+q3=1q_1+q_2+q_3=1

并且:

0<qi<10<q_i<1

所以 softmax 的作用是:

把神经网络的原始打分转换成互斥类别上的概率分布。

10.3 为什么单标签多分类需要 Softmax?

因为单标签多分类中,类别是互斥的。

例如一张图片只能是:

猫 / 狗 / 兔子

中的一个。

所以:

P(猫)+P(狗)+P(兔子)=1P(猫)+P(狗)+P(兔子)=1

这刚好和 softmax 的输出特点一致。


10.4 Softmax + CrossEntropy 的分工

Softmax 的作用:

把 logits 变成概率分布。

交叉熵的作用:

惩罚模型给真实类别的概率太低。

整体流程是:

logits ↓ softmax ↓ 预测概率分布 Q ↓ 和真实 one-hot 分布 P 计算交叉熵 ↓ 反向传播更新参数

所以:

Softmax 和交叉熵经常绑定,是因为它们一起解决了单标签多分类问题。

10.5 PyTorch 中不要手动 softmax

在 PyTorch 中,正确写法是:

logits = model(x) loss = torch.nn.CrossEntropyLoss()(logits, target)

不要写成:

prob = torch.softmax(logits, dim=1) loss = torch.nn.CrossEntropyLoss()(prob, target)

因为CrossEntropyLoss内部已经做了稳定版本的 softmax 和 log 计算。


11. Softmax + CrossEntropy 的梯度直觉

如果:

q=softmax(z)q = softmax(z)

交叉熵损失:

L=−∑iyilog⁡qiL = -\sum_i y_i \log q_i

那么对 logits 的梯度有一个非常简洁的形式:

∂L∂zi=qi−yi\frac{\partial L}{\partial z_i} = q_i - y_i

也就是说:

梯度 = 模型预测概率 - 真实标签概率。

例如真实类别是猫:

y=[1,0,0]y = [1,0,0]

模型预测:

q=[0.66,0.24,0.10]q = [0.66,0.24,0.10]

那么:

q−y=[−0.34,0.24,0.10]q-y = [-0.34,0.24,0.10]

直观含义:

  • 对正确类别猫,梯度会推动它的 logit 变大;

  • 对错误类别狗和兔子,梯度会推动它们的 logit 变小。

所以 Softmax + CrossEntropy 的训练行为是:

提高正确类别的分数,压低错误类别的分数。

这也是它们经常绑定的原因之一。


12. 今天我原本的疑惑和现在的理解

疑惑 1:熵到底是什么?

原本疑惑:

熵是不是系统的信息量?

现在理解:

熵是随机系统的平均信息量,也就是平均不确定性。 它可以理解成系统自己描述自己的信息代价。

疑惑 2:为什么熵和期望放在一起?

现在理解:

因为熵本质上就是信息量的期望。

公式:

H(X)=E[−log⁡p(X)]H(X)=E[-\log p(X)]


疑惑 3:交叉熵和 KL 散度怎么区分?

现在理解:

熵:自己描述自己。 交叉熵:用别人描述自己。 KL 散度:用别人描述自己,比自己描述自己多出来的代价。

公式:

DKL(P∥Q)=H(P,Q)−H(P)D_{KL}(P \| Q)=H(P,Q)-H(P)


疑惑 4:分类为什么用交叉熵?

现在理解:

分类模型输出的是一个概率分布 Q; 真实标签可以看成 one-hot 分布 P; 交叉熵可以衡量 Q 和 P 的差异。

在 one-hot 情况下:

CE=−log⁡Q真实类别CE=-\log Q_{\text{真实类别}}

所以分类训练就是:

让模型给真实类别的概率越来越大。

疑惑 5:二元交叉熵是不是只能用于两个类别?

现在理解:

不是。

BCE 的“二元”指的是:

每个标签都是一个二元判断:是 / 不是。

所以 BCE 可以用于:

  • 二分类;

  • 多标签分类。


疑惑 6:Softmax 为什么和交叉熵绑定?

现在理解:

Softmax 把 logits 转换成互斥类别上的概率分布; 交叉熵衡量这个预测分布和真实 one-hot 分布的差异。

所以:

Softmax + CrossEntropyLoss 适合单标签多分类任务。

13. 最终总结

今天学习内容可以浓缩成下面几句话:

信息量表示某个事件发生后带来的意外程度,概率越小,信息量越大。 熵是一个分布自身的平均信息量,可以理解为系统自己描述自己。 交叉熵是用另一个分布 Q 去描述真实分布 P 的平均代价。 KL 散度是用 Q 描述 P,相比用 P 自己描述自己,多出来的额外信息代价。 在分类任务中,真实标签 P 是固定的,模型预测分布 Q 是可训练的。 因此最小化交叉熵等价于让 Q 越来越接近 P。 对于单标签多分类任务,类别互斥,所以使用 softmax 把 logits 转成概率和为 1 的分布,再用 CrossEntropyLoss。 对于二分类或多标签任务,每个标签都是独立的是/否判断,所以使用 sigmoid + BCEWithLogitsLoss。

14. 代码层面的最终记忆

单标签多分类

import torch import torch.nn as nn logits = model(x) # shape: [batch_size, num_classes] target = torch.tensor([0, 2, 1]) # 类别编号,不是 one-hot loss_fn = nn.CrossEntropyLoss() loss = loss_fn(logits, target)

记住:

CrossEntropyLoss 接收 logits,不要手动 softmax。

二分类 / 多标签分类

import torch import torch.nn as nn logits = model(x) # shape: [batch_size, num_labels] target = torch.tensor([[1, 0, 1, 1]]).float() loss_fn = nn.BCEWithLogitsLoss() loss = loss_fn(logits, target)

记住:

BCEWithLogitsLoss 接收 logits,不要手动 sigmoid。

15. 最后自测题

题 1

为什么 KL 散度不是严格意义上的距离?

答案:

因为 KL 散度不对称: D_KL(P||Q) 不一定等于 D_KL(Q||P)。

题 2

真实类别是狗,类别顺序为:

[猫, 狗, 兔子]

模型预测:

Q=[0.2,0.6,0.2]Q=[0.2,0.6,0.2]

交叉熵是多少?

答案:

−log⁡0.6-\log 0.6


题 3

什么时候用 CrossEntropyLoss?什么时候用 BCEWithLogitsLoss?

答案:

单标签多分类,类别互斥,只能选一个,用 CrossEntropyLoss。 二分类或多标签分类,每个标签独立判断是/否,用 BCEWithLogitsLoss。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询