信息论速通:熵、交叉熵、KL 散度——ML 损失函数背后的统一语言

· 约 7 分钟 ·
本文目录(10 节)

交叉熵、信息增益和 KL 散度会出现在不同模型里,容易被记成几条互不相关的公式。把它们放回概率分布中就清楚些:一个事件有多意外,一组事件平均多不确定,用另一套概率描述它要付出什么代价?这篇沿着这些问题解释信息论概念与机器学习目标的联系。

这篇不是信息论课,是ML 工程师视角的信息论:每个概念回答「它在 ML 的哪个角落出现、为什么是那个形状」。

前置阅读:建议先读 机器学习数学基础(概率部分)。

信息量:为什么 -log p

一个事件携带多少信息?直觉要求三条:

  1. 必然发生的事(p=1)信息量 = 0——「太阳从东边升起」什么都没告诉你。
  2. 越不可能的事信息量越大——「国足赢了」才是新闻。
  3. 独立事件的信息量可相加——两件事的信息 = 各自之和。

满足这三条的函数只有对数家族:

I(x)=−log⁡2p(x)(单位:bit)I(x) = -\log_2 p(x) \quad \text{(单位:bit)}

验证第 3 条:两个独立事件 p₁、p₂ 同时发生概率 p₁p₂,信息量 −log(p₁p₂) = −log p₁ − log p₂,正好相加。负数是因为概率 <1 时 log 为负。这个式子是整个信息论的基石,后面全是它的加权平均。

import numpy as np

def info(p): return -np.log2(p)

print(info(0.5))    # 1.0 bit —— 抛硬币正面
print(info(0.125))  # 3.0 bit —— 八选一猜中
print(info(1.0))    # 0.0 bit —— 必然事件

熵:不确定性的度量

熵 = 信息量的期望:一个随机变量平均每次观测带来多少信息。

H(X)=−∑ipilog⁡2piH(X) = -\sum_i p_i \log_2 p_i

直觉:分布越均匀熵越大(最不确定),越集中熵越小。极端:确定事件熵为 0。

def entropy(p):
    p = np.asarray(p); p = p[p > 0]
    return -np.sum(p * np.log2(p))

print(entropy([0.5, 0.5]))        # 1.0 —— 最公平的硬币,最大熵
print(entropy([0.9, 0.1]))        # 0.47 —— 有偏
print(entropy([0.25]*4))          # 2.0 —— 四选一均匀,log2(4)

ML 出没点①:决策树。ID3/C4.5 选特征就是选「分裂后子节点熵最小」的那个,信息增益 = 分裂前熵 − 分裂后加权熵。决策树那篇里的 criterion=“entropy” 就是这东西。另一种准则 Gini 不纯度是熵的近似(log 在 p≈1 附近用泰勒展开替换),计算快,效果相当。

ML 出没点②:语言模型的困惑度。perplexity = 2^H,「模型平均要在几个等概率选项里猜」。GPT 类模型报告的 perplexity 本质就是交叉熵换了个马甲。

交叉熵:用错误分布编码的代价

场景:真实分布是 p,你以为分布是 q,按 q 来「编码」数据,平均多花多少 bit?

H(p,q)=−∑ipilog⁡qiH(p, q) = -\sum_i p_i \log q_i

ML 出没点:分类损失。one-hot 标签下 p = [0,…,1,…,0],求和只剩真实类别那一项:

H(p,q)=−log⁡qyH(p,q) = -\log q_{y}

就是「真实类别预测概率的负对数」——交叉熵损失就是负对数似然(NLL),每篇分类论文里的 loss 都是它:

def cross_entropy(y_true_onehot, y_pred_prob):
    return -np.sum(y_true_onehot * np.log(y_pred_prob + 1e-12))

# 预测 [0.1, 0.7, 0.2],真实第 2 类
print(cross_entropy([0,1,0], [0.1,0.7,0.2]))   # 0.357
print(cross_entropy([0,1,0], [0.1,0.2,0.7]))   # 1.609 —— 自信地错,罚得狠

注意第二行:预测把 0.7 押在错误类别上,损失飙升。交叉熵对「自信的错」惩罚极重——这是它的性格,也解释了为什么标签平滑(label smoothing)有时有用:给模型「留点余地」别过度自信。

KL 散度:两个分布的距离(不对称!)

交叉熵减去熵,剩下的就是「用 q 近似 p 多花的额外代价」:

DKL(p∥q)=H(p,q)−H(p)=∑ipilog⁡piqiD_{KL}(p \| q) = H(p, q) - H(p) = \sum_i p_i \log \frac{p_i}{q_i}

性质:≥ 0,当且仅当 p=q 时为 0;不对称(D(p‖q) ≠ D(q‖p))——所以叫散度不叫距离。

def kl(p, q):
    p, q = np.asarray(p), np.asarray(q)
    mask = p > 0
    return np.sum(p[mask] * np.log(p[mask] / q[mask]))

print(kl([0.5,0.5], [0.5,0.5]))   # 0
print(kl([0.9,0.1], [0.5,0.5]))   # 0.368
print(kl([0.5,0.5], [0.9,0.1]))   # 0.368 —— 巧合相同,换个例子就不同

ML 出没点①:知识蒸馏。学生模仿教师的 soft 分布,loss = KL(教师‖学生),温度 T 拉平分布暴露暗知识——蒸馏那篇讲过。用 KL 而非交叉熵的原因:教师的熵对学生是常数,min H(p,q) 等价于 min KL(p‖q),但 KL 有「0 = 完美」的清晰零点。

ML 出没点②:VAE。变分自编码器的损失 = 重构误差 + KL(后验 q(z|x) ‖ 先验 N(0,I))——把隐变量分布往标准正态拉,采样才有效。

ML 出没点③:RLHF/PPO。策略更新加 KL 惩罚项,防止新策略跑偏旧策略太远——ChatGPT 训练里稳定性的关键。

不对称性的直觉:D(p‖q) 要求「p 有的地方 q 必须有」(否则 log(p/0) 爆炸)——mode covering;D(q‖p) 则允许 q 躲在 p 的一个峰里——mode seeking。生成模型里这个区别决定了生成结果是「模糊全覆盖」还是「清晰但丢模式」。

互信息:两个变量共享多少信息

I(X;Y)=DKL(p(x,y)∥p(x)p(y))=H(X)−H(X∣Y)I(X;Y) = D_{KL}(p(x,y) \| p(x)p(y)) = H(X) - H(X|Y)

直觉:知道 Y 之后,X 的不确定性减少多少。独立变量互信息为 0。

ML 出没点:特征选择。sklearn 的 mutual_info_classif 就是算每个特征与标签的互信息,选 top-K:

from sklearn.feature_selection import mutual_info_classif
mi = mutual_info_classif(X, y)
# mi[i] 大 → 特征 i 和标签共享信息多 → 值得留

比相关系数强的地方:能抓非线性关系。y = x² 在 x 对称分布时相关系数为 0(完全漏报),互信息照样高——它不假设关系形状。

JS 散度与 Wasserstein:KL 的补丁

KL 散度在两个分布不重叠时为无穷大(log(p/0)),梯度消失——这正是原始 GAN 训练不稳定的数学根源。补丁:

  • JS 散度:JSD(p‖q) = ½KL(p‖m) + ½KL(q‖m),m=(p+q)/2。对称、有界(≤log 2)。
  • Wasserstein 距离(推土机距离):把一个分布「搬运」成另一个的最小代价。分布不重叠时依然有限且梯度平滑——WGAN 用它替换 JS 散度,训练稳定性质变。

知道这层「为什么」就够了,细节留给 GAN 与 Diffusion 那篇。

一张地图收拢全部

概念公式核心ML 出没地
信息量−log p一切的基石
熵 H(p)−Σ p log p决策树分裂、困惑度
交叉熵 H(p,q)−Σ p log q分类损失、语言模型
KL 散度Σ p log(p/q)蒸馏、VAE、PPO
互信息H(X)−H(X|Y)特征选择、InfoNCE 的理论根
JS/W 距离KL 的对称/平滑补丁GAN 训练

还有一条隐藏线索:CLIP 的 InfoNCE 损失本质是在最大化图文互信息的下界——对比学习的理论根就扎在互信息上。信息论不是数学装饰,它是现代 ML 损失函数的设计语言。

练习

  1. 手算并编程验证:分布 [0.7, 0.2, 0.1] 的熵;再算它对自己的 KL 散度(应为 0)。
  2. 造一个二分类 toy 数据集,分别用 entropy 和 gini 训练决策树,对比树结构和准确率。
  3. 实验标签平滑:把 one-hot 换成 [0.9, 0.05, 0.05] 训练同一个分类器,观察验证集准确率和预测置信度分布的变化。
  4. 用 sklearn 对 Iris 数据集计算每个特征与标签的互信息和 Pearson 相关系数,找出一个「相关系数低但互信息高」的特征对,画出散点图解释为什么。

面试常问

Q:为什么交叉熵比 MSE 更适合分类? 三个层面:① 概率解释——交叉熵 = NLL,是对「正确类别概率」的直接最大似然;② 梯度特性——softmax+交叉熵的梯度是 (预测−标签),形式干净且错得越狠梯度越大;MSE 在 sigmoid/softmax 饱和区梯度趋零,学不动;③ 信息论解释——MSE 假设误差高斯分布,和「类别概率」这种单纯形上的分布不匹配。

Q:KL 散度为什么不对称?举个例子。 定义里期望是对 p 取的。例子:p=[0.5,0.5]、q=[0.9,0.1],D(p‖q)=0.368、D(q‖p)=0.511。工程含义:用 q 近似 p 时,D(p‖q) 惩罚「p 大概率而 q 小概率」(漏掉模式),D(q‖p) 惩罚「q 大概率而 p 小概率」(错误自信)。蒸馏、VAE 选哪种方向是有讲究的。

Q:熵和 Gini 不纯度的关系? Gini = 1−Σp² 是熵在 −log p ≈ (1−p) 近似下的简化。两者曲线形状相似(都在均匀分布取最大值),分裂选择几乎一致。Gini 无对数运算略快,是 sklearn 默认;熵有信息论解释,特征重要性的含义更干净。实践中差异可忽略,选哪个更多是习惯。

Q:困惑度是什么,怎么算? PPL = exp(平均交叉熵)(自然对数版)。语言模型在测试集上每 token 的平均交叉熵是 4.0,PPL = e⁴ ≈ 54.6——直觉是「模型平均像在 55 个等概率选项里猜下一个词」。越低越好,但跨数据集比较 PPL 要小心词表和分词差异。

Q:InfoNCE 和互信息什么关系? InfoNCE 损失(CLIP 用的那种)是互信息 I(图;文) 的一个下界的负对数——最小化 InfoNCE 就是在最大化互信息的下界。batch 大小决定下界的紧度(负样本多 → 界更紧),这从理论上解释了为什么对比学习要堆大 batch。理论指导工程的经典案例。

相关阅读

信息论教会我的思维方式:任何「让两个分布靠近」的优化,都该先问「用什么度量、为什么是这个」。损失函数不是拍脑袋选的,它是对问题的翻译。

💬 评论

这套方案在哪一步卡住了?也欢迎补充你的实现方式或踩坑经验。

正在连接评论服务,首次访问可能需要几秒…