信息论:度量惊讶,而损失函数就建在它之上 本节摘要:信息论度量「惊讶(surprise)」,而几乎每个 ML 损失函数都建在它之上。你每次训练分类器都调 ,每篇语言模型论文都报 perplexity,VAE、知识蒸馏、RLHF 全都用到 KL 散度——它们不是互不相干的概念,而是同一个思想戴着不同帽子。香农 1948 年为通信问题发明了信息论,而训练神经网络本质上就是一个通信问题:模型正试图通过「学到的权重」这条有噪信道,把正确标签传输出去。
本节摘要:信息论度量「惊讶(surprise)」,而几乎每个 ML 损失函数都建在它之上。你每次训练分类器都调
CrossEntropyLoss(),每篇语言模型论文都报 perplexity,VAE、知识蒸馏、RLHF 全都用到 KL 散度——它们不是互不相干的概念,而是同一个思想戴着不同帽子。香农 1948 年为通信问题发明了信息论,而训练神经网络本质上就是一个通信问题:模型正试图通过「学到的权重」这条有噪信道,把正确标签传输出去。本节从信息量(surprise)出发,推到熵(平均惊讶)、交叉熵(用错分布编码多花的位数)、KL 散度(两分布间距)、互信息(知道一个变量对另一个的减熵量);从信息论、极大似然、梯度三个视角论证「为什么交叉熵就是分类的损失」;解释标签平滑为何等价于给目标分布加熵;最后落到 perplexity——语言模型「平均在多少个等概率候选词间犹豫」。
对应原课程:Phase 01 · Lesson 09 ·
information-theory(原英文phases/01-math-foundations/09-information-theory/docs/en.md)。前置:第 6 节(概率)。
阅读完本节,你应当能够:
你每次训练分类器都调 CrossEntropyLoss(),每篇 LM 论文都报 perplexity,VAE/蒸馏/RLHF 都用 KL 散度。它们不是互不相干的概念,而是同一个思想戴不同帽子。
信息论给了你一套语言来推理不确定性、压缩与预测。香农 1948 年为通信发明它,结果训练神经网络也是一个通信问题:模型在尝试通过「学到的权重」这条有噪信道把正确标签传出去。本节把每个公式从零搭起,让你看见它们从哪来、为什么管用。
越不可能发生的事件,携带的信息越多。硬币正面?不意外。中彩票?很意外。概率 p 的事件的信息量为:
I(x) = −log(p(x))
log 底 2 单位是 比特(bits),自然对数单位是 nats。
| 事件 | 概率 | 惊讶(bits) |
|---|---|---|
| 公平硬币正面 | 0.5 | 1.0 |
| 掷出 6 | 0.167 | 2.58 |
| 千分之一事件 | 0.001 | 9.97 |
| 必然事件 | 1.0 | 0.0 |
必然事件信息量为 0——你早就知道会发生。
熵是一个分布上所有可能结果的期望惊讶:
H(P) = −Σ p(x)·log(p(x))
公平硬币熵最大(二元变量 1 比特);99% 正面的偏心硬币熵低(0.08 比特)——你几乎知道会发生什么,每次抛掷几乎不带来信息。熵度量分布的不可约不确定性,你无法压缩到它以下。
交叉熵度量「用分布 Q 去编码本来自分布 P 的事件」时的平均惊讶:
H(P, Q) = −Σ p(x)·log(q(x))
P 是真实分布(标签),Q 是模型预测。若 Q 完美匹配 P,交叉熵等于熵;任何错配都让它变大。分类中 P 是 one-hot 向量(真实类概率 1、其余 0),交叉熵简化为:
H(P, Q) = −log(q(真实类))
这就是分类的交叉熵损失公式——最大化预测的正确类概率。
KL 散度度量「用 Q 代替 P 多出的惊讶」:
D_KL(P‖Q) = Σ p(x)·log(p(x)/q(x)) = H(P, Q) − H(P)
交叉熵 = 熵 + KL 散度。训练中真实分布熵恒定,故最小化交叉熵 = 最小化 KL 散度——你在把模型分布推向真实分布。KL 散度不对称:D_KL(P‖Q) ≠ D_KL(Q‖P),不是真正的距离度量。
互信息度量「知道一个变量告诉你多少关于另一个」:
I(X;Y) = H(X) − H(X|Y) = H(X) + H(Y) − H(X,Y)
X、Y 独立则互信息为 0;完全相关则等于其中任一变量的熵。特征选择中,特征与目标的互信息越高,该特征越有用。
H(Y|X) 度量「观测 X 后对 Y 还剩多少不确定性」:H(Y|X) = H(X,Y) − H(X)。恒有 0 ≤ H(Y|X) ≤ H(Y)。决策树每次分裂就选使 H(Y|X) 最小的特征 X——即最能消除标签不确定性的特征。
H(X,Y) = −ΣΣ p(x,y)·log(p(x,y))。关键性质:H(X,Y) ≤ H(X) + H(Y),等号当且仅当 X、Y 独立。共享的「消失的那部分熵」正是互信息。
性质:I(X;Y) ≥ 0 恒成立;I(X;Y)=0 当且仅当独立;对称 I(X;Y)=I(Y;X)(不像 KL);I(X;X)=H(X)。用于特征选择:对每个特征算 I(Xᵢ;Y),按分排序,留前 k 个。它能捕捉任意统计依赖(线性、非线性、单调与否),而相关系数只抓线性。
| 方法 | 检测 | 计算成本 | 支持类别型? |
|---|---|---|---|
| 皮尔逊相关 | 线性 | O(n) | 否 |
| 斯皮尔曼相关 | 单调 | O(n log n) | 否 |
| 互信息 | 任意统计依赖 | O(n log n)(分箱) | 是 |
标准分类用硬目标 [0,0,1,0]。标签平滑把它换成软目标:soft = (1−ε)·hard + ε/num_classes。ε=0.1、4 类时:[0.025, 0.025, 0.925, 0.025]。
从信息论看,标签平滑提高了目标分布的熵——硬 one-hot 熵为 0(无不确定),软目标熵为正。它防止 logit 被推到极端(完美匹配 one-hot 需无穷 logit)、充当正则、改善校准、缩小训练与推理的差距。
三个视角,同一结论:
∏ q(yᵢ),对数似然 = Σ log q(yᵢ),负对数似然 = −Σ log q(yᵢ)——这就是交叉熵。最小化交叉熵 = 最大化训练数据在模型下的似然。(预测 − 真实),干净、稳定、快,与 softmax 完美配对。唯一差别是 log 底:底 2 → bits(信息论传统),底 e → nats(ML 惯例),底 10 → hartleys(罕用)。1 nat ≈ 1.4427 bits。PyTorch/TensorFlow 默认用 nats。
Perplexity 是交叉熵的指数,告诉你模型「平均在多少个等概率候选间犹豫」:
Perplexity = 2^H(P,Q)(用 bits) 或 e^H(P,Q)(用 nats)
perplexity 50 的语言模型,平均而言像在 50 个等概率下一 token 里挑。越低越好。GPT-2 在常见基准上约 30,现代模型在充分代表的领域已到个位数。
完整源码见 phases/01-math-foundations/09-information-theory/code/。
def information_content(p, base=2): if p <= 0 or p > 1: return float('inf') if p <= 0 else 0.0 return -math.log(p) / math.log(base) def entropy(probs, base=2): return sum(p * information_content(p, base) for p in probs if p > 0)
def cross_entropy(p, q, base=2): total = 0.0 for pi, qi in zip(p, q): if pi > 0: if qi <= 0: return float('inf') total += pi * (-math.log(qi) / math.log(base)) return total def kl_divergence(p, q, base=2): return cross_entropy(p, q, base) - entropy(p, base)
def cross_entropy_loss(true_class, logits): probs = softmax(logits) return -math.log(probs[true_class]) # = 负对数似然
ce_loss = sum(cross_entropy_loss(y, z) for y, z in zip(true_labels, model_logits)) / n nll = -sum(math.log(softmax(z)[y]) for y, z in zip(true_labels, model_logits)) / n # 两者差 < 1e-10,证明 CE ≡ NLL
def mutual_information(joint_probs, base=2): rows, cols = len(joint_probs), len(joint_probs[0]) margin_x = [sum(joint_probs[i][j] for j in range(cols)) for i in range(rows)] margin_y = [sum(joint_probs[i][j] for i in range(rows)) for j in range(cols)] mi = 0.0 for i in range(rows): for j in range(cols): pxy = joint_probs[i][j] if pxy > 0: mi += pxy * math.log(pxy / (margin_x[i]*margin_y[j])) / math.log(base) return mi # 独立联合 [[0.25,0.25],[0.25,0.25]] → MI ≈ 0 # 依赖联合 [[0.45,0.05],[0.05,0.45]] → MI > 0
设计要点:实现中务必跳过
p=0的项——0·log0在极限下为 0,但浮点会得 NaN/−inf。
实战中用 NumPy 向量化:
def np_entropy(p): p = np.asarray(p, dtype=float); mask = p > 0 return -(p[mask] * np.log(p[mask])).sum() def np_cross_entropy(p, q): p, q = np.asarray(p), np.asarray(q); mask = p > 0 return -(p[mask] * np.log(q[mask])).sum() def np_kl(p, q): return np_cross_entropy(p, q) - np_entropy(p)
你刚从零实现了 torch.nn.CrossEntropyLoss() 内部做的事。现在你知道训练时损失为什么下降:模型预测分布在向真实分布靠近,用「浪费的 nats」来度量。
code/):熵、交叉熵、KL、互信息、perplexity。后续第 11 章(LLM 工程化)讲知识蒸馏、第 9 章(生成式 AI)讲 VAE 的 ELBO 时会反复复用。outputs/。[5.0, 2.0, 0.5],真实类为 1,手算交叉熵,再用函数验证。什么样的 logits 给零损失?D_KL(P‖Q) 与 D_KL(Q‖P),解释为何不同。(真实 token 下标, 预测 logits),返回序列的 perplexity。−log q(真实类)。下一节,我们用线性代数加信息论做降维——PCA、t-SNE、UMAP,以及如何在保留信息的同时把高维数据压到可可视化的二维。