这是 数学重学路线图 阶段五的子页面
信息论基础:用数学衡量”信息” 信息论是香农(Claude Shannon)在 1948 年创建的
核心问题:信息怎么度量?通信极限在哪?
对安全、大数据、后端都有直接应用
一、信息量:越不可能,信息越大 直觉 “明天太阳从东边升起” → 信息量 = 0(废话,你早就知道了)
“明天太阳从西边升起” → 信息量极大(这也太震惊了)
越不可能发生 的事,一旦发生,传达的信息量越大
公式 I(x) = -log₂ P(x)
P(x) 是事件发生的概率
单位:比特(bit)
例子 抛硬币正面朝上:P = 0.5,I = -log₂(0.5) = 1 bit
掷骰子出6:P = 1/6,I = -log₂(1/6) ≈ 2.58 bit
必然事件:P = 1,I = -log₂(1) = 0 bit(果然是废话)
为什么用 log? 独立事件的信息量可加 :A和B同时发生的信息量 = I(A) + I(B)
P(A∩B) = P(A)·P(B),所以 -log(P(A)·P(B)) = -log(P(A)) - log(P(B))
log 把乘法变加法,完美适配
Python 计算信息量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import numpy as npdef information (p ):"""计算信息量(单位:bit)""" if p <= 0 or p > 1 : raise ValueError("概率必须在 (0, 1] 范围内" ) return -np.log2(p)events = { "抛硬币正面" : 0.5 ,"掷骰子出6" : 1 /6 ,"生日在某天" : 1 /365 ,"彩票中奖" : 1 /10_000_000 ,"必然事件" : 1.0 ,} for name, prob in events.items():info = information(prob) print (f"{name:12s} : P={prob:.8 f} , 信息量={info:.2 f} bit" )
二、熵(Entropy):不确定性的度量 直觉 熵衡量一个随机变量的不确定性 有多大
或者说:平均来看,每次观测能获得多少信息
公式 H(X) = -Σ P(x) · log₂ P(x)
就是信息量的期望值 (加权平均)
关键性质 最大熵 = 均匀分布
所有结果等概率 → 不确定性最大
公平硬币:H = 1 bit(最大)
双面都是正面的硬币:H = 0 bit(毫无不确定性)
最小熵 = 确定事件
H = 0,完全没有不确定性
熵的范围:0 ≤ H(X) ≤ log₂(n),n 是可能结果数
直觉比喻 熵 = 惊喜度的平均值
高熵 → 经常被惊到 → 很难预测
低熵 → 很少被惊到 → 容易预测
Python 计算熵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import numpy as npdef entropy (probs ):"""计算离散分布的熵""" probs = np.array(probs, dtype=float ) probs = probs[probs > 0 ] return -np.sum (probs * np.log2(probs))print (f"公平硬币: H = {entropy([0.5 , 0.5 ]):.4 f} bit" ) print (f"偏硬币(0.9/0.1): H = {entropy([0.9 , 0.1 ]):.4 f} bit" ) print (f"公平骰子: H = {entropy([1 /6 ]*6 ):.4 f} bit" ) uniform = [0.25 , 0.25 , 0.25 , 0.25 ] concentrated = [0.97 , 0.01 , 0.01 , 0.01 ] print (f"均匀分布: H = {entropy(uniform):.4 f} bit" ) print (f"集中分布: H = {entropy(concentrated):.4 f} bit" )
熵的可视化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import numpy as npimport matplotlib.pyplot as pltp = np.linspace(0.001 , 0.999 , 500 ) h = -p * np.log2(p) - (1 - p) * np.log2(1 - p) plt.figure(figsize=(8 , 5 )) plt.plot(p, h, 'b-' , linewidth=2 ) plt.xlabel('P(正面)' ) plt.ylabel('熵 H (bit)' ) plt.title('二元熵函数:p=0.5时熵最大' ) plt.axvline(x=0.5 , color='r' , linestyle='--' , alpha=0.5 ) plt.grid(True ) plt.show()
三、交叉熵:用错误编码的代价 直觉 假设真实分布是 P,但你以为是 Q
你用 Q 来编码 P 的消息,平均需要多少 bit?
这就是交叉熵
公式 H(P, Q) = -Σ P(x) · log₂ Q(x)
注意:是 P 的概率乘 Q 的 log
性质 H(P, Q) ≥ H(P)(交叉熵总是 ≥ 真实熵)
当 Q = P 时,交叉熵 = 真实熵(最优编码)
Q 偏离 P 越远,交叉熵越大
机器学习中的交叉熵损失 分类任务中:P = 真实标签分布,Q = 模型预测分布
最小化交叉熵 = 让模型预测尽量接近真实分布
这就是为什么分类用交叉熵损失而不是 MSE
Python 交叉熵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import numpy as npdef cross_entropy (p, q ):"""交叉熵 H(P, Q)""" p = np.array(p, dtype=float ) q = np.array(q, dtype=float ) q = np.clip(q, 1e-15 , 1 ) return -np.sum (p * np.log2(q))p = [0.7 , 0.2 , 0.1 ] q_good = [0.65 , 0.25 , 0.10 ] q_bad = [0.33 , 0.33 , 0.34 ] print (f"真实熵 H(P): {cross_entropy(p, p):.4 f} bit" )print (f"好预测 H(P, Q): {cross_entropy(p, q_good):.4 f} bit" )print (f"差预测 H(P, Q): {cross_entropy(p, q_bad):.4 f} bit" )
四、KL 散度:两个分布有多”不同” 直觉 衡量用 Q 近似 P 时”浪费”了多少 bit
KL 散度 = 交叉熵 - 真实熵
公式 KL(P || Q) = Σ P(x) · log₂(P(x) / Q(x))
等价于:KL(P || Q) = H(P, Q) - H(P)
关键性质 KL ≥ 0,等号成立当且仅当 P = Q
不对称 :KL(P || Q) ≠ KL(Q || P)
所以 KL 不是真正的”距离”(不满足对称性)
Python 计算 KL 散度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import numpy as npfrom scipy.stats import entropy as scipy_entropydef kl_divergence (p, q ):"""KL(P || Q)""" p = np.array(p, dtype=float ) q = np.array(q, dtype=float ) q = np.clip(q, 1e-15 , 1 ) return np.sum (p * np.log2(p / q))p = [0.7 , 0.2 , 0.1 ] q = [0.33 , 0.33 , 0.34 ] print (f"KL(P || Q) = {kl_divergence(p, q):.4 f} bit" )print (f"KL(Q || P) = {kl_divergence(q, p):.4 f} bit" ) kl_scipy = scipy_entropy(p, q) kl_bits = kl_scipy / np.log(2 ) print (f"scipy KL = {kl_bits:.4 f} bit" )
五、互信息:两变量共享多少信息 直觉 知道了 X,对猜测 Y 有多大帮助?
互信息 = 0 → X 和 Y 独立,互不相干
互信息大 → 知道一个对猜另一个帮助很大
公式 I(X; Y) = H(X) - H(X|Y) = H(X) + H(Y) - H(X, Y)
与相关系数的区别 相关系数只能衡量线性 关系
互信息能捕捉任意 关系(包括非线性)
Python 互信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from sklearn.metrics import mutual_info_scoreimport numpy as npx = [0 , 0 , 1 , 1 , 2 , 2 ] y = [0 , 0 , 1 , 1 , 2 , 2 ] print (f"完全相关的互信息: {mutual_info_score(x, y):.4 f} " )np.random.seed(42 ) x_rand = np.random.randint(0 , 3 , 10000 ) y_rand = np.random.randint(0 , 3 , 10000 ) print (f"独立变量的互信息: {mutual_info_score(x_rand, y_rand):.4 f} " )y_partial = x_rand.copy() noise_idx = np.random.choice(10000 , 3000 , replace=False ) y_partial[noise_idx] = np.random.randint(0 , 3 , 3000 ) print (f"部分相关的互信息: {mutual_info_score(x_rand, y_partial):.4 f} " )
六、数据压缩:熵是压缩极限 核心思想 数据有冗余(熵 < 最大可能熵)→ 可以压缩
压缩极限 = 熵 ,不可能压到比熵还小
这就是香农的信源编码定理
霍夫曼编码 思路:高频字符用短编码,低频字符用长编码
保证前缀无歧义 (没有一个编码是另一个的前缀)
是最优的前缀编码
Python 手动实现霍夫曼编码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import heapqfrom collections import Counterclass HuffmanNode :def __init__ (self, char, freq ): self .char = char self .freq = freq self .left = None self .right = None def __lt__ (self, other ): return self .freq < other.freq def build_huffman_tree (text ):freq = Counter(text) heap = [HuffmanNode(ch, f) for ch, f in freq.items()] heapq.heapify(heap) while len (heap) > 1 : left = heapq.heappop(heap) right = heapq.heappop(heap) merged = HuffmanNode(None , left.freq + right.freq) merged.left = left merged.right = right heapq.heappush(heap, merged) return heap[0 ]def get_codes (node, prefix="" , codes=None ):if codes is None : codes = {} if node.char is not None : codes[node.char] = prefix or "0" return codes get_codes(node.left, prefix + "0" , codes) get_codes(node.right, prefix + "1" , codes) return codestext = "aaabbbccddddddeeeeeeeeee" tree = build_huffman_tree(text) codes = get_codes(tree) print ("字符 | 频率 | 编码" )print ("-" * 30 )freq = Counter(text) for ch, code in sorted (codes.items()):print (f" {ch} | {freq[ch]:2d} | {code} " )original_bits = len (text) * 8 compressed_bits = sum (len (codes[ch]) for ch in text) print (f"\n原始大小: {original_bits} bit" )print (f"压缩后: {compressed_bits} bit" )print (f"压缩率: {compressed_bits/original_bits:.1 %} " )import numpy as npprobs = [f / len (text) for f in freq.values()] H = -sum (p * np.log2(p) for p in probs) print (f"熵(理论极限): {H:.4 f} bit/字符" )print (f"实际平均编码长度: {compressed_bits/len (text):.4 f} bit/字符" )
七、安全中的信息论 密码强度 = 熵 密码熵越高,越难破解
密码熵 = log₂(字符空间 ^ 密码长度)
8位纯数字:log₂(10⁸) ≈ 26.6 bit(很弱)
12位混合大小写+数字+特殊:log₂(94¹²) ≈ 78.7 bit(还行)
Python 密码熵计算器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import mathimport stringdef password_entropy (password ):"""计算密码的熵(bit)""" charset_size = 0 has_lower = any (c in string.ascii_lowercase for c in password) has_upper = any (c in string.ascii_uppercase for c in password) has_digit = any (c in string.digits for c in password) has_special = any (c in string.punctuation for c in password) if has_lower: charset_size += 26 if has_upper: charset_size += 26 if has_digit: charset_size += 10 if has_special: charset_size += 32 if charset_size == 0 : return 0 entropy = len (password) * math.log2(charset_size) return entropypasswords = [ "12345678" , "password" , "Password1" , "P@ssw0rd!2024" , "correct horse battery staple" , ] print (f"{'密码' :<35s} {'熵(bit)' :>8s} {'强度' } " )print ("-" * 60 )for pwd in passwords:ent = password_entropy(pwd) strength = "弱" if ent < 40 else "中" if ent < 60 else "强" if ent < 80 else "很强" print (f"{pwd:<35s} {ent:>8.1 f} {strength} " )
随机数质量评估 好的随机数生成器产生的序列熵应接近最大熵
如果熵偏低 → 随机数有规律 → 可被预测 → 不安全
隐写分析 正常图片的像素分布有一定的熵
隐写术会改变像素分布 → 熵发生变化
通过检测熵的异常来发现隐写内容
八、大数据中的信息论 特征选择(互信息法) 计算每个特征与目标变量的互信息
互信息高的特征更有价值
比相关系数更强大(能捕捉非线性关系)
决策树分裂(信息增益) 决策树选择分裂特征的标准:信息增益
信息增益 = H(父节点) - Σ权重 · H(子节点)
选信息增益最大的特征来分裂
1 2 3 4 5 6 7 8 9 10 11 12 import numpy as npfrom sklearn.datasets import load_irisfrom sklearn.feature_selection import mutual_info_classifiris = load_iris() mi_scores = mutual_info_classif(iris.data, iris.target, random_state=42 ) print ("特征互信息得分:" )for name, score in zip (iris.feature_names, mi_scores):bar = "█" * int (score * 30 ) print (f" {name:20s} : {score:.4 f} {bar} " )
数据压缩 日志数据有大量重复 → 高冗余 → 高压缩率
加密数据熵接近最大 → 几乎不可压缩
这就是为什么要先压缩再加密(反过来效果很差)
九、后端中的信息论 日志压缩 日志有大量重复模式 → 熵远低于最大熵 → 可以高效压缩
gzip/zstd 等压缩算法本质上在利用低熵
传输优化 信息论告诉你:信道容量 = 带宽上限
压缩到接近熵 → 最大化信道利用率
缓存策略 高熵(不可预测)的访问模式 → 缓存命中率低
低熵(可预测)的访问模式 → 缓存命中率高
可以用熵来评估缓存策略的理论上限
十、练习题 题目1:信息量计算 一副 52 张扑克牌,随机抽一张。抽到红心 A 的信息量是多少 bit?
答案
P(红心A) = 1/52
I = -log₂(1/52) = log₂(52) ≈ 5.70 bit
题目2:熵的比较 两个骰子:A 是公平骰子(六面等概率),B 是灌铅骰子(6 出现概率 0.5,其他各 0.1)。谁的熵大?计算两者的熵。
答案
1 2 3 4 5 6 7 8 9 10 import numpy as npdef entropy (probs ):probs = np.array(probs) return -np.sum (probs * np.log2(probs))h_a = entropy([1 /6 ]*6 ) h_b = entropy([0.1 , 0.1 , 0.1 , 0.1 , 0.1 , 0.5 ]) print (f"公平骰子: H = {h_a:.4 f} bit" ) print (f"灌铅骰子: H = {h_b:.4 f} bit" )
公平骰子熵更大(不确定性更高)
题目3:交叉熵应用 为什么机器学习分类任务用交叉熵损失而不是 MSE?从信息论角度解释。
答案
交叉熵直接衡量预测分布和真实分布的差异
当预测概率接近 0 但真实标签是 1 时,交叉熵惩罚极大(-log(0) → ∞)
MSE 对这种情况的惩罚力度不够((1-0)² = 1,有限值)
交叉熵的梯度在错误预测时更大,训练更快
题目4:密码安全 你的系统要求密码至少 60 bit 熵。如果只允许小写字母 + 数字(36 个字符),密码至少要多少位?
答案
每个字符的熵 = log₂(36) ≈ 5.17 bit
需要位数 = 60 / 5.17 ≈ 11.6
所以至少需要 12 位
题目5:Python 实践 写一个函数,输入一段文本,输出每个字符的霍夫曼编码以及理论压缩率
答案
参考本页”霍夫曼编码”部分的完整代码
压缩率 = 霍夫曼编码总 bit 数 / (字符数 × 8)
理论极限 = 熵 / 8(每字符)
十一、总结与关联 信息论核心概念:
信息量 → 事件的惊讶程度
熵 → 平均惊讶程度(不确定性)
交叉熵 → 用错误模型的代价
KL 散度 → 两个分布的”距离”
互信息 → 两变量的关联度
关联笔记
← 26-优化与梯度下降 交叉熵作为损失函数
← 数学重学/18-概率基础 概率是信息论的地基
→ 28-密码学数学进阶 密码学中的熵和信息安全