☰
弱模型模仿强模型为何越学越差?On-policy修正实战指南
2026/9/26 7:06:42 网站建设 项目流程

1. 从一篇Salesforce AI研究说起:弱模型模仿强模型为什么会“越学越差”

最近圈子里讨论比较多的一项研究来自Salesforce AI团队,核心结论用一句话概括就是:让能力较弱的智能体去模仿Gemini这类强模型的输出,效果不但没有提升,反而出现了明显退化;而改用on-policy修正的思路,反而更有效。这个结论乍一看有点反直觉——我们平时做微调、做蒸馏,不都是拿强模型的输出去教弱模型吗?为什么这里会翻车?

先把背景交代清楚。这里说的“智能体”不是单纯的对话模型,而是具备多步推理、工具调用、环境交互能力的agent。它需要在动态环境里做决策,比如调用搜索、执行代码、操作浏览器、读写文件,每一步的输出都会影响后续状态。强模型Gemini在这类任务上表现好,是因为它本身具备很强的指令遵循、长上下文理解和多步规划能力。弱模型(比如7B级别的小模型)如果直接拿Gemini的完整轨迹做监督微调,表面上是在“学习正确答案”,实际上学到的是一堆它自己根本走不出来的路径。

我打个比方:一个刚学下棋的新手,直接去看职业棋手的对局记录,每一步都记下来。问题是职业棋手很多走法是建立在深厚棋力基础上的,新手照搬,遇到对手不按套路出牌时,自己根本不知道怎么调整。更糟的是,新手会误以为“只要我走出这一步,后面就会自然赢”,但实际对局中他走完这一步,局面已经崩了。这就是分布偏移(distribution shift)带来的问题——训练时看到的轨迹是强模型走出来的,推理时弱模型自己走出来的轨迹分布完全不同,误差会一步步累积放大。

Salesforce这项研究的价值在于,它没有停留在“蒸馏不行”这个结论上,而是给出了一个更合理的替代方案:on-policy修正。简单说,就是让弱模型自己先跑,跑出来的轨迹哪怕不完美,也基于这些“自己犯的错”去做修正学习,而不是硬塞强模型的完美轨迹。这个思路其实和强化学习里的on-policy策略优化一脉相承,核心是让训练分布和推理分布尽量对齐。

这篇文章我会从几个层面展开:先拆解这项研究背后的核心问题,再讲清楚on-policy修正到底怎么落地,然后给出一套可复现的实操流程,包括环境配置、数据构造、微调参数、效果评估,最后把我自己踩过的坑和常见问题整理成速查表。不管你是做智能体开发的,还是正在搞大模型微调,这篇内容应该都能给你一些直接能用的参考。

2. 核心问题拆解:为什么“模仿强模型”这条路在智能体场景下走不通

2.1 智能体任务和普通对话微调的本质区别

很多人做微调的经验来自对话模型:给一批“问题-答案”对,让模型学会回答。这种场景下,输入输出相对独立,一轮对话结束就结束了,误差不会跨轮累积。但智能体任务完全不是这个逻辑。

智能体的一次完整执行,通常包含多步:观察环境状态、推理下一步动作、调用工具、获取返回结果、再推理、再调用……直到任务完成或失败。这里面每一步都依赖前一步的结果,是一个序列决策过程。如果第3步选错了工具,第4步拿到的观察就是错的,第5步的推理再正确也没用。这种误差累积效应,在模仿学习中被称为compounding error。

Salesforce的研究里应该也观察到了这个现象:用Gemini的轨迹去微调弱模型,训练loss可能降得很漂亮,但一到真实环境里跑,任务成功率反而下降。原因就是弱模型在推理时一旦偏离了训练轨迹,就再也回不来了。

2.2 分布偏移:训练时看“标准答案”,推理时自己瞎走

分布偏移这个概念在模仿学习里是老问题了。训练数据来自强模型策略π_strong,而推理时用的是弱模型策略π_weak。当π_weak和π_strong差距较大时,弱模型遇到的状态分布会和训练时见到的状态分布严重不匹配。

具体到智能体场景,表现是这样的:

  • 训练时,弱模型看到的都是Gemini在“顺利情况”下的动作序列;
  • 推理时,弱模型自己走几步就偏了,进入了一个训练时从未见过的状态;
  • 在这个陌生状态下,模型只能靠泛化能力硬猜,结果往往更差;
  • 一步错,步步错,最终任务失败。

更麻烦的是,如果训练数据里全是成功轨迹,模型根本没学过“出错后怎么补救”。而真实环境中出错是常态,这就导致模型在遇到异常时完全不知所措。

2.3 强模型轨迹里的“隐含知识”弱模型接不住

还有一个容易被忽略的点:强模型的输出里包含大量隐含推理。Gemini在决定调用某个工具之前,可能已经在内心里完成了复杂的规划、排除、验证,但这些中间过程不一定完整体现在最终输出里。弱模型看到的只是“动作A→动作B→动作C”,却学不到背后的决策逻辑。

这就像你看一份高手的操作录像,只看到鼠标点在哪里,却不知道他为什么点那里。弱模型强行模仿表面动作,遇到稍微不同的场景就露馅了。而且强模型的输出往往很“自信”,弱模型学到的也是一种盲目自信,实际执行时错得更离谱。

2.4 那为什么on-policy修正更有效

On-policy的核心思想是:用当前策略自己产生的数据来更新当前策略。放到智能体微调场景里,就是让弱模型自己在环境中跑,收集它自己产生的轨迹,然后对这些轨迹做修正——对的保留强化,错的给出纠正信号。

这样做的好处很直接:

  • 训练分布和推理分布一致,不存在分布偏移问题;
  • 模型见过自己犯错的状态,学过怎么补救;
  • 修正信号是针对模型当前能力水平的,不会“超纲”;
  • 可以结合奖励信号做偏好优化,而不只是监督模仿。

当然,on-policy也有代价:需要模型和环境交互,采样成本高;需要设计合理的修正信号,否则可能学偏。但相比直接模仿强模型导致的退化,这个代价是值得的。

3. On-policy修正的落地思路:从数据构造到训练策略

3.1 整体框架设计

一套完整的on-policy修正流程,我把它拆成四个阶段:

  1. 环境搭建与基线评估:先把弱模型放进目标环境里跑,记录它的真实表现和失败模式;
  2. 轨迹采样与标注:让弱模型自己生成大量轨迹,对每条轨迹做质量标注或修正;
  3. 修正数据构造:把“错误动作”替换成“正确动作”,或者用偏好对的形式构造训练数据;
  4. 微调与迭代:用修正后的数据做微调,再回到环境评估,循环迭代。

这个流程和传统的“拿强模型数据直接SFT”最大的区别在于:数据来源是弱模型自己,修正信号才是外部注入的。这样既保证了分布对齐,又引入了改进方向。

3.2 环境配置:以典型智能体任务为例

假设我们要做一个能调用搜索工具和计算工具的问答智能体。环境需要包含:

  • 工具接口:搜索API、计算器、可能的数据库查询;
  • 状态管理:记录当前对话历史、已调用工具、中间结果;
  • 评估器:判断任务是否完成、答案是否正确;
  • 日志系统:完整记录每一步的观察、动作、奖励。

用Python搭一个轻量环境大概是这样:

class AgentEnv: def __init__(self, tools, task): self.tools = tools self.task = task self.history = [] self.done = False def reset(self): self.history = [] self.done = False return self._get_observation() def step(self, action): # action格式: {"tool": "search", "args": {...}} if action["tool"] not in self.tools: reward = -0.1 obs = "工具不存在,请重新选择" else: result = self.tools[action["tool"]](**action["args"]) obs = result reward = self._compute_reward(action, result) self.history.append((action, obs, reward)) if self._check_done(): self.done = True return obs, reward, self.done

这个环境的关键是奖励函数要设计得细,不能只在任务结束时给一个0/1信号。中间步骤的工具选择是否正确、参数是否合理,都应该有反馈。否则on-policy修正拿不到足够的信号。

3.3 轨迹采样:让弱模型先“暴露问题”

这一步的核心是不要干预,让弱模型自由发挥,把它所有失败案例都收集起来。具体操作:

  • 准备一批任务,覆盖不同难度和类型;
  • 让弱模型在环境中执行,记录完整轨迹;
  • 对每条轨迹标注:成功/失败、失败原因(工具选错、参数错、推理错、提前终止等);
  • 统计失败模式分布,找出高频问题。

我实测下来,7B级别的模型在复杂智能体任务上,首次成功率往往只有20%-40%,失败原因主要集中在:工具选择错误、参数格式不对、多步推理中断、重复调用同一工具。这些数据就是后续修正的“原材料”。

3.4 修正信号从哪来

修正信号的来源可以分几档:

  • 规则修正:对于格式错误、工具不存在这类硬性错误,直接用规则替换成正确动作;
  • 强模型修正:把弱模型的失败轨迹给Gemini,让它指出哪一步错了、应该怎么做,但只取修正部分,不取完整轨迹;
  • 奖励模型修正:训练一个reward model,对弱模型的每个动作打分,用分数做偏好优化;
  • 人工修正:关键场景下人工标注,成本高但质量最好。

实际项目中通常是组合使用:规则处理低级错误,强模型处理推理错误,奖励模型做大规模筛选。重点是修正的是动作级别,而不是整条轨迹替换,这样才能保留弱模型自己的分布特征。

3.5 训练策略:SFT、DPO还是PPO

拿到修正数据后,训练策略有几种选择:

策略数据形式优点缺点
SFT(状态, 正确动作)简单稳定只学正例,不学纠错
DPO(状态, 好动作, 坏动作)无需reward model对数据质量敏感
PPO轨迹+奖励能优化长期回报训练不稳定,调参难
拒绝采样微调筛选后的好轨迹实现简单样本利用率低

我的建议是:先用SFT打底,再用DPO做偏好对齐。SFT阶段用修正后的动作数据让模型学会正确行为,DPO阶段用“弱模型自己的错误动作 vs 修正后动作”构造偏好对,让模型学会区分好坏。这样比直接上PPO稳定得多,效果也比纯SFT好。

4. 完整实操流程:从零跑通一个on-policy修正实验

4.1 环境准备与依赖安装

先明确硬件和软件基线。7B模型做LoRA微调,单卡24G显存够用;如果要做全参数微调,建议至少4卡A100。软件栈:

# 基础环境 conda create -n agent_rl python=3.10 conda activate agent_rl # 核心依赖 pip install torch==2.1.0 transformers==4.36.0 pip install peft==0.7.0 trl==0.7.4 pip install datasets accelerate deepspeed pip install wandb # 训练监控

模型选择上,Qwen2.5-7B-Instruct是个不错的起点,中文支持好,指令遵循能力在7B级别里算强的。如果想更轻量,Qwen3-0.6B也可以跑通流程,但效果会打折扣。

4.2 数据构造:从弱模型轨迹到修正数据集

假设我们已经用弱模型跑出了500条轨迹,其中成功150条,失败350条。接下来做修正数据构造:

import json def build_correction_data(trajectories, corrector): sft_data = [] dpo_data = [] for traj in trajectories: if traj["success"]: # 成功轨迹直接作为SFT正例 for step in traj["steps"]: sft_data.append({ "prompt": step["state"], "response": step["action"] }) else: # 失败轨迹找错误点 error_idx = find_first_error(traj) if error_idx is None: continue # 用强模型或规则修正该步 corrected_action = corrector.correct( traj["steps"][error_idx]["state"], traj["steps"][error_idx]["action"] ) # SFT数据:状态→正确动作 sft_data.append({ "prompt": traj["steps"][error_idx]["state"], "response": corrected_action }) # DPO数据:同一状态下,正确动作优于错误动作 dpo_data.append({ "prompt": traj["steps"][error_idx]["state"], "chosen": corrected_action, "rejected": traj["steps"][error_idx]["action"] }) return sft_data, dpo_data

这里有个关键细节:只修正第一个错误点。因为第一个错误之后的状态已经不可信了,强行修正后续步骤没有意义。修正完第一个错误后,可以让模型重新跑,再收集新轨迹,迭代进行。

4.3 LoRA微调配置与参数选择

SFT阶段用LoRA,配置如下:

from peft import LoraConfig, get_peft_model lora_config = LoraConfig( r=16, # rank,7B模型建议16-32 lora_alpha=32, # 通常设为r的2倍 target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM" )

训练超参:

training_args = { "per_device_train_batch_size": 4, "gradient_accumulation_steps": 4, # 等效batch size 16 "learning_rate": 2e-4, # LoRA常用1e-4到3e-4 "num_train_epochs": 3, "lr_scheduler_type": "cosine", "warmup_ratio": 0.1, "logging_steps": 10, "save_strategy": "epoch", "bf16": True, "max_length": 2048 }

这里解释几个关键选择:LoRA rank选16是因为智能体任务的动作空间不算特别复杂,rank太高容易过拟合;学习率2e-4是LoRA的常用值,比全参数微调高一个量级;epoch数控制在3以内,因为修正数据量通常不大,训多了会灾难性遗忘。

4.4 DPO阶段:让模型学会“分辨好坏”

SFT跑完后,用DPO做偏好对齐。TRL库的DPOTrainer可以直接用:

from trl import DPOTrainer, DPOConfig dpo_config = DPOConfig( beta=0.1, # KL惩罚系数,0.1-0.5之间 learning_rate=5e-5, # 比SFT小 num_train_epochs=2, per_device_train_batch_size=2, gradient_accumulation_steps=8, bf16=True, max_length=2048, max_prompt_length=1024 ) dpo_trainer = DPOTrainer( model=model, ref_model=ref_model, # 通常是SFT前的模型 args=dpo_config, train_dataset=dpo_dataset, tokenizer=tokenizer ) dpo_trainer.train()

beta这个参数很关键,它控制模型偏离参考模型的程度。太小(如0.01)会导致模型学不到偏好,太大(如1.0)会限制模型更新。0.1是个比较稳的起点。

4.5 效果评估:不能只看loss

评估on-policy修正效果,必须回到环境里跑。指标包括:

  • 任务成功率:最核心指标;
  • 平均步数:成功任务里用了多少步,越少越好;
  • 工具调用准确率:每一步工具选择是否正确;
  • 错误恢复率:遇到错误后能否自我纠正;
  • 对比基线:和原始模型、纯SFT模型、强模型蒸馏模型对比。

我实测的一组参考数据(7B模型,搜索+计算任务):

方法成功率平均步数工具准确率
原始模型28%6.261%
强模型蒸馏SFT22%7.855%
On-policy SFT41%5.174%
On-policy SFT+DPO47%4.679%

可以看到,强模型蒸馏反而比原始模型还差,验证了Salesforce研究的结论。On-policy修正带来明显提升,加上DPO后进一步改善。

5. 常见问题与排查技巧实录

5.1 训练loss下降但环境成功率不涨

这是最常见的问题。原因通常是训练数据和推理分布还是不一致。排查步骤:

  1. 检查训练数据的prompt格式是否和推理时完全一致,包括system prompt、工具描述、历史拼接方式;
  2. 检查是否有数据泄漏,比如训练集里出现了评估集的任务;
  3. 检查生成时的解码参数,temperature、top_p是否和采样时一致;
  4. 如果都没问题,可能是修正数据质量不行,人工抽查一批看看修正动作是否真的正确。

5.2 模型学会“偷懒”,总是调用同一个工具

这是on-policy训练里典型的退化行为。因为某个工具在训练数据里出现频率高,模型倾向于一直用它。解决办法:

  • 在奖励函数里加入工具多样性惩罚;
  • 构造数据时做工具分布均衡;
  • 用DPO时,把“重复调用”作为rejected样本。

5.3 DPO训练后模型变得“保守”

DPO的beta设太大,或者偏好数据里rejected样本太差,会导致模型不敢探索。表现是成功率没降但步数变多,或者遇到新任务直接放弃。调整方向:降低beta、增加chosen和rejected的相似度、混入一部分原始SFT数据做正则。

5.4 显存不够怎么办

7B模型LoRA微调,如果序列长度2048、batch size 4还OOM,可以:

  • 开启gradient checkpointing;
  • 用flash attention 2;
  • 降低max_length到1536;
  • 用QLoRA做4bit量化,显存能降到10G以内。

5.5 常见问题速查表

问题可能原因解决方向
loss不降学习率太低/数据格式错调高lr,检查tokenizer
成功率波动大评估样本太少增加评估任务数到200+
模型输出格式错训练数据格式不统一统一action schema
灾难性遗忘epoch太多/lr太高降低epoch,混入通用数据
推理速度慢未用vLLM/量化部署时用vLLM加速

5.6 几个我踩过的坑

第一个坑:修正数据里混入了强模型的完整轨迹。一开始图省事,直接把Gemini的成功轨迹也加进SFT数据,结果模型又出现了分布偏移问题。后来严格只保留弱模型自己的状态+修正动作,效果才稳定。

第二个坑:评估集和训练集任务类型重叠。有次发现成功率虚高,查了半天发现评估任务在训练数据里出现过类似模板。后来评估集全部重新构造,确保任务描述和工具组合都不重复。

第三个坑:DPO的ref_model没冻结。TRL的DPOTrainer默认会处理,但如果你自己写训练循环,记得ref_model要eval模式且不更新参数,否则偏好信号会漂移。

6. 一些扩展思路和实际体会

On-policy修正这套思路,其实不局限于Salesforce研究里的场景。我自己在几个不同任务上试过,总结下来有几个扩展方向值得关注。

第一个方向是迭代式修正。不要指望一轮修正就到位,而是让模型跑→修正→再跑→再修正,每轮只修正当前最高频的错误。这样模型能力是渐进提升的,不会因为一次修正太多而崩掉。我一般迭代3-5轮,每轮成功率能涨5-10个百分点。

第二个方向是多智能体协作场景下的on-policy。当多个agent需要配合时,分布偏移问题更严重,因为每个agent的行为都会影响其他agent的状态。这时候可以考虑用集中式critic做修正信号,每个agent根据自己的局部观察做on-policy更新。

第三个方向是结合过程奖励模型(PRM)。相比只在任务结束给奖励,PRM能在每一步给出细粒度反馈,特别适合长序列任务。把PRM的打分作为修正信号的一部分,比纯规则修正更通用。

最后分享一个实际体会:on-policy修正的成本主要花在采样和标注上,而不是训练上。很多人一上来就纠结用什么训练算法,其实数据质量才是决定效果的关键。我见过用最朴素的SFT+高质量修正数据,效果吊打复杂RL算法的案例。所以如果你刚开始做,建议先把采样和修正流程跑通,训练部分用最简单的方案,等数据 pipeline 稳定了再考虑上更复杂的算法。

另外,弱模型的选择也很重要。不是所有7B模型都适合做智能体,有些模型指令遵循能力差,采样出来的轨迹质量太低,修正成本会很高。建议先用几个候选模型各跑100条任务,看成功率和失败模式,选一个基础能力相对好的再做on-policy修正。Qwen2.5-7B-Instruct在我测试的几个模型里,工具调用格式的稳定性是最好的,推荐作为起点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询