Files
maths-cs-ai-compendium-zh/chapter 05: probability/05. information theory.md
T
flykhan 2536c937e3 feat: 完整中文翻译 maths-cs-ai-compendium(数学·计算机科学·AI 知识大全)
翻译自英文原版 maths-cs-ai-compendium,共 20 章全部完成。

第01章 向量 | 第02章 矩阵 | 第03章 微积分
第04章 统计学 | 第05章 概率论 | 第06章 机器学习
第07章 计算语言学 | 第08章 计算机视觉 | 第09章 音频与语音
第10章 多模态学习 | 第11章 自主系统 | 第12章 图神经网络
第13章 计算与操作系统 | 第14章 数据结构与算法
第15章 生产级软件工程 | 第16章 SIMD与GPU编程
第17章 AI推理 | 第18章 ML系统设计
第19章 应用人工智能 | 第20章 前沿人工智能

翻译说明:
- 所有数学公式 $...$ / $$...$$、代码块、图片引用完整保留
- mkdocs.yml 配置中文导航 + language: zh
- README.md 已翻译为中文(兼 docs/index.md)
- docs/ 目录包含指向各章文件的 symlink
- 约 29,000 行中文内容,排除 .cache/ 构建缓存
2026-05-03 10:23:20 +08:00

192 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 信息论
*信息论量化了信息、惊奇度以及概率分布之间的差异。本文涵盖熵、交叉熵、KL散度、互信息和自信息——这些概念是机器学习中每一个分类损失函数、VAE目标和数据压缩方案背后的理论基础。*
- 信息论由克劳德·香农于1948年创立,为我们提供了量化信息的数学框架。它回答了诸如此类的问题:一个事件应当让你感到多惊讶?一条消息携带了多少信息?两个概率分布之间有多大的差异?
- 这些问题看似抽象,但它们是机器学习损失函数、数据压缩和通信系统的基础。交叉熵损失——分类中最常见的损失函数——直接源于信息论。
- 从最简单的问题开始:单个事件携带了多少信息?
- **自信息**surprisal,也称 self-information)衡量一个事件的惊奇程度。如果某件极有可能发生的事情真的发生了,你几乎学不到任何新信息。如果某件罕见的事情发生了,你则会获得大量信息。
- 如果你住在沙漠里,有人告诉你今天是大晴天,这并没有什么信息量。但如果他们告诉你正在下雪,那信息量就极其丰富。自信息将这种直觉形式化:
$$I(x) = \log_2 \frac{1}{p(x)} = -\log_2 p(x)$$
- 使用 $\log_2$ 时,单位是**比特**。一枚公平的硬币抛掷的自信息为 $-\log_2(0.5) = 1$ 比特。一个概率为 $1/8$ 的事件具有 $ \log_2(8) = 3$ 比特的自信息。
- 为什么用对数而不是简单的 $1/p$?三个原因:
- 必然事件($p = 1$)应给出零信息:$\log(1) = 0$ 但 $1/1 = 1$。
- 独立事件的信息应该是可加的:$\log(1/p_1 p_2) = \log(1/p_1) + \log(1/p_2)$。
- 我们需要一个平滑、性质良好的函数。$1/p$ 会爆炸;$\log(1/p)$ 则平缓增长。
- **熵**是自信息的期望值,即从一个分布中每次采样获得的平均信息量。它衡量该分布的不确定性或"不可预测性":
$$H(X) = E[I(X)] = -\sum_{x} p(x) \log_2 p(x)$$
![柱状图展示高概率事件具有低自信息,反之亦然;熵是加权平均值](../images/surprisal_entropy.svg)
- 一枚公平硬币的熵为 $H = -0.5\log_2(0.5) - 0.5\log_2(0.5) = 1$ 比特。不确定性最大。
- 一枚偏倚硬币,$p = 0.9$,其熵为 $H = -0.9\log_2(0.9) - 0.1\log_2(0.1) \approx 0.469$ 比特。不太确定,因此熵更小。
- 一个确定性事件($p = 1$)的熵为 $H = 0$。完全没有不确定性。
- 当所有结果等可能时,熵达到最大。对于 $n$ 个等可能结果,$H = \log_2 n$。一颗公平骰子的熵为 $\log_2 6 \approx 2.585$ 比特。
- 熵的实际意义在于**压缩**。香农的源编码定理指出,如果不丢失信息,你无法将数据压缩到低于其熵率。一幅每个像素都等可能的图像(最大熵)无法压缩。一幅几乎全是白色的图像(低熵)则可以很好地压缩。
- 快速感受一下数量级:一个灰度像素(256 个值)的最大熵为 8 比特。一张 1080p 的灰度图像最多有 $1920 \times 1080 \times 8 \approx 1660$ 万比特。真实图像的熵要低得多,因为相邻像素是相关的——这正是 JPEG 压缩能够工作的原因。
- 对于连续随机变量,离散求和变为积分。**微分熵**定义为:
$$h(X) = -\int_{-\infty}^{\infty} f(x) \log f(x)\, dx$$
- 方差为 $\sigma^2$ 的高斯分布的微分熵为 $h = \frac{1}{2}\log_2(2\pi e \sigma^2)$。在所有具有相同方差的分布中,高斯分布具有最大熵。这也是高斯分布在建模中如此常见的原因之一:它在指定均值和方差之外做出了最少的假设。
- **互信息**衡量知道一个变量能告诉你关于另一个变量的多少信息。它是观察到 $Y$ 后 $X$ 不确定性的减少量:
$$I(X; Y) = H(X) - H(X|Y) = H(Y) - H(Y|X)$$
- 等价形式:
$$I(X; Y) = \sum_{x,y} p(x,y) \log_2 \frac{p(x,y)}{p(x) p(y)}$$
- 如果 $X$ 和 $Y$ 独立,则 $p(x,y) = p(x)p(y)$,互信息为零。它们依赖程度越高,互信息就越大。
- 在机器学习中,互信息用于特征选择(挑选与目标具有高 MI 的特征)、信息瓶颈方法以及聚类质量评估。
- **交叉熵**衡量使用针对分布 $q$ 优化的编码方案来编码来自分布 $p$ 的事件所需的平均比特数:
$$H(p, q) = -\sum_{x} p(x) \log_2 q(x)$$
- 如果 $q$ 与 $p$ 完全匹配,则交叉熵等于熵:$H(p, p) = H(p)$。如果 $q$ 是一个糟糕的近似,交叉熵就会更高。"额外"的比特来自这种不匹配。
- 这正是交叉熵成为机器学习中分类标准损失函数的原因。真实标签定义了 $p$(一个 one-hot 分布),模型的预测概率定义了 $q$。最小化交叉熵推动 $q$ 趋近于 $p$:
$$\mathcal{L} = -\sum_{c} y_c \log \hat{y}_c$$
- 对于单个样本,若真实类别为 $c$,上式简化为 $\mathcal{L} = -\log \hat{y}_c$。该损失就是模型预测下真实类别的自信息。如果模型对正确类别赋予高概率,则损失较低。
- **KL 散度**Kullback-Leibler 散度,也称相对熵)衡量一个分布与另一个分布的差异程度:
$$D_{\text{KL}}(p \| q) = \sum_{x} p(x) \log \frac{p(x)}{q(x)} = H(p, q) - H(p)$$
- KL 散度是"使用分布 $q$ 而非真实分布 $p$ 的额外代价"。它总是非负的($D_{\text{KL}} \ge 0$),且仅在 $p = q$ 时为零。
![两个分布 p 和 q,它们之间的间隙表示 KL 散度](../images/kl_divergence.svg)
- KL 散度不是对称的:$D_{\text{KL}}(p \| q) \ne D_{\text{KL}}(q \| p)$。这种不对称性很重要。$D_{\text{KL}}(p \| q)$ 惩罚 $q$ 在 $p$ 具有高概率处放置低概率(因为 $\log(p/q)$ 会趋于无穷大)。$D_{\text{KL}}(q \| p)$ 则惩罚相反的情况。
- 这种不对称性导致了两种近似风格:
- 最小化 $D_{\text{KL}}(p \| q)$ 产生**矩匹配**行为:$q$ 覆盖 $p$ 的所有模态,但可能过于分散。
- 最小化 $D_{\text{KL}}(q \| p)$ 产生**模式寻找**行为:$q$ 集中于 $p$ 的某一个模态,但可能错过其他模态。变分推断使用的正是这一种。
- 由于 $H(p)$ 相对于模型是常数,最小化交叉熵 $H(p, q)$ 等价于最小化 $D_{\text{KL}}(p \| q)$。这就是为什么我们可以使用交叉熵损失,同时知道我们也在最小化真实分布与预测分布之间的 KL 散度。
- KL 散度在**贝叶斯更新**中扮演着核心角色。后验 $P(\theta | D)$ 是在 KL 散度意义上与先验 $P(\theta)$ 最接近且与观测数据一致的分布。每一次新的观测都会更新后验,减少关于 $\theta$ 的不确定性。
- 在变分自编码器(VAE)中,损失函数包含两项:重构损失(交叉熵)和一个 KL 散度项,后者对潜在空间进行正则化,使其保持接近标准正态分布。
- 将所有概念联系起来:熵告诉你一个分布内在的不确定性,交叉熵告诉你的模型对现实的近似程度,而 KL 散度则告诉你两者之间的差距。这三个量构成了现代机器学习优化的基石。
## 编程练习(使用 CoLab 或 notebook
1. 计算各种分布的熵,并验证在给定结果数量下,均匀分布的熵最大。
```python
import jax.numpy as jnp
def entropy(p):
"""以比特为单位计算熵。过滤掉概率为零的事件。"""
p = p[p > 0]
return -jnp.sum(p * jnp.log2(p))
# 公平骰子
fair = jnp.ones(6) / 6
print(f"公平骰子熵: {entropy(fair):.4f} 比特 (最大 = log2(6) = {jnp.log2(6.):.4f})")
# 灌铅骰子
loaded = jnp.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.5])
print(f"灌铅骰子熵: {entropy(loaded):.4f} 比特")
# 确定性
det = jnp.array([0.0, 0.0, 0.0, 0.0, 0.0, 1.0])
print(f"确定性: {entropy(det):.4f} 比特")
# 公平硬币
coin = jnp.array([0.5, 0.5])
print(f"公平硬币熵: {entropy(coin):.4f} 比特")
```
2. 计算真实分布与多个近似分布之间的交叉熵和 KL 散度。验证 $D_{\text{KL}}(p \| q) = H(p, q) - H(p)$。
```python
import jax.numpy as jnp
def cross_entropy(p, q):
return -jnp.sum(p * jnp.log2(jnp.clip(q, 1e-10, 1.0)))
def kl_divergence(p, q):
mask = p > 0
return jnp.sum(jnp.where(mask, p * jnp.log2(p / jnp.clip(q, 1e-10, 1.0)), 0.0))
def entropy(p):
p = p[p > 0]
return -jnp.sum(p * jnp.log2(p))
p = jnp.array([0.4, 0.3, 0.2, 0.1]) # 真实分布
for name, q in [("完全匹配", p),
("轻微偏差", jnp.array([0.35, 0.30, 0.25, 0.10])),
("严重偏差", jnp.array([0.1, 0.1, 0.1, 0.7]))]:
h_p = entropy(p)
h_pq = cross_entropy(p, q)
kl = kl_divergence(p, q)
print(f"{name:20s}: H(p)={h_p:.4f}, H(p,q)={h_pq:.4f}, "
f"KL={kl:.4f}, H(p,q)-H(p)={h_pq-h_p:.4f}")
```
3. 通过计算两个不同分布之间的 $D_{\text{KL}}(p \| q)$ 和 $D_{\text{KL}}(q \| p)$,证明 KL 散度不是对称的。
```python
import jax.numpy as jnp
def kl_div(p, q):
mask = p > 0
return float(jnp.sum(jnp.where(mask, p * jnp.log2(p / jnp.clip(q, 1e-10, 1.0)), 0.0)))
p = jnp.array([0.9, 0.1])
q = jnp.array([0.5, 0.5])
print(f"D_KL(p || q) = {kl_div(p, q):.4f}")
print(f"D_KL(q || p) = {kl_div(q, p):.4f}")
print("不相同!KL 散度是不对称的。")
```
4. 模拟训练过程中交叉熵损失的变化。创建一个"真实"的 one-hot 标签,展示随着模型预测概率的改善,损失如何下降。
```python
import jax.numpy as jnp
import matplotlib.pyplot as plt
# 真实标签:4 个类别中的第 2 类
true_label = jnp.array([0, 0, 1, 0])
# 模拟预测逐步改善
steps = []
losses = []
for confidence in jnp.linspace(0.25, 0.99, 50):
# 模型对类别 2 的置信度逐渐提高
remaining = (1 - confidence) / 3
pred = jnp.array([remaining, remaining, confidence, remaining])
loss = -jnp.sum(true_label * jnp.log(jnp.clip(pred, 1e-10, 1.0)))
steps.append(float(confidence))
losses.append(float(loss))
plt.figure(figsize=(8, 4))
plt.plot(steps, losses, color="#e74c3c", linewidth=2)
plt.xlabel("模型对真实类别的置信度")
plt.ylabel("交叉熵损失")
plt.title("交叉熵损失随预测改善而下降")
plt.grid(alpha=0.3)
plt.show()
```