KL散度与交叉熵的区别
交叉熵衡量用预测分布 q 描述目标分布 p 的平均代价;KL 散度衡量其中超出 p 自身熵的部分。它们满足 H(p,q)=H(p)+KL(p‖q),因此固定 p 时优化两者方向相同,但数值含义不同。
用两个候选把差别算出来
设 p=[0.9,0.1],q=[0.5,0.5],两个位置代表相同的两个事件。自然对数下,p 的熵约0.325083,交叉熵约0.693147,KL 约0.368064,单位都是 nat。三项满足0.693147≈0.325083+0.368064。
这些是人为构造的概率,没有对应真实模型或数据集。q 在两个事件上给出同样概率,所以每个事件的负对数概率都是 log 2。p 则更偏向第一个事件,它自身的不确定性较低。
差出来的一项就是 KL
对离散的同一事件空间,H(p)=-Σᵢpᵢlog pᵢ,H(p,q)=-Σᵢpᵢlog qᵢ。两式相减后得到 Σᵢpᵢ(log pᵢ-log qᵢ),也就是 KL(p‖q)。约定 pᵢ=0 的项贡献0;若 pᵢ>0 且 qᵢ=0,KL 和交叉熵为正无穷。
固定 p 时,H(p) 不随 q 改变,所以最小化交叉熵与最小化这个方向的 KL 具有相同最优目标。若 p 也在改变,例如两个分布都受训练参数影响,就不能把 H(p) 当常数丢掉。
这里讨论的是离散概率分布。SciPy 1.15.2 的官方 entropy 文档给出相应定义;库会对未归一化输入自动归一化,而下面教学实现选择拒绝它,避免悄悄改变输入。
方向换了,问题也换了
KL(p‖q) 的期望由 p 加权,KL(q‖p) 则由 q 加权。上例反向值约0.510826,与0.368064不同。它不是可任意交换参数的普通距离。
例如一个候选在目标 p 中概率很高、预测 q 中很低,正向 KL 会按目标概率放大这份代价。交换方向后权重也改变。做教师与学生分布比较时,应明确谁提供期望权重,而不是只写“加一个 KL loss”。
比较之前还需对齐事件编号。若一边第一个位置代表词 A,另一边代表词 B,即使两组向量形状一致,计算也失去了原本意义。不同 tokenizer 的词表不能直接逐位置相减;这与BPE 分词规则的绑定关系有关。
Python 检查恒等式与零概率
import math
def validate(p, q):
if not p or len(p) != len(q):
raise ValueError("Expected equal nonempty distributions")
for x in (p, q):
if not all(math.isfinite(v) and v >= 0 for v in x):
raise ValueError("Invalid probability")
if not math.isclose(math.fsum(x), 1.0):
raise ValueError("Probabilities must sum to one")
def metrics(p, q):
validate(p, q)
entropy = -math.fsum(a * math.log(a) for a in p if a > 0)
if any(a > 0 and b == 0 for a, b in zip(p, q)):
return entropy, math.inf, math.inf
cross_entropy = -math.fsum(a * math.log(b)
for a, b in zip(p, q) if a > 0)
kl = math.fsum(a * (math.log(a) - math.log(b))
for a, b in zip(p, q) if a > 0)
return entropy, cross_entropy, kl
p, q = [0.9, 0.1], [0.5, 0.5]
h, ce, kl = metrics(p, q)
assert math.isclose(ce, h + kl)
assert math.isclose(metrics(p, p)[2], 0.0, abs_tol=1e-12)
assert not math.isclose(kl, metrics(q, p)[2])
assert math.isinf(metrics([1., 0.], [0., 1.])[2])
assert metrics([1., 0.], [1., 0.]) == (0., 0., 0.)
for bad in ([0.2, 0.2], [-0.1, 1.1], [math.nan, 0.]):
try:
metrics(bad, q)
except ValueError:
pass
else:
raise AssertionError("Invalid input accepted")
print([round(x, 6) for x in (h, ce, kl)])
print(round(metrics(q, p)[2], 6))
原创代码已运行,检查交叉熵分解、同分布 KL 为0、方向不对称、目标有质量而预测为0的无穷情况,以及非法输入。输出前三项为 [0.325083,0.693147,0.368064],反向 KL 为0.510826。
实现使用自然对数,n 为事件数,时间 O(n),未创建随 n 增长的工作列表,额外空间 O(1),不计输入本身。计算中仍有舍入误差;不应把微小数值偏差误判成数学恒等式失效。
一个标签与软分布不是同一种输入
若目标 p 是 one-hot,真实类别 y 上概率为1,其余为0,那么 H(p)=0,交叉熵与 KL 都退化为 -log qᵧ。软目标则通常有非零熵,交叉熵不必在匹配目标时变成0。
这解释了为何不同目标分布、标签平滑设置或归约方式下,loss 数值不能随手横比。先记录目标的定义、概率或 logit 输入、对数底、归约维度和零概率处理,再解释数值。此文没有做训练效果实验,也不据此声称某种方向总能得到更好的模型。
参考资料
本文于2026年10月13日补发,文章日期保留原计划北京时间。参考资料早于计划时段,示例为原创教学计算。
支持
如果这篇文章对你有帮助,欢迎支持本站。
二维码可点击放大。更多支持方式见支持页面。


