原创教学项目 · 核对:2026-10-09。示例是合成数据;校验程序只检查结构与计算,不代替内容审核。
给训练器(trainer)一个 messages 字段,很容易启动 SFT。但几天后发现模型总引用旧资料,想回查是哪批数据教的,只有对话文本就不够用了。
我们分开保留两份东西:能追溯的原始记录,以及训练器实际吃到的样本。前者方便审核和重做,后者方便检查模板与 loss。
原始记录:不要过早丢掉来源
下面把一条 JSONL 记录展开显示。项目只处理一轮文字问答;多轮对话、工具调用或图片需要扩展 schema,不能直接套这个校验器。
{
"sample_id": "demo-001",
"group_id": "registration-deadline",
"split": "train",
"question": "When does registration close?",
"evidence": [
{"id": "policy-7", "revision": "v2", "text": "Registration closes on Wednesday."}
],
"answer": "Registration closes on Wednesday [policy-7].",
"citations": ["policy-7"],
"answerable": true,
"label_source": "synthetic-demonstration",
"review_status": "approved"
}
revision 让我们能找到当时的资料,label_source 区分人工示范与模型生成,group_id 把同一问题的改写放在一起。approved 只是流程状态,不证明答案正确;示范依然要抽查引用是否支持结论。
如果问题不可回答,明确记 answerable: false,写合适的拒答示范,引用列表为空。不是把答案留成空字符串,也不是随便选一篇资料充数。
先划分,再变成训练格式
先去重、确定分组,再划分 train / dev / test,然后分别做改写、模板化和 tokenization。先生成一堆近义改写再随机按行切分,会让测试集看起来比实际容易。
按问题分组并不能解决所有泄漏。如果目标是泛化到新文档,需要按文档或文档家族隔离;如果是未来数据,需要时间边界。选哪种取决于你要验证什么。正文 hash 能找完全重复,不能替代语义近重复检查。
| 产物 | 保留什么 | 可以复查什么 |
|---|---|---|
| 审核记录 | 原始问题、证据版本、答案、来源、分组 | 这条示范为什么能进训练 |
| 训练视图(view) | messages,另外保留 sample ID 对照表 |
系统指令和证据到底怎么拼接 |
| tokenized 缓存 | token、目标 mask、长度、预处理指纹 | 同一句话为什么变成这些监督位置 |
| 运行清单 | 数据快照(data snapshot)、模板、tokenizer revision、配置 | 更换依赖后是否还在做同一个实验 |
训练视图可使用 system 指令、包含问题和证据的 user 消息、作为示范的 assistant 消息。来源元数据另存一个附属文件,不要不加区分地全塞进模型上下文。证据要有清晰边界,并按不可信资料处理,不能让其中的文字覆盖系统指令。
模板也不是随意添加几个角色名。Transformers 的聊天模板文档说明了模型特定控制 token 的作用。训练使用完整示范,不额外加一个待生成的 assistant 开头;若先格式化成字符串再 tokenize,要避免重复添加 special tokens。
看一条样本的 labels,而不只看 JSON
设玩具 token 序列为 [11, 12, 21, 22, 2]:前两个表示上下文,后两个是答案,2 是结束 token。目标 mask 为 [0, 0, 1, 1, 1]。
input_ids 11 12 21 22 2
labels -100 -100 21 22 2
attention 1 1 1 1 1
在常见 causal-LM loss 接口中,模型内部把 logits[:, :-1] 与 labels[:, 1:] 对齐。于是位置 1(从 0 数起,即 token 12)的输出预测 token 21;并不是同一位置“偷看”答案后预测自己。接自定义 loss 时要确认谁负责 shift,不能重复移位。
用户 token 不作为 target,不代表模型看不见它们。attention_mask 控制可见或有效位置,labels=-100 是这里采用的忽略 loss 约定。更完整的手算见数据与目标。
还有一个很容易漏掉的情况:pad 与 EOS 可能使用同一个 token ID。若只按 input_ids == pad_id 屏蔽,就会连真实结束 token 一起抹掉。下面的校验程序按实际长度补 pad,因此保留真实 EOS 的监督。
长度预算与 packing:省掉的是什么
假设输入共 1,600 token,答案 500 token,而上限是 2,048,超出 52 token,至少要删掉这么多。机械保留前 2,048 个 token 会截掉答案尾部,可能恰好丢掉引用和结束标记。先决定缩短哪些证据或历史,再重新生成样本并验证支持关系;保不住目标就剔除或另分一桶,不静默裁掉。
| 做法 | 好处 | 代价与检查 |
|---|---|---|
| batch 内 padding | 边界清楚,容易排错 | 长短差距大时浪费计算;按长度分桶会改变取样顺序 |
| packing 多条样本 | 减少填充,让计算更多用于实际 token | 要确认 attention 隔离、position IDs 和样本边界处的 labels |
| 按 token 预算组 batch | 每批计算规模更接近 | 样本数量会变;要重新检查 loss 分母和累积逻辑 |
长度 3 和 5 的两条样本,padding 到 5 需要 10 个槽位,其中 8 个是真实 token,利用率是 80%。把它们拼起来可用 8 个槽位,但正确的独立样本训练还需要边界语义:第二条不能注意第一条,第一条的末尾也不能被训练成预测第二条开头。
只重置 position IDs,不保证所有 attention 后端都自动隔离。TRL 的 packing 行为依赖策略与后端;按锁定版本验证。下面代码只实现易检查的 padding,不假装实现高效 packing。
配好环境以后,先检查这几件事
依赖装好了、模型加载成功了,只说明可以开始排查。正式训练前,留一小组固定样本,把真实的数据路径走一遍;不要让 smoke test 使用另一套临时模板或 collator。
| 检查 | 具体看什么 | 通过后能说明什么 |
|---|---|---|
| 一条完整示范 | 解码输入和有效 labels,对照角色、证据、答案、EOS | 模板和监督位置符合预期,不代表答案内容正确 |
| 一条长示范和一条短示范 | 截断了哪里,新增 pad 是否被屏蔽,真实 EOS 是否保留 | 长度处理没有悄悄破坏这两条样本 |
| 一个训练 step | loss 和梯度有限,目标 token 数非零,可训练参数发生更新 | 这条训练路径有有效更新,不代表一定能泛化 |
| 一个 batch 拆成 microbatch | 固定样本顺序,关闭 dropout 后比较累积梯度,设置数值容差 | 累积与归一化是否一致;真实多卡还要单独测 |
| 中断后继续 | 检查下一批样本 ID、优化器状态、学习率、RNG 和数据版本 | 恢复的不只是权重;不能据此承诺跨设备逐 bit 相同 |
最开始可以在很小的数据集上试着过拟合(overfitting):如果模型连这些固定示范都学不动,先查模板、mask、学习率、冻结参数和数据读取。但“几条样本背下来了”只能作为排错信号,不能拿来证明数据方案有效。
涉及 RL 时,再加一个环境检查:固定初始状态,用一段已知的动作或工具调用序列走到终点,确认环境能重置、奖励(reward)符合规则,且能区分任务失败、工具报错与超时。对随机环境检查可重复的条件或统计范围,而不是强求每次逐字相同。
本站教学测试覆盖 padding、分母计算和简化 checkpoint 恢复,其中梯度检查使用可选的 CPU PyTorch。它们不等于真实模型的 tokenization、GPU 多卡或 RL 环境已经验收;上表中剩余检查需要在你实际采用的版本上运行。为什么这些细节会改变学习信号,见数据与环境在教什么。
跑一次,再故意把数据弄错
标准库实现包含记录校验和教学 collator。在仓库根目录运行:
python3 practice/post-training/code/training_contracts.py
python3 -m unittest discover -s site/tests -p 'test_training_contracts.py'
可以改两处试试:把引用 ID 换成不存在的资料、把全部 target mask 改成 0,两种都应报错。演示里的 pad ID 本来就和 EOS 相同(都是 2):第一条样本的真实 EOS 仍是 label,只有新补的两个位置是 -100;把 pad ID 改成 0 再跑,labels 应完全不变。测试还会拒绝跨集合的同组样本。
这些检查抓不住“资料存在,但日期读错了”。那部分仍需要内容审核、反例和固定评估。不要把 schema 校验通过写成数据质量已经过关。
下一步先手算 loss,再考虑多卡是否改变了这个目标。
本页术语 5
- 训练器 trainer
- 负责计算 loss、反向传播并更新模型参数的系统
- 视图 view
- 同一个对象的一种侧面表示;用户可以同时有画像视图和历史视图
- 数据快照 data snapshot
- 固定在某个时间点可见的数据版本,避免重跑时读到未来状态
- 过拟合 overfitting
- 训练集越来越好,没见过的数据反而变差
- 奖励 reward
- RL 里指导策略更新的标量信号