LLM 训练教程 · Ch12 端到端 CPT(继续预训练)
[!info] TL;DR 核心思想:CPT 是给成熟基座模型"增量注入新领域知识"的短程训练(200B tokens 级),核心敌人是灾难性遗忘——激进更新会把权重从通用分布的平坦极小值拽进新领域的尖锐深谷。 最大收获:三件事定生死——①新数据混比 α≈10%(5-20% 区间);②峰值 LR 降到原始的 1/10 且必须丢弃旧优化器状态;③同时盯领域内 PPL ↓ 和通用域 PPL 稳两条曲线。 为什么重要:这是"让模型成为医学专家,而不只是扮演医学专家的聊天机器人"的关键一步,也是最容易因一个 resume 习惯(加载了优化器状态)而翻车的环节。
button
type: cycle
id: read-status
cycle: [todo, in-progress, done]
style: default
actions: []笔记地图
- §1 定位:CPT vs SFT vs 从零预训练
- §2 核心挑战:灾难性遗忘
- §3 支柱一 · 数据策略
- §4 支柱二 · 优化器与学习率
- §5 支柱三 · 监控与评估
- §6 7B 端到端配置清单
- §7 常见陷阱诊断表
- 用自己的实验验证混比与 LR 起点
§1 定位:CPT 解决什么问题
三种训练处在生命周期的不同位置,解决不同的问题:
| 阶段 | 解决什么 | 数据形态 | 计算量 |
|---|---|---|---|
| 从零预训练 | 基础世界模型 + 语言能力 | 海量通用语料(自监督) | 巨大(1T 级,一次性) |
| CPT | 扩展知识边界 / 适应新数据分布 | 海量非结构化领域文本 | 增量(200B 级) |
| SFT | 对话行为 / 指令格式 | (prompt, response) 对 |
小 |
[!quote] 原文一句话总结 CPT 负责"学什么"(知识),SFT 负责"怎么说"(行为)。 当你的目标是让模型成为一个"医学专家",而不仅仅是"扮演医学专家的聊天机器人"时,CPT 是不可或缺的一步。
§2 核心挑战:灾难性遗忘的优化视角
预训练好的 θ_base 停在通用分布 P_general 的宽阔平坦极小值里——平坦性正是泛化能力的来源。CPT 要在混合分布 P_mix = (1-α)P_general + αP_new 上找新平衡点,但新领域的损失曲面可能又陡又远:
本章所有策略(混比、低 LR、重置优化器)都是同一件事的不同侧面:让 θ 从 θ_base 温和走到 θ_cpt_good,而不是被拽进 θ_cpt_bad。
§3 支柱一 · 数据策略
铁律:单纯喂新数据是 CPT 大忌,数据混合是必须的。
混比 α:5%–20% 区间,稳妥起点 10%
| α 取值 | 学新知识 | 遗忘风险 | 适用场景 |
|---|---|---|---|
| 低(~5%) | 慢,需更多步数 | 极低,非常安全 | 基座能力绝对不能受损的关键任务 |
| 中(10% 起点稳) | 平衡 | 低 | 默认起点 |
| 高(~20%) | 快 | 显著增高 | 新旧域差异不大 / 容忍一定泛化损失 |
Replay Buffer 思想:旧数据 P_general 不必用全部原始语料——从 1T tokens 里精选 200B 高质量、多样化子集作为"记忆回放缓冲池",显著降低存储与 I/O 负担。
混合方式:动态采样 > 静态预混
| 方式 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 静态混合 | 训练前按比例混好、打乱 | 实现简单 | 比例不可调;易出"数据热点"(连续 batch 同源) |
| 动态采样 | 数据加载器按温度 τ 对新旧数据流加权在线采样 | 每 batch 构成均匀;可做课程学习(如初期 α=10% → 后期 15%) | 需自定义 DataModule / IterableDataset |
细节:数据质量与序列长度分布(点开)
- 质量极敏感:低质量、噪声大的新数据会干扰学习甚至污染已有知识——严格清洗、去重、过滤不可省。
- 序列长度分布要匹配:如法律文书平均长度远超通用语料,会导致长文本建模偏向。确保 Packer 能有效处理,且评估时分层看不同长度文本的表现。
§4 支柱二 · 优化器与学习率
[!note] CPT 的"三低一重置" ① 峰值 LR = 原始的 1/10 ~ 1/5(7B:
3e-4→3e-5~6e-5) ② Warmup 极短甚至省略(~200 步即可) ③ Cosine Decay 的总步数按新训练计划重算(易错点!不能沿用原 1T 的调度) ④ 强制重置优化器状态——绝对不加载 checkpoint 里的m/v
为什么旧优化器状态是毒药:AdamW 的一阶矩 m(动量)和二阶矩 v(自适应分母)编码的是旧数据分布 + 旧学习率下的梯度历史:
- 旧
m带来巨大惯性 → CPT 初期把权重推向错误方向 - 旧
v适应旧梯度尺度 → 新梯度下部分参数 LR 过大/过小,训练不稳
正确操作:加载模型权重之后,重新实例化一个全新优化器。
冻结策略:默认全参数训练——知识要渗透到从词嵌入到高层推理的每一部分。极少数极端怕伤基础能力的场景可尝试冻结最底层几层 Transformer Block,但会牺牲新域学习效果,不推荐。
§5 支柱三 · 监控与评估
训练 loss 单独说明不了成败,三条核心指标 + 一张诊断表:
| 指标 | 期望走势 | 衡量什么 |
|---|---|---|
| Training Loss | 平稳下降;剧抖 → 查混比或 LR | 过程稳定性 |
| In-Domain Val PPL | 持续下降 | 学习效果(held-out 新领域集) |
| General Val PPL | 稳定 / 仅轻微上涨 | 遗忘护栏(held-out 通用集,如 C4/Pile) |
双 PPL 诊断表
| 模式 | Gen PPL | In-Domain PPL | 诊断 | 处方 |
|---|---|---|---|---|
| (A) 理想 CPT | 稳定 | 持续下降 | ✅ 目标状态 | 保持当前配方 |
| (B) 灾难性遗忘 | 急剧上升 ⬆ | 也降但代价惨重 | 被拽出平坦区 | 立即降 LR 或降 α |
| (C) 学习不足 | 稳定 | 下降缓慢 | 剂量不够 | 适度加 LR / 加 α / 加训练 tokens |
原文 ASCII 诊断图谱(点开对照)
(A) 理想的 CPT (B) 灾难性遗忘 (C) 学习不足
▲ PPL ▲ PPL ▲ PPL
| | |
|---- Gen PPL ---- |---- Gen PPL ---▲ |---- Gen PPL ----
| \ | \ / | \
| \ | \ / | \ In-Domain PPL
| \ In-Domain PPL | \ / | (下降缓慢)
| \ | \ In-Domain PPL |
+-----------> Time +-----------> Time +-----------> Time定期绘制两条 PPL 曲线:(A) 是目标;(B) → 降 LR 或新数据比例;(C) → 适度增加。
§6 端到端配置清单:7B 模型示例
| 参数项 | 从零预训练(基线) | CPT(推荐配置) | 理由 |
|---|---|---|---|
| 基座模型 | 随机初始化 | pretrained_7B.pt |
站在训练成熟的权重上 |
| 总训练 Tokens | 1T | 200B | 短程训练:目标是适应而非重塑 |
| 数据混比 α | 100% 通用 | 90% 通用 + 10% 新领域 | 核心:安全与效率的平衡起点 |
| 混合方式 | N/A | 动态采样 | batch 分布均匀 + 支持课程学习 |
| Global Batch Size | 4M tokens | 2M–4M tokens | LR 已降,大 batch 的信噪比增益不再是首要矛盾 |
| Peak LR | 3.0e-4 | 3.0e-5 | 1/10 原始 LR,小步长探索新极小值区域 |
| Warmup | 2000 步 | 200 步 | 权重已稳定,短 warmup 让新优化器热身即可 |
| LR Scheduler | Cosine (~250k 步) | Cosine (~50k 步) | ⚠️ 易错:总步数按新计划重算 |
| 优化器状态加载 | False | False(强制) | 丢弃旧 m/v,梯度历史不适用 |
| AdamW Betas | (0.9, 0.95) | (0.9, 0.95) | 对 CPT 不如 LR 敏感,无需改 |
| Weight Decay | 0.1 | 0.1 | 过高反而阻碍向新权重空间适应 |
| 层冻结 | 无 | 无(全参数训练) | 知识需在整个模型中充分渗透 |
§7 常见陷阱诊断表(Gotchas)
像查表一样用:先对症状,再开处方。
| # | 陷阱 | 症状 | 处方 |
|---|---|---|---|
| 1 | 灾难性遗忘(最常见) | Gen PPL 急涨,通用任务上像"傻瓜" | LR 减半;或 α 15% → 8% |
| 2 | 学习停滞 | In-Domain PPL 降得慢 / 不降 | 先查新数据质量 → 再提 LR / α → 或加训练 tokens |
| 3 | 加载了优化器状态 | 初期 loss 剧烈震荡 / NaN / 不收敛 | 确保优化器在加载权重之后重新创建 |
| 4 | Tokenizer 不匹配 | 新领域术语 OOV / 专有名词被切碎 | 扩展词表 + embedding 手术(见下) |
| 5 | 隐蔽的能力衰退 | Gen PPL 稳定,但数学推理 / 代码悄悄变差 | 建 benchmark suite 前后对比;replay buffer 定向补数据 |
陷阱 4 详解:Tokenizer 手术(超出标准 CPT,谨慎)
新领域引入大量新词时,标准 CPT 无解,需要"模型手术":
- 训练新的 Tokenizer(或扩展现有词表)
- 扩展模型的
token_embedding和lm_head层 - 特殊处理新旧 embedding 的初始化(新行不能随机乱初始化)
这已超出标准 CPT 范畴,务必谨慎评估是否真的需要。
陷阱 5 详解:为什么 PPL 会"瞒报"能力衰退
PPL 是宏观指标——通用验证集上平均 perplexity 稳定,不代表数学推理、代码生成这些具体能力没掉。
- 预防:CPT 前先跑一套多能力 benchmark suite,存下基线分数
- 治疗:发现某能力受损后,在 replay buffer 里有针对性地提高该能力对应数据的比例(如数学语料)
我的批注
- ⚡ 全章最有"杠杆比"的一条是重置优化器状态:零成本(一行代码),但很多人带着 resume 思维惯性把 checkpoint 全家桶照单全收,然后对着 NaN 排查一天。
- ⚡ 陷阱 5 点破了 PPL 的盲区:宏观 PPL 稳 ≠ 下游能力没掉。评估体系必须 PPL + benchmark 双轨。
- ❓ Replay buffer 从 1T 精选 200B 的选择标准是什么?质量/多样性打分?原文未展开,值得查 DoReMi / 数据混合相关文献。
- ❓ α=10% 起点是否随模型规模缩放?教程以 7B 为例,更大模型(70B+)是否可用更低 α?
- 🔗 关联:数据混比思想与 [[SFT、RL 与 OPD:分布视角]] 中"训练分布决定模型分布"的视角一致;LR / 调度细节可对照 [[UltraScale Playbook 笔记]] 的 pretraining 部分。
行动项
- CPT 实验前:先跑 benchmark suite 存基线(防陷阱 5)
- 自检训练脚本:优化器是否在
load_state_dict之后重新创建(防陷阱 3) - 调研 replay buffer 子集选择方法(质量/多样性打分)
- 延伸阅读:[[UltraScale Playbook 笔记]]
相关阅读笔记
TABLE venue, date, key-idea
FROM "LLM Training"
WHERE type = "reading-note"
SORT date DESC