交叉熵、信息增益和 KL 散度会出现在不同模型里,容易被记成几条互不相关的公式。把它们放回概率分布中就清楚些:一个事件有多意外,一组事件平均多不确定,用另一套概率描述它要付出什么代价?这篇沿着这些问题解释信息论概念与机器学习目标的联系。
这篇不是信息论课,是ML 工程师视角的信息论:每个概念回答「它在 ML 的哪个角落出现、为什么是那个形状」。
前置阅读:建议先读 机器学习数学基础(概率部分)。
信息量:为什么 -log p
一个事件携带多少信息?直觉要求三条:
- 必然发生的事(p=1)信息量 = 0——「太阳从东边升起」什么都没告诉你。
- 越不可能的事信息量越大——「国足赢了」才是新闻。
- 独立事件的信息量可相加——两件事的信息 = 各自之和。
满足这三条的函数只有对数家族:
验证第 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 —— 必然事件
熵:不确定性的度量
熵 = 信息量的期望:一个随机变量平均每次观测带来多少信息。
直觉:分布越均匀熵越大(最不确定),越集中熵越小。极端:确定事件熵为 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?
ML 出没点:分类损失。one-hot 标签下 p = [0,…,1,…,0],求和只剩真实类别那一项:
就是「真实类别预测概率的负对数」——交叉熵损失就是负对数似然(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 多花的额外代价」:
性质:≥ 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。生成模型里这个区别决定了生成结果是「模糊全覆盖」还是「清晰但丢模式」。
互信息:两个变量共享多少信息
直觉:知道 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 损失函数的设计语言。
练习
- 手算并编程验证:分布 [0.7, 0.2, 0.1] 的熵;再算它对自己的 KL 散度(应为 0)。
- 造一个二分类 toy 数据集,分别用 entropy 和 gini 训练决策树,对比树结构和准确率。
- 实验标签平滑:把 one-hot 换成 [0.9, 0.05, 0.05] 训练同一个分类器,观察验证集准确率和预测置信度分布的变化。
- 用 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。理论指导工程的经典案例。
相关阅读
- 机器学习数学基础——概率论前置
- 决策树实战——熵的第一个应用现场
- 模型压缩与部署——KL 散度在蒸馏中的应用
- GAN 与 Diffusion——JS/W 距离的战场
- ViT 与 CLIP——InfoNCE 与互信息
信息论教会我的思维方式:任何「让两个分布靠近」的优化,都该先问「用什么度量、为什么是这个」。损失函数不是拍脑袋选的,它是对问题的翻译。
💬 评论
这套方案在哪一步卡住了?也欢迎补充你的实现方式或踩坑经验。