LLM 后训练实践
第1课:后训练概述与SFT基础

1.2 监督微调核心概念

掌握 ChatML 和 Llama 聊天模板格式,理解掩码损失(Masked Loss)的原理,了解数据质量的重要性

聊天模板(Chat Template)

SFT 的第一步是将指令-回复对格式化为模型能理解的输入格式。不同模型家族使用不同的聊天模板,格式错误是 SFT 最常见的 bug 来源

ChatML 格式(Qwen3 使用)

ChatML(Chat Markup Language)是 Qwen3 系列采用的聊天模板格式。它使用特殊 token 标记每个角色的消息边界:

<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
用三句话介绍量子计算<|im_end|>
<|im_start|>assistant
量子计算是一种利用量子力学原理进行信息处理的新型计算范式。
它使用量子比特(qubit)代替经典比特,能够同时处于多个状态的叠加。
在特定问题上,量子计算机有望实现指数级的加速。<|im_end|>

关键元素说明:

Token含义
<|im_start|>消息开始标记(im = internal monologue)
<|im_end|>消息结束标记
system系统角色,设定模型行为和约束
user用户角色,提供指令或问题
assistant助手角色,模型的回复

Qwen3 的思考模式在 ChatML 基础上增加了 <think>...</think> 标签。在思考模式下,assistant 回复的开头是 <think> 标签包裹的内部推理,随后才是面向用户的最终回复。

Llama 格式

Meta 的 Llama 系列使用不同的模板格式:

<|begin_of_text|><|start_header_id|>system<|end_header_id|>

You are a helpful assistant.<|eot_id|><|start_header_id|>user<|end_header_id|>

用三句话介绍量子计算<|eot_id|><|start_header_id|>assistant<|end_header_id|>

量子计算是一种利用量子力学原理进行信息处理的新型计算范式。
它使用量子比特(qubit)代替经典比特,能够同时处于多个状态的叠加。
在特定问题上,量子计算机有望实现指数级的加速。<|eot_id|>

多轮对话格式

ChatML 天然支持多轮对话,只需按顺序排列多轮 user/assistant 消息:

<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
什么是机器学习?<|im_end|>
<|im_start|>assistant
机器学习是人工智能的一个分支,通过数据驱动的方式让计算机自动学习模式和规律。<|im_end|>
<|im_start|>user
它和深度学习有什么区别?<|im_end|>
<|im_start|>assistant
深度学习是机器学习的子集,特指使用深层神经网络的方法。传统机器学习依赖手工特征工程,而深度学习能自动从原始数据中学习层次化的特征表示。<|im_end|>

使用 apply_chat_template 自动转换

在实践中,不要手动拼接模板字符串。使用 Hugging Face Transformers 提供的 apply_chat_template 方法可以自动处理格式转换:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-1.7B")

messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "用三句话介绍量子计算"},
    {"role": "assistant", "content": "量子计算是..."}
]

# 自动应用 ChatML 模板并 tokenize
formatted = tokenizer.apply_chat_template(
    messages,
    tokenize=False,        # 返回字符串而非 token ID
    add_generation_prompt=False  # 训练时不添加生成提示
)
print(formatted)

关键原则:始终使用 tokenizer.apply_chat_template() 而非手动拼接。不同模型的特殊 token ID 不同,手动拼接极易出错。格式错误通常不会报错,但会导致训练后的模型行为异常。


掩码损失(Masked Loss)

为什么需要掩码

标准的语言建模训练会在所有 token 上计算交叉熵损失。但在 SFT 中,我们只希望模型学习"如何回答",而不是学习"如何复述问题"

考虑以下训练样本:

<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
什么是量子计算?<|im_end|>
<|im_start|>assistant
量子计算是一种利用量子力学原理进行信息处理的新型计算范式。<|im_end|>

如果在所有 token 上计算损失:

  • 模型会花费大量梯度学习如何生成 system 提示和 user 问题的内容
  • 这不是我们想要的——用户输入在推理时是给定的,不需要模型生成

掩码损失公式

SFT 的训练目标是仅在 assistant token 上计算交叉熵损失

LSFT=tAlogPθ(xtx<t)\mathcal{L}_{\text{SFT}} = -\sum_{t \in \mathcal{A}} \log P_\theta(x_t | x_{<t})

其中 A\mathcal{A} 表示 assistant 回复对应的 token 位置集合。

具体实现中,使用一个二值掩码 mtm_t 来控制哪些位置参与损失计算:

LSFT=1At=1TmtlogPθ(xtx<t),mt={1if tA0otherwise\mathcal{L}_{\text{SFT}} = -\frac{1}{|\mathcal{A}|}\sum_{t=1}^{T} m_t \cdot \log P_\theta(x_t | x_{<t}), \quad m_t = \begin{cases} 1 & \text{if } t \in \mathcal{A} \\ 0 & \text{otherwise} \end{cases}

掩码示意图

Token:    <|im_start|> system You are ... <|im_end|> <|im_start|> user 什么是... <|im_end|> <|im_start|> assistant 量子计算... <|im_end|>
Mask:     0            0      0   0   ... 0          0            0    0    ...  0          0            0         1    1   ... 1
          ←───────── 不计算损失 ──────────────────────────────────────────────────────────→  ←──── 计算交叉熵损失 ────→

在 TRL SFTTrainer 中的实现

Hugging Face TRL 库的 SFTTrainer 通过 DataCollatorForCompletionOnlyLM 自动实现掩码损失:

from trl import SFTTrainer, SFTConfig

# SFTConfig 默认会在 assistant token 上计算损失
sft_config = SFTConfig(
    # ...其他配置
    dataset_text_field="text",           # 格式化后的文本字段
    max_seq_length=2048,                  # 最大序列长度
    packing=False,                        # 是否启用样本拼接
)

也可以使用 DataCollatorForCompletionOnlyLM 显式指定 response template:

from trl import DataCollatorForCompletionOnlyLM

# 指定 assistant 回复的起始标记
response_template = "<|im_start|>assistant\n"
collator = DataCollatorForCompletionOnlyLM(
    response_template=response_template,
    tokenizer=tokenizer
)

数据质量重于数量

SFT 的一个关键发现是:精选少量高质量数据往往优于大量低质量数据

LIMA:1,000 条精选数据的力量

LIMA(Zhou 等,2023)是证明数据质量重于数量的里程碑论文:

  • 仅使用 1,000 条精心挑选的指令-回复对进行 SFT
  • 在人类评估中,效果接近甚至超过使用 50,000+ 条数据训练的模型(如当时的 GPT-4 Alpaca)
  • 提出了 "Superficial Alignment Hypothesis"(表层对齐假说):模型的知识和能力主要来自预训练,SFT 的作用仅仅是教会模型使用正确的输出格式和风格

表层对齐假说的启示:SFT 不是在教模型新知识,而是在"激活"预训练阶段已经学到的能力。因此,SFT 数据的关键是格式和风格的多样性,而非信息量。

GRAPE:选择"适合"模型的数据

GRAPE(2025)方法进一步细化了数据选择策略:

  • 核心思想:不是所有高质量数据对特定基座模型都同样有效
  • 方法:选择与基座模型分布匹配的数据——既不太简单(模型已经会),也不太难(模型学不会)
  • 效果:在相同数据量下,GRAPE 选择的数据子集显著优于随机选择

MAGPIE:合成数据从零生成

MAGPIE(Xu 等,ICLR 2025)提出了一种创新的指令数据合成方法:

利用对齐模型的自动补全

向一个已经对齐的 LLM(如 Qwen3-32B Instruct)仅输入系统提示和用户消息的前缀 token(<|im_start|>user\n),让模型自动补全出一条指令。

生成回复

将生成的指令重新输入模型,获得对应的回复。

质量过滤

使用多维度过滤(长度、多样性、质量评分)筛选高质量样本。

MAGPIE 的关键优势:

特性MAGPIE传统合成方法(Self-Instruct)
是否需要种子数据不需要需要 175 条种子任务
指令多样性高(来自模型自身分布)受种子数据限制
生成速度快(单次前向传播)慢(多次 API 调用)
成本依赖 API 调用成本

数据质量核心原则

综合以上研究,SFT 数据准备的核心原则可总结为:

  1. 质量 > 数量:1K 条精选数据可以超越 50K 条噪声数据
  2. 多样性很关键:涵盖多种任务类型、输出格式、难度等级
  3. 匹配基座模型:选择适合当前模型能力水平的数据
  4. 格式一致性:确保所有数据使用统一的聊天模板格式
  5. 避免有害内容:数据中不应包含有害、偏见或不准确的回复

本节小结

概念要点
ChatMLQwen3 使用的聊天模板,<|im_start|> / <|im_end|> 标记消息边界
Llama 格式Meta 系列的聊天模板,使用 <|start_header_id|> 等标记
apply_chat_template自动处理格式转换,永远不要手动拼接
掩码损失仅在 assistant token 上计算交叉熵,忽略 system/user token
LIMA1,000 条精选数据胜过 50,000 条噪声数据
GRAPE选择与基座模型分布匹配的数据
MAGPIE利用对齐模型自动补全生成指令数据,无需种子