4.1 基础熵与互信息
概率论用分布描述不确定性,但没有给出不确定性的数量指标。两个随机变量哪个更不确定、观测到
本节的组织顺序是:先定义单个结果的意外程度(自信息),再取其平均得到熵;随后引入两个变量的联合熵与条件熵,推导链式法则;再由三者的差定义互信息,说明互信息的四种等价表达与核心性质;最后讨论两个信息论中最有用的不等式(数据处理不等式与 Fano 不等式),并把熵的概念从单个变量推广到随机过程(熵率),介绍长序列行为的基本结论(渐近均分性质与典型性)。典型性理论是后面编码与信道容量两节的技术基础,本节给出它的直观与结论,详细的编码构造留到 4.4 节与 4.5 节。
4.1.1 自信息
从意外程度到对数
一个事件的信息量应当由它的意外程度决定。抛一枚均匀硬币得到正面,这一结果的意外程度低;掷骰子得到六点,意外程度高一些;抽卡得到概率为
第一,概率越小的事件带来的信息越多,函数应当关于
满足这三条要求的函数形式唯一,就是对数的负值:
把
底数与单位
对数的底数决定信息的单位。取底
底数的选择不影响结论的结构,只改变数值的换算。涉及两个分布之比的量(KL 散度、互信息)对底数不敏感,因为比值在换底时同时出现在分子与分母上而抵消;只有单独的熵与自信息需要说明单位。
import numpy as np
def self_information(p, base=2):
"""自信息,默认单位为比特"""
return -np.log(p) / np.log(base)
print(f"抛均匀硬币正面:{self_information(0.5):.3f} 比特")
print(f"掷骰子得六点: {self_information(1/6):.3f} 比特")
print(f"概率 0.01 的结果:{self_information(0.01):.3f} 比特")
print(f"必然事件: {self_information(1.0):.3f} 比特")
# 概率每减半,信息量增加 1 比特4.1.2 信息熵
定义
自信息描述单个结果,信息熵(information entropy,也称香农熵)描述整个随机变量的平均意外程度,定义为自信息的期望:
连续情形用微分熵(differential entropy)替代(4.4 节讨论它的性质差异):
定义中
基本性质
非负性:
上界:若
等号当且仅当分布均匀。证明用 KL 散度的非负性(4.2 节):设
这就是
凹性:
凹性的实际意义是混合两个分布不会减少不确定性。证明用对数函数的凹性与 Jensen 不等式。作为推论,在约束下最大化熵的问题(4.3 节的最大熵原理)是一个凸优化问题,可以用第三章的工具求解。
对称性:熵只依赖概率的取值,与结果的具体标签无关。把
伯努利分布的熵
取
函数关于
import numpy as np
import matplotlib.pyplot as plt
def entropy(p):
"""离散分布的熵,输入为概率数组,单位为比特"""
p = np.asarray(p, dtype=float)
p = p[p > 0]
return float(-(p * np.log2(p)).sum())
p_grid = np.linspace(1e-6, 1 - 1e-6, 400)
h_bern = [entropy([p, 1 - p]) for p in p_grid]
print(f"伯努利分布的最大熵(p = 0.5):{entropy([0.5, 0.5]):.4f} 比特")
print(f"p = 0.9 时的熵:{entropy([0.9, 0.1]):.4f} 比特")
fig, ax = plt.subplots(figsize=(5, 3))
ax.plot(p_grid, h_bern)
ax.set_xlabel("p")
ax.set_ylabel("H(p) / bit")
ax.grid(alpha=0.3)
print("曲线在 p = 0.5 处取最大值 1 比特,在两端趋于 0")4.1.3 联合熵、条件熵与链式法则
联合熵
两个随机变量的联合熵(joint entropy)按联合分布定义:
它度量一对结果的整体不确定性。把
条件熵
条件熵(conditional entropy)定义为给定
计算时先算出每个
链式法则
联合熵与条件熵由链式法则(chain rule)联系:
推导直接从条件概率的乘积分解得到:
两边取负期望即得结论。推广到
链式法则把联合熵展开为逐步条件熵之和,是信息论中使用频率最高的恒等式。语言模型的困惑度、序列压缩比、熵率的存在性都由它推出。
次可加性与条件作用减少熵
由链式法则与条件熵的定义可以推出两条不等式。
次可加性:
右边的量正是互信息,非负性在 4.1.4 节证明。等号成立当且仅当
条件作用减少熵:
条件化的两个方向
条件作用减少熵
一个具体的例子来自 2.6 节的对撞结构:才华
import numpy as np
def joint_entropy(P):
"""联合熵,输入为联合概率矩阵"""
P = np.asarray(P, dtype=float)
P = P[P > 0]
return float(-(P * np.log2(P)).sum())
def entropy(p):
p = np.asarray(p, dtype=float)
p = p[p > 0]
return float(-(p * np.log2(p)).sum())
# 一个 2x2 的联合分布:两个二元变量
P = np.array([[0.4, 0.1],
[0.1, 0.4]])
px = P.sum(axis=1)
py = P.sum(axis=0)
H_xy = joint_entropy(P)
H_x, H_y = entropy(px), entropy(py)
H_y_given_x = H_xy - H_x
print(f"H(X) = {H_x:.4f}")
print(f"H(Y) = {H_y:.4f}")
print(f"H(X, Y) = {H_xy:.4f}")
print(f"H(Y|X) = {H_y_given_x:.4f} 链式法则验证:H(X) + H(Y|X) = {H_x + H_y_given_x:.4f}")
print(f"次可加性:H(X, Y) ≤ H(X) + H(Y),即 {H_xy:.4f} ≤ {H_x + H_y:.4f}")
print(f"互信息 I(X;Y) = H(X) + H(Y) - H(X, Y) = {H_x + H_y - H_xy:.4f}")4.1.4 互信息
定义与四种表达
互信息(mutual information)度量两个随机变量共享的信息量,定义为联合分布与边缘分布乘积之间的 KL 散度:
这一形式说明互信息度量的是实际联合分布与独立假设下的联合分布之间的差异。由 KL 散度的非负性(4.2 节给出证明)立即得到
由链式法则可以推出四种等价表达:
第一种表达解释为知道
| 关系 | 表达式 | 含义 |
|---|---|---|
| 互信息 | 共享的信息量 | |
| 边缘熵 | 各自的不确定性 | |
| 联合熵 | 整体不确定性 | |
| 条件熵 | 剩余不确定性 | |
| 等价式 | 三者之和等于联合熵 |
性质
对称性:
非负性:
自信息量:
有界性:
与相关性的对比:互信息为零等价于独立,而相关系数为零只意味着没有线性关联。
条件互信息
条件互信息(conditional mutual information)定义为在已知
它满足与互信息平行的一组性质:非负、对称、在给定
互信息也有链式法则:
它把多样本对同一目标的信息分解为逐项贡献之和,每一项是在已知前序变量的条件下的新增信息。这一形式在特征选择的贪心算法中直接使用。
import numpy as np
def mutual_information(P):
"""联合分布矩阵的互信息(比特)"""
P = np.asarray(P, dtype=float)
px = P.sum(axis=1, keepdims=True)
py = P.sum(axis=0, keepdims=True)
with np.errstate(divide="ignore", invalid="ignore"):
ratio = np.where(P > 0, P / (px * py), 1.0)
terms = np.where(P > 0, P * np.log2(ratio), 0.0)
return float(terms.sum())
# 独立情形
P_indep = np.outer([0.5, 0.5], [0.3, 0.7])
# 强相关情形
P_dep = np.array([[0.45, 0.05],
[0.05, 0.45]])
# 对撞结构下的情形:X、Y 独立,条件化于 Z 后相关(这里只比较边缘互信息)
print(f"独立时互信息 = {mutual_information(P_indep):.6f}")
print(f"相关时互信息 = {mutual_information(P_dep):.4f}")
# 用模拟样本估计互信息(直方图法,含有限样本偏差)
rng = np.random.default_rng(3)
n = 200_000
x = rng.normal(size=n)
y = x + 0.8 * rng.normal(size=n) # 强相关
bins = 40
H, _, _ = np.histogram2d(x, y, bins=bins, density=False)
P_hat = H / H.sum() + 1e-12
print(f"模拟样本的互信息估计 = {mutual_information(P_hat):.4f}")
print(f"同样本量下独立数据的估计值 = {mutual_information(np.outer(np.histogram(x, bins=bins)[0] / n, np.histogram(rng.normal(size=n), bins=bins)[0] / n) + 1e-12):.4f}")
# 独立数据的估计值应接近 0,但仍为正,这是有限样本的偏差4.1.5 数据处理不等式与 Fano 不等式
马尔可夫链与数据处理不等式
若给定
数据处理不等式(data processing inequality):若
直觉是信息经过任何处理只会损失,不会增加。证明思路是把
第一行中
数据处理不等式有大量直接推论。对数据做任何确定性变换(特征工程、量化、哈希)都会使互信息不增,这解释了为什么精细量化会损害分类性能。对数据进行可逆变换不改变信息量,这解释了为什么特征标准化、坐标旋转不影响理论上限,只影响算法的收敛速度。
Fano 不等式
Fano 不等式(Fano's inequality)把条件熵与推断误差联系起来。设
其中
推导思路是用
Fano 不等式的用途是给出误差下界。它说明无论用什么方法从
即至少
在机器学习语境下,Fano 不等式把特征与标签的互信息与可达的分类精度联系起来:互信息给定了误差的下界,增加模型容量无法突破这一界限,改进的方向只能是获取更多信息的特征。4.6 节会给出这一联系的更精细版本。
import numpy as np
def entropy(p):
p = np.asarray(p, dtype=float)
p = p[p > 0]
return float(-(p * np.log2(p)).sum())
def fano_bound(H_x_given_y, n_classes):
"""由条件熵给出的误差下界"""
return (H_x_given_y - 1) / np.log2(n_classes)
for h in [0.5, 1.5, 2.5]:
print(f"H(X|Y) = {h:.1f} 比特(8 类):误差下界 = {max(0.0, fano_bound(h, 8)):.4f}")
# 条件熵越小,误差下界越低;条件熵低于 1 比特时该下界不再有约束力(公式给出负值,取零)4.1.6 熵率
随机过程的熵率
单个随机变量的熵描述一个位置的不确定性,序列的整体不确定性增长速度由熵率(entropy rate)描述。对随机过程
极限的存在性由次可加性保证:令
平稳过程的熵率
对平稳过程(2.8 节),熵率有更便于计算的表达:
极限同样存在,且条件是单调递减的(由条件作用减少熵可知
独立序列与马尔可夫链
独立同分布序列:
一阶马尔可夫链:设转移矩阵为
这是各状态流出熵按平稳概率的加权平均。若链的某个状态确定性地转移到下一个状态(
import numpy as np
def markov_entropy_rate(P):
"""一阶马尔可夫链的熵率(比特/符号),P 为转移矩阵"""
P = np.asarray(P, dtype=float)
# 平稳分布:解 pi = pi P
eigvals, eigvecs = np.linalg.eig(P.T)
idx = np.argmin(np.abs(eigvals - 1))
pi = np.real(eigvecs[:, idx])
pi = pi / pi.sum()
row_ent = -np.sum(np.where(P > 0, P * np.log2(np.where(P > 0, P, 1)), 0), axis=1)
return float(pi @ row_ent), pi
# 天气链:晴/雨,晴天有 90% 概率继续晴
P = np.array([[0.9, 0.1],
[0.5, 0.5]])
rate, pi = markov_entropy_rate(P)
print(f"平稳分布 = {np.round(pi, 4)}")
print(f"熵率 = {rate:.4f} 比特/符号")
print(f"独立均匀序列的熵率(对照)= {np.log2(2):.4f} 比特/符号")自然语言的熵率。香农在 1951 年的实验中让人逐字猜测英文文本,用猜对的次数估计熵率,得到的量级约为每个字符
4.1.7 渐近均分性质与典型性
渐近均分性质
设
这是
这一结论称为渐近均分性质(asymptotic equipartition property,AEP)。它的含义是:几乎全部概率质量集中在那些概率接近
典型集
固定
典型集有三条关键性质。
概率趋于一:当
大小有上界:
大小有下界:对足够大的
两条界合起来给出:典型序列的条数量级为
这一结论是数据压缩的理论基础。需要区分的序列只有约
典型性的几种版本
弱典型与强典型的区别在于对序列概率的约束方式。弱典型(上面的定义)只约束经验熵接近真实熵;强典型要求序列的经验分布(每个符号出现的频率)接近真实分布,条件更强。强典型集合的自信息界与弱典型类似,但不依赖真实分布的尾部性质,在证明中更容易处理。
联合典型(jointly typical)把定义推广到序列对:
需要说明的是,典型序列的性质是高概率性质,而不是对每条序列都成立。非典型序列依然可能出现,只是概率极小;在有限
import numpy as np
# 用伯努利序列验证 AEP:经验熵随 n 收敛到真实熵
def empirical_entropy(seq):
"""由序列的经验分布计算熵"""
p1 = seq.mean()
ps = np.array([p1, 1 - p1])
ps = ps[ps > 0]
return float(-(ps * np.log2(ps)).sum())
rng = np.random.default_rng(11)
p_true = 0.3
H_true = -(p_true * np.log2(p_true) + (1 - p_true) * np.log2(1 - p_true))
print(f"真实熵 H(X) = {H_true:.4f} 比特")
for n in [10, 100, 1000, 10_000, 100_000]:
seq = rng.binomial(1, p_true, size=n)
est = empirical_entropy(seq)
print(f" n = {n:>7,}:经验熵 = {est:.4f},偏差 = {abs(est - H_true):.4f}")
# 典型集大小的估计:统计自信息落在 [H-0.1, H+0.1] 内的序列比例
n = 200
trials = 20_000
seqs = rng.binomial(1, p_true, size=(trials, n))
self_info = -(seqs * np.log2(p_true) + (1 - seqs) * np.log2(1 - p_true)).mean(axis=1)
in_typical = np.abs(self_info - H_true) <= 0.1
print(f"\n{trials} 次抽样中,典型序列(n = {n})的比例 = {in_typical.mean():.4f}")
print(f"典型序列条数的对数尺度的上界 ≈ n(H + 0.1) = {n * (H_true + 0.1):.1f}")4.1.8 本节小结
从自信息到典型性
本节从三条要求推出自信息必须取对数形式,取其期望得到熵。熵的三条基本性质(非负、上界为
熵度量的是单个分布的不确定性。许多问题关心的是两个分布之间的距离:用近似分布替代真实分布损失了多少信息、两个模型的预测分布相差多远、生成样本的分布与真实分布差多少。这类需求需要新的量,它们中的一些(KL 散度、交叉熵)正是本节互信息定义的构件。下一节系统整理这些散度与距离,说明各自的定义、性质与适用条件,并给出它们在优化与统计中的对应关系。
练习题
第 1 题 概念推导
设
参考答案
设
由
不用 KL 的证明:对数函数是凹函数,由 Jensen 不等式,
这里把
两条路径的关系是:Jensen 不等式的差正好等于 KL 散度,所以上面的推导同时给出了
第 2 题 计算推理
某推荐系统把用户分成两类(活跃、不活跃),把内容分成三类(新闻、娱乐、教程)。观测到的联合分布如下表。
| 新闻 | 娱乐 | 教程 | |
|---|---|---|---|
| 活跃 | 0.24 | 0.16 | 0.10 |
| 不活跃 | 0.06 | 0.14 | 0.30 |
请计算
参考答案
边缘分布:按行求和得
熵:
联合熵:逐项计算
条件熵:
互信息:
条件熵的分解:分别计算两类用户的条件熵。活跃用户的条件分布为
策略含义:互信息
第 3 题 代码验证
用模拟验证三条结论:其一,数据处理不等式,对含噪声的观测做量化处理后互信息不增;其二,渐近均分性质,经验熵随序列长度收敛到真实熵;其三,熵率,一阶马尔可夫链的熵率等于按平稳分布加权的状态流出熵。
参考答案
import numpy as np
def joint_mi(P):
P = np.asarray(P, dtype=float)
px = P.sum(axis=1, keepdims=True)
py = P.sum(axis=0, keepdims=True)
with np.errstate(divide="ignore", invalid="ignore"):
ratio = np.where(P > 0, P / (px * py), 1.0)
return float(np.where(P > 0, P * np.log2(ratio), 0.0).sum())
rng = np.random.default_rng(7)
n = 500_000
# 一条马尔可夫链 X -> Y -> Z
x = rng.binomial(1, 0.5, size=n)
y = rng.binomial(1, 0.85 * x + 0.15 * (1 - x)) # Y 含 X 的信息
z = rng.binomial(1, 0.65 * y + 0.35 * (1 - y)) # Z 只通过 Y 得到信息
def mi_from_samples(a, b, levels=2):
P = np.zeros((levels, levels))
for i, j in zip(a, b):
P[i, j] += 1
return joint_mi(P / P.sum())
print(f"I(X;Y) = {mi_from_samples(x, y):.4f}")
print(f"I(X;Z) = {mi_from_samples(x, z):.4f}")
print(f"I(Y;Z) = {mi_from_samples(y, z):.4f}")
# I(X;Z) 明显小于 I(X;Y),验证数据处理不等式
# 量化处理:把连续观测分箱后互信息下降
u = rng.normal(size=n)
v = u + 0.5 * rng.normal(size=n)
for bins in [4, 16, 64]:
qu = np.digitize(u, np.quantile(u, np.linspace(0, 1, bins + 1)[1:-1]))
qv = np.digitize(v, np.quantile(v, np.linspace(0, 1, bins + 1)[1:-1]))
P = np.zeros((bins, bins))
np.add.at(P, (qu, qv), 1)
print(f"分箱数 {bins:3d}:量化后的互信息 = {joint_mi(P / P.sum()):.4f}")
# 分箱越粗,互信息越小,量化损失的正是被抹平的细节
# 马尔可夫链熵率
P = np.array([[0.8, 0.1, 0.1],
[0.2, 0.7, 0.1],
[0.1, 0.2, 0.7]])
w, V = np.linalg.eig(P.T)
pi = np.real(V[:, np.argmin(np.abs(w - 1))])
pi = pi / pi.sum()
row_ent = -np.sum(np.where(P > 0, P * np.log2(np.where(P > 0, P, 1)), 0), axis=1)
print(f"\n平稳分布 = {np.round(pi, 4)}")
print(f"熵率(解析)= {pi @ row_ent:.4f} 比特/符号")
# 用长轨道估计熵率:条件熵 H(X_n | X_{n-1}) 的样本估计
states = np.empty(200_000, dtype=int)
states[0] = 0
for t in range(1, len(states)):
states[t] = rng.choice(3, p=P[states[t - 1]])
P_hat = np.zeros((3, 3))
for a, b in zip(states[:-1], states[1:]):
P_hat[a, b] += 1
P_hat = P_hat / P_hat.sum(axis=1, keepdims=True)
row_ent_hat = -np.sum(np.where(P_hat > 0, P_hat * np.log2(np.where(P_hat > 0, P_hat, 1)), 0), axis=1)
print(f"由轨道估计的转移熵(按平稳分布加权)= {pi @ row_ent_hat:.4f}")三项验证的结果说明:数据处理不等式在具体数据上表现为观测链条越长信息越少;量化会单调地降低互信息,粗量化损失更大;马尔可夫链的熵率可以由解析公式算出,也可以由长轨道上的经验条件熵估计,两者一致。第三条的实践意义是熵率可以从数据估计,这使压缩比的理论下界可以直接计算。
第 4 题 综合应用
某团队研究一个文本分类任务,
参考答案
误差下界:由
代入 Fano 不等式:
即在当前特征下,无论用什么分类器,错误率都不可能低于约
把误差降到 10% 以下所需的互信息:要
对应
特征变换的利弊:由数据处理不等式,任何确定性变换
常见错误
错误 1 · 把熵当作随机变量自身的属性
原因:熵由分布决定,与取值的具体标签无关。把
解决:计算熵之前先写出分布,把标签视为等价的符号。需要利用取值之间的结构(例如有序类别、数值距离)时,改用能反映结构的度量,如 Wasserstein 距离或基于核的散度,见 4.2 节。
错误 2 · 认为增加条件总是减少熵
原因:条件作用减少熵
解决:区分两条结论:无条件熵大于等于任何单条件下的熵,这条成立;多个条件之间的关系需要具体分析依赖结构,对撞结构下条件化会增加关联,共因结构下条件化会减少关联。
错误 3 · 混淆比特与奈特
原因:同一分布的熵在不同底数下数值不同,换算因子约为
解决:在全文与代码中统一底数,代码里显式写出
错误 4 · 用有限样本的直方图估计互信息而不做偏差修正
原因:分箱估计互信息存在系统性上偏。即使两个变量独立,有限样本下估计值也大于零,因为每个箱内的样本都很少、经验分布必然偏离独立结构。样本量越小、分箱越细,偏差越大。这一偏差在特征选择中会导致大量无关特征被评为相关。
解决:控制分箱数量(每箱至少有几十个样本),或改用有偏差修正的估计方法(如 Miller-Madow 修正、基于 k 近邻的 Kozachenko-Leonenko 估计、神经网络互信息估计,见 4.7 节)。比较不同特征时使用相同的样本量与分箱方案,并用置换检验给出零分布作为基准。