本节摘要:loss 曲线告诉你模型"平均猜词准不准",但说不清它"会不会讲一个有头有尾的故事"——这中间隔着一层台阶式的能力涌现。社区 walkthrough 在 nanochat 预训练途中反复用同一个"直升机故事"提示让模型续写,亲眼看着输出从乱码、成词、成句、复读到成段情节逐级点亮(社区实测口径)。本节把这套做法提炼成正式的中期评测方法:探针三固定纪律(固定提示、固定解码、固定长度)、基座与 SFT 两条涌现观察清单、以及与 3.2 节 loss 三阶段的对照裁决——loss 说"还在学",探针说"学成了什么",两张图放在一起,才敢做继续、回退还是收手的决定。
阅读完本节,你应当能够:
loss 是全体验证 token 的平均交叉熵——一个数字概括百万次预测的平均误差。它有两个先天盲区:
| 问题 | loss 的回答 | 为什么不够 |
|---|---|---|
| 模型会讲连贯的故事吗 | 只能给一个平均分 | 连贯是句与句之间的关系,单步猜词的误差累不出现象 |
| 能力是什么时候出现的 | 看不出来 | 涌现是台阶状的:某能力常在一段步数后突然可辨,loss 却平滑下降 |
| 该不该提前收手 | val 拐点给一半答案 | 拐点说"开始背数据了",但"行为最好的那一格"还得看生成 |
社区 walkthrough 的直观发现(对 nanochat 预训练的公开复现日志):预训练 loss 从 10.4 一路降到 2.32(6,612 步,社区实测),曲线平滑得没有任何故事;但每隔一段就用同一个提示让模型写"直升机的故事",输出质量却在几个步数区间里明显跳档——loss 是平均数,能力是切片。中期评测就是定期切几片下来看。
探针(probe):一组写死在脚本里、每隔若干 checkpoint 对模型跑一遍的固定生成任务。纪律是三固定:
max_new_tokens 固定——长度本身也是观测量(模型何时停、会不会停)。基座探针题组(预训练途中用,英文为主——FineWeb-Edu 喂出来的基座"母语"是英文):
| 探针 | 提示(示意) | 观察什么 |
|---|---|---|
| 直升机故事 | Once upon a time there was a little helicopter... | 从乱码到情节的涌现主刻度(社区 walkthrough 同款) |
| 常识补全 | The sun rises in the ... | 高频搭配是否形成 |
| 自由续写 | 截一段百科首句 | 题材漂移与复读倾向 |
SFT 探针题组(训练途中每几百步一次,3.2 已埋点):观察项直接取自 0.1 的行为差异三表现——事实问答(答完会停吗)、身份(你是谁)、指令("写一首四行小诗"是否照做)、多轮("第二句是什么意思"接得住指代吗)。
# probe_checkpoints.py —— 对多个 checkpoint 跑固定探针题组(写法示意) import torch from transformers import AutoModelForCausalLM, AutoTokenizer PROBES = [ # 三固定之一:提示硬编码 {"role": "user", "content": "用两句话讲一个关于直升机的小故事。"}, {"role": "user", "content": "你是谁?"}, {"role": "user", "content": "写一首关于秋天的四行小诗。"}, ] def probe(ckpt: str) -> None: tok = AutoTokenizer.from_pretrained(ckpt) model = AutoModelForCausalLM.from_pretrained(ckpt, dtype=torch.bfloat16) for msg in PROBES: ids = tok.apply_chat_template([msg], tokenize=True, add_generation_prompt=True, return_tensors="pt") out = model.generate(ids, max_new_tokens=80, do_sample=False) # 三固定之二:贪心 text = tok.decode(out[0][ids.shape[1]:], skip_special_tokens=False) print(f"[{ckpt}] {msg['content'][:12]}... -> {text}") # 三固定之三:80 token del model if __name__ == "__main__": for ckpt in ["runs/sft_124m/checkpoint-200", "runs/sft_124m/checkpoint-600", "runs/sft_124m/final"]: probe(ckpt)
把输出按 checkpoint 顺序贴进一页文档,就是最便宜的能力成长日志——3.2 要求的"每几百步抽问固定问题",从此有脚本可依。
基座预训练阶段(对照社区 walkthrough 的直升机故事观察,分期位置随种子与数据浮动,示意口径):
| 期 | 生成样貌 | 直升机故事的样子 |
|---|---|---|
| 乱码期 | 无意义字符流 | 字母堆,无空格 |
| 成词期 | 高频英文单词,无语法 | helicopter 一词反复出现但不成句 |
| 成句期 | 局部语法正确,句间无关 | The helicopter was a helicopter. 式循环 |
| 复读期 | 单句/单段无限重复 | 同一句话写到 80 token 上限 |
| 成段期 | 句间连贯,有简单情节 | 有起因、动作、结尾,会自己停 |
SFT 阶段的清单(对照 0.1 三表现,出现顺序大体如下):
| 期 | 观察项 | 通过标准 |
|---|---|---|
| 格式期 | special tokens 排布 | 输出不再以标记开头,回答后跟终止符 |
| 停止期 | 答完即停 | 不再顶到 max_new_tokens 上限 |
| 身份期 | "你是谁" | 以助手自居,不替用户编话 |
| 指令期 | 写诗/列举类任务 | 按指令的形式约束输出 |
| 多轮期 | 指代与追问 | 结合上文回答,不装失忆 |
⚠️ 涌现点不是里程碑:换种子、换数据,台阶会移位。清单的用途是分期与沟通,不是"loss 降到 X 必然会 Y"的预测表——把分期写死进自动流程,是过拟合评测的另一种形态。
把 3.2 的 SFT loss 三阶段和探针放在同一横轴上:
| loss 阶段(3.2) | 探针典型表现 | 决策 |
|---|---|---|
| 快降区(前 ~10%) | 格式与停止迅速到位(SFT);成词成句(基座) | 正常,继续 |
| 平台区 | 内容质量缓慢爬升;探针进步停滞但 val 未升 | 再观察一个存档周期 |
| 过拟合区(train 降 val 升) | 3.2 三征兆在探针上现形:复读、变短变小、逐字复述 | 回退上一个健康 checkpoint |
交叉裁决的两条经验:
训练途中的观察能力有了;训练完成之后,"它到底会不会聊天"需要一张正式的记分牌——5.2 节搭 rubric、裁判与抽检表。