跳到主要内容

[LLM 6/10] 上下文蒸馏:把 system prompt 搬进模型权重里

· 阅读需 8 分钟
Kobkrit Viriyayudhakorn
CEO, iApp Technology

用户每一次给你的聊天机器人发消息,你都会把那段几百 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 cc 的学生, 让它的行为等同于一个看得见 cc 的老师——换句话说,把 cc 的作用从 prompt 搬进权重。 最早的 offline 思路可以追溯到 Askell 等人(2021)的工作, 而本章用的版本是 OPCD(On-Policy Context Distillation), 出自 Ye、Dong、Wu、Huang 和 Wei(2026,arXiv:2602.12275), 它加了两味关键配料,第 3 节会一味一味拆开看:

  1. 学生采样自己的回答(on-policy),并且看不见 cc
  2. 在这些回答上,最小化相对于"看得见 cc 的老师"的 reverse KL
本章的核心观点

system prompt 是被放错地方的知识——放在 prompt 里,你每次 request 都要付; OPCD 把它搬进权重,于是你只在训练时付一次

而在这一章里,老师和学生是同一份权重——老师就是眼前摆着 cc 的那个模型, 学生就是同一个模型但没有 cc。这两者之间的距离所度量的,纯粹就是"cc 的影响力"。

有一句话我想现在就先钉在这里,因为第 7 章还会出现另一个常被混淆的 "distillation":

Context distillation 改变的是"模型不用被告知就已经知道的东西"——model distillation 改变的是"模型的大小"

这一章里模型不会变小哪怕一个参数,它只是不再需要那段 prompt 了。 而第 7 章讲的是把大模型压缩成小模型——完全是另一根轴。

3. 公式(Equation)

3.1 OPCD 的 objective

L(θ)=E(x,c),  yπθ(x)[1yt=1yDKL(πθ(x,y<t)πteacher(c,x,y<t))]\mathcal{L}(\theta) = \mathbb{E}_{(x,c),\; y\sim\pi_\theta(\cdot|x)}\left[\frac{1}{|y|}\sum_{t=1}^{|y|} \mathbb{D}_{\text{KL}}\Big(\pi_\theta(\cdot \mid x, y_{<t}) \,\Big\|\, \pi_{\text{teacher}}(\cdot \mid c, x, y_{<t})\Big)\right]

其中每个 token 位置上的 KL,是在整个 vocabulary V\mathcal{V} 上求和:

DKL(πθπteacher)=vVπθ(vx,y<t)logπθ(vx,y<t)πteacher(vc,x,y<t)\mathbb{D}_{\text{KL}}\Big(\pi_\theta \,\Big\|\, \pi_{\text{teacher}}\Big) = \sum_{v\in\mathcal{V}} \pi_\theta(v \mid x, y_{<t})\,\log\frac{\pi_\theta(v \mid x, y_{<t})}{\pi_{\text{teacher}}(v \mid c, x, y_{<t})}
  • cc = 想搬进 weights 的 context(persona + 安全政策,约 400 token)
  • xx = 用户的问题,yy = 学生自己采样出来的、没看见 cc 的回答
  • πθ\pi_\theta = 学生(只看得见 xx 去预测),πteacher\pi_{\text{teacher}} = 老师(同一份权重,但还看得见 cc
  • 1y\frac{1}{|y|} = 按 token 取平均,防止长回答拿到过高的权重(是不是有点眼熟——第 4 章的 length bias)

注意这里并不是跟任何"标准答案"算 cross-entropy——目标是老师在每一个 token 位置上的 整行概率分布。学生学的不是"下一个词是什么",而是 "如果眼前摆着 cc,vocab 里每一个词的概率会长成什么样"。

这个式子里有两个决定,它们各自扛着整套方法的一半重量,我们分开看。

3.2 第一个决定——KL 必须是 reverse(πθ\pi_\theta 放在前面)

KL 不对称,而它的顺序就是在选择行为:

  • Forward KL DKL(πteacherπθ)\mathbb{D}_{\text{KL}}(\pi_{\text{teacher}} \| \pi_\theta) 会在老师有质量而学生没有的地方爆掉 → 学生被迫去"覆盖"老师的每一个 mode(mode-covering) 如果容量不够,它就会把质量摊平铺开去盖住一切,包括老师从来没去过的、两个 mode 之间的山谷—— 用 LLM 的话说,那就是"两种风格混在一起混出怪东西"的回答,或者 hallucination
  • Reverse KL DKL(πθπteacher)\mathbb{D}_{\text{KL}}(\pi_\theta \| \pi_{\text{teacher}}) 会在学生有质量而老师没有的地方爆掉 → 学生被迫不去做老师不做的事,然后挑老师的某一个 mode 死死咬住(mode-seeking,模式寻找)

对本章这件事来说——persona 和安全政策——我们想要的显然是后者,根本不用犹豫: 一个"能稳稳当当地按老师的某一种方式做事"的学生, 远比一个"给老师的每一条路都留概率,连老师明令禁止的路也留"的学生有价值。

如果你觉得在哪儿见过——没错,第 3–4 章 RLHF 公式里的 KL 也是把 π\pi 放在前面的, 理由完全一样:我们要管住的是正在训练的那一个的行为,而不是参考模型的。

3.3 第二个决定——rollout 必须是学生自己的(on-policy)

注意 3.1 式里的 yπθ(x)y\sim\pi_\theta(\cdot|x):用来训练的回答是从学生那里采样的,不是从老师那里。

更省事的做法是让老师(看得见 cc)先写好一批回答,然后让学生照着做 SFT—— 但那条路有一个结构性的毛病,叫 exposure bias:学生只在老师写出来的文本路径上被教过, 可真正上线时,它必须从自己写出来的 prefix 往下接。 错一个 token 就会掉进一个从没被教过的状态,然后误差一路复利滚下去。

on-policy 采样从结构上消掉了这个问题:学生在训练中遇到的状态, 和它在 inference 时会遇到的状态是同一类,因为两边都是它自己造出来的。 老师只有一个职责,就是"站在学生的路径上做批改"——告诉它,在你刚刚走到的这个点上, 如果有 cc,接下来该往哪边走(这和第 5 章必须采样自己的回答、而不是继续用 DPO 的理由是同一个)。

一行诚实的备注:算 gradient 的时候,我们把采样出来的 yy 当作常量, 不让 gradient 反向穿过采样这一步——这是 on-policy distillation 的标准做法。

3.4 必须打赢的 baseline:offline context distillation

大多数博客说的 "context distillation",指的是 offline 版本:

Loffline(θ)=Eyπteacher(c,x)[t=1ylogπθ(ytx,y<t)]\mathcal{L}_{\text{offline}}(\theta) = -\mathbb{E}_{y\sim\pi_{\text{teacher}}(\cdot|c,x)}\left[\sum_{t=1}^{|y|}\log\pi_\theta(y_t \mid x, y_{<t})\right]

直白地读出来就是:让看得见 cc 的老师写出回答,然后拿这些回答去 SFT 看不见 cc 的学生 ——就是在老师文本上的普通 cross-entropy,没有整行 KL,也没有 on-policy。

这可不是个稻草人,它是一个真正结实而且更便宜的 baseline(训练方式跟第 2 章一模一样)。 第 9 节会让 OPCD 在同一批数据上跟它公平较量。 如果 OPCD 那两味配料(reverse KL + on-policy)真的有价值,它就应该赢在理论说它会赢的地方: 向没见过的 prompt 类型上泛化

完整内容在课程中

这篇文章大约是本章的前 30%。其余部分——环境准备、数据准备、核心代码、实测结果与总结——都在免费的 LLM Finetuning 课程中,使用 Google 登录即可阅读。

在课程中阅读完整章节 →