2536c937e3
翻译自英文原版 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/ 构建缓存
192 lines
10 KiB
Markdown
192 lines
10 KiB
Markdown
# 信息论
|
||
|
||
*信息论量化了信息、惊奇度以及概率分布之间的差异。本文涵盖熵、交叉熵、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)$$
|
||
|
||

|
||
|
||
- 一枚公平硬币的熵为 $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$ 时为零。
|
||
|
||

|
||
|
||
- 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()
|
||
```
|