为什么交叉熵偏偏要用 log:从模型开发者脑回路理解 PPL

从模型开发者会遇到的概率连乘、数值下溢和训练优化问题出发,还原 log、ln、exp、交叉熵与 PPL 之间的动机链条。

为什么交叉熵偏偏要用 log:从模型开发者脑回路理解 PPL

上一篇讲了 PPL:

PPL = exp(cross_entropy)

你卡住的地方很正常:

为什么突然冒出 log、ln、exp、自然底数 e?
这些不是数学课里硬塞的吗?
搞模型的人为什么会想到它们?

这篇不从公式开始,而是从一个模型开发者会遇到的问题开始。

假设你就是做大模型的顶级开发者。你现在要设计一个训练和评估规则,判断模型预测下一个 token 的质量。你会一步一步被现实逼到:

概率 -> 连乘 -> log -> 负 log -> 交叉熵 -> exp -> PPL

1. 你首先要评估:模型有没有把正确 token 看得足够重

语言模型每一步都在做一件事:

给下一个 token 的所有候选分配概率

比如真实下一个 token 是:

学习

模型 A 给它:

P("学习") = 0.80

模型 B 给它:

P("学习") = 0.05

直觉上,模型 A 好很多。它虽然不一定最终采样出 学习,但它知道真实答案很可能是 学习

所以第一个设计原则出现了:

评估模型时,不只看最后猜没猜中,
还要看它给真实 token 分了多少概率。

这就是 PPL、交叉熵、negative log likelihood 这条线的共同起点。

2. 第一个朴素方案:直接把正确概率加起来

你可能会先想:

每一步都看真实 token 的概率 p。
p 越大越好。
把所有 p 平均一下,不就行了吗?

比如 3 个位置:

p1 = 0.8
p2 = 0.6
p3 = 0.5

平均正确概率是:

(0.8 + 0.6 + 0.5) / 3 = 0.633

这个想法不蠢,但它很快会遇到一个大问题:一整段文本的概率不是相加,而是相乘。

模型生成一段真实文本的概率是:

P(text) = p1 * p2 * p3 * ... * pN

每一步都要押中,整段文本才成立。所以模型开发者真正关心的是:

模型给整段真实文本的概率有多高?

而整段概率天然是乘法结构。

3. 乘法世界的第一个麻烦:数字会小到没法算

假设一段文本有 1000 个 token,而且模型每一步给真实 token 的概率都还不错:

p = 0.2

整段概率就是:

0.2^1000

这是一个极小极小的数。计算机用浮点数表示它时,很容易下溢,最后变成 0。

这不是理论洁癖,而是工程问题:

一旦整段概率算成 0,
你就没法比较两个模型到底谁更好。

于是你作为开发者会想:

有没有办法不要直接处理一长串小数相乘?

这时 log 出场了。

4. log 的第一性原理作用:把乘法变加法

对数最重要的性质是:

log(a * b) = log(a) + log(b)

所以:

log(p1 * p2 * p3 * ... * pN)
= log(p1) + log(p2) + log(p3) + ... + log(pN)

这一下就把“很多小概率相乘”的问题,变成了“很多 log 值相加”的问题。

这就是开发者会自然想到 log 的第一个原因:

语言模型的整段概率是连乘;
log 可以把连乘变成求和。

求和有三个好处:

1. 数值更稳定,不容易下溢成 0
2. 每个 token 的贡献可以加起来
3. 加完以后可以除以 token 数,得到平均每步难度

这不是为了炫数学,是为了让训练和评估能算。

5. 为什么还要加一个负号:因为 log(p) 是负数

概率 p 在 0 到 1 之间:

0 < p <= 1

而对这个区间取 log,会得到小于等于 0 的数:

ln(1.0) = 0
ln(0.5) ≈ -0.693
ln(0.1) ≈ -2.303
ln(0.01) ≈ -4.605

概率越低,log 值越负。

但训练模型时,我们习惯最小化一个 loss:

loss 越小越好

所以把它取负:

-ln(p)

就变成:

p = 1.0  -> -ln(p) = 0
p = 0.5  -> -ln(p) = 0.693
p = 0.1  -> -ln(p) = 2.303
p = 0.01 -> -ln(p) = 4.605

现在方向就顺了:

模型给真实 token 的概率越高,loss 越小;
模型给真实 token 的概率越低,loss 越大。

这就是 negative log likelihood:

NLL = -log(模型给真实答案的概率)

6. 交叉熵是什么:很多步 NLL 的平均

一段文本有 N 个 token,每一步真实 token 的概率是:

p1, p2, p3, ..., pN

每一步的惩罚是:

-ln(p1), -ln(p2), -ln(p3), ..., -ln(pN)

把它们平均:

cross_entropy = - (ln(p1) + ln(p2) + ... + ln(pN)) / N

这就是语言模型评估里常见的交叉熵。

人话:

交叉熵 = 平均每一步,真实答案让模型有多意外

如果模型每一步都给真实 token 很高概率,交叉熵低。

如果模型经常把真实 token 看得很不可能,交叉熵高。

7. 那为什么是 ln,自然底数 e 又是哪来的

这里要分清两件事:

log 是思想:把乘法变加法
ln 是具体选择:以 e 为底的 log

其实你用不同底数也可以:

log2(p)  -> 单位叫 bit
ln(p)    -> 单位叫 nat
log10(p) -> 也能用,但机器学习里少见

它们之间只差一个常数倍。比如:

ln(x) = log2(x) * ln(2)

所以换底不会改变哪个模型更好,只会改变数字单位。

那为什么机器学习默认常用自然对数 ln

因为深度学习靠梯度优化,而 eln 在求导里最干净:

exp(x) = e^x

它有一个很舒服的性质:

exp(x) 的变化率还是 exp(x)

自然对数 ln(x) 也和它刚好互为反操作:

ln(exp(x)) = x
exp(ln(x)) = x

再加上 softmax 本来就用 exp 把 logits 变成正数概率:

prob_i = exp(logit_i) / sum(exp(logit_j))

于是用 ln 来写损失最自然,推导梯度也最简洁。

你可以把它理解成工程选择:

log 是因为概率连乘需要变加法;
ln 是因为和 exp、softmax、梯度优化配合最顺。

不是说自然底数 e 有某种玄学魔力。它只是连续优化里最省事的坐标系。

8. 为什么最后又要 exp 回来:人不擅长读 log 空间

交叉熵很好算,也适合训练。但它有一个缺点:

人不直觉理解 log 空间里的数。

比如:

cross_entropy = 2.079

这个数是什么意思?很难一眼看出来。

但如果把它 exp 回来:

PPL = exp(2.079) ≈ 8

就好懂了:

平均每一步的不确定性,约等于在 8 个同样可能的候选里选。

所以:

PPL = exp(cross_entropy)

不是又搞了一个新概念,而是把交叉熵从 log 空间翻译回普通概率空间。

交叉熵适合训练和数学推导。PPL 适合人读。

9. 一个完整小例子:从概率到 PPL

假设只有 3 个位置,模型给真实 token 的概率分别是:

p1 = 0.5
p2 = 0.25
p3 = 0.125

整段概率是:

0.5 * 0.25 * 0.125

取负 log 并平均:

cross_entropy
= - (ln(0.5) + ln(0.25) + ln(0.125)) / 3
≈ 1.386

再 exp 回来:

PPL = exp(1.386) ≈ 4

为什么是 4?

因为这三个概率的倒数是:

2, 4, 8

PPL 对应的是它们的几何平均:

(2 * 4 * 8)^(1/3) = 4

这就是那句:

PPL 是平均有效岔路数。

它不是简单算术平均,因为文本概率本来就是乘法结构。

10. 回到大模型训练:这套脑回路真正解决了什么

如果你是模型开发者,这套设计同时满足了几个关键要求。

第一,它尊重概率本质:

整段文本概率 = 每一步条件概率相乘

第二,它工程上稳定:

用 log 避免很多小概率连乘下溢

第三,它能按 token 平均:

不同长度文本可以比较平均难度

第四,它适合梯度优化:

ln、exp、softmax 配合起来推导干净

第五,它能翻译成人能理解的指标:

cross_entropy -> exp -> PPL

所以交叉熵和 PPL 不是随便选的数学符号,而是被任务形态一步步逼出来的:

我要评估整段文本概率
-> 概率是连乘
-> 连乘太小且不好平均
-> 用 log 变求和
-> 训练要最小化,所以取负
-> 每个 token 平均,得到 cross entropy
-> 人不懂 log 空间,所以 exp 回 PPL

11. 和 temperature 的关系:PPL 通常不靠温度

你前面提到“预设温度下的 token 自信度”。这里要单独拆开。

生成时,temperature 会改模型分布:

softmax(logits / T)

T 小,分布更尖;T 大,分布更平。

但标准 PPL 评估通常不做采样,也不靠 temperature 调味。它直接看模型原始分布给真实下一个 token 的概率。

原因是:

PPL 要比较模型本身的语言建模能力;
temperature 是生成策略,会人为改分布。

如果你拿不同 temperature 去算 PPL,就像给不同考生改卷时用不同评分尺,很难公平比较。

12. 最后记住这张路线图

把这条链背下来,比背单个公式更重要:

真实文本概率
= p1 * p2 * ... * pN

log 概率
= ln(p1) + ln(p2) + ... + ln(pN)

平均负 log 概率
= cross_entropy

从 log 空间还原
= PPL = exp(cross_entropy)

一句话总结:

log 是为了把概率连乘变成可训练、可平均、数值稳定的加法;
ln 是因为它和 exp、softmax、梯度优化配合最自然;
PPL 是把交叉熵翻译回“平均有效岔路数”。

自检

  1. 为什么一整段文本的概率是相乘,而不是相加?
  2. log(a * b) = log(a) + log(b) 解决了什么工程问题?
  3. 为什么 -ln(p) 里要有负号?
  4. 为什么 PPL = exp(cross_entropy) 是“还原”,不是又发明一个新指标?