SFT 到底在「修」什么?
你大概听过这样的故事:一个"什么都懂"的大模型,被训练成"什么都会答"的聊天助手。 中间发生了什么?最关键的一步,叫 SFT — Supervised Fine-Tuning,有监督微调。
很多教程上来就丢公式、丢代码。我们换一种讲法——先让你亲眼看到它修好的东西, 再一点点往下挖,看看到底是怎么修的。
会基本的机器学习(知道 loss、梯度下降、神经网络的样子)、 基本的微积分(知道导数是什么)、用过几次 ChatGPT。 不需要懂大模型——我们从直觉讲起,公式只在它真正帮助理解时才出现。
§1同一个模型,差一步 SFT,行为天差地别
下面这个对话框,请你随便问点什么——比如"如何做番茄炒蛋?"或者"写一首关于猫的诗"。 左边是没经过 SFT 的"基础模型",右边是经过 SFT 的同一个模型。 它们的权重几乎一样,差别只在于后者多看了几万条"问-答"示范。
Llama-2-7B 量级的模型,差别只有「是否经过 SFT」这一步训练。
注意基础模型把你的问题当成了"上文"继续写下去——这正是它被预训练的方式。
SFT 不给模型添加新知识。"番茄炒蛋"该怎么做、"猫"是什么, 基础模型其实已经全部知道。SFT 只做一件事—— 教模型「在被问问题时,应该给出回答,而不是继续延展问题」。 它教的是交互的格式,而不是世界的知识。
预训练让模型"博览群书",SFT 让模型"学会回话"。这一份讲解,会一步步告诉你后者是怎么做到的。
§2预训练 vs 微调:从汪洋到航道
把一个大语言模型的"全部可能行为"想象成一片权重空间—— 每一个点都是一种"会说话的方式"。 随机初始化的模型像是漂在角落里的一条小船,连句子都说不通顺。
预训练 (Pre-training) 让小船在数万亿 token 的海洋上漂流数月, 最终停在"流畅说话"的大陆上——它能写出语法正确、逻辑连贯的文本。但它不知道自己应该 回答 什么、什么时候 停下。
SFT 让它从这片大陆出发,沿着几千条"问-答"路标,走到一片很小、但很有用 的山头上——那里的模型会"听话"。
预训练通常耗费 ~104 GPU·天 + 数万亿 token; SFT 通常只需 ~100 GPU·天 + 几万条样本。 SFT 的成本不到预训练的 1%, 但它带来的"行为改变"对终端用户来说远比"知识增加"明显。 正因为 SFT 这么便宜,2023 年之后,开源社区才能"人手一个 Alpaca"。
"微调"具体在改什么?
最直接的答案:权重。模型里有几十亿个浮点数参数 $\theta$,
SFT 通过梯度下降把它们沿着"让训练样本变得更可能"的方向轻轻推一推。
"轻轻"两个字很关键:SFT 的学习率通常只有预训练的 1/10 到 1/100
(典型值 lr ≈ 2e-5),目的是不把预训练学到的语言能力推丢。
预训练塑造能力,微调塑造行为。
§3"监督"从哪来?看一眼真正的训练数据
SFT 全名是 Supervised Fine-Tuning——"有监督的微调"。
"监督" 来自人类(或一个更强的模型)写好的标准答案。
每条训练样本都是一对:(问题, 理想回答)。
下面是来自著名的 Stanford Alpaca 数据集的真实样本。点上方按钮切换样本,下方三个标签页给你三种角度看同一条数据:
模型本质上只会处理一长串 token——没有"消息"、"角色"或"对话"这些概念。
所谓"chat 模型",只是模型见过足够多
<|im_start|>user...<|im_end|><|im_start|>assistant... 这种模板,
于是学会了"看到 assistant 这个 token,接下来该我说话"。
模板是人为约定,不是模型架构的一部分。换一个模板,重新 SFT,模型就会换种说话方式。
数据格式总览
| 格式 | 结构 | 适用场景 |
|---|---|---|
| Alpaca | (instruction, input?, output) | 单轮问答,最简单 |
| ShareGPT | conversations: [{from, value}, ...] | 多轮聊天 |
| ChatML / OpenAI messages | role: system/user/assistant | 现代主流,工具调用 |
数据决定模型学到什么。SFT 的全部"监督信号",都藏在这些 (问, 答) 对里。
§4训练的最小单位:预测下一个 token
无论是预训练还是 SFT,大模型的"学习"全部归结为一件事: 给定前面已经出现的 token,预测下一个 token 是什么。
每一步,模型对词表里 每个 token 都会输出一个"概率"。 我们希望"正确那个 token"的概率尽可能高。差距用 交叉熵损失 (cross-entropy loss) 来度量:
$$ \mathcal{L} = -\log P_\theta(\text{正确 token} \mid \text{前文}) $$这个公式直白得令人意外:"惊讶度"就是损失。 模型给"对的答案"分配的概率越低 → 它越"没想到" → 损失越大 → 梯度越大 → 参数被推得越狠。 点击下方按钮,一步步看:
"训练"听起来很神秘,但放到最小单位,就只是一遍又一遍地"猜下一个 token + 看错多少 + 调一下参数"。 预训练这么干、SFT 也这么干、RLHF 之前的所有阶段都这么干。 唯一的区别是谁来决定"正确答案"——预训练用网上抓来的原文,SFT 用人类写的回答。
§5SFT 的灵魂:只对"回答"算损失
到现在我们说的"训练",对预训练和 SFT 都成立。那 SFT 究竟特殊在哪?
答案藏在一个不起眼的技术细节里:训练时,只对"回答"部分计算损失,
而忽略"问题"部分。在代码里,这通过把问题对应 token 的 label 设为 -100
(PyTorch 默认的 ignore_index)实现。
这个"小技巧"决定了 SFT 的全部精神气质。下面让你亲眼看到差别:
如果不掩码,模型会努力"也学会生成问题"——浪费一半学习容量在你不想要的能力上。 更糟的是,被训练去"预测问题"的模型,在推理时会倾向于继续延展用户输入, 而不是回答它。这就是为什么 §1 里基础模型把"如何做番茄炒蛋"当成上文继续写下去的根因—— 它从没学过"问题之后应该有回答"。
同样的损失函数、同样的优化器、同样的数据,只是 label 上多了一层 -100 的掩码——
就把"语言模型"变成了"对话助手"。这就是 SFT。
§6把一切串起来:训练循环与超参数
现在你已经知道损失怎么算了。剩下的就是把它最小化。 SFT 的训练循环用伪代码看是这样:
for epoch in range(num_epochs):
for batch in dataloader: # 一批 (prompt, response)
logits = model(batch.input_ids)
loss = cross_entropy(
logits[:, :-1], batch.labels[:, 1:],
ignore_index=-100 # ← 这就是掩码!
)
loss.backward() # 反向传播算梯度
optimizer.step() # AdamW 更新权重
scheduler.step() # 学习率衰减
optimizer.zero_grad()
剩下的就是调超参数。下面这个模拟器让你亲手"调坏"训练—— 通过失败学习什么样的超参数才合理:
- 全参数 SFT:
lr ≈ 2e-5,cosine 衰减 + 3% warmup - LoRA 微调:
lr ≈ 1e-4 ~ 2e-4(因为只动很少参数,可以激进点) - Epochs:3 次是默认起点,少于 1 通常欠拟合,多于 5 易过拟合
- Batch size:尽可能大(用 gradient accumulation 凑),有效 batch 32~128 常见
§7显存救星:LoRA & QLoRA
一个 7B 模型的全参数 SFT,至少需要 ~110 GB 显存 (权重 + 梯度 + 优化器状态都是 fp32 时)。消费级 GPU(24 GB)想都别想。怎么办?
LoRA(Low-Rank Adaptation,2021 年微软提出)的核心赌注是: 微调时的权重变化 $\Delta W$ 本身是"低秩"的—— 它的大部分信息可以用一个非常小的子空间表达。
$$ W_{\text{new}} = W_0 + \Delta W \approx W_0 + B \cdot A $$其中 $W_0$ 是冻结的原始权重(不动),$A \in \mathbb{R}^{r \times k}$、$B \in \mathbb{R}^{d \times r}$ 是要训练的"小矩阵",秩 $r \ll \min(d,k)$。我们只训练 $A$ 和 $B$ —— 参数量瞬间锐减。 拖动下方滑块感受这个"省了多少":
(冻结)
2023 年的 QLoRA 论文 把 $W_0$ 4-bit 量化后冻结,只用 fp16 训练 LoRA 适配器。 最终:一块 48 GB 的 GPU 能 SFT 一个 65B 的模型。 个人/学生也能玩得起的大模型微调,几乎全靠 LoRA / QLoRA 撑起来。
常见超参数
| 参数 | 典型值 | 说明 |
|---|---|---|
| rank $r$ | 8 / 16 / 32 / 64 | 风格微调 8;通用 SFT 16~32;代码/复杂任务 64+ |
| alpha $\alpha$ | α = 2r | 前向时贡献缩放为 $\frac{\alpha}{r} BAx$ |
| target_modules | all-linear | 最低 q/v 即可,现代实践全连接层都加 |
| lr | 1e-4 ~ 2e-4 | 比全参 SFT 高一个量级 |
§8SFT 在大模型训练流水线里的位置
你已经懂 SFT 是怎么回事了。最后把它放回大图景里——它是对齐的第一步, 但远不是最后一步。
点击上方任一阶段
没有一个好的 SFT 模型,后面的 RLHF / DPO 都救不回来—— 因为它们都用 SFT 模型作为初始化和参考分布(KL 散度的 anchor)。 工业界一句通行说法:"SFT 决定模型能力的上限,RLHF 决定下限"。
SFT 名人堂时间线
2023 年的"开源大模型寒武纪大爆发",几乎完全由 SFT 数据集驱动。下面这些工作每一个都值得了解:
带着 SFT 视角看新闻
读到下面这些说法时,你现在应该能"看穿"它们在说什么:
- "我们用 1 万条高质量数据微调出了一个客服机器人" → 在做 SFT,大概率用了 LoRA。
- "模型出现幻觉/输出不稳定" → SFT 数据里有低质样本,或 epoch 太多过拟合。
- "我们的模型是某基础模型的 chat 版本" → 在基础模型上做了 SFT(+ 可能的 RLHF)。
- "灾难性遗忘" → SFT 数据太偏,把预训练的通用能力"覆盖"掉了。
SFT 不教模型新知识,它教模型如何调用已有的知识。 短短一句话总结了为什么它便宜、为什么它有效、也为什么它需要后续的 RLHF 来更进一步。 —— 这就是 SFT。