HelloAI
L1 第 5 篇 🐣 难度 🕒 13 分钟

信息论:熵、交叉熵、KL 散度的直觉

损失函数为什么长 -log P 这样?这一篇从信息论角度回答。理解了,你看到任何损失函数都不再陌生。

阿莱
2026/6/18

L1-04 我们看到 ML 的损失函数 logP(yx,θ)-\sum \log P(y|x,\theta)为什么是 log?为什么是负号?

答案藏在 1948 年 Shannon 的一篇论文里——信息论。这一篇我们打开它的核心,理解为什么所有 ML 损失都长这样。

第一站:信息的”量”怎么衡量

凭直觉,信息量取决于”惊讶程度”

  • “明天太阳会升起” — 没什么信息(几乎确定)
  • “明天会下雨” — 有点信息(不太确定)
  • “你今晚中彩票头奖” — 信息巨大(极不可能)

事件越罕见,它发生时携带的信息越大

Shannon 用数学表达这个直觉。一个概率为 pp 的事件,发生时携带的”信息量”是:

I(x)=logp(x)I(x) = -\log p(x)

注意:

  • p1p \to 1(确定的事),logp0\log p \to 0I0I \to 0(没信息)
  • p0p \to 0(极罕见),logp\log p \to -\inftyI+I \to +\infty(信息巨大)
  • 负号:让 II 总是正数

底数通常取 2(单位叫”比特”)或 ee(单位叫”奈特”)。ML 里用 loge\log_e(自然对数)。

第二站:熵 = 平均信息量

如果一个变量 XX 服从某个分布,它的”熵”是它每次取值的平均信息量

H(X)=xp(x)logp(x)H(X) = -\sum_x p(x) \log p(x)

直觉:

  • 确定的分布(一个值概率为 1,其它为 0):熵 = 0(每次结果都没意外)
  • 均匀分布:熵最大(每次结果都最让人意外)

举例:抛一枚公平硬币(p=0.5p=0.5):

H=0.5log0.50.5log0.5=log20.693H = -0.5 \log 0.5 - 0.5 \log 0.5 = \log 2 ≈ 0.693

抛一枚作弊硬币(p=0.99p=0.99 正面):

H=0.99log0.990.01log0.010.056H = -0.99 \log 0.99 - 0.01 \log 0.01 ≈ 0.056

作弊硬币熵小 — 因为几乎不让人惊讶。

💡 一句话直觉

熵是”不确定性”的数学度量。分布越均匀、越不可预测,熵越大。

第三站:交叉熵

到现在为止讲的都是真实分布 pp 的事。但 ML 还有一个”预测分布” qq ——模型给出的概率分布。

交叉熵就是衡量”用 q 去近似 p”的代价:

H(p,q)=xp(x)logq(x)H(p, q) = -\sum_x p(x) \log q(x)

注意符号顺序:左边是真实,右边是预测。它是不对称的

直觉:

  • 如果 q=pq = p(预测完美),H(p,q)=H(p)H(p, q) = H(p) —— 等于 p 的熵,是最小可能值
  • qq 偏离 pp 越远,H(p,q)H(p, q) 越大

这就是 ML 训练的损失

真实标签 pp 是 one-hot:[0, 0, 1, 0, …](第 3 类对)。 模型预测 qq 是概率分布:[0.1, 0.05, 0.7, 0.15, …]。

交叉熵 = log0.7-\log 0.7 ≈ 0.357

训练让 qq 越来越靠近 pp,交叉熵越来越小。

第四站:KL 散度

KL 散度衡量两个分布的”距离”

DKL(pq)=xp(x)logp(x)q(x)D_{KL}(p \| q) = \sum_x p(x) \log \frac{p(x)}{q(x)}

性质:

  • DKL(pp)=0D_{KL}(p \| p) = 0(自己和自己没差异)
  • DKL0D_{KL} \ge 0 总是
  • 不对称!DKL(pq)DKL(qp)D_{KL}(p \| q) \ne D_{KL}(q \| p)

它和交叉熵的关系:

H(p,q)=H(p)+DKL(pq)H(p, q) = H(p) + D_{KL}(p \| q)

翻译:交叉熵 = 真实分布的熵 + 真实和预测之间的 KL 散度。

训练时 H(p)H(p) 是常数(标签固定),所以最小化交叉熵 = 最小化 KL 散度。两个说法在 ML 里是等价的。

第五站:所有损失函数都是它

回过头看 ML 各种损失:

1. 多分类交叉熵(图像分类、文本分类)

L=iyilogy^iL = -\sum_i y_i \log \hat{y}_i

直接是交叉熵。

2. 二分类交叉熵

L=[ylogy^+(1y)log(1y^)]L = -[y \log \hat{y} + (1-y) \log(1-\hat{y})]

是上面的特例(只有 2 个类别)。

3. LLM 训练(语言模型)

L=tlogP(wtw<t)L = -\sum_t \log P(w_t | w_{<t})

模型预测下一个词,真实 token 是 one-hot,所以又是交叉熵。

4. VAE 重建损失

L=DKL(q(zx)p(z))+reconstructionL = D_{KL}(q(z|x) \| p(z)) + \text{reconstruction}

显式用 KL 散度做正则化。

5. 强化学习 PPO

L=advantageratioβDKL(πnewπold)L = \text{advantage} \cdot \text{ratio} - \beta \cdot D_{KL}(\pi_{new} \| \pi_{old})

也用 KL 散度做”新旧策略不要差太远”的约束。

6. 知识蒸馏

L=DKL(teacherstudent)L = D_{KL}(\text{teacher} \| \text{student})

让学生模型的输出分布逼近老师模型。

🔬 一个领悟瞬间

你看到的 ML 损失函数,几乎全部都是某种形式的交叉熵或 KL 散度。所有”让模型预测靠近真实”的训练,本质都在做”让两个概率分布更近”。

第六站:MSE 也是交叉熵?

很多人以为均方误差(MSE)和交叉熵不搭。其实它也是交叉熵的特例

如果你假设真实数据 yy 服从高斯分布 N(y^,σ2)\mathcal{N}(\hat{y}, \sigma^2)

P(yy^)=12πσ2exp((yy^)22σ2)P(y | \hat{y}) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(y - \hat{y})^2}{2\sigma^2}\right)

取负对数:

logP=(yy^)22σ2+const-\log P = \frac{(y - \hat{y})^2}{2\sigma^2} + \text{const}

这就是 MSE!只是去掉常数和缩放。

结论MSE = 假设噪声是高斯分布时的最大似然估计

它不是”另一种”损失,它和交叉熵是同一个东西的不同视角。

第七站:用 Python 看看

import numpy as np

# 真实分布(one-hot)和模型预测
y_true = np.array([0, 0, 1, 0, 0])           # 第 3 类
y_pred = np.array([0.1, 0.05, 0.7, 0.1, 0.05])

# 交叉熵
ce = -np.sum(y_true * np.log(y_pred + 1e-9))
print(f"交叉熵: {ce:.4f}")  # 0.3567

# KL 散度(注意:one-hot 时严格地讲不能算,因为 log 0 = -inf)
# 用一个平滑的 y_true
y_true_smooth = np.array([0.01, 0.01, 0.96, 0.01, 0.01])
kl = np.sum(y_true_smooth * np.log(y_true_smooth / y_pred))
print(f"KL 散度: {kl:.4f}")

# 一个分布的熵
p = np.array([0.5, 0.3, 0.2])
h = -np.sum(p * np.log(p))
print(f"熵: {h:.4f}")  # ≈ 1.0297

# 均匀分布熵最大
uniform = np.array([1/3, 1/3, 1/3])
h_max = -np.sum(uniform * np.log(uniform))
print(f"均匀分布熵: {h_max:.4f}")  # ≈ 1.0986

一句话总结

熵 = 不确定性。交叉熵 = “用预测替代真实”的成本。KL 散度 = “两个分布之间的距离”。

几乎所有 ML 损失都是它们的某种形式。理解了这一篇,你看到任何新的”奇怪损失函数”,都能秒翻译成”这是在让某个分布靠近另一个分布”。

💡 L1 数学块进度
  • ✓ 线性代数(L1-02)
  • ✓ 导数与梯度(L1-03)
  • ✓ 概率与最大似然(L1-04)
  • ✓ 信息论(本篇)
  • 下一篇:链式法则与反向传播 —— 把所有数学武器组合起来训练真神经网络
📬

读到这里说明你认真在学 🎯

订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。

💬

讨论区

· 用 GitHub 账号登录评论
⚠️ Giscus 评论未配置 —— 在 src/components/Comments.astro 顶部填入 仓库 ID 和分类 ID(见组件注释里的配置步骤)。