资讯动态

指令微调 SFT:从“会说话“到“会干活“

发布时间:2026/10/26 1:55:56 来源:尧图企业网站定制
指令微调 SFT从会说话到会干活你给 Base 模型一段话“请把下面这句话翻译成英文今天天气不错。”它可能会接着写“请把下面这句话翻译成英文今天天气不错。请把下面这句话翻译成英文今天天气不错……”你当场沉默。它不是不会翻译它是没搞懂你现在是在提问不是在给它一段要续写的文本。本文的关系图Base 模型会续写不会听指令 ↓ 准备数据指令 回答带角色模板 一条条 (prompt, response) 样本 ↓ 前向 → 只对回答算 loss → 反向 学会看到「问题」就输出「回答」而不是继续编问题 ↓ Instruct / Chat 模型会听话但知识还是原来那些一、Base 模型缺的不是知识是上班经验先把一个常见的误会掰正Base 模型不是笨它只是没上过班。它读过海量文本知道语法、事实、代码、翻译甚至知道很多冷门知识。但它学到的唯一技能是接着往下写。你给它一个问句它最自然的反应不是回答而是把问句后面通常会跟什么继续写出来。所以在它眼里请介绍一下杭州不是一个问题而是一段文章的开头。它可能接着写请介绍一下杭州并说明它有哪些著名景点、适合几月份去、人均消费多少……它以为自己在帮你完善提纲其实你只是想让它说两句话。SFTSupervised Fine-Tuning监督微调干的事就是给这个读完了整座图书馆的人做岗前培训。不是重新上学是教他客户问什么你先回答问题别接着替客户提问先给结论再给理由不知道就说不知道别硬编回答完就停别没完没了。知识还是那些知识变的是什么时候开口、怎么开口。顺便说一句严格意义上的指令微调instruction tuning是 SFT 最常见的一种用法日常语境里两者基本混着叫本文也不抠这个字眼。二、SFT 到底在训什么SFT 的数据长这样指令请把下面这句话翻译成英文今天天气不错。 回答The weather is nice today.一条一条都是输入 标准答案。训练目标和预训练一模一样——还是猜下一个 Token。区别在于只对回答那部分算 loss指令部分不参与。这里有个容易望文生义的地方prompt 当然要喂给模型不然它根本不知道在回答什么。mask 掉的只是对 prompt 的预测误差不是 prompt 本身。它看得见问题只是不用为复述问题负责。这就是 SFT 最核心、也最容易被写错的一个细节。为什么要这样因为你要教的是P(回答 | 指令) 要高 P(继续编问题 | 指令) 要低如果你把指令也拿来算 loss模型就会花很多力气去学怎么把问题复述一遍——那不是你要的。你要的是一个客服不是一个复读机。所以 SFT 的本质不是往脑子里灌知识而是把条件概率的形状掰过来同样一个开头以前它倾向于继续提问现在它倾向于给出答案。用一张表对比一下维度预训练SFT数据无标注文本指令 回答对数据量几万亿 Token几万到几百万条目标猜下一个 Token同样但只算回答部分主要学到语言、知识、推理种子格式、任务、语气、边界成本极高一次低几个数量级产出Base 模型Instruct / Chat 模型一句话预训练给它能力SFT 教它把能力用出来。三、一条训练样本长什么样真实训练时一条样本不是一个字符串而是一串带角色的 Token|system| 你是助手。 |user| 中国的首都是哪里 |assistant| 北京。 |end|四个特殊标记各干各的活|system|设定人设和约束比如你是简洁的助手“只回答与客服相关的问题”|user|用户的指令|assistant|轮到模型说话了这个头本身不参与学习它只是一个发令枪|end|回答结束。没有它模型不知道什么时候该停可能一直说到天荒地老。下面这段代码把一条样本拼出来并标出哪些 Token 参与 lossimportmathfromcollectionsimportdefaultdict SYSTEM你是助手。SAMPLES[(中国的首都是哪里,北京。),(日本的首都是哪里,东京。),(法国的首都是哪里,巴黎。),(德国的首都是哪里,柏林。),(1 加 1 等于几,2。),(天空为什么是蓝色的,因为瑞利散射。),]defbuild(system,user,answer):tokens[|system|]list(system)tokens[|user|]list(user)tokens.append(|assistant|)startlen(tokens)# 从这里开始才是要学的部分tokenslist(answer)[|end|]returntokens,start ALL[build(SYSTEM,u,a)foru,ainSAMPLES]seq,resp_startALL[0]print(① 一条训练样本)print( 不学习 .join(seq[:resp_start]))print( 要学习 .join(seq[resp_start:]))print(f prompt{resp_start}个 Token回答{len(seq)-resp_start}个 Token)N,K,BOS2,0.01,|bos|Vlen({tfors,_inALLfortins}|{BOS})defpairs(tokens,n):p[BOS]*(n-1)tokensreturn[(tuple(p[i-n1:i]),p[i])foriinrange(n-1,len(p))]deftrain(sequences,n):mdefaultdict(lambda:defaultdict(int))fors,_insequences:forctx,nxtinpairs(s,n):m[ctx][nxt]1returnmdefnll(m,ctx,tok):cm.get(ctx,{})return-math.log((c.get(tok,0)K)/(sum(c.values())K*V))mtrain(ALL,N)losses[nll(m,ctx,tok)forctx,tokinpairs(seq,N)]p_losssum(losses[:resp_start])r_losssum(losses[resp_start:])p_n,r_nresp_start,len(seq)-resp_start totalp_lossr_lossprint(② loss 被谁贡献)print(f prompt{p_n}个 Tokenloss{p_loss:.2f}占{p_loss/total:.0%})print(f 回答{r_n}个 Tokenloss{r_loss:.2f}占{r_loss/total:.0%})print(f prompt 平均 loss{p_loss/p_n:.2f})print(f 回答平均 loss{r_loss/r_n:.2f})print(f 不 mask 平均 loss{total/(p_nr_n):.2f})print(f mask 后平均 loss{r_loss/r_n:.2f})真实运行结果本机实测完整脚本见code/09-sft-demo.py① 一条训练样本 不学习|system| 你 是 助 手 。 |user| 中 国 的 首 都 是 哪 里 |assistant| 要学习北 京 。 |end| prompt 17 个 Token回答 4 个 Token ② loss 被谁贡献 prompt17 个 Tokenloss 6.02占 65% 回答 4 个 Tokenloss 3.18占 35% prompt 平均 loss0.35 回答平均 loss0.80 不 mask 平均 loss0.44 mask 后平均 loss0.80这里的 n-gram 只是把 mask 的账算清楚的小玩具真实 SFT 用的是 Transformer但 loss 的定义和 mask 的逻辑一模一样。四、为什么必须 mask65% 的 loss 在教它复述问题看 ② 那段输出这是整篇最值得盯住的数字。在这条样本里prompt 有 17 个 Token回答只有 4 个。但如果不 maskprompt 贡献了 65% 的 loss。真实对话里system prompt 和多轮历史更长这个比例只会更夸张。注意这不是因为 prompt 难——prompt 的平均 loss 只有 0.35回答是 0.80。prompt 的单个 Token 更容易但它人多。17 个人一起交作业总分自然被它拉走。后果是什么模型会把大部分梯度花在怎么复述问题上而不是怎么回答问题。你训了半天它学会的是看到中国的首都是哪里就把它再写一遍看到请翻译这句话就把它再抄一遍。它成了一个非常擅长重复你说话的模型。mask 掉之后梯度只从回答那 4 个 Token 来。loss 数字看起来变高了0.44 → 0.80这不是变差是分母换成了真正要学的部分。就像考试只算大题分平均分当然会变但那才是你关心的。实现上常见做法是把 prompt 部分的 label 设成-100PyTorch 的ignore_index或者维护一个 0/1 的 loss mask。另外两个坑padding 不能被算进 loss否则模型会努力学习怎么生成空白packing 时要注意 attention mask别让后一条样本偷看前一条样本的内容。很多 SFT 翻车不是模型笨是 mask 写错了。五、数据从哪来SFT 的胜负手在数据不在模型SFT 的模型结构通常不变超参也不复杂。真正决定效果的是数据。来源优点风险人工编写质量高、意图清楚、覆盖可控贵、慢、规模有限强模型合成便宜、量大、风格统一同质化、错误被放大、许可证问题真实用户日志分布最真实、覆盖长尾隐私 / PII、噪声大、质量参差公开指令数据集起步快、可对比风格杂、领域不匹配、可能被污染人工数据是天花板但请人写一万条高质量指令-回答成本不低。合成数据是现在的主流用强模型批量生成再用规则或另一个模型筛。它能把量做起来但有两个副作用风格越来越像同一个模子以及错误会被继承和放大——老师错了学生跟着错。真实日志最宝贵因为它就是用户真正会问的东西。但里面有手机号、地址、内部项目名清洗不干净就是事故。还有一个反直觉的结论SFT 数据不是越多越好。Meta 的 LIMA 实验用一千条左右精心筛选的指令数据就训出了相当不错的指令跟随能力。这说明质量和多样性比堆量更重要。当然生产环境要覆盖的任务多几千条通常不够但十万条低质数据往往打不过一万条高质量、覆盖广的数据。数据配比也要讲究任务要杂问答、改写、总结、抽取、分类、写代码、拒答、多轮对话长度要杂一句话的问题和长文档摘要都得有要教它说不知道不然它遇到不会的也会硬编混一点预训练数据防止它在窄领域里训过头把通用能力忘了工具调用要单独教JSON 格式、参数名、什么时候该调工具这些不会凭空出现。一句话SFT 数据不是越多越好是越像你希望它面对的真实请求越好。你拿十万条写诗数据训它它写诗确实厉害你问它报销流程它可能给你写一首诗。六、训练细节低学习率、少轮次、别训忘了SFT 是微调不是重训所以有几个和预训练相反的习惯学习率要低。预训练可能用 1e-4 量级SFT 常用 1e-5 到 2e-5LoRA 可以到 1e-4。你是在给一个已经会走路的人纠正姿势不是在教婴儿站立。步子太大它会把原来会的东西也踩坏。轮次要少。通常 1 到 3 个 epoch。同一批指令反复训太多遍模型会开始背答案甚至在无关问题上也套同一个模板。小心灾难性遗忘。如果你只用客服话术训它它可能连写代码、做数学的能力都退化。常见做法是混入一部分预训练数据或者用 LoRA 这类参数高效方法只动一小部分权重。LoRA / QLoRA值得单独说一句它们不更新全部参数只训练一小块低秩适配器。好处是显存小、成本低、不容易把底座训坏而且一个底座可以挂多个 LoRA 切换不同任务。代价是上限通常略低于全量微调——但对大多数团队这个性价比是压倒性的。模板必须一致。训练时用|user|、|assistant|推理时也得用同一套。训练时一套模板、推理时另一套等于教它普通话、考它粤语。特殊 Token 还要在分词器里正确注册别让它们被拆成普通字符。评测别只看 loss。SFT 之后要看的至少有三类指令跟随该答的答了没有格式对不对事实性有没有为了像答案而编造回归原来会的任务现在还会不会。人工评测最可靠但慢LLM-as-judge 便宜但有偏见任务专用测试集最客观但覆盖有限。三样搭配着用别拿一个 loss 数字当结论。七、SFT 能给什么不能给什么能力SFT 能给吗说明听指令✅这是 SFT 的主业输出格式✅JSON、Markdown、固定字段都能教语气 / 人设✅客服、老师、编程助手都能演工具调用格式✅前提是数据里教过拒答 / 边界⚠️能教例子但不保证稳新知识⚠️能灌一点但不可靠容易变成自信的胡说实时信息❌训练数据没有就是没有复杂推理⚠️能教解题格式教不了解题能力安全对齐⚠️SFT 是起点不是全部最值得记住的是新知识这一行。你当然可以用 SFT 教它一条新事实“我们公司的报销上限是 5000 元。” 它会记住至少在训练集上。但这种方式不可靠数据一多、问题一绕、上下文一变它可能就答错而且错得很自信。事实性知识更适合放在模型外面用检索的方式在推理时喂给它。SFT 更适合教怎么回答而不是答案是什么。这就是那句话SFT 是把能力翻译成行为不是把知识灌进脑子。它能让模型会按格式回答但不能让它知道答案。一个是演技一个是台词。八、五个常见误区误区一SFT 就是给模型灌知识。不是。SFT 主要改的是行为什么时候答、怎么答、答多长、遇到不会的怎么办。知识的上限在预训练就定得差不多了。SFT 能让它像个专家但不能让它真的是专家。误区二数据越多越好。十万条低质数据可能打不过一万条高质量、覆盖广的数据。重复、同质、错误的指令数据会让模型学会一套固定的废话。先想清楚要它面对什么请求再决定收什么数据。误区三loss 越低模型越好。SFT 的 loss 只算回答部分低 loss 可能只是背下了训练集。要看留出集、看人工评测、看原来会的任务有没有退化。loss 是体温计不是体检报告。误区四随便套个模板就行。模板里的角色标记、特殊 Token、结束符都是模型学习轮到谁说话的信号。训练和推理不一致效果会莫名其妙地崩。模板不是格式问题是语义问题。误区五SFT 能替代 RLHF。SFT 教的是模仿标准答案RLHF / DPO 优化的是在两个回答里更喜欢哪个。前者教像不像后者教好不好。SFT 让模型会答题RLHF 让它答得更符合人类偏好——两件事不能互相替代。九、把这件事放回地图上把大模型的概念按依赖关系从下往上摞成几层——最底下是算力和数据往上依次是文字的表示、模型结构、训练、调用最顶上才是增强和智能体L6 智能体层 Agent / Function Calling / ReAct / 记忆 / MCP L5 增强层 RAG / 向量检索 / Rerank / GraphRAG / 微调 L4 调用层 Prompt / Few-shot / CoT / 温度 / Top-p L3 训练层 预训练 / ★ SFT ★ / RLHF / DPO / LoRA / Scaling Law L2 结构层 Transformer / 注意力 / 参数量 / 上下文窗口 / 开源闭源 L1 表示层 Token / 分词 / 词表 / Embedding L0 底座 算力 / 数据 / 成本SFT 在 L3 的中间预训练之后偏好对齐之前。顺便澄清一个容易混的点L5 里的微调指的是面向业务的领域微调和 L3 的 SFT 不在一个层面——SFT 是训练流程的一步业务微调是拿模型去适配场景的用法。往下看预训练给了它知识和语言能力SFT 只是把这些能力接到指令格式上。底座不行SFT 数据再好也救不回来底座行了SFT 才能四两拨千斤。往上看RLHF / DPO 在 SFT 的基础上继续调偏好RAG 在推理时补事实Agent 用的工具调用格式很多时候就是 SFT 教出来的。SFT 是从模型到产品的第一道加工也是性价比最高的一道。它的边界也很清楚SFT 能让模型会干活但改不了它能干多大的活。想让模型知道新事实靠检索想让模型更符合偏好靠对齐想让模型更强只能回到预训练。三条可以带走的结论SFT 教的是接口和行为不是知识。它把 Base 模型的续写能力翻译成听指令的能力。关键是 mask只对回答算 loss。不 mask大部分梯度会花在教模型复述问题上。数据质量和多样性 数量。模板一致性、防遗忘、评测比调参更能决定 SFT 的成败。《大模型概念地图》专栏 · 第 9 篇

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

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

免费获取报价 →
↑