[LLM 6/10] 上下文蒸馏:把 system prompt 搬进模型权重里
用户每一次给你的聊天机器人发消息,你都会把那段几百 token 的 system prompt 原封不动地一起送过去—— 系统活多久就付多久,每一次 request 都要重付,永远没有尽头。 这一章我们要把那一坨知识从 prompt 搬进模型权重,用的技术叫 Context Distillation(上下文蒸馏),而且是 on-policy 版本(OPCD)。 其中最漂亮的一点是:老师和学生是完全同一个模型——唯一的区别只是谁看得见 prompt。
Open in Colab06_context_distillation.ipynb
1. 问题(Problem statement)
一个典型的泰语客服助手,system prompt 大致长这样:设定 persona, 强制永远用泰语回答,必须礼貌地以 ครับ/ค่ะ 结尾(泰语礼貌助词,分别由男性/女性说话者使用), 禁止给出医疗和法律建议。认真写下来大约就是 400 token——而且它跟着每一次 request 一起发出去。
算一笔账:一个每天承接 100,000 次 request 的系统,等于为同一段文字反复付出 每天 4,000 万 token、每月 12 亿 token——而这段内容一个字符都没变过。 而这还没算上账单里看不见的另外两项代价:
- Latency —— 模型每次都得先 prefill 完 400 token,才能开始想第一个字
- Context budget —— persona 占掉的每一个 token,都是从对话历史和附件文档那里抢走的
放到本系列的框架里看,知识有三个可以存放的地方,而每个地方的"付款方式"都不一样:
| 知识存放的地方 | 什么时候付钱 | 适合什么 |
|---|---|---|
| System prompt | 每次 request,永远 | 还在频繁变动的行为/政策 |
| RAG | 每次 request(检索 + prompt 变长) | 数量庞大、变动频繁、需要标注来源的事实 |
| 模型权重 | 训练时付一次 | 已经稳定下来的行为/政策 |
一段已经稳定下来、却仍然每次都要附带的 system prompt,就是被放错地方的知识—— 它应该从这张表的第一行,搬到最后一行去。这一章讲的就是怎么搬。
2. 我们要做什么(Solution)
Context distillation 就是训练一个看不见 context 的学生, 让它的行为等同于一个看得见 的老师——换句话说,把 的作用从 prompt 搬进权重。 最早的 offline 思路可以追溯到 Askell 等人(2021)的工作, 而本章用的版本是 OPCD(On-Policy Context Distillation), 出自 Ye、Dong、Wu、Huang 和 Wei(2026,arXiv:2602.12275), 它加了两味关键配料,第 3 节会一味一味拆开看:
- 学生采样自己的回答(on-policy),并且看不见
- 在这些回答上,最小化相对于"看得见 的老师"的 reverse KL
system prompt 是被放错地方的知识——放在 prompt 里,你每次 request 都要付; OPCD 把它搬进权重,于是你只在训练时付一次。
而在这一章里,老师和学生是同一份权重——老师就是眼前摆着 的那个模型, 学生就是同一个模型但没有 。这两者之间的距离所度量的,纯粹就是" 的影响力"。
有一句话我想现在就先钉在这里,因为第 7 章还会出现另一个常被混淆的 "distillation":
Context distillation 改变的是"模型不用被告知就已经知道的东西"——model distillation 改变的是"模型的大小"
这一章里模型不会变小哪怕一个参数,它只是不再需要那段 prompt 了。 而第 7 章讲的是把大模型压缩成小模型——完全是另一根轴。
3. 公式(Equation)
3.1 OPCD 的 objective
其中每个 token 位置上的 KL,是在整个 vocabulary 上求和:
- = 想搬进 weights 的 context(persona + 安全政策,约 400 token)
- = 用户的问题, = 学生自己采样出来的、没看见 的回答
- = 学生(只看得见 去预测), = 老师(同一份权重,但还看得见 )
- = 按 token 取平均,防止长回答拿到过高的权重(是不是有点眼熟——第 4 章的 length bias)
注意这里并不是跟任何"标准答案"算 cross-entropy——目标是老师在每一个 token 位置上的 整行概率分布。学生学的不是"下一个词是什么",而是 "如果眼前摆着 ,vocab 里每一个词的概率会长成什么样"。
这个式子里有两个决定,它们各自扛着整套方法的一半重量,我们分开看。
3.2 第一个决定——KL 必须是 reverse( 放在前面)
KL 不对称,而它的顺序就是在选择行为:
- Forward KL 会在老师有质量而学生没有的地方爆掉 → 学生被迫去"覆盖"老师的每一个 mode(mode-covering) 如果容量不够,它就会把质量摊平铺开去盖住一切,包括老师从来没去过的、两个 mode 之间的山谷—— 用 LLM 的话说,那就是"两种风格混在一起混出怪东西"的回答,或者 hallucination
- Reverse KL 会在学生有质量而老师没有的地方爆掉 → 学生被迫不去做老师不做的事,然后挑老师的某一个 mode 死死咬住(mode-seeking,模式寻找)
对本章这件事来说——persona 和安全政策——我们想要的显然是后者,根本不用犹豫: 一个"能稳稳当当地按老师的某一种方式做事"的学生, 远比一个"给老师的每一条路都留概率,连老师明令禁止的路也留"的学生有价值。
如果你觉得在哪儿见过——没错,第 3–4 章 RLHF 公式里的 KL 也是把 放在前面的, 理由完全一样:我们要管住的是正在训练的那一个的行为,而不是参考模型的。
3.3 第二个决定——rollout 必须是学生自己的(on-policy)
注意 3.1 式里的 :用来训练的回答是从学生那里采样的,不是从老师那里。
更省事的做法是让老师(看得见 )先写好一批回答,然后让学生照着做 SFT—— 但那条路有一个结构性的毛病,叫 exposure bias:学生只在老师写出来的文本路径上被教过, 可真正上线时,它必须从自己写出来的 prefix 往下接。 错一个 token 就会掉进一个从没被教过的状态,然后误差一路复利滚下去。
on-policy 采样从结构上消掉了这个问题:学生在训练中遇到的状态, 和它在 inference 时会遇到的状态是同一类,因为两边都是它自己造出来的。 老师只有一个职责,就是"站在学生的路径上做批改"——告诉它,在你刚刚走到的这个点上, 如果有 ,接下来该往哪边走(这和第 5 章必须采样自己的回答、而不是继续用 DPO 的理由是同一个)。
一行诚实的备注:算 gradient 的时候,我们把采样出来的 当作常量, 不让 gradient 反向穿过采样这一步——这是 on-policy distillation 的标准做法。
3.4 必须打赢的 baseline:offline context distillation
大多数博客说的 "context distillation",指的是 offline 版本:
直白地读出来就是:让看得见 的老师写出回答,然后拿这些回答去 SFT 看不见 的学生 ——就是在老师文本上的普通 cross-entropy,没有整行 KL,也没有 on-policy。
这可不是个稻草人,它是一个真正结实而且更便宜的 baseline(训练方式跟第 2 章一模一样)。 第 9 节会让 OPCD 在同一批数据上跟它公平较量。 如果 OPCD 那两味配料(reverse KL + on-policy)真的有价值,它就应该赢在理论说它会赢的地方: 向没见过的 prompt 类型上泛化。
这篇文章大约是本章的前 30%。其余部分——环境准备、数据准备、核心代码、实测结果与总结——都在免费的 LLM Finetuning 课程中,使用 Google 登录即可阅读。
在课程中阅读完整章节 →