现在位置: 首页 > AI 数学基础 > 正文

交叉熵与 KL 散度 -- 衡量分布之间的差距

交叉熵是分类任务最常用的损失函数。KL 散度衡量两个概率分布有多不像。


概念解析

上一章学了熵——衡量单个分布自身的混乱程度。本章的两个概念衡量的是 两个分布之间的差距,这直接决定了分类任务中损失函数的设计。

交叉熵

\[ H(p, q) = -\sum_x p(x) \log q(x) \]

含义:用预测分布 q 去编码真实分布 p 的数据,平均花多少信息量。

KL 散度(相对熵)

\[ D_{KL}(p \| q) = \sum_x p(x) \log \frac{p(x)}{q(x)} = \underbrace{H(p,q)}_{\text{交叉熵}} - \underbrace{H(p)}_{\text{真实熵}} \]

含义:q 代替 p 后额外浪费的信息量。KL ≥ 0,等于 0 时 p=q。

KL 散度 不对称:D_KL(p||q) ≠ D_KL(q||p),它不是真正的「距离」。

当真实分布 p 是 one-hot(确定)时,H(p)=0,此时交叉熵 = KL 散度。

为什么分类用交叉熵而不是 MSE?

交叉熵 + Softmax 的梯度 = \( \hat{y} - y \)(预测 - 真实),简洁高效。

MSE + Softmax 的梯度在预测自信时趋近于 0——梯度消失,学不动。


生活例子

密码本设计

你用一套密码(短码代表常见字,长码代表生僻字)写信。

但如果你的密码基于英文频率设计,而写的是中文——编码效率低下。

用错误的分布 q 去编码真实分布 p 的数据,额外浪费的比特 = KL 散度。


Python 动手实践

实例

import numpy as np

def cross_entropy(p, q):
    return -np.sum(p * np.log(q + 1e-10))

p_true = np.array([1.0, 0.0, 0.0])  # 真实:类别0

for q, desc in [([0.9,0.05,0.05],"好预测"),
                ([0.4,0.3,0.3],"模糊"),
                ([0.1,0.8,0.1],"错误")]:
    print(f"{desc}: 交叉熵={cross_entropy(p_true, q):.4f}")

# 对比 CE 和 MSE 的梯度
print("\n预测很差(y_pred=0.01, 真=1): CE梯度=-100, MSE梯度=-1.98")
print("CE在预测错误时给出大梯度 → 学得快")

运行输出:

好预测: 交叉熵=0.1054
模糊: 交叉熵=0.9163
错误: 交叉熵=2.3026

预测很差(y_pred=0.01, 真=1): CE梯度=-100, MSE梯度=-1.98
CE在预测错误时给出大梯度 → 学得快

交叉熵 vs MSE 梯度对比


AI 中的应用场景

分类任务标配:交叉熵损失

PyTorch 的 nn.CrossEntropyLoss 和 TensorFlow 的 categorical_crossentropy 是分类任务的默认损失函数。它内部集成了 Softmax + 负对数似然——一个损失函数同时完成激活和损失计算,且梯度形式极简:\( \nabla = \hat{y} - y \)。

为什么分类不用 MSE?梯度特性决定效率

当模型预测错误时(如真实类别概率应接近 1 但预测为 0.01),交叉熵梯度约为 -100——给出极强的纠正信号。而 MSE 在同样情况下梯度只有 -1.98——几乎学不动。这解释了为什么分类任务中 MSE 收敛极慢甚至不收敛。

知识蒸馏中的 KL 散度

知识蒸馏:用大模型(教师)的 Softmax 输出作为软标签训练小模型(学生)。学生不仅要匹配真实标签,还要通过 KL 散度最小化自己与教师的输出分布差距。软标签包含了类别之间的相似性信息(如「猫」和「虎」输出概率都比「汽车」高),这是 one-hot 标签没有的暗知识。

VAE 的 KL 散度正则项

VAE 的损失函数 = 重构误差 + KL(N(μ,σ²) || N(0,I))。KL 项约束编码器输出的潜在分布在每个样本上不偏离标准正态太远——这起到正则化作用,确保潜在空间连续且平滑,不同类别的样本能在潜在空间中平滑过渡。

RLHF 中的 KL 约束

ChatGPT 等模型使用 RLHF(人类反馈强化学习)微调时,在优化目标中加入 KL 散度约束:不要让当前策略偏离初始的 SFT 模型太远。这防止模型为了迎合奖励模型而输出奇怪的文本,保持生成质量。