让小模型"够一够就能学会":ZPPO 把教师塞进提示词,而不是梯度里
论文标题:Zone of Proximal Policy Optimization: Teacher in Prompts, Not Gradients arXiv:https://arxiv.org/abs/2606.18216 (arXiv ID: 2606.18216)
先说一个我自己踩过好几次的坑。
你想把一个 27B 大模型的本事,蒸馏给一个 0.8B 的小模型。最直接的办法就是 logit 蒸馏——让小模型去拟合大模型每个 token 的输出分布。听起来很合理对吧?大的会,小的照着学就行。
但实际跑下来,你会发现一个很别扭的现象:小模型在训练集分布内确实变好了,可一拿到训练语料之外的 benchmark 上,泛化能力反而塌了。我一开始以为是数据不够、训练步数不够,加了一圈发现都不是。
这篇来自 NVIDIA 团队(Byung-Kwan Lee、Ximing Lu、Shizhe Diao、Pavlo Molchanov、Yejin Choi、Yu-Chiang Frank Wang 等)的论文,给这个现象起了个挺到位的诊断,也给了一个我觉得"对,就该这么干"的方案。核心一句话就能说清:别把教师塞进梯度里,把它塞进提示词里。
📖 核心摘要
知识蒸馏在"小学生"场景下是脆的。强迫一个小模型去模仿大得多的教师的 logits,会把它的概率质量挤到教师最尖锐的几个峰上,训练集内还行,一出训练语料的 benchmark 家族就垮。
那换成 RL(强化学习)呢?RL 训练在学生自己的 rollout 上,天然避开了 logit 模仿。但它有个死穴:碰到那种"所有采样全错"的硬题,group-relative advantage 全是零,这道题被静默丢弃,等于白采。有人想了个补丁——把教师的正确答案直接注入 policy gradient,假装是学生自己生成的。问题来了,这破坏了 on-policy 假设,会引发 policy drift(策略漂移)。
ZPPO 的解法借了维果茨基"最近发展区"的概念:教师只待在 prompt 里,绝不进梯度。 对硬题,它构造两种重写后的提示——BCQ(把一个教师对的答案和一个学生错的答案匿名混在一起,让学生自己去分辨)和 NCQ(把学生所有错误 rollout 聚到一起,让它看清自己共有的错误模式)。再配一个 prompt replay buffer 把硬题反复循环,直到学生"毕业"或被挤出去。
效果?在 Qwen3.5 家族四个学生规模(0.8B–9B)、27B 教师、31 个 benchmark 上,ZPPO 全面打过 off/on-policy 蒸馏和 GRPO,而且模型越小、增益越大——0.8B 的 VLM benchmark 平均涨了 9.3 个点。最关键的是,它在蒸馏会损害的那些训练语料外领域(LLM、Video),反而把泛化做上去了。
我的判断:这篇的价值不在某个花哨的 loss,而在它把"教师该以什么形态出现"这件事想清楚了。是一个工程直觉很强、可复现性也写得很细的工作。
🤔 先搞清楚:到底是哪两个坑
论文开篇画了张图,把它要解决的两个失败模式摆在一起。这张图值得停下来看一眼。

图1(Figure 1):Qwen3.5 家族在 10 个 LLM(左)、16 个 VLM(中)、5 个 Video(右)benchmark 上的平均准确率增益 Δ(单位 pp,百分点)。所有方法都在同一份多模态数据上训练,再到各类 benchmark 上测泛化。† 表示带 prompt replay buffer 的增强版。可以很直观地看到,ZPPO 那条线在每个规模都甩开其它方法,而且越往左(模型越小)差距越大。
回到那两个坑。
第一个坑是蒸馏的 mode-seeking。 你让 0.8B 去拟合 27B 的 logits,27B 的分布是很"尖"的——它对自己擅长的题有很强的确定性。小模型容量有限,硬去对齐这些尖峰,结果就是把有限的概率质量全压在教师最自信的那几个模式上,牺牲了对其它情况的覆盖。训练集内看着提升了,分布外直接拉胯。
第二个坑是 RL 的零梯度黑洞。 GRPO 这类方法靠组内对比算优势:一道题采样 G 个回答,谁比平均好谁的 advantage 为正。但碰到硬题——所有 G 个回答全错——平均就是全错,每个回答相对平均都是零优势,标准差也趋零。这道题对梯度的贡献是零,等于没训。偏偏这些硬题才是最该学的。
有人的补丁是:既然学生全错,那我把教师的正确答案掺进这个 group 里当一个"回答",advantage 不就有了?听着挺机灵。但这条回答不是学生当前策略生成的,把它的 token 拿去算 policy gradient,就是在用别人的轨迹更新自己的策略——off-policy 的毒开始累积,训练曲线会慢慢歪掉。

图2(Figure 2):(a) 两个失败模式。左边是小学生场景下 logit 蒸馏的 mode collapse,右边是把教师注入梯度导致的 on-policy 假设破坏。(b) ZPPO 的高层思路——教师只出现在 prompt 里,学生永远用自己的 rollout 算梯度。这张概念图把"teacher in prompts, not gradients"这句话画得很清楚。
这两个坑其实指向同一个本质问题:教师的知识,到底该以什么形态喂给学生? logit 蒸馏是把教师的"答案分布"硬塞进学生的输出层;梯度注入是把教师的"具体轨迹"硬塞进学生的更新信号。两者都越过了学生当前的能力边界。
维果茨基那套教育心理学里有个概念叫"最近发展区"——孩子能独立完成的事和在帮助下能完成的事之间,有一个区间,教学最有效的地方就在这个区间里。ZPPO 干的事,就是把教师的帮助限制在"提示"这个层面,让学生踮踮脚自己够,而不是直接抱起来放到终点。
🏗️ 方法:BCQ、NCQ 和一个循环 buffer
ZPPO 整套机制其实就三个零件,外加一个把它们串起来的底座(GRPO)。先看总览图。

图3(Figure 3):ZPPO 总览。(a) 硬题(平均 rollout 准确率低于一半)被路由到两条重写路径;(b) BCQ 把一个教师正确答案和一个学生错误答案匿名混合;(c) NCQ 聚合学生所有错误 rollout;学生在重写后的 prompt 上重新采样,所有梯度仍来自学生自己生成的 token。底部是 prompt replay buffer 的循环逻辑。
硬题怎么定义
很简单:一道题 x,学生采样若干 rollout,平均准确率 \(\bar{r}_x \lt 0.5\),就是硬题。
为什么卡在 0.5?这不是拍脑袋。在 {0,1} 二元奖励下,伯努利分布的标准差在 \(\bar{r}_x = 0.5\) 时取最大。而 group-relative advantage 的分母正是这个标准差——换句话说,准确率在 0.5 附近的题,携带的学习信号最强。这个阈值选得有数学依据,我挺欣赏这种"顺手把道理讲清楚"的写法。
BCQ:让学生在两个匿名候选之间做判别
BCQ 全称 Binary Candidate-included Question。对每道至少有一个教师正确答案的硬题,它干这么几件事:
均匀采一个教师正确答案 \(y_T^{(+)}\),再均匀采一个学生错误答案 \(y_S^{(-)}\)。两个答案都先由冻结的教师做一次候选压缩——用统一的压缩提示和统一的 token 上限,改写成简短的推理轨迹。这一步是为了抹掉表面线索(比如教师答案天然更长更工整,学生一眼就能猜出哪个是对的)。
然后把这两个压缩后的候选,匿名塞进相同的 <candidate> 标签里,随机打乱顺序,附到原题后面,配一条"请在两个候选里判断并推理出正确答案"的指令。
关键在最后一步:学生从这个重写后的 prompt 重新采样一组新的 rollout。每一个响应 token 都是学生当前策略生成的,所以 policy gradient 在响应层面严格 on-policy。教师只是以"两个待判别的候选"这种形态出现在输入里,它的轨迹从不进入梯度。
教学效果来自哪?来自把"对的"和"错的"并排放——匿名、打乱、不贴正确性标签——逼着学生自己去分辨、去推理为什么这个对那个错。这跟直接告诉它答案,是完全不同的两件事。
NCQ:让学生看清自己的"集体错误"
NCQ 全称 Negative Candidate-included Question。它处理的是另一种情况:把当前这一组里学生所有的错误 rollout 收集起来,解析出每个的最终答案,在 prompt 里显式列出来,再把每个教师压缩后的推理轨迹作为 <candidate> 块附上,明确告诉学生"这些全是错的,给我正确答案"。
这里有个特别精巧的点。在标准的 RL rollout 组里,每个错误回答是独立贡献优势的,组内的 rollout 互相"看不见"彼此的错误答案。NCQ 是整个训练循环里第一个让学生的失败尝试集体可见的地方——它把一堆孤立的负样本,拧成了一个共享信号,提示学生去识别"我反复在哪种坑里栽"。
说实话,第一次读到这块我愣了一下。我们平时做 RL,错误样本要么被零优势丢掉,要么各算各的。把它们聚拢起来当成一面镜子照给模型看,这个视角我之前真没想到过。
Prompt Replay Buffer:把硬题反复循环
光有 BCQ/NCQ 还不够,硬题得有机会被反复啃。buffer \(\mathcal{B}\) 就干这个,它只存问题 x(图像和文本),从不存任何 rollout 响应。
- 准入:每个训练步后,\(\bar{r}_x \lt 0.5\) 的题被存进来。
- 毕业(移除):已准入的题,在后续任意步里 \(\bar{r}_x \geq 0.5\)(达到一半)就算掌握,移出 buffer。
- FIFO 驱逐:buffer 满了,按先进先出挤掉最老的。
- 采样:每个 rollout batch 把数据加载器的新题,和从 buffer 里均匀抽的 replay 题混在一起,replay 的比例是固定的 \(\rho_{replay}\)。
有个细节我觉得是点睛之笔:只有 plain(原始)学生 rollout 用来判定准入和毕业,BCQ/NCQ 产生的 rollout 只贡献梯度、不参与 buffer 成员判定。 意思是,一道题只有在学生不靠候选参考也能独立解出来的时候,才算真正"毕业"。这就杜绝了"学生只是学会了看候选作弊"的假掌握。
而且每次访问(无论新题还是 replay)都重新采样教师 rollout,所以 BCQ 的候选每次都在变,不会退化成几个固定的标准答案让学生死记。
跟 GRPO 是什么关系
ZPPO 是搭在 GRPO 上的。它老老实实交代了每个组件的来源,这种诚实我给好评:
- 来自 GRPO:group-relative advantage 公式、PPO 风格的 clipped surrogate 目标。group-relative advantage 长这样:
- 来自 DAPO:非对称 clip-higher,\((\epsilon_{low}, \epsilon_{high}) = (0.20, 0.28)\);token 级 loss 聚合;不加 KL 惩罚。
- 来自 REINFORCE++ 的部分:两步 advantage 估计(先减组内均值,再跨非平凡组做 batch 归一化)。
- ZPPO 自己的 recipe 选择:从 batch 统计里排除零优势组(zero-advantage-group exclusion),以及用 \(I=4\) 而非标准的 \(I=16\) iterations per step。
- ZPPO 真正的贡献:BCQ、NCQ、prompt replay buffer,以及 buffer 与 BCQ+NCQ 之间的超可加(super-additive)组合。
这里的归一化设置有个值得一提的推导。ZPPO 默认用 "Norm w/o Zero"——只在非平凡组上做归一化,平凡组保持优势为零。论文给了个闭式结果:如果把零优势组也算进归一化(Norm w/ Zero),标准差会变成 \(\sigma_G = \sqrt{f_{nt}} \cdot \sigma_{G^*}\),导致每个非平凡优势被 \(1/\sqrt{f_{nt}}\) 均匀放大,更容易把 importance ratio 推出 PPO 的裁剪窗口,训练曲线变差。一个小设计,背后有清楚的道理。
🧪 实验:小模型受益最大
实验做得相当扎实。Qwen3.5 家族四个学生规模(0.8B / 2B / 4B / 9B),27B 教师(同时兼任 LLM-as-a-judge),都后训练成 VLM,在 31 个 benchmark(16 VLM + 10 LLM + 5 Video)上评。训练数据是自建的 ZPPO-77K 多模态 RL 数据集。硬件是 64 块 H100。
主结果:16 VLM benchmark
先看 VLM 上的主表。0.8B 和 2B 的平均分:
| 方法 | 0.8B Avg | 2B Avg |
|---|---|---|
| Qwen3.5 基线 | 41.0 | 56.8 |
| ++ Off-Distill | 41.0 | 56.9 |
| ++ Off-Distill† | 41.5 | 57.3 |
| ++ On-Distill | 41.9 | 57.7 |
| ++ On-Distill† | 42.3 | 58.0 |
| ++ GRPO | 43.8 | 58.7 |
| ++ GRPO† | 45.4 | 59.2 |
| ++ ZPPO | 50.3 | 62.0 |
| Δ(对最强非 ZPPO) | +4.9 | +2.8 |
0.8B 上,ZPPO 比最强的非 ZPPO 方法(GRPO†)还高 4.9 个点,比基线高 9.3 个点。注意一个细节:off-policy 蒸馏几乎没动基线(41.0 → 41.0),on-policy 蒸馏也就涨了不到 1 个点。蒸馏在这个小模型场景下,基本是失灵的。
单项里有些数字很扎眼,比如 WeMath 涨了 9.2 个点,MVision 涨了 8.3 个点,MVerse 涨了 8.2 个点。
泛化才是真考验:LLM 和 Video
这张表我觉得是全文最能打的证据。模型只在多模态 VLM 数据上训练,然后去测纯 LLM 和 Video benchmark——也就是训练语料完全没覆盖的领域。
| 方法 | 0.8B-LLM | 0.8B-Video | 2B-LLM | 2B-Video |
|---|---|---|---|---|
| 基线 | 25.2 | 48.3 | 45.3 | 60.6 |
| ++ Off-Distill† | 22.5 | 45.0 | 43.1 | 58.6 |
| ++ On-Distill† | 23.2 | 45.8 | 43.7 | 59.2 |
| ++ GRPO† | 28.7 | 50.5 | 47.3 | 61.9 |
| ++ ZPPO | 33.1 | 52.8 | 50.4 | 63.2 |
| Δ | +4.4 | +2.3 | +3.1 | +1.3 |
看出问题了吗?蒸馏在这里是负增益。 Off/On-policy 蒸馏在 LLM 和 Video 上全面跌破基线——0.8B 的 LLM 从 25.2 掉到 22.5,Video 从 48.3 掉到 45.0。这就是开篇说的 mode-seeking 后果:拟合教师把分布外的泛化牺牲掉了。而 ZPPO 不光没跌,还把 LLM 拉到 33.1(涨 7.9 个点)。
单项里 GPQA-D 涨了 16.9 个点,MultiCh 涨 7.8 个点,2B 的 IMO-AB 涨了 10.2 个点。这些都是纯推理类的硬 benchmark,能在没见过的领域涨这么多,说明学到的是"怎么推理"而不是"教师的答案长什么样"。
组件消融:超可加效应
消融做得很到位,回答了一个关键问题——这三个零件,是各管各的,还是有协同?
| 配置 | 0.8B Avg | 2B Avg |
|---|---|---|
| GRPO | 43.8 | 58.7 |
| GRPO†(只加 buffer) | 45.4 | 59.2 |
| GRPO ++ Both(只加 BCQ+NCQ,无 buffer) | 45.2 | 58.9 |
| GRPO† ++ NCQ | 46.2 | 60.1 |
| GRPO† ++ BCQ | 48.6 | 60.8 |
| ZPPO(全部) | 50.3 | 62.0 |
结论很清楚:
单独用某个组件,提升都有限。 只加 buffer(GRPO†),0.8B 涨 1.6 个点;只加重写(GRPO + Both),涨 1.4 个点。
但 buffer × 重写是超可加的。 BCQ 配上 buffer,0.8B 直接到 48.6——远超两者孤立效果之和。这说明硬题需要"反复循环"和"以候选形态重写"两件事同时发生才真正起作用:buffer 保证硬题不被丢掉、反复回来,重写保证每次回来时学生能学到东西。
还有个有意思的规模规律:模型越小越靠 BCQ,越大越靠 NCQ。 弱学生需要教师的正确候选来指方向,强学生则更受益于"集体失败分析"——它已经有能力,缺的是看清自己反复栽在哪。
Buffer 动态:硬题真的被啃下来了

图5(Figure 5):(a) 整个训练过程中单个 rollout batch 的组成——Easy(\(\bar{r}_x \geq 0.5\))、BCQ、NCQ 三部分的占比,按学生规模分。(b) prompt replay buffer 的累计毕业 vs. FIFO 驱逐比例。可以看到小模型更多在毕业前就被挤出去(容量有限、硬题太多),大模型则更多能在被挤出前毕业。
论文给了组很有说服力的数字(2B,Sec. 4.3):在准入时 rollout 准确率为 0% 的题目上——也就是普通 RL 完全零梯度信号的死亡区——ZPPO 毕业了 28%(432/1568),而 GRPO† 只有 4%(73/2035)。在次难的题上(准入约 14% 准确率),是 54% vs. 14%。
这组对比直击要害:那些被普通 RL 静默丢弃的硬题,ZPPO 真的把相当一部分啃下来了。
跟 Hint、Prefix 比:为什么是"判别"而不是"提示"
一个很自然的质疑:你 BCQ 不就是给提示吗?那我直接给个 hint,或者给个答案前缀,不也行?论文专门做了对比。
| 方法 | 0.8B-LLM | 0.8B-VLM | 2B-LLM | 2B-VLM | 2B-Video |
|---|---|---|---|---|---|
| GRPO† | 28.7 | 45.4 | 47.3 | 59.2 | 61.9 |
| ++ Hint | 28.8 | 47.2 | 47.6 | 60.4 | 61.6 |
| ++ Prefix | 27.1 | 45.5 | 46.3 | 59.5 | 61.0 |
| ++ BCQ | 31.5 | 48.6 | 49.1 | 60.8 | 62.6 |
| ZPPO | 33.1 | 50.3 | 50.4 | 62.0 | 63.2 |
Hint 能帮到 VLM,但往 LLM/Video 迁移就差——因为 hint 是方向性捷径,告诉你"往这边走"却不说"为什么这边对"。Prefix 更糟,甚至把 LLM 拉到基线以下(27.1 \lt 28.7),因为前缀是 off-policy 的,drift 随训练累积。而单是 BCQ 就把两者都打过了——区别就在于,学生被训练去在两个匿名候选之间判别和推理,而不是被动接收方向。
这组对比把"为什么要做成判别任务"这个设计动机坐实了。
教师规模的影响

图7(Figure 7):跨教师规模的全局平均增益 Δ(pp)。更大的教师放大 ZPPO 的增益;把教师缩到 4B/9B,增益坍缩回 GRPO†+NCQ 的水平。下降在 0.8B 学生处最陡——因为这里 ZPPO 增益最被 BCQ 主导,而 BCQ 要求教师在学生失败处能成功。教师变小,正确候选池枯竭,BCQ 就没料可用了。
这个结果其实暴露了 ZPPO 的一个内在依赖:它吃教师的"正确率"。BCQ 的前提是教师在硬题上得有对的答案可供采样。教师一弱,硬题上它也答不对,BCQ 就退化了。这点论文自己在局限性里也承认了。
💡 我的判断
先说优点。
问题诊断准。 "教师该以什么形态出现"这个问题,被拆成 logit 蒸馏的 mode-seeking 和梯度注入的 off-policy drift 两个具体失败模式,再用"教师只进 prompt 不进梯度"一刀切开。这个抽象层次很舒服——不是堆 trick,是想清楚了再动手。
泛化证据硬。 在训练语料完全没覆盖的 LLM/Video 上还能正增益,而蒸馏在这里是负的。这个对比比任何训练集内的提升都更有说服力,说明学到的是推理能力而非答案模式。
写得诚实。 每个组件来自 GRPO/DAPO/REINFORCE++ 的哪一部分都标得清清楚楚,没有把别人的东西包装成自己的"首创"。归一化那个闭式推导、0.5 阈值的标准差论证,都是顺手把道理讲透,不糊弄。
再说几个我会打问号的地方。
对教师的依赖是硬约束。 Figure 7 已经说明,教师一弱 BCQ 就废。论文也承认在学生能力上限附近,教师和学生都失败的题没有 BCQ 候选,zone 坍缩成 NCQ-only。换句话说,ZPPO 最擅长的是"教师会、学生暂时不会"这个区间,一旦逼近教师的天花板,方法的威力就减弱。这其实正是"最近发展区"概念本身的边界——很自洽,但也意味着它替代不了真正的能力突破。
和 dynamic sampling 有张力。 dynamic sampling 的常规做法是删掉全对/全错的题,而 ZPPO 偏偏要把全错的题存进 buffer 反复啃。两套思路朴素叠加是冲突的,怎么融合论文没深入,留了个坑。
成本。 每道硬题每次访问都要重新跑教师 rollout、做候选压缩、还有 LLM-as-a-judge,64 块 H100 的配置不便宜。论文有 per-run 成本表(Table 9),但能不能在更紧的预算下复现这套收益,是落地时要算的账。
工程上如果你也在做小模型的后训练,我觉得这套思路值得试——尤其是"把错误 rollout 聚合成共享信号"(NCQ)和"只用原始 rollout 判定掌握、防止学候选作弊"这两个点,迁移成本不高,单独拿出来用都可能有效。BCQ 的前提是你手头得有个明显更强的教师。
放到行业里看,这类"把强模型的知识以提示而非参数形态传递"的思路,我觉得会越来越多。直接蒸参数在小模型上的天花板已经看到了,怎么让小模型"自己够一够学会",可能才是性价比更高的方向。
觉得有启发的话,欢迎点赞、在看、转发。跟进最新 AI 前沿,关注我