交叉熵与 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 动手实践
实例
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 模型太远。这防止模型为了迎合奖励模型而输出奇怪的文本,保持生成质量。
