信息论计算实战:从香农熵、互信息与信道容量到霍夫曼编码、汉明纠错与率失真理论的完整工程链路

信息论不是通信工程师的专利,它是所有"从噪声中提取信号、从冗余中压缩信息、从不确定性中做决策"系统的共同数学底座。本文沿着香农 1948 年的原始脉络,把信息论拆成可运行、可验证的工程零件:先建立熵与互信息这套信息度量的"语言",再落地信源编码(霍夫曼)、信道容量(香农极限)、信道编码(汉明纠错)、率失真(有损压缩极限)四条主线,最后把每一块映射回 AI/深度学习——你会发现交叉熵损失、VAE 正则、信息瓶颈、量化蒸馏,全都是信息论原语的旧瓶装新酒。

一、信息度量:自信息、香农熵与 KL 散度

信息的单位来自"惊讶程度"。一个必然会发生的事件不携带信息;一个概率极低的事件一旦发生,携带大量信息。香农把单点自信息定义为 I(x) = -log2 P(x)(单位 bit),把整条分布的不确定性总量定义为熵 H(X) = -Σ P(x) log2 P(x)。熵是衡量随机性的标尺:公平硬币 H=1 bit,确定事件 H=0,而越均匀的分布熵越高。

互信息 I(X;Y) 衡量两个变量共享的信息量,等价于"知道 Y 后 X 不确定性减少了多少"。一个极其重要的性质是 I(X;Y) = H(X) - H(X|Y),且互信息非负、I(X;Y) ≤ min(H(X), H(Y))。KL 散度(相对熵)D_KL(P‖Q) 衡量两个分布的"距离"(不对称),它是信息论与机器学习之间最频繁的桥。下面用纯 Python 把这套度量全部实现并验证。


import math

def log2(x):
    return math.log2(x) if x > 0 else 0.0

def entropy(p):
    """香农熵 H(X) = -Σ p_i log2 p_i"""
    return -sum(log2(pi) * pi for pi in p)

def joint_entropy(pxy):
    """pxy: dict {(x,y): prob}"""
    return -sum(log2(v) * v for v in pxy.values())

def mutual_information(pxy):
    """I(X;Y) = ΣΣ p(x,y) log2 p(x,y)/(p(x)p(y))"""
    px = {}; py = {}
    for (x, y), v in pxy.items():
        px[x] = px.get(x, 0) + v
        py[y] = py.get(y, 0) + v
    mi = 0.0
    for (x, y), v in pxy.items():
        mi += v * log2(v / (px[x] * py[y]))
    return mi

def kl_divergence(p, q):
    """D_KL(P||Q) = Σ p_i log2(p_i/q_i)  (base-2 → bits)"""
    return sum(pi * (log2(pi) - log2(qi)) for pi, qi in zip(p, q) if pi > 0)

# --- 验证 ---
fair = [0.5, 0.5]
print("fair coin H =", round(entropy(fair), 3))   # 1.0

# 独立随机变量 → I=0
pxy_ind = {(0,0):0.25,(0,1):0.25,(1,0):0.25,(1,1):0.25}
print("independent I(X;Y) =", round(mutual_information(pxy_ind), 3))  # 0.0

# 相关变量
pxy_corr = {(0,0):0.4,(0,1):0.1,(1,0):0.1,(1,1):0.4}
mi = mutual_information(pxy_corr)
HX = entropy([0.5,0.5]); HY = entropy([0.5,0.5])
print("correlated I(X;Y) =", round(mi, 3))        # >0 (≈0.279)
print("I <= min(HX,HY)?", mi <= min(HX,HY)+1e-9)  # True

# KL >=0 且不对称
p = [0.5,0.3,0.2]; q = [1/3,1/3,1/3]
print("KL(P||Q) =", round(kl_divergence(p,q),3))  # >0
print("KL(Q||P) =", round(kl_divergence(q,p),3))  # 与上式不同

运行结果完全符合理论预期:公平硬币熵恰为 1.0 bit;独立变量互信息严格为 0;相关变量互信息 ≈0.279 且被 min(H(X),H(Y))=1 牢牢上界约束;KL 散度非负且 D_KL(P‖Q)≠D_KL(Q‖P),这正是它在变分推断里"前向/反向"两种用法行为截然不同的根源。

二、信源编码:霍夫曼最优前缀码

香农源编码定理告诉我们,无损压缩的平均码长 L 必然满足 H(X) ≤ L < H(X)+1。霍夫曼算法给出一个贪心构造的最优前缀码:每次合并两个概率最小的节点,给左支标 0、右支标 1,直到合成一棵树,从根到叶的路径就是该符号的码字。它保证平均码长最小,且没有任何码字是别的码字的前缀(前缀码),因而解码无歧义。


import heapq, math

def huffman(freqs):
    """freqs: dict symbol->count. 返回 code dict {symbol: bits}"""
    heap = [[w, [sym, ""]] for sym, w in freqs.items()]
    heapq.heapify(heap)
    while len(heap) > 1:
        lo = heapq.heappop(heap); hi = heapq.heappop(heap)
        for pair in lo[1:]: pair[1] = "0" + pair[1]
        for pair in hi[1:]: pair[1] = "1" + pair[1]
        heapq.heappush(heap, [lo[0]+hi[0]] + lo[1:] + hi[1:])
    return {sym: cd for sym, cd in heap[0][1:]}

def encode(text, code):
    return "".join(code[ch] for ch in text)

def decode(bits, code):
    rev = {v:k for k,v in code.items()}
    out=""; cur=""
    for b in bits:
        cur += b
        if cur in rev:
            out += rev[cur]; cur=""
    return out

freqs = {"a":5,"b":9,"c":12,"d":13,"e":16,"f":45}
code = huffman(freqs)
print("codes:", code)
text = "abfacfebfacdeaf"
bits = encode(text, code)
dec = decode(bits, code)
print("roundtrip ok:", dec == text)   # True

total = sum(freqs.values())
H = -sum((w/total)*math.log2(w/total) for w in freqs.values())
avg_len = sum(len(code[s])*w for s,w in freqs.items())/total
print("entropy H =", round(H,3), "avg code len =", round(avg_len,3))
print("H <= avg_len < H+1 ?", H-1e-9 <= avg_len < H+1)   # 香农源编码定理

高频符号 f 拿到最短码字(1 bit),低频符号拿到更长码字,往返编解码完全一致,且平均码长恰好落在 [H, H+1) 区间内——教科书式的香农定理落地。

三、信道与容量:从二元对称信道到香农极限

信道容量 C 是信道每单位时间能可靠传输的最大信息率(bit/符号或 bit/s)。最经典的玩具模型是二元对称信道(BSC):每位以概率 p 翻转为相反值,其容量为 C = 1 - H2(p),H2 是二元熵。当 p=0 或 p=1 时信道完美或可逆,容量回到 1;当 p=0.5 信道纯噪声,容量为 0——无论发什么都无法区分。

真实物理信道(加性高斯白噪声,AWGN)遵循香农-哈特莱公式 C = B·log2(1+SNR):带宽 B 与信噪比 SNR 共同决定极限。它给出了"在任意误码率下可通信"的理论天花板,是所有调制编码、5G/光纤系统的终极裁判。


import math

def binary_symmetric_capacity(p):
    """二元对称信道容量 C = 1 - H2(p)"""
    if p == 0 or p == 1: return 1.0
    return 1.0 + p*math.log2(p) + (1-p)*math.log2(1-p)

def shannon_hartley(B, snr_linear):
    """AWGN 信道容量 C = B log2(1+SNR)"""
    return B*math.log2(1+snr_linear)

print("BSC p=0.1 C =", round(binary_symmetric_capacity(0.1),3))  # ≈0.531
print("BSC p=0.5 C =", round(binary_symmetric_capacity(0.5),3))  # 0.0
print("Shannon 1MHz, SNR=100x C =",
      round(shannon_hartley(1e6,100)/1e6,3), "Mbps")             # ≈6.658
print("C(B,2SNR) > C(B,SNR)?",
      shannon_hartley(1e6,200) > shannon_hartley(1e6,100))        # True

p=0.1 时每符号还能可靠传 0.531 bit;p=0.5 时彻底归零;1 MHz 带宽在 100 倍信噪比下容量约 6.658 Mbps,且容量随 SNR 严格单调——这就是"带宽换 SNR、SNR 换速率"的定量依据。

四、信道编码:汉明码的单比特纠错

容量定义了上限,但要让收发双方在噪声里真正"零差错"通信,必须主动插入冗余——这就是信道编码。汉明 (7,4) 码用 3 个校验位保护 4 个数据位,构成一个能检测并纠正单个比特错误的最小完备码。三个校验位按奇偶方程组生成,接收端重新计算校验和得到"症候群(syndrome)",症候群的值直接指定位号,翻转即修复。


def hamming74_encode(d):
    """d: 4 bits [d1..d4]. 码字布局 [p1,p2,d1,p3,d2,d3,d4]"""
    d1,d2,d3,d4 = d
    p1 = d1 ^ d2 ^ d4
    p2 = d1 ^ d3 ^ d4
    p3 = d2 ^ d3 ^ d4
    return [p1,p2,d1,p3,d2,d3,d4]

def hamming74_decode(r):
    """r: 7-bit 接收字。纠正单比特错误,返回 (corrected, data, syndrome)"""
    p1,p2,d1,p3,d2,d3,d4 = r
    s1 = p1 ^ d1 ^ d2 ^ d4
    s2 = p2 ^ d1 ^ d3 ^ d4
    s3 = p3 ^ d2 ^ d3 ^ d4
    s = s1 + (s2<<1) + (s3<<2)   # 1..7 指定位号
    if s:
        r[s-1] ^= 1
    p1,p2,d1,p3,d2,d3,d4 = r
    return r, [d1,d2,d3,d4], s

data = [1,0,1,1]
code = hamming74_encode(data)
print("encoded:", code)
recv = code[:]; recv[3] ^= 1          # 注入单比特错误(位置 4 = p3)
corr, dec, syn = hamming74_decode(recv)
print("syndrome:", syn, "decoded:", dec, "match:", dec == data)  # syn=4, True

# 双错误:汉明码无法纠正(会误纠成别的字)
recv2 = code[:]; recv2[0]^=1; recv2[5]^=1
_, dec2, _ = hamming74_decode(recv2)
print("double-error decoded == data?", dec2 == data)  # False:仅能纠正 1 位

综合症精确定位并翻转了被篡改的第 4 位,数据完美还原;而故意注入的双比特错误被正确"拒识"为不可纠正——这直观展示了信道编码的纠错能力上限,也解释了为什么现代存储/通信要用更强的 LDPC 或 BCH 码来处理多比特突发错误。

五、率失真理论:有损压缩的极限

并非所有信息都值得无损保留。率失真理论回答一个反直觉的问题:如果要允许最多 D 的平均失真,最少需要多少 bit/symbol?对高斯信源,率失真函数闭式给出 R(D) = ½ log2(σ²/D)(0)。它随 D 单调下降,在 D=σ²(失真等于方差,等于直接发均值)时归零——这正是 JPEG/MPEG/语音编码"质量换码率"的数学根基。与之同源的 KL 散度在同方差高斯下也有闭式 D_KL = (μ1-μ2)²/(2σ²),非负性一眼可证。


import math

def gaussian_rate_distortion(sigma2, D):
    """高斯信源率失真 R(D) = 1/2 log2(sigma^2 / D), 0 < D <= sigma^2"""
    if D >= sigma2: return 0.0
    return 0.5*math.log2(sigma2/D)

sig2 = 1.0
print("R(0.5) =", round(gaussian_rate_distortion(sig2,0.5),3))   # 0.5
print("R(0.1) =", round(gaussian_rate_distortion(sig2,0.1),3))   # ≈1.661
print("R at D=sigma^2 ->", gaussian_rate_distortion(sig2, sig2)) # 0.0
print("monotonic decreasing?",
      gaussian_rate_distortion(sig2,0.1) > gaussian_rate_distortion(sig2,0.5))  # True

def kl_gauss(m1, m2, s2):
    return (m1-m2)**2/(2*s2)
print("KL(N(0,1)||N(2,1)) =", round(kl_gauss(0,2,1),3))  # 2.0
print("KL self =", kl_gauss(0,0,1))                       # 0.0:非负性

允许更大失真(D=0.5)时只需 0.5 bit/symbol,收紧到 D=0.1 时陡增到 ≈1.661,失真等于方差时彻底归零;KL 闭式在 μ 相差 2 个标准差时给出 2.0 bit,自比时严格为 0——率失真与 KL 两条曲线把"信息-保真度-代价"的权衡定量钉死。

六、与 AI / 深度学习的映射

信息论是当代 AI 的隐形语法,下面这张映射表几乎覆盖了一线模型训练的每一个角落:

信息论原语 在 AI / DL 中的对应
交叉熵 H(P,Q) 分类损失函数(softmax + CE),最小化即让模型分布逼近标签分布
KL 散度 D_KL VAE 正则项、策略蒸馏、GAN 的 JS 散度近亲、分布对齐
互信息 I(X;Y) InfoMax 表征学习、特征选择、互信息神经估计(MINE)、对比学习下界
熵 H(X) 决策树分裂准则、熵正则探索(RL)、不确定性估计、最大熵模型
信道容量 / 信息瓶颈 信息瓶颈理论(IB):压缩输入保留对标签的相关信息,解释深网泛化
率失真 模型量化、剪枝、知识蒸馏的"保真度-码率"权衡,有损压缩即近似推理

一个常被忽略的视角:交叉熵损失 H(P,Q) 展开后等于 H(P) + D_KL(P‖Q),其中 H(P) 是标签熵(训练时固定),所以最小化交叉熵等价于最小化模型分布与标签分布的 KL 散度——这解释了为什么标签平滑(label smoothing)本质上是在给 P 加熵、软化目标分布。信息瓶颈则把"深层网络为什么能泛化"表述为:逐层压缩输入 X 的互信息 I(X;T),同时保留 I(T;Y),在保真与压缩之间找最优平衡点。

七、工程边界与工具选型

  • 有界精度 vs 算力:熵、互信息的经验估计在小样本下偏差显著(尤其在长尾分布上),工程上常用 Miller-Madow 校正或直接上神经估计器(MINE)。
  • 可观测性:真实信道的 SNR 往往是时变、估计出来的,容量只是"可达"上界,实际码率要留余量(编码增益)。
  • 纠错边界:汉明码只能纠 1 位,闪存/DRAM/深空通信要 LDPC、BCH、Turbo、Polar 码,且纠错本身消耗延迟与功耗。
  • 率失真陷阱:有损压缩的 D 必须结合下游任务的容忍度来定,盲目压到低码率会摧毁下游模型精度。

工程选型表:统计度量用 scipy.stats / numpy;熵与互信息估计用 npeet(k-NN 非参估计);信道仿真与纠错码用 commpy / pyldpc;信息瓶颈与互信息估计用 minepy;大规模率失真/量化研究可用 torch 端到端联合优化码率与失真。

结语

信息论给了工程一套关于"不确定性、相关性与极限"的精确语言:熵度量混乱,互信息度量关联,容量度量可达,率失真度量代价。当你下一次写下 CrossEntropyLoss,本质上是在用 1948 年香农写下的公式做分布对齐;当你调量化比特数,本质是在沿率失真曲线滑动。理解这套底层语法,比记住任何框架 API 都更能让你看清 AI 系统的边界在哪里。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿
网站二维码

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部