系列文章:AI大模型知识体系 | 第二周·第二篇


一、引言:预训练模型的"尴尬"

上一篇我们聊完了预训练(Pre-training)。经过预训练的模型,本质上是一个超级"文字接龙选手"——你给它一段文字,它会拼命往后续写。

这就带来一个很尴尬的问题。

假设你问模型:"中国的首都是哪里?"

预训练模型不会回答"北京",它大概率会续写成这样:

中国的首都是哪里?美国的首都是华盛顿,日本的首都是东京,韩国的首都是首尔……

看到了吗?它把你的问题当成了"填空题的题干",顺着往下写了。因为在预训练阶段,它读了几万亿个token的文本,学到的本领就是"给定前面的文字,预测后面的文字"。

它从来没有学过"回答问题"这件事。

这就好比你招了一个读过几百万本书的天才,但你从来没教过他怎么跟人对话。他知识很丰富,但你问他一个问题,他可能会把你的问题抄一遍然后续写下去,而不是回答你。

那怎么让模型从"续写机器"变成"有问必答的助手"呢?答案就是今天的主角——SFT(Supervised Fine-Tuning,监督微调)


二、SFT是什么:给模型来一场"入职培训"

SFT,全称 Supervised Fine-Tuning,直译就是"有监督的微调"。

用一个生活类比来理解:

预训练 = 大学四年读了无数课本,积累了海量知识 SFT = 入职培训,老员工手把手教你怎么干活

一个新员工(预训练后的模型)虽然知识储备很丰富,但他不知道怎么跟客户沟通、怎么写报告、怎么按公司的规范做事。入职培训(SFT)就是拿一批"标准示范"给他看:客户这样问,你应该这样答;领导这样要求,你应该这样写。

在技术层面,SFT做的事情很简单:

  1. 准备一批"标准问答对"——告诉模型"看到这样的输入,你应该输出这样的内容"

  2. 让模型在这些数据上继续训练——模型慢慢学会"回答问题"而不只是"续写文本"

  3. 微调,不是从头训练——在预训练权重的基础上做小幅调整,而不是重新训练一个新模型

这里的"监督"(Supervised),意思就是我们有"标准答案"。每一条训练数据都包含"正确的输出",模型可以对照着学。


三、指令数据集长什么样

SFT用到的数据叫做指令数据集(Instruction Dataset),核心格式是一个三元组:

字段

含义

类比

instruction

任务指令,告诉模型要做什么

老板的需求

input

可选的额外输入/上下文

老板给的参考材料

output

期望的标准回答

老板心目中的满分答案

我们来看几条不同场景的真实样本:

样本1:问答

{
  "instruction": "简要解释什么是黑洞。",
  "input": "",
  "output": "黑洞是一种天体,其引力强大到连光都无法逃逸。它通常由大质量恒星在生命末期坍缩形成,中心是一个密度无限大的奇点,周围有一个被称为'事件视界'的边界。"
}

样本2:文本摘要

{
  "instruction": "请对以下文章进行摘要总结。",
  "input": "人工智能(Artificial Intelligence,简称AI)是计算机科学的一个分支,它试图理解智能的本质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器。该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。",
  "output": "人工智能是计算机科学的一个分支,旨在创造具有类人智能的机器,研究范围涵盖机器人、语音/图像识别、自然语言处理和专家系统等领域。"
}

样本3:代码生成

{
  "instruction": "用Python写一个函数,判断一个数是否为素数。",
  "input": "",
  "output": "def is_prime(n):\n    if n < 2:\n        return False\n    for i in range(2, int(n**0.5) + 1):\n        if n % i == 0:\n            return False\n    return True"
}

样本4:角色扮演

{
  "instruction": "你是一位经验丰富的Python面试官,请对以下代码进行Code Review,指出潜在问题并给出改进建议。",
  "input": "def process(data):\n    result = []\n    for item in data:\n        if item != None:\n            result.append(item)\n    return result",
  "output": "这段代码有以下几个可以改进的地方:\n1. `item != None` 应该改为 `item is not None`,这是PEP 8推荐的写法。\n2. 函数名 `process` 过于模糊,建议改为 `filter_none_values` 等更具语义的名称。\n3. 可以用列表推导式简化:`return [item for item in data if item is not None]`。"
}

看到了吧?这些数据的共同特点是——每条数据都清楚地告诉模型:遇到这类指令,标准回答是什么。 这就是"监督"的含义。


四、SFT的训练过程:跟预训练有什么不同?

SFT和预训练在底层用的都是同一个技术——自回归语言建模(预测下一个token)。但有一个关键区别:

预训练:所有token都算loss

预训练时,模型对文本中的每一个token都计算损失(loss)。比如输入"今天天气真好",模型需要预测"天""气""真""好"每一个位置,所有位置的预测误差都会被累加到loss里。

SFT:只在output部分算loss

SFT时,我们把一条数据拼成这样的格式:

### 指令:用Python写一个判断素数的函数
### 回答:def is_prime(n): ...

训练时,指令部分("### 指令:...")的loss被设为 -100(即忽略不计),只有回答部分("### 回答:...")才计算loss

为什么这很重要?

用一个类比:

想象一个老师给学生布置作业。老师出了一道题,学生在答题区写答案。老师批改的时候,只看答案区域对不对,不会去批改题目本身。

如果把题目的loss也算上,就相当于老师把题目也"批改"了一遍——"这个题目的'计'字你写得不够标准,扣两分"——这就离谱了。

在技术上,忽略instruction部分的loss,是为了让模型专注于学习"如何生成回答",而不是去学习"如何复述问题"。如果不做这个区分,模型会把精力浪费在记住指令的措辞上,回答质量就会下降。


五、SFT数据的质量远比数量重要

这可能是SFT最重要的一条经验。

很多人的直觉是:数据越多越好,10万条肯定比1000条强吧?

事实恰恰相反。

2023年Meta发表的 LIMA 论文LIMA: Less Is More for Alignment)给出了一个震撼业界的发现:

仅仅用 1000条 精心筛选的高质量数据做SFT,训练出的模型在人类评估中的表现,就能与使用大量数据训练的模型相媲美甚至更好。

LIMA的1000条数据是这样来的:

  • 从StackExchange上精选高质量的问答

  • 从Reddit上筛选高赞回复

  • 由人类专家手工撰写复杂推理样本

  • 每一条都经过严格审查,确保回答准确、完整、有帮助

反过来,如果你用10万条质量参差不齐的数据(比如从网上随便爬的问答),模型会学到:

  • 错误的事实信息

  • 不完整的回答

  • 低质量的表达

  • 有害或偏见内容

模型就像一个新员工,你给他看1000个优秀案例,他学到的都是精华;你给他看10万个良莠不齐的案例,他可能学到一堆坏习惯。

这也是为什么现在很多团队在SFT阶段会投入大量人力做数据标注和质量审核——因为这是投入产出比最高的环节。


六、实操环节:用Transformers + TRL做SFT

理论讲完了,我们来看怎么用代码实现。这里使用HuggingFace的 transformerstrl(Transformer Reinforcement Learning)库。

1. 准备数据

from datasets import Dataset

# 准备指令数据(实际项目中从JSON文件加载)
data = {
    "instruction": [
        "用一句话解释什么是机器学习。",
        "将以下英文翻译成中文。",
    ],
    "input": [
        "",
        "The quick brown fox jumps over the lazy dog.",
    ],
    "output": [
        "机器学习是人工智能的一个分支,让计算机通过数据自动改进性能,而无需显式编程。",
        "敏捷的棕色狐狸跳过了懒狗。",
    ]
}
dataset = Dataset.from_dict(data)

2. 构建训练用的prompt格式

def formatting_prompt(example):
    """将instruction/input/output拼接成SFT训练格式"""
    if example["input"]:
        text = f"### 指令:{example['instruction']}\n### 输入:{example['input']}\n### 回答:{example['output']}"
    else:
        text = f"### 指令:{example['instruction']}\n### 回答:{example['output']}"
    return {"text": text}

dataset = dataset.map(formatting_prompt)

3. 配置模型和训练

from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from trl import SFTConfig, SFTTrainer

# 加载预训练模型和分词器
model_name = "Qwen/Qwen2-0.5B"
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="auto")
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 训练配置
training_args = SFTConfig(
    output_dir="./sft_output",
    num_train_epochs=3,
    per_device_train_batch_size=4,
    learning_rate=2e-5,
    warmup_ratio=0.1,
    logging_steps=10,
    save_strategy="epoch",
    max_seq_length=512,
)

# 启动训练
trainer = SFTTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    processing_class=tokenizer,
)

trainer.train()

就这么十几行代码,一个SFT训练流程就跑起来了。trl 库的 SFTTrainer 已经帮我们处理好了"只在回答部分计算loss"的逻辑,不需要手动去mask掉指令部分的token。

提示:实际项目中,建议先用小数据集(100-200条)跑通流程,确认没有bug后,再上完整数据集。GPU显存不够的话,可以加上LoRA(下下周会专门讲)。


七、SFT之后,还有什么问题?

经过SFT,模型已经学会了"回答问题"。你问它"中国的首都是哪里",它会老老实实回答"北京",而不会再续写下去了。

但这里有一个关键问题:SFT只教了模型"怎么回答",没教它"怎么回答得更好"。

举个例子。你问模型:"给我推荐一部好看的电影。"

模型可能的回答:

  • A:"推荐《肖申克的救赎》,它是一部关于希望和自由的经典影片……"(高质量回答)

  • B:"推荐《逐梦演艺圈》,特别好看。"(低质量回答)

在SFT阶段,只要训练数据里写了B这种回答,模型一样会学到。SFT没有"好坏"的概念,它只知道"模仿训练数据中的回答"。

更麻烦的是,模型可能学会了一些"坏毛病":

  • 看起来在回答,实际上在胡说八道(幻觉问题)

  • 回答冗长啰嗦,抓不住重点

  • 对有害问题也照答不误(安全问题)

模型学会了"回答",但没有人告诉它哪些回答好、哪些回答差。

怎么让模型学会区分"好回答"和"坏回答",并主动选择更好的回答?这就需要下一阶段的技术——RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习)

这也是我们下一篇文章的主题。


八、总结

来回顾一下今天的核心知识点:

要点

内容

SFT解决什么问题

让预训练模型从"文字续写"变成"指令回答"

核心思路

用带标准答案的指令数据做有监督微调

训练关键

只在output部分计算loss,不关注instruction部分

数据策略

质量远重于数量,1000条精选数据 > 10万条粗糙数据

SFT的局限

只教了模型"回答",没教它"回答得好"——需要RLHF

用一张图概括大模型训练的完整流程:

预训练(学知识)→ SFT(学回答)→ RLHF(学好回答)→ 对齐(Alignment)
      ↑                ↑              ↑
   读遍互联网       入职培训       高级研修班

下一篇预告:《RLHF:教大模型区分"好回答"和"坏回答"——大模型对齐的关键一步》

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注三连支持!你的支持是我持续更新的最大动力。有问题可以在评论区交流,我会逐一回复。我们下一篇见!

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐