hello,我是半硅人 Scott。这里是《高中生也能听懂的 AI 课》第 21 篇。
本文回答3个问题:
1. SFT 是什么?
2. SFT 是怎么训练的?
3. SFT 训练数据从哪里来?
上一篇文章中,我们讲了预训练:模型在海量文本中不断学习如何更准确地预测下一个 token。
预训练完成后得到的模型叫作“基础模型”,有时也简称“基模”。
它读过很多文字,学到了语言、知识、常识、代码和一些推理模式。但它会续写,不等于它会按人的要求回答。
比如你输入一个很像作文题的指令:
请以《AI会不会取代人类》为题,写一篇文章。只做过预训练的基础模型,可能把它当成试卷或写作材料的一部分,继续往后接:
要求:观点明确,条理清楚,不少于800字。这句话确实合理,但答非所问。我们真正希望它输出的是一篇文章:
AI会不会取代人类每当一种新技术出现,人们都会担心自己的工作会不会消失。AI 的确会替代一部分重复性工作,但“替代工作”和“取代人类”并不是一回事……模型得先认出这是一条指令,再按照要求组织标题、正文、观点和论证,SFT 专门训练模型学会做这件事。在典型的大模型训练流程中,SFT 是后训练的第一步。
什么叫后训练呢?预训练完成后,模型还要继续接受一系列训练,才能变成真正可用的 AI 助手,这个阶段统称为后训练。
花5分钟读完本文,你就会明白:
SFT,就是用大量“用户要求 -> 理想回答”的样本,继续训练基础模型,让它学会理解指令,并按照要求回答。
▍问题 1:SFT 是什么?
SFT 的全称是 Supervised Fine-Tuning,中文通常叫监督微调。
“监督”的意思是,每条训练数据都已经写好了一个理想回答,模型要学习这个回答。
SFT 阶段,模型学习的是一组组整理成“用户要求 -> 理想回答”的文本。不同模型使用的具体格式可能不同,下面用 OpenAI 公开过的 ChatML 格式举例:
<|im_start|>user请以《AI会不会取代人类》为题,写一篇文章。<|im_end|><|im_start|>assistantAI会不会取代人类每当一种新技术出现,人们都会担心自己的工作会不会消失。AI 的确会替代一部分重复性工作,但“替代工作”和“取代人类”并不是一回事……<|im_end|>这里的 <|im_start|> 和 <|im_end|>,是模型词表中预先定义的特殊 token,用来标记一条消息的开始和结束。user 和 assistant 是角色标记,分别表示后面的内容是“用户的要求”和“助手的理想回答”。
预训练时,文档、代码、故事等内容通常只被当作一段普通文本,模型学习的是:这段文字后面更可能出现什么?
SFT 阶段,模型反复学习“用户要求 -> 理想回答”这样的样本,会逐渐学会不同指令对应什么动作。
用户让它写文章,它就开始写文章;用户让它解释或总结,它就切换到相应的回答方式;用户要求三句话、表格或固定格式,它也会尽量照做。
其实,模型并不是到了 SFT 阶段才知道文章、解释和总结是什么样,这些模式大多已经在预训练中学过了。
SFT 是进一步让模型知道:用户这样问时,应该调用哪种已有能力,又该用什么形式回答。
SFT 的主要作用,是把基础模型已经学会的能力,组织成符合用户要求的回答。
▍问题 2:SFT 是怎么训练的?
从最底层的训练步骤看,SFT 和预训练一样,仍然是预测下一个 token、计算 loss、反向传播,再用优化器更新参数。
本文讨论的这种指令 SFT 有两个主要变化:训练数据变成了“用户要求 -> 理想回答”,并且只用助手回答部分计算 loss。
上一问已经讲过 SFT 训练数据的样子。这一问重点看第二个变化,也就是 SFT 的 loss 怎么算。为了方便理解,我们先省略表示角色和边界的特殊 token。
假设用户问题包含 m 个 token,理想回答包含 n 个 token,那么整个文本序列可以写成:
q1, q2, ... , qm, a1, a2, ... , an其中,q 代表问题中的 token,a 代表回答中的 token。
我们在《交叉熵是什么》那篇文章中讲过,预训练会把一整条序列当作普通文本,在每个位置预测下一个 token,再把这些位置上的 loss 取平均。
如果把上面这条序列当作普通文本进行预训练,那么问题部分和回答部分都会参与计算:
预训练 loss= [L(q2) + ... + L(qm) + L(a1) + ... + L(an)] / (m + n - 1)这里的 L(q2),表示模型根据 q1 预测 q2 时产生的 loss。L(a1),表示模型读完全部问题 token 后,预测回答的第一个 token a1 时产生的 loss。
SFT 仍然会在整条序列的每个位置预测下一个 token,但在计算平均 loss 时,只保留回答部分:
SFT loss= [L(a1) + L(a2) + ... + L(an)] / n也就是说:
预训练:问题部分和回答部分都参与计算 lossSFT:只用助手回答部分计算 loss问题部分虽然不参与最终 loss,却仍然必须输入模型。模型只有先读到 q1 到 qm,才能预测 a1;读到问题和 a1 后,才能继续预测 a2。
问题是预测答案的条件,答案才是 SFT 的训练目标。
真实训练时,这些位置的预测会通过矩阵运算一次并行算出来。把它们写成 q1、q2、a1、a2,只是为了方便我们理解。
算出 SFT loss 后,反向传播计算梯度,优化器再根据梯度更新参数。这套流程仍然和预训练相同。
那 SFT 会更新多少参数?
最直接的做法,是让基础模型原有的参数都参与更新,这叫全量 SFT。
我们来看大模型里最常见的一种向量变换。假设输入向量是 x,原来的参数矩阵是 W:
y = x * W^T经过全量 SFT 后,W 会发生一些变化。我们把这次变化记作 ΔW。希腊字母 Δ 读作 Delta,通常用来表示“变化量”。
SFT 后的矩阵 = W + ΔW因此,新的输出是:
y = x * (W + ΔW)^T把括号展开:
y = x * W^T + x * ΔW^T所以,全量 SFT 可以理解成:在预训练得到的 W 上,继续学出一份变化量 ΔW。
但这里有一个成本问题。假设 W 是一张 4096 × 4096 的矩阵,那么完整的 ΔW 也有 4096 × 4096 个参数,相当于要学习约 1678 万个数字。
能不能用更少的参数表示这份变化量?
一种常见方法叫 LoRA,全称是 Low-Rank Adaptation,中文叫低秩适配。
LoRA 做了一个假设:SFT 对模型产生的有效调整,可能不需要 1678 万个数字各自独立变化,而是可以归纳成少数几个主要的调整方向。
“低秩”可以先理解成:独立的调整方向比较少。
比如我们假设,这次 SFT 只需要学习 8 个主要的调整方向。LoRA 不再直接学习完整的 ΔW,而是用两张窄矩阵的乘积来表示它:
A:8 × 4096B:4096 × 8ΔW = B * AB * A 的结果仍然是一张 4096 × 4096 的矩阵,和 W 的形状完全相同。但 A 和 B 加起来只包含约 6.5 万个参数,比完整 ΔW 的 1678 万个参数少得多。
为什么要用两张矩阵?
因为只用一张矩阵时,如果它既要接收 4096 维输入,又要输出 4096 维结果,它就仍然是一张 4096 × 4096 的大矩阵。两张窄矩阵相乘,既能得到和 W 相同形状的变化量,又能大幅减少需要训练的参数。
把 ΔW = B * A 代回前面的公式。为了先看清结构,下面省略真实 LoRA 中用来控制修正量大小的缩放系数:
y = x * W^T + x * (B * A)^T = x * W^T + (x * A^T) * B^T这条新增的计算路径可以写成:
x-> 乘 A^T:4096 维变成 8 维-> 乘 B^T:8 维变回 4096 维-> 得到对原输出的修正量这里,可以把 B 理解成保存了 8 个不同的输出修正方向。x * A^T 得到的 8 个数字,是当前输入对应的 8 个权重。B 再按照这些权重,把 8 个修正方向加权组合起来,得到一份 4096 维的输出修正量。
训练时,原来的 W 保持不动,只调整 A 和 B。最终输出就是:
最终输出= 原矩阵 W 的输出 + LoRA 学到的修正量这看起来有点像残差连接,因为两者都把一份新结果加到原结果上,但它们保留的东西不同。
残差连接:保留进入这一层之前的输入 xLoRA:保留预训练矩阵 W 原来的计算结果LoRA 训练完成后,还可以把学到的变化量直接合并进原矩阵:
W_new = W + B * A合并以后,模型仍然只需要计算:
y = x * W_new^T那为什么 LoRA 是先降维再升维,而前面讲过的 FFN 却是先升维再降维?
因为它们的目标正好相反。
FFN:4096 维 -> 11008 维 -> 4096 维LoRA:4096 维 -> 8 维 -> 4096 维FFN 先升维,是为了把 token 向量展开到一个更丰富的空间里,再通过激活函数组合出更多特征。LoRA 先降维,是为了限制这次参数调整的自由度,只用少数几个方向表示 ΔW。
FFN 的两个矩阵之间有激活函数,因此不能直接合并成一个矩阵。LoRA 的 A 和 B 之间没有激活函数,所以可以先算出 B * A,再把它合并进 W。
FFN 先升维,是为了增强模型的表达能力;LoRA 先降维,是为了压缩 SFT 需要学习的变化量。
无论使用全量 SFT,还是冻结原参数、只训练 LoRA 的 A 和 B,训练数据和 loss 的计算方法都没有变。它们的区别只是:哪些参数可以根据 loss 继续调整。
▍问题 3:SFT 训练数据从哪里来?
SFT 训练数据的基本格式是:
用户要求 -> 理想回答最直接的数据来自人类标注。标注员根据问题写出理想回答;数学、代码、医学、法律、金融等专业领域,还需要专家来写或审核。好回答不只要语句通顺,还得准确、有用,并且真正符合题目要求,所以这类数据很贵。
为了获得 SFT 和其他后训练阶段所需的高质量数据,业界真的会招募专业人士来出题、作答和审核。《时代》杂志在 2025 年报道,OpenAI、Google 和 Anthropic 等公司都在大量寻找专家数据,一些高难度样本甚至由拥有博士学位的专家写下完整解题过程。
还有一部分数据来自已有任务的改造。翻译、总结、分类、信息抽取等传统任务,都可以整理成指令:
请把这段英文翻译成中文。请把这篇文章总结成 100 字。请判断这条评论是正面还是负面。请从下面的文本里抽取人物、地点和时间。真实产品中的用户问题也很有价值,因为它们能反映用户到底会怎么问。不过,这类数据必须先做隐私处理,再经过质量筛选和安全过滤。
现在还有大量 SFT 数据由更强的模型生成,比如数学解题步骤、代码解释、写作样例和工具调用样例。合成数据容易扩大规模,但必须经过规则、小模型或人工审核。否则,里面的错误、废话和固定套路也会被模型学走。
这些内容在预训练阶段有没有可能出现过?有。教程、问答网页、论坛讨论和代码解释,本来就可能出现在预训练数据里。 差别在于,预训练把它们当普通文本,SFT 则把它们整理成明确的“用户提问、助手回答”。前者让模型见过这些知识和表达,后者让模型学会在什么问题下给出什么回答。
至于写作、代码、数学等数据各占多少,并没有统一配方。
通用助手会增加通用问答、写作和总结;代码助手会增加代码生成、解释、debug 和测试用例;工具型助手则会加入搜索、读文件、调用函数和数据库操作的样本。
Meta 在 Llama 3 报告中披露过一组 SFT 数据配比:
General English:52.66% Code:14.89% Multilingual:3.01% Exam-like:8.14% Reasoning and tools:21.19% Long context:0.11%
这组比例按样本数统计,不是按 token 数统计。长上下文样本虽然只有 0.11%,但每条很长,所以它占用的 token 比例会更高。
DeepSeek-V3 的技术报告提到,SFT 阶段使用了约 150 万条指令数据。推理类数据包括数学题、代码竞赛题和逻辑题,其中很多由内部 DeepSeek-R1 模型生成;非推理类数据包括创意写作、角色扮演和简单问答,由 DeepSeek-V2.5 生成并经过人工验证。
Qwen2.5 技术报告也提到,SFT 使用了超过 100 万条样本,后续还进行了多阶段强化学习,继续提升指令跟随、长文本生成和结构化数据分析能力。
这些配比会直接影响模型最终像什么样的助手。写作样本多,模型更熟悉写作任务;代码样本多,代码回答通常更强;工具调用样本多,模型也更容易学会什么时候该调用工具。反过来,如果样本里充满套话或经常答非所问,模型同样会把这些毛病学走。
SFT 数据不能只看数量,还要看答案质量、任务分布和筛选方法。
那 SFT 对整体能力的提升到底占多少?这个问题没法用一个固定百分比回答。 从训练规模看,SFT 通常远小于预训练。预训练可能使用万亿级 token,SFT 的数据量和算力都小得多。但从用户感受看,变化很大,因为用户最先在意的是:我让它解释、总结、写文章或输出表格时,它到底会不会照做。
预训练决定模型有没有这项基础能力,SFT 决定用户提出要求时,模型能不能把这项能力稳定地用出来。
SFT 也有边界。它能让模型更会按照要求回答,却不能单独保证回答可信、遇到危险问题时处理得当,也不能保证数学、代码和复杂逻辑题一定做对。前一类问题要靠后续的偏好和安全训练继续改善,后一类问题还需要专门的推理训练。
▍把整篇串起来
到这里,我们知道了:
SFT 的训练数据,是一组组“用户要求 -> 理想回答”的样本 SFT 仍然在预测下一个 token,问题部分提供条件,助手回答部分计算 loss loss 算出来以后,模型通过反向传播更新参数 SFT 可以更新全量模型参数,也可以冻结原参数,只训练 LoRA 的 A和B底层训练步骤没有变,训练数据和计算 loss 的位置变了 经过 SFT,基础模型从会续写,变成了会按要求回答
但同一个问题仍然可能有很多种合格回答,SFT 没有直接教模型该怎样给它们排序。哪一个更有用、更可信,遇到危险请求时又该不该拒绝?
下一篇,我们继续讲:
偏好训练是什么:大模型怎么学会“什么回答更好”。
夜雨聆风