从0训练LLM · 现代大模型体系 阅读 + 实验 ≈ 30 分钟 页内实验 ×4
C14 评测与训练仪表:分数能信吗 第 1 课

训练跑着的时候,只看 loss 曲线够吗?该看哪些仪表?

训练仪表:loss 之外还要看什么
我们项目里一个只把学习率调大 10 倍的 run:第 20 步它的验证 loss 比参照还低,最大注意力 logit 却已是参照的 10 倍,梯度范数在第 10 步就冲到了参照的 13 倍;400 步后它比参照差 1.19。loss 是结果,不是状态。本课在真实日志上装四块仪表:开局对 ln V、裁剪前梯度范数、最大 logit、spike score,并看它们各自会漏掉什么。
❓

本课一问:训练一跑就是几万步。盯着一条 loss 曲线,它在往下走,就算训练正常吗?还有哪些读数该一起看?

这一课先不碰:训练完之后拿 benchmark 考分(第 2 课);小实验的结论怎么推到大模型(第 3 课);spike 为什么会发生、怎么按住 logit(C8 已经讲过,这里只读仪表、不再推机制)。

从已有出发

「小明把苹果给了小王」在锚点模型里是 5 个 token。还没训练时,每个位置的 loss 都应该约等于 ln 248,320 = 12.42(C0 第 5 课):那是训练曲线的第一个点,也是全课的第一块仪表。一次训练循环里,还有几处能「接表」:

从已有出发 · 训练的一步(点任一环节看它在 Qwen/Qwen3.8-27B 上有多大) 本课本章后面的课
🔁

这一课为什么换例子。仪表读的是一整次训练的轨迹,一句话上看不出来。所以本课改用我们项目 04-实践项目 里真实跑过的 run:3.7M 参数、《三國志演義》语料、词表 2,048(开局应当约等于 ln 2,048 = 7.6246);每个 run 只改一项配置,参照是基线配置 ref_baseline。所有数字由页面从导出文件 c08-stability.json、c14-eval.json 现读。

先自己设计:只看 loss

最省事的读法:把 loss 曲线画出来,它在往下走就放心。下面这个 run 就是这样一条曲线——先只给你这一条:

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案只看 loss 曲线:它在降,就当训练正常
只看 loss:这个 run 训得怎么样?项目真实日志 · 3.7M 参数 · 《三國志演義》

失败有两层:

🎯

试三件事(展开上面的改进方案):① 拖到第 20 步,读表格里四块仪表的「差 / 倍数」一列,再看「第一次报警」一列哪块最早;② 在「要检查的 run」里换成 c4_preln_nowarmup_lr1e-2(学习率 10⁻²、不 warmup):它的 loss 同样一路下降,第 20 步最大 logit 已是 212,第 1–5 步梯度范数峰值 18.4(参照 3.18);③ 换成 c8_qknorm_lr3e-2(开 QK-Norm、学习率 3×10⁻²):logit 那块始终不报警——QK-Norm 把它按住了——可最终 loss 还是比参照差,报警只来自梯度范数和 loss。一块仪表不响,不等于没事。

改进:四块仪表

① 开局自检:第 0 步的 loss 对 ln V

还没训练时,模型对 V 个 token 近似均匀地猜,loss 应当约等于 ln V(C0 第 5 课推过)。这是最便宜的一块仪表:第一个评估点一出来就能核对。Karpathy 在《A Recipe for Training Neural Networks》里把它列为训练前的第一批检查之一:「verify loss @ init」。

我们项目里每个 run 的第 0 步都记了下来。下表按「差多少」排序,条形长度按对数画:

开局自检:每个 run 第 0 步的验证 loss 与 ln V项目真实日志 · 3.7M 参数 · 《三國志演義》

除了改初始化的三个 run,其余 run 的差都在 0.01–0.13 之间(输出层的 logit 不是严格的 0,C0 第 5 课的实验台 ② 模拟过这件事);只有把初始化标准差调大的两个 run 远远偏离:std 0.2 时开局 12.51,高出 ln V 4.89;std 1.0 时开局 55.33,高出 47.71。

⚠

常见误解:「开局对上了 ln V,初始化就没问题。」c8_init0.002(初始化标准差 0.002)开局 7.6246,与 ln V 只差 -5.8e-5——比参照对得还准;可 400 步后它的验证 loss 是 5.352,比参照差 0.71。开局对上 ln V 只说明 logits 一开始接近均匀;权重太小、训练起步太慢,这块仪表看不见。它是必要条件,不是充分条件。

② 裁剪前的全局梯度范数

全局梯度范数(裁剪之前记录)
= ( )½
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
我们项目每 5 步记一次区间内的均值与最大值;大模型通常每步都记。

OLMo 2 在自己的训练日志里观察到两条规律:剧烈的梯度范数尖峰常常先于 loss 尖峰;梯度范数缓慢上涨,与尖峰越来越频繁相关 。上面的回放里也是这样:学习率调大 10 倍的 run,第 6–10 步的梯度范数峰值 22.8,是参照同一区间 1.73 的 13.2 倍。按回放里写明的报警规则,梯度范数在第 10 步报警,最大 logit 在第 20 步,loss 要到第 40 步。

⚠

常见误解:「梯度范数越小,训练越稳。」两个出事的 run 在第 50 步之后,梯度范数(区间均值)的中位数分别是 0.357 和 0.247,都低于参照的 0.560。原因是注意力 logit 已经大到 softmax 近乎只看一个位置,饱和区的梯度本来就小(C8 第 4 课)。安静的梯度可能是「稳」,也可能是「卡死了」——要和 logit、loss 一起读。

③ 最大注意力 logit 与输出 logit

这块仪表不是在训练循环里顺手记的,而是每次评估时多做一次前向的「探针」:在 8 个固定的验证窗口上,取所有层、所有头、所有可见位置里 softmax 之前的最大值。形状是这样的:

探针:最大注意力 logit 从哪个张量上取(我们项目的真实维度,每层各算一次)
m = max可见格 ( Q·Kᵀ ÷ √dh )
输出中间结果 渐变 = 分数(连续值) 留白 = 结构上恒为 0
边长按对数比例缩放,块边上的数字是真实值;同一维度符号的边在全图等长。
维度含义 真实规模 小规模
H 头数(n_head) 8 3
T 验证窗口长度(block_size) 256 5
dh 单头维度 = 256 ÷ 8 32 4
1 一个数 1 1
项目探针对 4 层 × 8 个固定验证窗口各算一次再取最大(04-实践项目/experiments/common.py 的 probe_hook);输出 logit 那块仪表取的是 lm_head 输出的最大绝对值。

Wortsman 等人在小模型上用高学习率复现了大模型训练里的两类不稳定:注意力 logit 增长,和输出 logit 与 log 概率发散 ;Kimi K2 用原版 Muon 训练时,最大注意力 logit 很快超过 1,000,于是加了 QK-Clip(C5 第 7 课、C8 第 4 课)。所以这块仪表有现成的刻度:参照 run 的最大 logit 从 2.4 慢慢涨到 49.9;学习率调大 10 倍的 run 第 20 步就是 25.7,最高到 7,977。

④ 把「尖峰」变成一个可以比较的数:spike score

尖峰在曲线上一眼能看到,可「这个 run 比那个 run 尖峰多」要一个数。OLMo 2 的定义很简单:

spike score(OLMo 2 §3.2 的定义)
= 𝟙[ | − | ≥ · ]
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
一个天然的盲区:序列开头没有历史可比。下面的实现要求至少 5 个历史点,所以第 1–25 步的尖峰它看不见——这正是开局自检要单独做的原因。
项目真实日志 · spike score 在真实梯度范数序列上现算
spike score:把「尖峰」变成一个可以比较的数项目真实日志 · 3.7M 参数 · 《三國志演義》

🎯

试三件事:① 默认 W = 20、k = 5,读学习率调大 10 倍的 run 与参照的 spike score;② 把 k 调到 7(OLMo 2 的取值):我们每个 run 只有 80 个日志点,7σ 在这么短的序列上很难达到,几乎什么都标不出——阈值要和日志长度一起定;③ 换成 c8_init1(初始化 std 1.0):它的梯度范数峰值 51.0 出现在第 25 步,是参照的 16 倍,可 spike score 是 0——开局的尖峰没有历史可比。每块仪表都有它看不见的地方。

在演进地图上的位置

评测的演进:每一代都被上一代的某个真数字逼出来(点名字去那一课;完整版在章首页)
  1. 只看 loss(和 PPL)
  2. 仪表:开局对 ln V、梯度范数、最大 logit、spike score
  3. 公开 benchmark
  4. n-gram 去污染 + 干净子集
  5. 换一套没见过的题 / 按时间切分
  6. 交换性置换检验(可证检测)
  7. 非公开题 + 第三方预先定好的评测集
  8. 三层可信度 + 口径核对
  9. 多种子、信噪比
  10. 平滑指标 + 两步预测
起点只看 loss(和 PPL)训练曲线的默认读法你在这里
解决了一个数就能看训练进度;PPL = eloss(C0 第 5 课)。
卡在项目 run「只把学习率从 3×10⁻³ 调到 3×10⁻²」:第 20 步验证 loss 6.671,比参照的 6.683 还低;同一时刻最大注意力 logit 已是参照的 10.8 倍(25.7 对 2.38)。
训练中仪表:开局对 ln V、梯度范数、最大 logit、spike scoreKarpathy 2019;Wortsman 2023;OLMo 2(2024)你在这里
解决了在 loss 变坏之前报警:OLMo 2 观察到剧烈的梯度范数尖峰常先于 loss 尖峰;改初始化后梯度范数的 spike score 从 0.40 降到 0.03。
卡在训练完了,仪表回答不了「模型会不会做题」——要拿题来考。
数字口径:第一格由构建时读取项目真实日志 c08-stability.json 现算(runs 为 c6_lrw_d256_lr0.03 与 ref_baseline);其余出自原文——GPT-3 §4 与附录 C(本地 PDF)、HF《What's going on with the Open LLM Leaderboard?》(2023)、GSM1k 附录 F、Oren 2023 表 1、NIST CAISI 页面(2026-05-01)、DataDecide 摘要、Schaeffer 2023 §1、Hu 2024 摘要、OLMo 2 §3。

证据三问

问本课的回答
① 它解决什么问题?loss 是结果,变坏时往往已经晚了;仪表读的是模型的状态,能更早报警,而且自带刻度
② 数学上为什么可能有效?均匀猜时 loss = ln V ;logit 放大后 softmax 饱和、敏感度 ∝ logit 尺度 ;梯度范数对少数大分量敏感是平方和的性质
③ 实验上为什么最后选了它?OLMo 2 的日志里梯度范数尖峰常先于 loss 尖峰 ;Wortsman 在小模型上复现了 logit 增长导致的不稳定 ;我们项目的回放里,学习率调大 10 倍时梯度范数与 logit 都比 loss 早报警

一句话

📌

loss 是结果,仪表看状态:开局对 ln V,过程中盯裁剪前梯度范数和最大 logit,它们比 loss 更早报警,但要几块一起读。

🚦

先停在这里:记住四块仪表各自的刻度,以及「安静不等于没事」,进下一课。
再深一层:在回放里把 16 个带曲线的 run 挨个过一遍,找出一个「loss 报警了、其他仪表都没报」的 run——它说明什么?(提示:看 c8_qknorm_lr3e-3,它最后比参照还好。)

下一个缺口

训练仪表回答的是「训练有没有出事」。训练完了,模型到底会不会做题,要拿题来考——在公开 benchmark 上报一个分数,这个分数能信吗? → 第 2 课:benchmark 分数与污染

你可能会问

这一课还没有收到真实提问;你的提问会记到 05-问答记录/C14-L1.md。

🔍 大模型训练时,这些仪表真的有人在看吗?

公开报告里能看到三种做法。① 把仪表写进训练日志并公开:OLMo 2 公开了完整训练日志与数千个中间 checkpoint,报告第 3 节整节讲稳定性,spike score 就出自那里。② 在报告里只给结论:DeepSeek-V3 写的是全程没有不可恢复的 loss spike、没有回滚;Kimi K2 写的是 15.5T token 全程零 loss spike(C8 第 3、4 课)——这些属于自报,我们看不到仪表本身。③ 拿仪表做压力测试:Qwen3.8-Flash-Next 报告把学习率固定在最优值的 4 倍,数每万步出现多少次 loss 尖峰(C8 第 3 课)。第 2 课的岔路会专门讲这类「自报数字」该信几分。