☰
大模型监督微调(SFT)实战:数据、模板与训练细节详解
2026/10/20 3:09:53 网站建设 项目流程

如果你像我一样,辛辛苦苦攒了一堆卡,把某个开源底座模型从零预训练到收敛,满心欢喜地跑去问它“鲁迅为什么写《阿Q正传》”,结果它给你回了一大段“鲁迅是浙江绍兴人,原名周树人,字豫才……”再往后就开始顺着字面疯狂接龙,什么“绍兴是著名的水乡,水乡的桥很多,桥下的乌篷船……”这时候你就会意识到一个扎心的事实:模型缺的根本不是知识,而是规矩。

监督微调(Supervised Finetuning,简称SFT)就是用来立规矩的。

它是大模型训练链路里承上启下的关键一步——承接预训练阶段学到的海量语言知识和世界知识,把模型从“一个只会续写文本的统计机器”改造成“一个能听懂人话、按指令办事的助手”。这篇是“大模型训练三部曲”的第二篇,上一篇聊完预训练怎么“读书”,这篇我们重点把SFT的数据、模板、损失函数和训练细节掰开揉碎讲清楚,让你看完之后不仅知道怎么做,还能明白每一步背后的“为什么”。

1. SFT在大模型训练链路中的定位:从“接龙选手”到“答题选手”

先说一个很多人容易混淆的点:预训练之后的底座模型,其实已经“什么都知道”了,但它不知道“怎么回答问题”。这不是知识问题,而是行为模式问题。

1.1 预训练到底教会了模型什么

预训练的本质是自回归语言建模:给定前文,预测下一个token。模型在海量网页、书籍、代码上疯狂阅读,干的事情只有一件——把“世界知识”压缩进参数里,同时学会语言的统计规律。

这个阶段的默认行为非常单一:写出在统计意义上最像“下一句”的话。所以你问它问题,它会接着你的问题继续往下写,而不是停下来认真作答。生活化类比一下:预训练模型像一个博览群书但没上过学的小孩,你问他“一加一等于几”,他会顺着你的语气接一句“等于几这个问题很有趣”,然后开始滔滔不绝讲数学是什么。你说他错了吗?好像也没错,但他完全没有在“回答问题”。

这就是为什么单纯的预训练模型永远没法直接当助手用。它缺的是一套“交互规矩”:知道用户发来的是一道题,知道什么时候该停止续写,知道回答要用什么口吻、什么长度、什么结构。

1.2 SFT、RLHF与预训练的三角关系

整个对齐链条可以粗略拆成三段:

  • 预训练:解决“模型懂不懂”的问题,提供知识底座。
  • SFT:解决“模型听不听话”的问题,用人工标注的高质量(指令,回答)对,教会模型模仿期望的交互行为。
  • RLHF/DPO:解决“模型答得好不好”的问题,在SFT的基础上进一步用偏好信号优化回答质量,让模型学会“择优”而不是“照抄”。

SFT处于中间层,也是性价比最高的一层。很多团队不做RLHF也照样能上线一个可用的助手,因为只要SFT数据足够扎实,模型就已经能应付绝大多数日常对话场景。RLHF更像是在SFT画好的底稿上精修,底稿要是歪了,后面怎么调都费劲。

我在模拟项目X里做过一次对照:同一个底座模型,一个直接微调,一个先SFT再走偏好优化,最终效果差距非常明显。直接跳到偏好优化的那个模型经常出现“格式正确但内容胡说”的情况,因为它在行为层面压根没被教明白什么叫“像样地回答”。

2. 数据准备:SFT的胜负手,一句话决定成败

聊SFT的人总喜欢先聊LoRA、学习率、双卡训练这些技术参数,但我的真实体会是:SFT最难的、最值得花时间的,永远是数据。

2.1 数据质量远大于数据量

先破一个执念:不是数据越多越好。我见过某团队用几十万条网上拼来的指令数据微调,训出来的模型回答风格千奇百怪,一会儿像客服,一会儿像营销号,一会儿又开始胡编乱造。问题就出在数据太杂、太多、噪声太大。

SFT的机制是让模型“有样学样”,它不会去分辨哪条数据是正确的、哪条是垃圾,它只会照单全收地把汇总统计规律当成“人类应有行为”。所以你喂进去一万条质量参差不齐的数据,模型学到的就是“参差不齐”本身;你喂五千条精心筛选、语料干净、回答正确率高的数据,模型学到的就是“精准得体”的行为模式。

我自己做过的实验是:用某开源指令数据集直接微调,训到loss 0.6左右,生成结果经常出现答非所问;后来把数据筛掉一半,只保留指令清晰、回答有据、风格统一的样本,同样是loss 0.6,生成质量直接上了一个台阶。在SFT阶段,五万条好数据的价值远超五十万条凑数数据。

2.2 怎么搭自己的SFT数据集

搭数据集没有一个万能公式,但有一个很实用的工作流,我管它叫“种子—扩展—清洗—校验”四步法。

第一步,定义任务边界。先想清楚你的模型上线后主要服务哪些场景,比如代码问答、写作辅助、百科知识问答、闲聊陪伴。不要贪多,场景定得越清晰,数据纯度越高。我习惯把这个写成一页纸的“任务清单”,后续所有数据生成都围绕清单来。

第二步,写种子指令。每个任务类型手工写50到100条高质量种子样本,覆盖不同问法、不同难度、不同边界情况。这些种子样本是后面批量扩展的“锚”,千万不能马虎。

第三步,扩展数据。可以用更大的模型辅助生成类似指令,也可以去开源数据集里捞相关样本。但重点在于:模型生成的样本必须人工过一遍。不要偷懒直接全量并入训练集,AI生成的内容里隐藏着大量正确但啰嗦、合理但风格跑偏的样本,混进去会把模型带歪。

第四步,校验和清洗。把扩展后的样本丢回种子样本旁边做比对,看风格是否一致、答案是否完整、有没有包含危险或越权内容。这一步通常要耗费整个SFT项目一半以上的人力,但它的收益会直接体现在最终模型的回答质量上,非常值得。

2.3 数据清洗与去重的实操原则

清洗阶段有几个容易被忽略的细节:

  • 剔除“指令没头没尾”的样本。比如“帮我讲讲”,这种模板看起来是在测试模型的追问能力,实际只会让模型学会含糊其辞。
  • 去除“答案带明显立场倾向”的样本。SFT阶段要尽量保持中立、客观的表达,立场类的偏好应该留给偏好优化阶段去处理。
  • 语义去重很重要。很多开源数据集里存在大量“换汤不换药”的同义样本,文本层面上看并不重复,但语义完全一样。光靠哈希去重根本没有用,建议用向量嵌入做近重复检测,相似度超过0.85的样本只保留一条。

清洗这块我有一个小习惯:给每条数据打上“来源标签”,比如来自种子集、来自大模型扩写、来自开源数据集清洗。这样后续如果发现模型在某个场景表现异常,可以快速回溯是哪个数据来源出了问题。

3. 指令模板:模型理解“语境”的基石

模板在设计SFT时是一个细节,但它对最终效果的影响相当大。很多人觉得模板不外乎在文本前后加几个分隔符,随便用哪个都行,实际踩过坑就知道不是这么回事。

3.1 模板的本质是条件反射

预训练模型的输入是纯文本序列,它并不知道“这段是用户说的”“那段是模型回答的”。SFT训练时,我们通过固定的分隔符和角色标记,把这种区分信息显式地写进文本里,模型在训练中反复看到这个结构,就会形成一种条件反射:看到“用户”标记后面跟着的问题,就应该生成“助手”标记后面的回答作为续写。

生活化类比:你跟狗说“握手”时,每次都先掏出零食再伸手,狗很快就建立了“零食—伸手—抬爪”的关联。模板就是那个“零食”,它是模型理解交互场景的锚点。

所以模板一旦选定,训练和推理时必须保持一致。很多人训练时用一种模板,部署时换成了另一种“更好看”的模板,结果模型在推理时完全识别不出角色边界,回答错乱得一塌糊涂。

3.2 几种主流模板的演进与拆解

业界经过几个阶段的迭代,模板从简单到复杂大致有以下三种形态:

第一种是极简双段式。直接把指令和回答拼在一起,用特殊分隔符隔开。这种模板实现简单,适合早期验证和资源受限的场景,缺点是系统级指令无处安放,模型难以区分“上下文”和“直接指令”。

第二种是三代式,包含系统提示词、用户输入和模型输出三个区块。系统提示词用来设定模型的人设和行为边界,这对做垂直领域助手非常重要。我在模拟项目X中最常用的是这个结构:

[System] 你是一个耐心、严谨的技术问答助手,用中文回答用户的问题。 [User] 请解释一下反向传播的原理。 [Assistant] 反向传播是一种用于训练神经网络的算法,核心思路是……

第三种是结构化角色模板,业界常称为ChatML风格。它把每个区块显式标注角色,结构更清晰,多轮对话时不容易错位,目前很多开源模型都采用这种格式。它的样子大致是:

<|im_start|>system 你是一个技术问答助手,回答要简洁、准确。 <|im_end|> <|im_start|>user 请解释反向传播。 <|im_end|> <|im_start|>assistant 反向传播的核心是链式法则…… <|im_end|>

从第一代到第三代,核心变化是:模型对“谁在说话”这件事的感知越来越明确。在多轮对话场景里,结构化角色模板的优势尤其明显,因为它能把用户、助手、系统提示彻底区分开。

3.3 模板与损失计算的配合

模板不只是文本结构,它还直接决定损失函数里哪些token需要被“屏蔽”。这是SFT实现里最容易出错的地方,也是很多人忽略的细节。

标准做法是:训练时只对“助手回答部分”计算损失,指令、系统提示词、历史对话这些输入部分都要用掩码挡住,不参与梯度回传。

为什么?因为模型的任务是“学会生成正确答案”,而不是“学会复述用户的提问”。如果让指令部分的loss也反向传播,模型会把“背下用户说过的话”当成一种被鼓励的行为,实际表现就是答着答着开始重复用户问题。我见过好几个团队踩过这个坑——loss看起来一直在降,生成结果却总觉得怪怪的,排查到最后发现是指令部分的掩码没做对。

4. 训练环节的核心参数与损失设计

数据准备好了,模板定好了,接下来才是真正动手训练。这一节把训练参数和损失设计的细节都过一遍,每个参数我都会给出选择逻辑,不是拍脑袋给的。

4.1 全参微调还是LoRA

先给结论:如果你只有一卡或两卡,显存不够宽裕,SFT阶段首选LoRA;如果你有足够的算力资源,并且数据量在十万条这个量级以上,全参微调会更稳。

全参微调的意思是把整个底座模型的参数全部放开参与训练。它能让模型充分适应新任务,但代价是显存开销大、训练时间长,而且在小数据量下容易过拟合。LoRA则是在冻结原模型权重的情况下,额外插入一些低秩矩阵来模拟权重更新,可训练参数量通常只有原模型的百分之一左右。

选LoRA的时候有几个经验值:

  • 秩的选择:通用对话场景16到32比较合适,垂直领域的简单任务8就够。秩太小模型学不进去,秩太大又失去了低秩适配的意义,还徒增显存占用。
  • 适配范围:建议对全部线性层做LoRA,只适配注意力层的话模型对行为模式的塑造能力会弱不少。
  • 缩放因子:一般设成秩的2倍。比如秩16就设alpha为32,这个比例在多数场景下表现稳定。

个人经验是:如果数据量小于五万条,LoRA的效果通常不输全参,而且训练速度快好几倍,迭代效率更高。

4.2 学习率、epoch与batch size怎么定

SFT不是预训练,不需要那么长的训练周期,关键是要防止模型在标注数据上反复打转、开始过拟合。

  • 学习率:全参微调建议从1e-5起步,LoRA可以放宽到1e-4到3e-4。学习率太高,模型会把原有知识冲掉;太低则SFT行为学不进去。我习惯用余弦退火调度,warmup步数设成总步数的百分之二到三。
  • epoch数:3以内是安全区。数据量一万到十万条时,跑两个epoch通常就够了;超过三个epoch,loss虽然还能继续降,但模型开始出现“死记硬背”的迹象,回答越来越像训练集里的原文。
  • batch size:总batch size(所有卡累计)建议保持在32到128之间。batch太小模型行为波动大,batch太大训练时间拉长内存也吃紧。梯度累积是个好用的小技巧,可以帮你用小显存模拟大batch。

4.3 损失函数的掩码细节

SFT的损失函数底层就是交叉熵,但关键是token掩码怎么做。直接看代码更直观,下面是一段PyTorch风格的伪代码,展示了SFT训练时最常见的损失计算方法:

import torch import torch.nn.functional as F def compute_sft_loss(logits, labels, loss_mask): """ logits: [batch, seq_len, vocab_size] labels: [batch, seq_len],非忽略位置为真实 token id loss_mask: [batch, seq_len],1 表示参与损失计算,0 表示屏蔽 """ # 转置为 [batch, seq_len, vocab_size] -> [batch * seq_len, vocab_size] vocab_size = logits.size(-1) logits_flat = logits.reshape(-1, vocab_size) labels_flat = labels.reshape(-1) loss_mask_flat = loss_mask.reshape(-1) # 标准交叉熵 per_token_loss = F.cross_entropy( logits_flat, labels_flat, reduction="none" ) # 掩码:只保留 assistant 回复部分的 loss masked_loss = per_token_loss * loss_mask_flat # 归一化时只除以有效 token 数量,而不是整条序列长度 num_valid_tokens = loss_mask_flat.sum().clamp(min=1) return masked_loss.sum() / num_valid_tokens

这里最关键的两行就是乘上loss_mask以及除以有效token数。如果不乘mask,模型会去学习预测用户的提问和系统提示词,相当于白白浪费大量训练容量;如果除以总token数而非有效token数,loss数值会被稀释,曲线看起来“很低”但模型实际没学好回答。

4.4 序列打包与动态padding

SFT数据长度差异很大,有的指令只有十几个token,有的回答能有一两千token。怎么处理长度不一是训练效率的分水岭。

最简单的做法是统一padding到固定长度,比如1024。但这样短样本会浪费大量算力在pad token上,长样本又会丢信息。我推荐两个方案:

方案一是动态padding。按batch内最长样本进行padding,每个batch的长度不同。这样能有效减少不必要的计算量,代价是实现稍微复杂。

方案二是样本打包。把多个样本首尾相连拼成一个长序列,在样本之间插入结束标记。这样能最大化利用序列长度,训练速度能提升50%以上。但打包时要注意跨样本的注意力隔离,不然模型会“串味”,从上一个样本的上下文里学出错误关联。

5. 训练过程的监控与质量评估

模型训练时盯着loss曲线看是本能反应,但SFT阶段loss能反映的信息非常有限,必须搭配其他指标一起判断训练状态。

5.1 训练曲线到底该看什么

SFT的loss通常从预训练结束时的2到3开始下降,最终收敛到0.5到1.2之间。但loss低不代表模型好——它可能只是在死记数据。

我训练时除了看loss,还会盯另外两个指标:

第一个是梯度范数。正常训练中梯度范数应该相对平稳,如果出现极端尖峰,说明数据里有离群样本,需要回去查数据;如果梯度范数持续降到接近0而loss还在降,说明模型开始过拟合了。

第二个是验证集loss。从训练数据里留出一千条左右不参与训练,每个epoch结束跑一次验证。验证loss和训练loss走势一旦开始背离(训练降、验证升),过拟合立刻实锤,可以提前停掉。

5.2 离线评测不能只看loss:搭建真实问题集

评估SFT效果最有效的方式是准备一套“百问集”:一百个覆盖你目标场景的真实问题,在训练前、训练中、训练后分别跑一遍,对比生成质量。

评测维度我建议至少看四项:

  • 指令遵循度:模型有没有按用户要求的格式、长度、风格回答。比如用户明确说“用三句话回答”,模型绝不能输出五段长文。
  • 内容可信度:模型有没有编造事实、张冠李戴。这个维度需要人工逐条判断,机器自动评测目前还替代不了。
  • 格式规范性:如果涉及列表、代码块、表格,模型是否用了正确的markdown结构。
  • 安全边界:模型面对敏感和危险请求时会不会拒绝,拒绝方式是否自然得体,不是生硬背诵“我不能回答这个问题”。

这套百问集不要每次只测同一批,要留一部分替换,防止模型在测试集上“背答案”。

6. 常见问题与排查:我踩过的那些坑

最后分享一些训练SFT时经常遇到的真实问题。这些问题在文档里通常写得比较隐晦,但实际项目里几乎人人都能碰上。

6.1 loss下降了,回答还是乱

这是新手最容易困惑的场景:训练loss从2.0降到0.7,曲线很漂亮,但模型生成结果还是前言不搭后语。

排查思路一般是三步。先看模板是否匹配——训练模板和推理加载时用的对话模板是否完全一致,不一致会导致模型在推理时不知道“助手”该从哪个位置开始生成。再看数据质量——随机抽样几十条训练数据,逐条看回答是否真正靠谱,如果数据本身大量存在“正确开头+错误结尾”,模型大概率学的就是这种残缺模式。最后看掩码——确认指令部分没有参与loss计算。

6.2 模型开始“复读机”和“幻觉”

SFT训太狠最常见的后遗症就是模型变成复读机:你问它什么都给你回训练集里的固定答案,稍微偏离一点就开始一本正经地胡说八道。

这个问题的根源几乎都是epoch数过多和数据多样性不足。解决方法是先降epoch到3以内,再回去给数据集增加指令的多样性。比如同一个问题,至少要有五种不同问法,避免模型陷入“看到关键词就背答案”的路径依赖。

幻觉问题则要区分来看。SFT能缓解一部分幻觉,比如当模型看到“不确定”指令时会学会说“我不太确定,但根据目前的信息……”。但如果模型在预训练阶段形成的错误知识关联太强,SFT的纠正力度是有限的,这种情况要靠后面偏好优化或检索增强来解决,不要指望SFT一步到位。

6.3 通用能力退化的应对

很多人在SFT之后发现模型原来的代码生成能力、推理能力变差了,这是一种常见的灾难性遗忘现象。

缓解手段有三个:第一,在训练数据里保留一部分通用能力样本,比如通用问答、代码补全、数学推理,比例控制在10%到20%;第二,控制学习率不要过高,全参微调的学习率不要超过2e-5;第三,对关键通用任务建立单独的评测集,每次训练完都跑一遍,一旦发现明显退化就回调数据配比。

我个人的习惯是,在训练脚本里加一个“通用指标监测”环节,每次训练结束自动跑一遍预先存好的通用评测集,输出分数对比。这能让你在问题还没扩大之前就发现并处理。

一点个人体会

SFT这个阶段,模型学的不是知识本身,而是行为习惯——该以什么形式开口、该在什么位置停下、该用怎样的口吻把一个知识表达出来。而行为习惯的养成,靠的就是你喂进去的每一行数据,以及你对模板、掩码、训练节奏这些细节的把控。

我在模拟项目X里反复磨了很久SFT,印象最深的一条经验是:如果你发现训练过程哪儿哪儿都不对,先别急着调学习率,去翻数据。八成问题在数据,不是数据和模板,就是数据本身。把时间花在整理数据上,永远是你做SFT最划算的投资。

等SFT稳定了之后,你想让回答质量再上一个台阶,就可以考虑把注意力转向偏好优化——也就是三部曲的第三篇要聊的话题。在那之前,先把这个阶段的规矩立好,模型才知道之后该怎么“择优”,而不是在五花八门的回答风格里迷失自己。

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

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

立即咨询