知道 next-token prediction 的公式以后,很容易觉得预训练(pre-training)就是“把文本喂进去,循环跑 loss”。但两次训练即使用同一个模型、同一个目标,也可能学出很不一样的东西:文本选了什么、重复了多少遍、怎样拼成序列,都会改变模型实际看到的任务。
这篇跟着一份虚构的小语料走一次。数字只用于计算,不是推荐配方。
先留下来源,再处理文本
假设我们有网页、代码和技术文档三类数据。先为每条文档保留来源、采集时间、内容 hash、语言和处理版本,再清理导航栏、乱码、重复模板等内容。
为什么不只保存最终字符串?因为发现某批文档解析错了时,需要能找回它们;调整过滤阈值时,也需要知道删掉的是哪些语言和领域。更重要的是,能抓取不等于有权用于训练,许可、隐私和删除要求要先核对。
| 操作 | 想排除什么 | 可能误伤什么 |
|---|---|---|
| 格式与语言检查 | 解析失败、乱码、明显不在范围内的文本 | 混合语言和特殊符号 |
| 质量过滤 | 模板堆砌、无意义拼接 | 方言、短文、少见专业写法 |
| 精确去重 | 完全相同的重复文档 | 本来有意义的重复出现 |
| 近似去重 | 稍微改写的镜像页面 | 相似但结论不同的内容 |
RefinedWeb是研究过滤和去重的一个公开实例,不意味着照搬它的规则就适合任何语料。过滤后要抽样读被保留和被删掉的内容。
训练与验证应按来源、重复簇或时间等合适单位分开,不能把同一文档的两个近似副本随机分到两边。基准题的改写、答案泄漏也不会被简单字符串去重全部发现。
混合比例决定模型反复看到什么
假设去重后剩下 800 万网页 tokens、100 万代码 tokens、100 万文档 tokens。按原始占比采样是 80/10/10;如果希望训练中的比例变成 50/30/20,代码就会被反复抽到。
设总训练预算为 2000 万 tokens:
| 来源 | 唯一 tokens | 训练抽取量 | 平均使用次数 |
|---|---|---|---|
| 网页 | 800 万 | 1000 万 | 1.25 |
| 代码 | 100 万 | 600 万 | 6 |
| 文档 | 100 万 | 400 万 | 4 |
多读几遍代码不等于多得到了 500 万独立的新代码。过采样能改变能力分配,也会增加记忆和过拟合(overfitting)风险。比较配方时,固定总 tokens 或计算预算,同时看各领域验证结果,别只看混合后的平均 loss。
这里按 token 比例记账。如果按文档抽样,长文和短文的长度差异会改变实际比例。
Packing 不只是省 padding
两篇文档切成:
文档 1:[A, B, EOS]
文档 2:[C, D, EOS]
拼接后:[A, B, EOS, C, D, EOS]
是否允许 C 看见 A、B?这是训练定义,不是无关紧要的实现细节:
- 连续 token 流:普通 causal mask 允许后文看前文;EOS 提示边界,但不会自动切断 attention。
- 独立文档 packing:使用块对角 causal mask;C 只能看文档 2。位置编号是否重置,也要与实现约定一致。
如果不想训练 EOS 后跨文档预测 C,还要遮掉那一项 loss。attention mask 控制“能看谁”,loss mask 控制“哪项预测算分”,两者不能互相替代。文档 2 的首 token C 可以不算,也可以在文档 2 开头放 BOS、由 BOS 预测,取决于数据格式。
| 输入位置 | 下一目标 | 连续流可计入 | 本例独立文档、不设 BOS |
|---|---|---|---|
| A | B | 是 | 是 |
| B | EOS | 是 | 是 |
| EOS | C | 是 | 否 |
| C | D | 是 | 是 |
| D | EOS | 是 | 是 |
padding 位置也不能当成普通 target。检查时打印一条完整的 tokens、labels、document IDs 和 masks,比看一条下降的 loss 更有用。
一次更新,到底平均了什么?
有效位置集合为 $\mathcal V$,token 平均损失为:
$$ \mathcal L=-\frac{1}{|\mathcal V|} \sum_{t\in\mathcal V}\log p_\theta(x_{t+1}\mid x_{\le t}). $$
几个 microbatch 的有效 target 数不同时,先各自求均值、再平均这几个数,就换成了另一个目标;梯度累积也有同样的问题。一次训练用 2 和 8 个 token 的例子算了一遍:按 token 平均是 2.6,直接平均两个均值是 2。
一般流程是:固定这一更新要覆盖的有效 token 数,累积相应 loss 的梯度,必要时还原缩放并裁剪,然后 optimizer step。多卡同步和归一化见分布式训练。
学习率和预算为什么要一起看?
warmup 让学习率逐步升高;后续可以按约定退火。它不是修复脏数据或错误 mask 的药。记录 schedule 按 optimizer steps 还是按 tokens 推进,改变 batch 或恢复训练时才不会悄悄换了节奏。
粗略估算 dense Transformer 训练的主要矩阵计算,常写作 $C\approx6ND$:$N$ 是参数量,$D$ 是训练 tokens。长序列 attention、重算、通信和硬件利用率不包含在这个简式里,不能直接把 FLOPs 当耗时;从 FLOPs 换算成时间要先估硬件利用率,见 MFU 与 HFU。
Chinchilla研究的是给定训练计算预算下模型规模与数据量的分配;它不是“任何项目都按某个 tokens/parameter 比例训练”的定律。实际还要考虑可用数据、质量、重复,以及模型上线后被调用多少次。
例如全局每步处理 262,144 个有效 targets,计划 1000 步,就是 262,144,000 个 targets。若每步 1 秒是实测值,可以估训练段用时;若只是猜测,就不能据此承诺 GPU 预算。
什么才算一次能复现的训练?
至少一起保存模型、optimizer、学习率调度、随机状态、数据配方和读取位置;混合精度可能还需要 scaler 状态。只保存权重后“从第 1001 步继续”,未必真的接上了同一条训练轨迹。
验证时固定 tokenizer、切分、mask 和计分单位,再分别看代码、语言、长文本等子集。token perplexity 不能跨不同 tokenizer 不加说明地比较。最后还要看真实任务:文本预测更好,不自动意味着更会按要求办事。
可以先做三个便宜检查:很小的一批数据能否拟合;更换 padding 是否改变有效 token 的 loss;中断再恢复的一步,是否与连续训练在约定容差内一致。先让这些通过,再扩到大规模。
本页术语 2
- 预训练 pre-training
- 先用大量数据学习通用表示和能力,再适配具体任务
- 过拟合 overfitting
- 训练集越来越好,没见过的数据反而变差