为什么交叉熵偏偏要用 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?
因为深度学习靠梯度优化,而 e 和 ln 在求导里最干净:
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 是把交叉熵翻译回“平均有效岔路数”。
自检
- 为什么一整段文本的概率是相乘,而不是相加?
log(a * b) = log(a) + log(b)解决了什么工程问题?- 为什么
-ln(p)里要有负号? - 为什么
PPL = exp(cross_entropy)是“还原”,不是又发明一个新指标?