《信息论基础・逐式推导|01 熵的直觉、公理与 Python 验证》

35 阅读1分钟

信息论・逐式推导|01 熵的直觉、公理与 Python 验证

翻开 Cover & Thomas 的《信息论基础》,"仅需大学数学基础"——可能他们的"大学"是数学系,我的"大学"是校门口小吃一条街。
满书公式让人沉默,但我是真心喜欢。看不懂就慢慢啃,用 Python 一行行实现、调试、画图。
把熵、互信息、信道容量写成代码,比干瞪眼推导踏实多了。


写在前面

这本封面上的《信息论基础》(Cover & Thomas)堪称信息论领域的"圣经"。它之所以经典,是因为它把香农的思想用极其严谨又不失直观的方式呈现了出来。

熵,正是这整个理论体系的基石。下面我们对这个公式进行一次深度拆解,并用 Python 验证它的关键性质。


一、核心公式与直觉

对于一个取有限个值的离散随机变量 XX,其概率分布为 p(x)=P(X=x)p(x) = P(X=x),它的熵 H(X)H(X) 定义为:

H(X)=xXp(x)logp(x)H(X) = -\sum_{x \in \mathcal{X}} p(x) \log p(x)

约定:

  • 通常取以 2 为底的对数,熵的单位是 比特(bits)。
  • p(x)=0p(x)=0 时,规定 0log0=00 \cdot \log 0 = 0(符合极限结果)。

三个等价视角

视角一:不确定性的度量
抛一枚公平硬币前,你的不确定性最大;如果硬币两面都是正面,则毫无悬念。熵就是量化"不可预测程度"。概率越平均,熵越大。

视角二:信息内容的期望
logp(x)-\log p(x) 是观察到事件 xx 时获得的"信息量"或"惊异度"。概率越小,信息量越大。熵 H(X)H(X) 是所有可能事件信息量的期望值

H(X)=EX[log1p(X)]H(X) = \mathbb{E}_{X} \left[ \log \frac{1}{p(X)} \right]

视角三:高效编码的极限
香农的源头洞见:如果一个信源以分布 p(x)p(x) 产生符号,无论怎么设计编码,平均每个符号所需的最少比特数就是 H(X)H(X)。这是熵的操作性定义。


二、公理化推导:为什么必须是这个形式?

为什么不能用 1maxp(x)1 - \max p(x) 或其他函数?香农提出三条公理,证明了 plogp-\sum p \log p 是唯一解。

H(p1,,pn)H(p_1, \dots, p_n) 是待求度量。

公理1:连续性HHpip_i 连续变化。

公理2:极大性 – 当所有事件等概率 pi=1/np_i = 1/n 时不确定性最大;且随 nn 增加而增加。

公理3:可分解性 – 总不确定性 = 分组决策的不确定性 + 组内决策不确定性的加权平均。

例:有事件 A(1/2), B(1/3), C(1/6)。
总不确定性 H(1/2,1/3,1/6)H(1/2, 1/3, 1/6) 应等于: 先判断是 {A} 还是 {B, C} 的不确定性 H(1/2,1/2)H(1/2, 1/2)
然后以 1/2 的概率进入 {B, C} 组,再区分 B 和 C 的不确定性 H(2/3,1/3)H(2/3, 1/3)
即:

H(12,13,16)=H(12,12)+12H(23,13)H\left(\frac{1}{2}, \frac{1}{3}, \frac{1}{6}\right) = H\left(\frac{1}{2}, \frac{1}{2}\right) + \frac{1}{2}H\left(\frac{2}{3}, \frac{1}{3}\right)

为什么强制对数?
概率乘法 p(组合)=p()p(组内)p(组合) = p(组) \cdot p(组内) 需映射为信息量加法,而对数恰好满足 log(AB)=logA+logB\log(AB) = \log A + \log B


三、数学性质与 Python 验证

1. 对数底数的选择

  • 底数 2 → 比特(计算机标准)
  • 底数 e → 纳特(微积分方便)
  • 底数 10 → 哈特(工程)

换算:log2p=lnp/ln2\log_2 p = \ln p / \ln 2

2. 熵的上界

0H(X)logX0 \le H(X) \le \log |\mathcal{X}|
  • 下界:分布完全确定(一个概率1,其余0)
  • 上界:均匀分布时取等,利用 Jensen 不等式证明。

3. 链式法则

H(X,Y)=H(X)+H(YX)H(X, Y) = H(X) + H(Y|X)

Python 验证:绘制伯努利分布的熵曲线

"""
Python 验证:绘制伯努利分布的熵曲线

功能:
1. 定义熵的计算函数
2. 生成概率值并计算对应熵值
3. 绘制熵随概率变化的曲线
4. 验证均匀分布(p=0.5)时熵最大
5. 保存图片到项目 assets 目录
"""

import numpy as np
import matplotlib.pyplot as plt
import os


def set_chinese_font():
    """设置 matplotlib 支持中文显示"""
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
    plt.rcParams['axes.unicode_minus'] = False


def entropy(p):
    """计算二值随机变量的熵(单位:比特)"""
    p = np.clip(p, 1e-12, 1 - 1e-12)  # 避免 log(0)
    return - (p * np.log2(p) + (1-p) * np.log2(1-p))


def main():
    # 设置中文字体
    set_chinese_font()
    
    # 生成概率值(0到1之间取100个点)
    p_vals = np.linspace(0, 1, 100)
    h_vals = entropy(p_vals)

    # 创建绘图
    plt.figure(figsize=(8, 5))
    
    # 绘制熵曲线
    plt.plot(p_vals, h_vals, linewidth=2, color='#00E5FF')
    plt.fill_between(p_vals, 0, h_vals, alpha=0.2, color='#00E5FF')
    
    # 添加标签和标题
    plt.xlabel('p (正面概率)', fontsize=12)
    plt.ylabel('熵 H (比特)', fontsize=12)
    plt.title('伯努利分布的熵:不确定性在 p=0.5 时最大', fontsize=14)
    
    # 添加网格和参考线
    plt.grid(True, alpha=0.3)
    plt.axvline(0.5, linestyle='--', alpha=0.5, color='gray')
    plt.axhline(1.0, linestyle='--', alpha=0.5, color='gray')
    plt.text(0.5, 1.02, '最大熵 = 1 bit', ha='center')
    
    # 创建 assets 目录(如果不存在)
    assets_dir = os.path.join(os.path.dirname(__file__), '..', 'assets')
    os.makedirs(assets_dir, exist_ok=True)
    
    # 保存图片
    output_path = os.path.join(assets_dir, 'entropy_curve.png')
    plt.savefig(output_path, dpi=100, bbox_inches='tight')
    plt.close()  # 关闭图形释放内存
    
    print(f"图片已保存到: {output_path}")
    
    # 输出关键验证结果
    max_entropy = np.max(h_vals)
    max_index = np.argmax(h_vals)
    max_p = p_vals[max_index]
    print(f"验证结果:")
    print(f"最大熵值: {max_entropy:.4f} 比特")
    print(f"对应概率: p = {max_p:.2f}")
    print(f"结论: 均匀分布(p=0.5)时熵最大,符合理论预期")

运行结果:

验证结果:
最大熵值: 0.9999 比特
对应概率: p = 0.49
结论: 均匀分布(p=0.5)时熵最大,符合理论预期

[伯努利分布的熵曲线]

entropy_curve.png

验证链式法则(简单示例)

"""
验证链式法则(简单示例)

功能:
1. 定义联合分布 P(X,Y)
2. 计算边缘熵 H(X)、条件熵 H(Y|X)、联合熵 H(X,Y)
3. 验证链式法则:H(X,Y) = H(X) + H(Y|X)
"""

import numpy as np


def main():
    # 联合分布 P(X,Y)
    # X 取值 0,1;Y 取值 0,1
    p_xy = np.array([[0.2, 0.3],
                     [0.1, 0.4]])
    
    # 计算边缘分布 P(X)
    p_x = p_xy.sum(axis=1)  # [0.5, 0.5]
    
    # 计算条件分布 P(Y|X)
    p_y_given_x = p_xy / p_x[:, None]
    
    # 计算各熵值
    H_X = -np.sum(p_x * np.log2(p_x))
    H_Y_given_X = -np.sum(p_xy * np.log2(p_y_given_x))
    H_XY = -np.sum(p_xy * np.log2(p_xy))
    
    # 输出结果
    print("=== 链式法则验证 ===")
    print(f"联合分布 P(X,Y):")
    print(p_xy)
    print(f"\n边缘分布 P(X): {p_x}")
    print(f"\n条件分布 P(Y|X):")
    print(p_y_given_x)
    print(f"\n=== 熵值计算 ===")
    print(f"H(X) = {H_X:.4f} 比特")
    print(f"H(Y|X) = {H_Y_given_X:.4f} 比特")
    print(f"H(X,Y) = {H_XY:.4f} 比特")
    print(f"\n=== 链式法则验证 ===")
    print(f"H(X) + H(Y|X) = {H_X + H_Y_given_X:.4f} 比特")
    
    # 验证等式是否成立
    if np.isclose(H_XY, H_X + H_Y_given_X):
        print("\n✓ 链式法则验证通过:H(X,Y) = H(X) + H(Y|X)")
    else:
        print("\n✗ 链式法则验证失败")

运行结果:

=== 链式法则验证 ===
联合分布 P(X,Y):
[[0.2 0.3] [0.1 0.4]]

边缘分布 P(X): [0.5 0.5]

条件分布 P(Y|X):
[[0.4 0.6] [0.2 0.8]]

=== 熵值计算 ===
H(X) = 1.0000 比特
H(Y|X) = 0.8464 比特
H(X,Y) = 1.8464 比特

=== 链式法则验证 ===
H(X) + H(Y|X) = 1.8464 比特

验证通过!链式法则 H(X,Y)=H(X)+H(YX)H(X,Y) = H(X) + H(Y|X) 成立。


四、Cover & Thomas 书中的严谨化

典型集与 AEP(渐近均分性质)
Xip(x)X_i \sim p(x) 独立同分布,则 1nlogp(X1,,Xn)H(X)-\frac{1}{n}\log p(X_1,\dots,X_n) \to H(X)
所有序列的概率质量几乎全集中在约 2nH(X)2^{nH(X)} 个"典型序列"上。这正是数据压缩的理论依据。

与 KL 散度的联系

H(X)=logXD(pu)H(X) = \log |\mathcal{X}| - D(p \| u)

其中 uu 为均匀分布。你的实际分布离"完全无知"越远(KL 散度越大),不确定性越小。


五、结语与下期预告

熵的公式 plogp-\sum p \log p 不是凭空捏造,它是衡量"不确定性"的唯一自洽尺度。
《信息论基础》用代码推导,是我的"降熵"方式

下一篇:敬请期待。

代码仓库准备中:
👉 即将同步至 Gitee(info-theory-playground)

欢迎在评论区评论指正。


技术标签#信息论 #熵 #Python #机器学习 #数学基础

专栏:《信息论・逐式推导》第 1 篇
敬请期待下一篇:敬请期待