Chapter 03

SFT:把能力对齐到「听话」

上一章看了"能力"怎么长出来——一个满脑子知识却只会续写的基座模型。这一章是后训练第一步 SFT(监督微调):用同一个 loss、换一种数据,把它从"会续写"调成"会回答"。

本章你将建立的 schema

  • 对齐问题:为什么"有用 / 无害"压根不是一个能直接求导的 loss,后训练因此存在
  • SFT 的机制:还是 §1.2 的交叉熵,只是换成"指令→回答"数据并对 prompt 做掩码
  • SFT 的能与不能:能调行为,灌新知识却不可靠,还会"教模型幻觉"——以及由此推出的微调 vs RAG 选型

3.1对齐问题:从"会续写"到"会回答"

基座模型有能力但不听话;后训练要解决的是行为没对齐,而不是能力不够。

§1.3 的能力 vs 对齐已经画出这张图:基座模型的能力条几乎被预训练填满,行为条却是空的。具体表现是:给基座模型输入"请解释一下反向传播",它多半不给解释,而是续写出"请解释一下反向传播。这是一个深度学习领域的核心问题。请解释一下梯度消失……"——知识在,回答意愿不在。

这不是能力缺失,是行为没有对齐到"被提问时应当回答"这种姿态。SFT 要修的正是这一点。

为什么对齐问题难

人类想要的"有用、无害、诚实",本质上是主观偏好,不是一个能写出来然后对其求导的函数。人类判断才是 ground truth,但它昂贵(标注一条需要人时间)、主观(不同标注员意见不同)、可被 Goodhart 定律钻空子(模型一旦学会"看起来有用",和真正有用之间就出现裂缝)。整个后训练工程,都是在绕开"无法直接求导"这个根本困难。

本章 SFT 只解决第一层:"听指令、有格式、会回答"。更精细的问题——"哪个回答更好""价值观怎么对齐"——留给第 4 章的偏好优化。SFT 是行为克隆(behavioral cloning):给模型看大量"好的行为示范",让它学会模仿。

想一想

同一个基座模型,给它加几条"问→答"格式的 few-shot 示例放在 prompt 里,有时就能让它开始"好好回答"。既然 in-context prompting 能临时对齐行为,SFT 还有什么不可替代的价值?

展开答案(先停 10 秒再点)

in-context 对齐每次都要消耗 context 窗口,推理成本高;行为也不稳定,一旦 prompt 稍变就退回续写模式。SFT 把行为刻进参数,推理时无需额外示例,稳定且零额外 token 开销。此外,SFT 可以调整模型对特殊 token(如 <|user|>)的敏感度,这是 prompt 做不到的。

3.2SFT 的机制:换数据,不换机器

SFT 与预训练用同一个交叉熵 loss;唯一区别是数据格式和 loss 的计算位置。

§1.2 建立的那台机器——token → 预测下一个 → 交叉熵 loss → 梯度下降——在 SFT 里原封不动地运行。变化的是两件事:

  • 数据格式:不再是海量无标注文本,而是人写或合成的"(指令,理想回答)"对。每条样本都有明确的"该说什么"。
  • loss 的计算位置:只在回答部分的 token 上计算交叉熵;指令(prompt)部分的 token 被掩码,label 设为 -100,梯度不经过这些位置。优化器只被告知"给定这种输入,回答是什么",而不是"输入本身是什么"。

图示:prompt 掩码

<|user|> 解释 反向 传播 <|eot_id|> <|assistant|> 反向 传播 是… <|eot_id|> 指令段 · label = −100 · 不算 loss 回答段 · 算 loss · 梯度回传 一条 SFT 样本的 token 序列(chat 模板展开后)
图 3.1SFT prompt 掩码示意:指令段(灰)label 设 −100,梯度不流经此处;回答段(红)正常计算交叉熵 loss。注意:chat 模板特殊 token(<|user|>、<|assistant|>、<|eot_id|>)属于回答段的起始定界,决定模型是否将其纳入 loss 计算因实现而异——但训练与推理时的模板格式必须完全一致,否则行为静默退化。

伪代码:labels 掩码

sft_masking.py Python
# 伪代码:SFT 样本的 labels 掩码构造
# input_ids: [bos, u1, u2, ..., <eot>, a1, a2, ..., <eot>]
# 其中 u* 是 prompt tokens,a* 是 response tokens

IGNORE_INDEX = -100  # PyTorch CrossEntropyLoss 忽略此值

def build_labels(input_ids, prompt_len):
    """
    prompt_len: prompt 占用的 token 数(含特殊 token)
    返回同长度的 labels,prompt 部分设为 IGNORE_INDEX
    """
    labels = input_ids.clone()
    # 对 prompt 段做掩码——优化器不在这里算梯度
    labels[:prompt_len] = IGNORE_INDEX
    return labels

# 训练 step 示意
for batch in dataloader:
    input_ids = batch["input_ids"]          # (B, T)
    labels    = batch["labels"]             # (B, T),prompt 部分已是 -100
    logits    = model(input_ids).logits     # (B, T, vocab_size)
    # CrossEntropyLoss 自动跳过 label == -100 的位置
    loss = cross_entropy(
        logits[:, :-1].reshape(-1, vocab_size),
        labels[:, 1:].reshape(-1),          # next-token prediction
        ignore_index=IGNORE_INDEX,
    )
    loss.backward()
    optimizer.step()
labels[:prompt_len] = IGNORE_INDEX 这一行是 SFT 与预训练在实现上的唯一区别。把它去掉,代码就退化成普通的语言模型预训练。

chat 模板与特殊 token

<|user|>、<|assistant|>、<|eot_id|> 这类角色标记是词表的一部分,在 tokenizer 训练阶段就已注册。SFT 期间,模型学会了"看到 <|assistant|> 就该开始回答"。推理时若 chat 模板与训练时不一致(哪怕只是空格或换行位置不同),对应的 token id 序列就会错位,行为静默掉点。这是部署时最常见的格式陷阱之一。

数据规模:质量远重于数量

三个代表性数据集说明了这条规律:InstructGPT(2022)使用约 1.3 万条人工示范;Alpaca(2023)用 GPT-3.5 合成了 5.2 万条数据;LIMA(2023)只用 1000 条精选数据,对齐效果已相当有竞争力。数量差距高达 52 倍,对齐质量的差距远没有这么大。这个现象引出了下一节。

3.3表层对齐假说:SFT 到底教会了什么

LIMA 的实验说明:对齐教的是"用哪种格式/风格的子分布",不是在教新知识。

LIMA(2023)提出表层对齐假说(Superficial Alignment Hypothesis):模型的知识与能力几乎全部在预训练阶段习得;对齐只是教模型"在与用户交互时,该从预训练学到的分布中采用哪个子集"——即回答风格、格式偏好、"应该先说什么"这类表层决策。这正是 §1.3 那条不对称实证的直接反映:能力条是预训练填满的,SFT 动的是行为条。

假说的预测是:少量高质量示范就能饱和对齐效果,因为格式/风格信号的信息量本来就不大。1000 条数据的 LIMA 验证了这个预测。

洞察 · 假说的边界

2024 年,"Revisiting the Superficial Alignment Hypothesis"给出了更细致的图景:表层对齐假说方向正确但被高估。风格/格式维度约 100 条示范就达到饱和,但推理能力随 SFT 数据规模继续呈幂律提升——对于需要多步推理或多领域覆盖的模型,数据量仍然重要。此外,SFT 也能整合一部分新知识,只是效率远低于预训练。诚实结论:"纯格式"说法过于简化,但"主要是行为,不是知识"的大方向成立。

3.4教模型幻觉:SFT 最隐蔽的失败模式

在模型内部没有依据的问答对上做行为克隆,等于在教模型"无论知不知道,都要自信作答"。

SFT 本质上是行为克隆:给定一个(指令,回答)对,优化器只知道"看到这种输入,就该输出这种回答",不知道也不关心"模型内部是否真的掌握了这个答案的依据"。

John Schulman 在多个公开场合指出这个机制性问题:标注者给出的答案,未必对应模型参数里已有的知识。在这样的样本上做 SFT,梯度在教的不是"如何正确回答",而是"在没有内部依据时,也要输出一个听起来合理的答案"——即学会了"永远回答"这个行为,却没有学会"只回答有内部依据的内容"这个边界。

Gekhman 等(2024)对此做了量化:在含有模型原本不知道的新知识的样本上微调,幻觉倾向与新知识样本的数量呈线性正相关。新知识样本在训练中学得更慢(loss 下降缓慢),一旦被"记住",模型反而泛化出"编造"的行为倾向——因为优化器学到的是"遇到不确定输入也要给出确定答案"这个更广泛的模式。

陷阱 · 用微调"灌知识"是双重错误

第一重错误:SFT 数据量远小于预训练,灌不动知识条(§1.3)。第二重错误:强行在模型不知道的知识上做 SFT,不只是灌不进去——还会主动训高幻觉倾向,让模型在不确定时更倾向于编造而非拒绝。修复方向:行为克隆用来教行为(格式、风格、交互姿态);教模型"何时该说不知道"这个边界,需要第 4 章的强化学习——当模型说"我不确定"得到的奖励高于"自信编造"时,边界才能被学进去。

3.5SFT 的能与不能:微调 vs RAG vs LoRA

改行为用微调,加可更新事实用 RAG;两者常组合,LoRA 是微调的省显存变体。

微调 vs RAG:先问目标

两类需求对应两种工具,判断标准只有一个:目标是改行为还是加事实?

  • 改行为 / 格式 / 语气 / 领域推理风格:用微调(全量或 LoRA)。行为刻在参数里,推理时零额外开销,稳定。
  • 加可更新、可溯源的事实:用 RAG(检索增强生成)。事实存在外部知识库,可以随业务更新,无需重新训练,且检索来源可以展示给用户核实。

Lewis 等(arxiv 2312.05934)的对比研究表明,RAG 在知识注入上稳定优于无监督微调。两者并不互斥:常见的生产配置是微调管格式/语气,RAG 管事实——每一层的工具用在对应的目标上。

LoRA:冻结权重,只训低秩旁路

全量微调更新模型的全部参数(表达力最强,但显存占用是模型本身的数倍,且容易灾难性遗忘预训练行为)。LoRA(Low-Rank Adaptation)提出一种参数高效方案:冻结预训练权重矩阵 W,只在旁路引入两个低秩矩阵 B(维度 d×r)和 A(维度 r×d),前向传播变为:

h = W·x + B·A·x

其中秩 r 通常取 4–64,约占原参数量的 1–5%。W 不参与梯度更新,只有 B 和 A 被训练。显存需求显著下降,且由于预训练权重保持不变,遗忘风险也更低。

x W d × d 冻结 W·x A r × d B d × r + h 只训 B、A(r ≪ d) h = W·x + B·A·x · 秩 r 通常 4–64,约占全量参数的 1–5%
图 3.2LoRA 低秩分解:预训练权重 W(冻结)走主路径,旁路由两个低秩矩阵 B·A(可训)组成,输出相加。注意:LoRA 改变的仍是行为条(§1.3),不改变知识条——它只是让微调更省显存,并不解决"灌新知识不可靠"的根本问题。

四种手段的对比

表 3.1 · 灌知识 / 改行为的四种手段
手段 改什么 适合的目标 主要代价
全量微调(Full SFT) 全部参数 行为 / 格式 / 推理风格;数据充足时表达力最强 显存高;易灾难性遗忘;训练周期长
LoRA / 参数高效微调 低秩旁路(1–5% 参数) 行为 / 风格调整;显存受限时的首选 表达力略低于全量;r 需调参
RAG(检索增强) 不改参数;检索时注入上下文 可更新 / 可溯源的动态事实;知识频繁迭代的场景 推理延迟增加;检索质量决定上限
长上下文(In-context) 不改参数;在 prompt 里放文档 临时注入少量事实或示范;无需任何训练 消耗 context 窗口;推理成本高;不稳定

表中 RAG 被标注为"当目标是加可更新事实"时的选型。改行为时,在全量微调和 LoRA 之间按显存预算和数据量选择;两种方式不互斥,生产配置中常见"LoRA 调行为 + RAG 管事实"的组合。

提示 · §1.5 表中的 SFT 行

回到 §1.5 的全书地图:SFT 行的三列分别是——loss(直觉):同样交叉熵,只在"回答"部分计算;信号来源:人写或合成的指令→回答;显存里几个模型:1(与预训练相同)。这三列与预训练的唯一差异在第一列的"只在回答部分"——这就是 prompt 掩码的全部工程含义。

§本章 self-check

先合上答案,把能想到的写在纸上或编辑器里,写完再点开对照。

  1. SFT 和预训练用的是同一个 loss 吗?两者在训练机制上的唯一区别是什么?
  2. 表层对齐假说说"SFT 只教格式不教知识"。2024 年的后续研究补充了哪一点让它不再绝对?
  3. 为什么"在模型并不知道答案的问答对上做 SFT"会训出更爱幻觉的模型?
  4. (设计题)业务要求模型用每周更新的内部产品文档回答用户。选微调还是 RAG?若还要它"用固定话术开头",这部分应用哪种手段?
答案(先做完再展开)
  1. 是同一个交叉熵 loss(−log P(正确 token),即 §1.2 的那台机器)。唯一区别:SFT 只在回答段的 token 上计算 loss,对 prompt 段做掩码(label 设为 −100),梯度不经过 prompt 部分。
  2. 表层对齐假说方向正确但被高估:风格/格式维度约 100 条示范就饱和,但推理能力随 SFT 数据规模继续呈幂律提升;SFT 也能整合部分新知识,只是效率低。"主要是行为,不是知识"成立,"只教格式"过于简化。
  3. SFT 是行为克隆,优化器只知道"看到这种输入就输出这种答案",不关心模型内部是否有依据。在没有依据的样本上微调,梯度教的是"无论确不确定都要给出答案"这个更广的行为模式——幻觉倾向随新知识样本数量线性增加(Gekhman 2024)。
  4. 产品文档每周更新、内容动态且需可溯源:选 RAG,检索后注入上下文,无需重训。固定话术/开头格式属于行为/格式调整,稳定不变:选微调(全量或 LoRA)。两者组合——RAG 管事实,微调管格式——是生产中的标准配置。
进阶挑战 · 刚好够不着

LIMA 1000 条——是否意味着"SFT 数据越少越好"?

LIMA 用 1000 条精选数据对齐效果不错——是否说明 SFT 数据越少越好?什么情况下 1000 条远远不够?

提示(卡住再展开)

1000 条能把"格式/风格"这条饱和得快的曲线压平(表层对齐假说的有效范围)。但 §3.3 的 2024 反驳指出,推理能力随 SFT 数据继续呈幂律上升。面向需要复杂多步推理、多领域覆盖、或高度特化任务的模型,少量精数据只能覆盖行为层,能力层的提升仍需数据规模。结论:1000 条对"格式/风格对齐"足够,对"推理深度对齐"不够——目标不同,阈值不同。