模型 SFT(Supervised Fine-Tuning,有监督微调)

SFT 是在预训练基座模型上,用”指令 + 理想回答”的成对数据继续训练,让它从”会续写”变成”会听指令、会对话、会按格式输出”的关键一步。它是把基座模型装修成可用 Instruct 模型的”精装”阶段。

一句话类比:基座模型是”毛坯房”(读完整个互联网,只会接着上文往下写);SFT 是”装修”——用成千上万条”用户会怎么问、理想回答长啥样”的样本,把它训成能按人类期望作答的”精装可入住”房。这也正是 foundation_model.md 里说 “Hermes 是 SFT 的产物” 的含义。

关联:SFT 只解决”格式 / 风格 / 指令遵循”,不负责灌大量新知识——新知识靠 RAG(见 embedding_model.md)。hermes.md 就是 SFT 在 Llama/Qwen 上做的产物;SFT 训练目标仍是交叉熵,见 ../machine-learning/cross-entropy-loss.md

1. 为什么需要它:从”毛坯”到”能对话”

基座模型(Base)在海量文本上做自监督预训练,学到的是”下一个 token 的概率分布”,本质是个超强续写器,并不懂”按指令作答”。SFT 用人工/合成的高质量配对数据做有监督训练,把模型的对齐方式从”续写”扳到”遵循指令 + 给出理想回答”。

关键认知:训练目标没变(还是预测下一个 token / 最小化交叉熵),变的是”喂什么数据、在哪些 token 上算 loss”。SFT 是预训练的延续,不是另一种范式。

2. 核心概念

概念含义
训练数据成对样本:instruction(+input) → output;或多轮 system/user/assistant 对话
Chat Template把对话转成模型训练时见过的特殊标记格式(如 `<
Loss Masking只在 assistant 的回答 token 上算 loss,prompt 部分 mask 掉不反传
全量 SFT (Full FT)更新模型全部参数;效果好但吃显存、易遗忘
LoRA / QLoRA参数高效微调(PEFT):只训低秩适配器,省显存、防灾难性遗忘
数据类型通用指令(OpenHermes、ShareGPT、FLAN)、领域数据、函数调用数据(如 Hermes-Function-Calling)

3. 工作原理(训练流程)

配对数据 (instruction → output)
   │
   ▼ 套上该模型的 Chat Template(拼成训练文本)
[模型] 前向:对整段预测下一个 token
   │
   ▼ Loss Masking:只保留 output 部分的 token 算交叉熵
[反向传播] 更新参数(全量 or LoRA 适配器)
   │
   ▼ 多 epoch 后 → Instruct 模型(会听指令、会按格式答)

为什么 mask prompt:如果让模型也去”预测用户问题”,它学到的是”模仿提问”而非”给出好回答”。只在回答上优化,才能把能力聚焦到”如何作答”。

4. 变体辨析:全量 vs LoRA vs QLoRA

维度全量 SFTLoRAQLoRA
更新参数100%仅低秩矩阵(<1%)仅低秩矩阵
显存最高(要存全量梯度/优化器状态)最低(基座量化到 4bit 再训)
灾难性遗忘风险高低(原权重冻结)
适用数据极大、要深度改造绝大多数场景单卡/消费级显卡

实践里 LoRA/QLoRA 是默认选择:省资源、可插拔(一个基座挂多个 LoRA 适配器)、且因冻结底座而更不易忘本。

5. 基本用法(以 TRL + LoRA 为例)

from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import SFTConfig, SFTTrainer
from peft import LoraConfig
 
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B")
 
dataset = load_dataset("teknium/OpenHermes-2.5", split="train")  # 指令配对数据
 
# LoRA:只训低秩适配器,省显存、防遗忘
lora = LoraConfig(r=16, lora_alpha=32, lora_dropout=0.05,
                  target_modules="all-linear")
 
trainer = SFTTrainer(
    model=model,
    args=SFTConfig(max_length=2048, per_device_train_batch_size=4),
    train_dataset=dataset,
    peft_config=lora,
    # formatting_func:把样本套成模型的 chat template(system/user/assistant)
)
trainer.train()   # 得到的 LoRA 权重可单独保存、即插即用
# 数据长这样(Alpaca 风格)
{"instruction": "用一句话解释梯度下降",
 "input": "",
 "output": "梯度下降是沿损失负梯度方向逐步调整参数、最小化误差的优化算法。"}
# 多轮对话则是 system/user/assistant 交替的列表

6. 典型工作流

  1. 想让某个基座”会听话 / 会某种格式 / 会调某类工具” → 选 SFT(而非改架构)。
  2. 收集或合成配对数据:通用指令 + 你的领域/函数调用样本(如 hermes.md 的 FC 数据)。
  3. 用 LoRA/QLoRA 训练,控制学习率、保留少量通用数据防遗忘。
  4. 评测指令遵循/格式正确率;若要进一步对齐价值观,再上 RLHF/DPO。

7. 常见误区

  • ❌ “SFT 能灌大量新知识” → 微调擅长格式/风格/指令遵循,灌新知识效率远低于 RAG,且易遗忘、易幻觉。新知识走 RAG(见 embedding_model.md)。
  • ❌ “数据越多越好” → 质量 > 数量;脏数据、重复、格式不一致的害处远大于数量收益。需要多样性覆盖目标场景。
  • ❌ “SFT 一定会破坏底座能力” → 过度 SFT 才会灾难性遗忘;用 LoRA、控学习率、混入通用数据可显著缓解。
  • ❌ “不用对应模型的 Chat Template 也行” → 必须用该模型训练时见过的 template,否则分布偏移、效果骤降。
  • ❌ “只有全量微调才有效” → LoRA/QLoRA 通常足够且更稳,除非要做极深度的领域改造。

8. 延伸阅读 / 关联概念