上一章节说到,训练语言模型时,每个位置都要计算模型的预测「有多差」,然后根据这个数字调整参数。
但「多差」不能是一个模糊的感觉。机器需要一个精确的数字——这个数字必须满足两个条件:预测得好时它要小,预测得差时它要大;而且它必须是可以用数学推导出调整方向的(否则机器不知道旋钮该往哪边转)。
满足这两个条件的数字叫做交叉熵。它是整条训练的核心指标。
从一道选择题开始
想象你在做一道四选一的选择题。正确答案是 B。
情况一:你学得很好,你觉得 B 的可能性是 90%,A 是 5%,C 是 3%,D 是 2%。
情况二:你完全没复习,毫无头绪,四个选项各给 25%。
情况三:你学偏了,你非常确信答案是 A(90%),B 你只给了 3%。
哪种情况最糟糕?
直觉上很清楚:第三种最差——不仅错了,还错得很有信心,几乎完全排除了正确答案。第二种居中——虽然没信心,但至少没有把正确答案排除掉。第一种最好——正确答案得到了最高的概率。
交叉熵就是把这个直觉翻译成数字的工具。
交叉熵的公式
公式只有一行:
交叉熵 = −log(模型给正确答案的概率)
这里的 log 是自然对数。如果你没有接触过对数也不要紧——你只需要知道它的效果。让我们把三种情况都算出来:
| 情况 | 给正确答案(B)的概率 | −log(概率) | 好坏 |
|---|---|---|---|
| 情况一 | 90% | 0.105 | 很好 |
| 情况二 | 25% | 1.386 | 一般 |
| 情况三 | 3% | 3.507 | 很差 |
数字越小越好。 给正确答案的概率越高,交叉熵越低。
如果你想自己验算,可以用 Python:
import math
print(-math.log(0.90)) # 输出 0.105
print(-math.log(0.25)) # 输出 1.386
print(-math.log(0.03)) # 输出 3.507
math.log 算的就是自然对数。前面加一个负号,让「好」对应小数字、「差」对应大数字。
为什么用 log 而不是直接用概率
你可能会想:直接用「正确答案的概率」来衡量不好吗?概率越高越好,概率越低越差,也很直观。为什么要多此一举取一个 log?
第一个原因:让乘法变成加法。
一段文本有很多个位置。如果你要衡量模型对整段文本的预测能力,需要把每个位置的概率乘起来。比如一段 100 个词的文本,假设每个位置正确答案的概率都是 0.1,那整段文本的联合概率就是:
0.1 × 0.1 × 0.1 × …(100 个 0.1 相乘)= 10 的 −100 次方
这个数字小到计算机无法精确存储——它会变成 0,丢失所有信息。
取 log 之后,乘法变成了加法:
log(0.1) + log(0.1) + … = 100 × log(0.1) = −230
−230 是一个普通的数字,计算机处理起来毫无压力。这就是为什么在实际训练中,几乎所有的概率计算都在 log 空间进行。
第二个原因:惩罚的轻重更合理。
看两个对比:
把正确答案的概率从 50% 降到 10%——交叉熵从 0.69 升到 2.30,增加了 1.61。 把正确答案的概率从 10% 降到 1%——交叉熵从 2.30 升到 4.61,增加了 2.31。
同样是降了 40 个百分点(50→10)和 9 个百分点(10→1),后者的交叉熵增长反而更大。为什么?因为从 10% 降到 1%,意味着模型几乎完全排除了正确答案——这是一种更严重的错误,应该受到更重的惩罚。
如果直接用概率,这两种降低在惩罚上的区别不够明显。log 放大了低概率区域的梯度,迫使模型优先避免「对正确答案给出极低概率」这种灾难性错误。
困惑度:交叉熵的翻译
交叉熵是 3.36。这到底意味着什么?好还是不好?跟什么比?
困惑度(perplexity)把交叉熵翻译成一个更直观的数字:
困惑度 = e 的交叉熵次方
e 约等于 2.718,是自然对数的底数。困惑度的含义可以这样理解:模型在每个位置相当于在多少个选项中平均地猜。
如果困惑度是 1,意味着模型每次都 100% 确信且 100% 正确——它在 1 个选项里「猜」,当然不会错。
如果困惑度是 10,意味着模型的不确定性相当于每次在 10 个等概率的选项中选一个。
如果困惑度是 100,那就是在 100 个选项中猜。
用一张表来展示 Sophia 训练过程中这个数字的变化:
| 训练阶段 | 交叉熵 | 困惑度 | 相当于 |
|---|---|---|---|
| 训练开始(参数随机) | 11.39 | ≈ 88,000 | 在近九万个词里随机猜 |
| 训练中期 | 5.5 | ≈ 245 | 在 245 个词里猜 |
| 训练结束 | 3.36 | ≈ 29 | 在 29 个词里猜 |
训练开始时,Sophia 的参数全是随机的,她对语言一无所知。面对每个位置,她就像面对一张九万个选项的选择题在随机涂答题卡。
训练结束时,她的困惑度降到了 29。她已经学会了语言的大部分规律——哪些词常跟在哪些词后面,什么样的句子是通顺的——虽然她在每个位置仍然面对约 29 个合理的选择(语言本身就有模糊性,很多位置确实有不止一种合理的接法),但她已经能把候选范围从九万缩小到三十。
在训练中,Loss 就是交叉熵
在语言模型训练的语境中,有好几个词指的是同一件事:
- Loss(损失)
- Training loss(训练损失)
- Cross-entropy(交叉熵)
当你看到技术报告里说「train loss 从 11.39 降到 3.36」,它说的就是平均交叉熵从 11.39 降到了 3.36。
Loss 是训练的罗盘。训练程序每走一步(处理一批数据、调整一次参数),都会计算当前的 loss。如果 loss 在稳定下降,说明训练在正常进行——模型在变好。如果 loss 突然跳上去,说明出了问题——可能是学习率太大导致参数跳过了好的位置,或者数据有异常。如果 loss 不再下降了,说明模型已经从当前的数据中学到了它能学到的一切。
Sophia 的训练曲线从 11.39 平滑地降到 3.36,中间没有跳变、没有中断。这条曲线覆盖了 200 亿个 token——每一个 token 都在让她对语言的理解加深一点,直到最后。
回顾:训练的完整循环
到这里,你已经知道了训练语言模型的完整框架。让我们把前四个章节串起来:
1. 文本进来(第 03 章节)
↓
2. 模型预测下一个词的概率分布(第 03 章节)
↓
3. 交叉熵衡量预测有多差(本章节)
↓
4. 学习算法根据交叉熵调整参数(第 01 章节)
↓
5. 回到第 1 步,换下一段文本
这个循环重复 200 亿次,就是预训练。
但这里有一个还没回答的问题:文本到底是怎么「进来」的?模型只能处理数字,而文本是由汉字、英文字母、标点符号组成的。一个汉字在机器眼里是什么?一句话怎么变成一串数字?
这是下一个单元的问题。