资讯动态

SOD分步同策略蒸馏:让小语言模型学会大模型的思考与行动策略

发布时间:2026/8/24 3:21:53 来源:尧图企业网站定制
1. 项目概述当小模型也想拥有“大智慧”最近在跟几个做模型落地的朋友聊天大家普遍有个痛点大语言模型LLM能力是强推理、规划、工具调用样样精通但那个成本和延迟实在让人头疼动不动就几百亿参数部署在云端响应慢想塞到端侧设备里更是天方夜谭。而另一边参数量在百亿甚至十亿级别的小语言模型SLM倒是轻快便宜可一旦让它们去执行需要多步推理、调用外部工具或API的复杂任务比如写个爬虫脚本、分析一份财报、规划一次旅行表现就有点“力不从心”经常卡壳或跑偏。这背后的核心矛盾在于构建一个能干的智能体Agent不仅仅是让模型“说得好”更要让它“做得好”。大模型通过思维链CoT、ReAct等框架展现了出色的分步推理和行动规划能力。但如何把这种“大智慧”有效地、可靠地迁移到小模型身上而不仅仅是做简单的输出模仿就成了一个关键挑战。我最近深入研究了SODStep-wise On-policy Distillation这套方法它直指这个痛点。简单来说SOD不是让小模型去死记硬背大模型给的最终答案而是像一个教练一样手把手地教小模型“思考过程”和“行动策略”。它让小模型在“实战”On-policy中学习大模型每一步的决策从而实现从“模仿结果”到“学会方法”的升华。这听起来是不是比传统的知识蒸馏KD只蒸馏最终输出概率或中间层特征要来得更“实在”这正是SOD的精髓所在也是我们今天要拆解的核心。2. 核心思路拆解为什么是“分步”且“同策略”的蒸馏在深入SOD的细节之前我们得先弄明白它要解决的根本问题以及为什么“分步Step-wise”和“同策略On-policy”是解开这个问题的两把钥匙。2.1 传统蒸馏的局限与智能体学习的特殊性传统的知识蒸馏无论是响应式蒸馏只学最终回答还是中间层特征对齐对于智能体任务来说都存在一个根本的**分布偏移Distribution Shift**问题。想象一下教一个新手司机。传统蒸馏就像只给他看老司机开完全程的行车记录仪最终轨迹或者告诉他“看到红灯要踩刹车”这条规则最终输出。但当新手自己坐上驾驶座面对第一个路口是黄灯闪烁时他可能就懵了因为记录仪里没这个场景规则也没细说这时候该加速还是减速。智能体任务也是如此它是一个序列决策过程。大模型教师在面对某个任务时根据其内部状态和环境反馈生成了一系列的“思考-行动”步骤。如果小模型学生只是记住了这个特定的步骤序列那么一旦环境稍有变化比如API返回格式变了工具不可用了或者因为模型容量小导致前几步的生成有细微偏差后续的步骤就会因为累积误差而完全偏离正轨这就是复合错误Compounding Error。因此智能体的蒸馏关键不在于模仿一条固定的路径而在于学会生成路径的策略Policy——即在任意给定的“状态”包括任务描述、历史步骤、当前观察下都应该能做出接近最优的“动作”下一步该思考什么或调用哪个工具。2.2 SOD的双重设计哲学SOD的框架正是围绕“学习策略”这个目标构建的其设计体现在两个关键词上1. Step-wise分步对齐决策单元而非最终轨迹这是对复合错误问题的直接回应。SOD将任务分解为连续的决策步骤。在每一步教师模型大模型都会根据当前状态生成一个动作如“我需要计算一下利润率”的思考或“调用calculator工具输入公式(revenue-cost)/revenue”的行动。学生模型的学习目标就是在相同的输入状态下让自己的动作概率分布去逼近教师模型的动作概率分布。这就好比教练在每一个弯道、每一个岔路口都实时指导新手“现在看后视镜打半圈方向”而不是事后复盘整条路线。通过这种逐步的对齐学生模型内化的是每一步的“条件反射”或“决策逻辑”从而具备了应对轨迹偏移的鲁棒性。2. On-policy同策略在“实战”中学习避免纸上谈兵这是SOD最具创新性也最关键的设定。“On-policy”源于强化学习意指“依据当前正在学习的策略来生成数据并用于训练”。在SOD中这意味着训练数据是动态生成的不是用一个固定的、由教师模型跑一遍任务得到的数据集。而是在每一轮训练或每隔几轮都用当前的学生模型去实际尝试完成任务。教师提供实时指导对于学生模型自己生成的这条可能不完美甚至错误的轨迹教师模型会介入对每一步进行评估和“纠正”。具体来说教师模型会被问到“如果任务历史走到了学生模型生成的这一步接下来最好的动作是什么”学习的是状态-动作映射学生模型学习的是在它自己可能遇到的、千奇百怪的状态下包括它自己之前犯错导致的状态应该如何正确行动。这极大地扩展了训练数据的覆盖范围让学生模型不仅学会在“黄金路径”上怎么走更学会了在“歧路”上如何回归正轨从而学到了真正泛化的策略。注意这里的“On-policy”与强化学习中的严格定义略有不同更强调“使用学生自身产生的轨迹作为学习上下文”。它解决了离线蒸馏中“学生没见过自己犯错时的状态该如何纠正”的问题。将两者结合SOD的流程就像一个闭环的师徒教学系统学生尝试做任务 - 学生可能走偏 - 老师针对学生走到的“当前位置”给出正确指导 - 学生根据这个指导更新自己“在这个位置该如何做”的认识。如此循环学生的策略越来越接近老师。3. SOD框架的详细实现与实操解析理解了Why我们再来拆解How。要实现SOD我们需要构建一个完整的训练循环涉及数据构造、模型交互、损失计算等多个环节。下面我结合一个具体的智能体任务场景——“网页信息查询与摘要”涉及解析指令、决定是否搜索、提取关键信息、组织回答来一步步说明。3.1 系统组成与数据流一个典型的SOD训练系统包含以下核心组件任务池Task Pool一系列需要智能体完成的复杂任务描述例如“找出某公司最新季度的营收增长率并总结原因”。环境模拟器Environment Simulator根据智能体的动作如调用搜索工具返回工具执行结果如网页片段、API返回的JSON。在初期我们可以用简化的模拟环境比如一个返回固定格式文本的搜索工具Mock。教师模型Teacher LLM一个大语言模型如GPT-4、Claude-3或开源的Qwen2.5-72B负责提供专家级的决策。学生模型Student SLM待训练的小语言模型如Qwen2.5-7B、Llama-3.1-8B。轨迹收集与蒸馏引擎控制训练循环的核心逻辑。其数据流和训练循环如下图所示概念描述[开始] | v 从任务池采样一个任务 | v 用当前学生模型运行任务 - 生成一条“学生轨迹” 轨迹包含状态s0, 动作a0学生, 观察o1, 状态s1, 动作a1学生... | v 对轨迹中的每一步状态s_t | v 冻结学生模型将状态s_t作为输入询问教师模型 “给定当前任务和历史下一步最佳动作是什么” | v 获得教师的“专家动作” a_t* 及其生成概率分布 | v 计算蒸馏损失让学生模型在s_t下预测的动作分布去逼近教师分布 | v [循环结束] | v 用累积的损失更新学生模型参数 | v [进入下一个训练循环]3.2 关键实现细节与代码片段这里有几个技术实现上的关键点直接影响了蒸馏的效果。1. 状态State的表示与构建状态s_t是模型每一步决策的依据必须包含所有必要信息。通常我们会将状态构建为一个文本字符串格式如下任务: {task_instruction} 历史: {s0} - {a0} - {o1} {s1} - {a1} - {o2} ... 当前: {s_t}其中s可以是模型内部的“思考”Chain-of-Thoughta是动作如Tool_Call[calculator]o是环境观察工具返回结果。我们需要编写一个状态渲染函数来动态生成这个字符串。2. 动作空间与教师指导的获取对于教师模型我们不能简单地问“接下来怎么做”因为回答可能过于开放。我们需要将其构造成一个条件文本生成任务。更好的方式是使用函数调用Function Calling或结构化输出来约束教师的输出格式使其与学生模型需要学习的动作空间对齐。例如我们可以定义智能体的动作空间为Think[内容]: 内部推理Search[查询词]: 调用搜索工具Extract[关键信息]: 从当前内容提取信息Finish[答案]: 输出最终答案在询问教师时我们使用精心设计的Prompt你是一个专家助手。请根据以下任务和历史决定下一步最合适的动作。 只输出一个JSON对象格式必须严格如下{action_type: Think|Search|Extract|Finish, content: ...} 任务{task} 历史{history} 当前状态{current_state}这样教师的输出就是结构化的便于我们解析出动作a_t*。3. 蒸馏损失函数的设计最核心的部分是如何让学生模型的输出分布逼近教师分布。假设我们已获得教师模型在状态s_t下对所有可能token的logits分数记为logits_teacher。学生模型对应输出logits_student。常用的损失是KL散度Kullback-Leibler DivergenceLoss KL(Softmax(logits_student / T) || Softmax(logits_teacher / T))其中T是温度系数用于平滑分布。T 1时分布更平滑教师提供的“暗知识”非最高概率token之间的关系也能被学习。但在实践中对于动作生成这种相对短序列的决策我们可能更关注教师生成的具体动作序列。因此一个更直接且稳定的方法是使用交叉熵损失将教师生成的动作序列a_t*作为标准答案让学生模型去预测它Loss CrossEntropyLoss(logits_student, labelsa_t*)这相当于一种强监督但前提是教师的动作序列是高质量的。我们可以将两种损失结合。4. 代码框架示意伪代码风格import torch from transformers import AutoModelForCausalLM, AutoTokenizer class SODTrainer: def __init__(self, student_model, teacher_model, tokenizer, env): self.student student_model self.teacher teacher_model # 通常通过API调用或加载更大的本地模型 self.tokenizer tokenizer self.env env self.optimizer torch.optim.AdamW(self.student.parameters(), lr5e-6) def run_episode_with_student(self, task): 用学生模型跑一个任务生成轨迹 trajectory [] state self.env.reset(task) done False while not done: # 学生根据当前状态生成动作 with torch.no_grad(): input_ids self.tokenizer(state, return_tensorspt).input_ids.to(student.device) student_output self.student.generate(input_ids, max_new_tokens50, ...) action self.parse_action(student_output[0]) # 解析出动作文本如“Search[苹果财报]” # 环境执行动作得到新观察和状态 next_state, observation, done self.env.step(action) trajectory.append({ state: state, student_action: action, observation: observation }) state next_state # 更新状态包含新观察 return trajectory def get_teacher_guidance(self, state): 给定状态获取教师的专家动作 prompt self.build_prompt(state) # 构建包含动作格式要求的prompt teacher_response call_teacher_model(prompt) # 调用教师模型API expert_action self.parse_teacher_response(teacher_response) # 解析出结构化的专家动作 # 如果需要logits对于开源教师模型可以本地计算 # teacher_inputs self.tokenizer(prompt, return_tensorspt).to(teacher.device) # with torch.no_grad(): # teacher_logits self.teacher(**teacher_inputs).logits return expert_action #, teacher_logits def distillation_step(self, trajectory): 对一条轨迹进行蒸馏训练 total_loss 0 for step in trajectory: state step[state] expert_action self.get_teacher_guidance(state) # 准备学生模型的输入 inputs self.tokenizer(state, return_tensorspt).to(self.student.device) labels self.tokenizer(expert_action, return_tensorspt).input_ids.to(self.student.device) # 前向传播计算损失 outputs self.student(**inputs, labelslabels) loss outputs.loss # 交叉熵损失 # 或者更复杂的将labels与教师logits结合计算KL散度 total_loss loss self.optimizer.zero_grad() total_loss.backward() self.optimizer.step() return total_loss.item()3.3 实操心得与参数选择在实际实现SOD时有几个细节决定了成败1. 教师模型的选择与调用成本教师模型的质量直接决定学生模型的上限。如果预算允许GPT-4、Claude-3 Opus是最佳选择。为了降低成本可以混合使用用强教师模型生成一小部分高质量种子数据然后用一个稍弱但便宜的中等模型如GPT-3.5-Turbo、Claude-3 Haiku或开源的70B级别模型在种子数据上进行监督微调SFT得到一个“廉价教师”再用于大规模的SOD循环。这本质是做了一个教师模型的蒸馏。2. 温度系数T的调节温度T在KL散度损失中至关重要。初期训练时可以设置较高的T如3.0-5.0让学生更多地学习教师分布中的“暗知识”即各种可能动作的相对可能性。训练后期可以逐渐降低T如降至1.0让学生更聚焦于教师认为的最优动作。这类似于课程学习。3. 轨迹采样与回放缓冲区完全On-policy每一步都用最新学生模型生成的数据效率可能较低。可以引入一个回放缓冲区Replay Buffer存储历史上学生生成的各种轨迹及其对应的教师指导。每次训练时从缓冲区中采样一批数据。这能提高数据利用率并让模型学习到更多样化的状态。需要定期用最新的学生模型更新缓冲区中的数据。4. 动作解析的鲁棒性学生和教师模型生成的动作文本需要通过解析器转化为程序可识别的结构如动作类型和参数。这里的解析必须非常鲁棒能处理模型输出的各种微小变异多一个空格、换行、说明文字等。否则解析失败会导致训练信号丢失。一个技巧是在Prompt中严格要求JSON输出并在解析时使用json.loads()并配合try-except和一定的后处理。4. 效果评估与对比实验设计训练完成后我们如何知道SOD是否真的有效不能只看训练损失下降必须设计严谨的评估来证明其相对于基线方法的优势。4.1 评估指标对于智能体任务评估应是多维度、任务导向的任务完成成功率Task Success Rate在独立的测试任务集上智能体能否在限定步数内输出一个可被接受的正确答案。这是最核心的指标。平均步骤数Average Steps成功完成任务平均需要多少步。步数越少通常意味着决策效率越高。轨迹质量Trajectory Quality人工或使用强LLM作为裁判评估生成的整个思考-行动轨迹的合理性、连贯性。可以打分1-5分。泛化能力Generalization在训练中未见过的、但同类型的任务上的表现。例如训练时用的是金融数据查询任务测试时换成科技新闻查询。4.2 对比基线为了凸显SOD的价值应至少设置以下基线进行对比方法描述预期劣势SLM基础模型未经任何智能体训练的小模型。缺乏规划能力任务成功率极低。SLM SFT标准监督微调使用教师模型生成的任务-最终答案或任务-完整黄金轨迹数据进行微调。可能过拟合到特定轨迹泛化差易受复合错误影响。SLM 离线轨迹蒸馏使用教师模型预先跑完一批任务生成的固定轨迹数据进行蒸馏。学生只学习了“黄金路径”无法处理自身偏差导致的状态On-policy能力弱。SLM SODOurs本文方法On-policy分步蒸馏。预期在成功率、轨迹质量、泛化上表现最佳4.3 一个简单的实验记录示例假设我们在一个“多步计算与推理”的测试集如GSM8K数学题但要求模型输出计算步骤上评估可能得到如下趋势结果虚构数据仅为示意模型 (7B规模)训练方法任务成功率 (%)平均步骤数轨迹合理性得分 (1-5)Baseline无特定训练15.2N/A (多中途失败)1.8SFT (on final answer)答案微调41.76.32.5Offline Trajectory Distill离线轨迹蒸馏68.45.13.6SOD (Ours)分步同策略蒸馏82.94.74.2从这样的结果可以看出SOD在让小型模型掌握序列决策能力方面确实有显著优势。它不仅成功率更高而且完成的步骤更少、轨迹更合理说明它学到了更高效的策略。5. 常见问题、挑战与优化方向在实际操作SOD的过程中你肯定会遇到一些坑。这里我总结几个常见问题和解决思路。5.1 训练不稳定损失震荡大问题SOD训练初期由于学生模型策略很差生成的轨迹质量低下甚至无意义导致从这些轨迹状态中获取的教师指导可能也不稳定例如教师面对一个无厘头的状态可能给出随机的动作从而使得训练信号噪声很大。排查与解决预热阶段不要一开始就进行完全的On-policy训练。先用教师模型生成一批高质量的“示范轨迹”即离线数据对学生模型进行几轮的监督微调SFT。这相当于给学生模型一个“初始策略”让它至少能生成一些像样的轨迹然后再开启SOD循环。这能大幅提升初始稳定性。过滤机制对学生生成的轨迹进行质量检查。如果一条轨迹过早失败比如第二步就调用了不存在的工具或者状态文本过于混乱可以丢弃这条轨迹不用于本次训练更新。或者可以只选取轨迹中前面几步状态还比较正常的步骤进行蒸馏。调整批次大小使用较大的批次大小batch size可以对冲单条轨迹噪声的影响。5.2 教师模型指导成本过高问题每一步都需要调用大教师模型尤其是GPT-4 API训练成本难以承受。排查与解决本地廉价教师如前所述训练一个本地化的“中等教师”。先用GPT-4生成1万条高质量的状态-专家动作配对数据然后在一个较大的开源模型如Qwen-72B上做SFT得到一个本地教师。在SOD循环中大部分步骤由这个本地教师指导偶尔用GPT-4进行校验或生成新的种子数据。价值模型Value Model训练一个小的“价值模型”来评估学生动作的好坏替代一部分教师调用。价值模型输入状态和候选动作输出一个得分。对于得分低的动作我们再调用教师获取详细指导对于得分高的动作我们认为学生已经做得不错可以跳过教师询问。这需要先收集数据训练价值模型。缓存与重用对相同的或高度相似的状态s_t将其对应的教师指导缓存起来。下次遇到相似状态时直接使用缓存避免重复调用。5.3 学生模型“模仿过度”缺乏探索问题学生模型过于保守完全复制教师的行为即使在教师可能不是最优的情况下也不敢尝试新路径导致性能天花板被教师限制。排查与解决引入策略熵正则化在损失函数中加入一项鼓励策略多样性的熵正则项。Loss_total Loss_distill - β * H(π(student))其中H是熵β是系数。这能让学生模型在模仿教师的同时保留一定的探索性。集成多个教师如果条件允许使用多个不同的大模型如GPT-4, Claude, Gemini作为教师委员会。对于同一个状态收集多个教师的建议动作。学生模型可以学习去拟合这些动作的“共识”或“分布”而不是单个教师的输出。这能让学生接触到更多样化的决策视角。课程学习与任务难度递增从简单的任务开始SOD训练逐步增加任务复杂度。在简单任务上教师策略通常接近最优模仿是安全的。在复杂任务上可以适当放宽对模仿一致性的要求允许学生有一些自由发挥并在最终结果层用奖励信号任务是否成功进行微调这类似于结合了强化学习。5.4 长轨迹下的遗忘与效率问题问题对于需要很多步如超过20步的复杂任务SOD循环中模型需要处理很长的历史上下文。这可能导致训练效率低下且模型在轨迹后期可能会“忘记”早期的关键决策逻辑。排查与解决状态摘要State Abstraction不是将完整的原始历史拼接到状态中而是训练一个小的网络或使用一个简单的规则对历史轨迹进行摘要。例如只保留最近3步的详细记录将更早的历史总结为“已确认用户需求”、“已获取A、B两项数据”等几个关键点。这能大幅缩短上下文长度。分层SODHierarchical SOD将长任务分解为多个子目标。先训练一个高层策略用SOD负责规划子目标序列如1. 搜索信息 2. 提取数据 3. 进行计算 4. 组织报告。再为每个子目标训练一个低层策略也用SOD负责执行具体的动作。这样每个策略需要处理的轨迹长度都变短了。SOD为小型语言模型注入智能体能力提供了一个强大而灵活的框架。它的核心魅力在于将模仿学习从静态的“结果复制”升级为动态的“策略学习”。虽然实现起来比传统微调更复杂涉及到环境模拟、交互式数据生成等环节但其带来的性能提升是显著的。对于任何希望将智能体能力部署到资源受限环境的研究者和工程师来说深入理解并实践SOD都是一项极具价值的工作。从我个人的实验经验来看成功的关键往往在于对训练数据循环、教师指导质量和损失函数设计的精细把控这其中的每一个调优点都可能是模型效果提升的突破口。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价