大模型微调入门:SFT(监督微调)原理与实践
一、什么是 SFT?
SFT(Supervised Fine-Tuning,监督微调)是大模型训练中的一种重要方法。它通过大量的输入和对应的标准答案,让模型学习如何按照指定的任务要求生成回答。
可以简单理解为:预训练让模型学会语言和知识,SFT 让模型学会按照要求完成任务。
例如,一个预训练模型可能知道什么是 Python,但不一定能按照指定的 JSON 格式返回结果。通过 SFT,可以让它学习这种特定的任务模式。
1.1 SFT 与预训练的区别
| 对比维度 | 预训练(Pretraining) | 监督微调(SFT) |
|---|---|---|
| 主要目标 | 学习语言规律和通用知识 | 学习任务要求和回答方式 |
| 训练数据 | 大规模文本、代码等 | 指令、输入与标准答案 |
| 数据规模 | 通常非常大 | 可以相对较小 |
| 训练成本 | 通常较高 | 通常较低 |
| 训练结果 | 通用基础模型 | 更符合特定需求的模型 |
SFT 通常建立在预训练模型的基础上,而不是从零开始训练一个大模型。
1.2 SFT 的常见应用场景
-
指令跟随: 按照用户要求完成任务。
-
领域适配: 学习特定行业的术语和任务流程。
-
结构化输出: 按照指定的 JSON Schema 输出结果。
-
代码辅助: 学习代码解释、代码审查和错误分析。
-
企业内部 AI 助手: 学习企业规定的工作流程和回答规范。
需要注意:SFT 可以帮助模型学习特定领域的任务模式,但不意味着模型一定掌握了该领域的全部事实。对于需要实时信息或准确检索的任务,通常还需要结合 RAG、工具调用或其他机制。
二、SFT 的核心原理
理解 SFT,需要先理解三个概念:
-
Token:模型处理文本的基本单位。
-
Next Token Prediction:预测下一个 Token。
-
Loss:衡量模型预测结果与标准答案之间的差异。
2.1 Token:文本如何进入模型?
大模型通常不会直接处理完整的自然语言文本,而是先使用 Tokenizer 将文本转换成 Token ID。
例如:
原始文本:
你好,世界!
Token ID:
[1234, 5678, 9012]
上面的数字仅用于示意,实际 Token ID 取决于具体模型的 Tokenizer。
模型接收 Token ID 后,将其转换为向量,再通过 Transformer 网络进行计算。
2.2 Next Token Prediction:预测下一个 Token
大多数自回归语言模型的训练目标都是预测下一个 Token。
例如,假设文本经过分词后得到:
今天 天气 很 好
模型学习的目标类似于:
| 已有上下文 | 需要预测的下一个 Token |
|---|---|
| 今天 | 天气 |
| 今天 天气 | 很 |
| 今天 天气 很 | 好 |
模型根据已有的 Token 预测下一个 Token,并与真实目标进行比较。
2.3 Loss:模型如何知道自己预测得好不好?
SFT 常用的损失函数是交叉熵损失(Cross Entropy Loss)。
模型会为词表中的每个 Token 预测一个分数,再经过 Softmax 转换为概率分布。
例如,模型预测下一个 Token 的概率如下:
| Token | 预测概率 |
|---|---|
| 好 | 0.70 |
| 不错 | 0.20 |
| 差 | 0.10 |
如果标准答案是“好”,模型就会获得相对较低的损失;如果标准答案是“差”,模型就会获得更高的损失。
对于单个目标 Token,其交叉熵损失可以写为:
\[ L=-\log P(y\mid x) \]
其中:
-
\(x\):模型已有的上下文。
-
\(y\):正确的目标 Token。
-
\(P(y\mid x)\):模型预测正确 Token 的概率。
-
\(L\):损失值。
训练的目标就是让正确答案的预测概率逐渐提高,从而降低损失。
对于一段回答,通常会对所有参与训练的目标 Token 的损失进行汇总或平均。
三、SFT 的完整训练流程
一个典型的 SFT 训练流程如下:
准备训练数据
↓
清洗和格式校验
↓
加载预训练模型和 Tokenizer
↓
将文本转换成 Token ID
↓
构造 input_ids、labels 和 attention_mask
↓
模型前向传播
↓
计算 Loss
↓
反向传播计算梯度
↓
优化器更新参数
↓
重复训练多个 Batch 和 Epoch
↓
验证模型效果
↓
保存模型或 LoRA Adapter
3.1 数据预处理
训练前需要将原始数据转换成模型能够使用的格式。
常见字段包括:
-
input_ids:输入文本对应的 Token ID。 -
attention_mask:标识哪些位置是有效 Token,哪些位置是填充 Token。 -
labels:模型需要预测的目标 Token。
对于使用标准因果语言模型目标的训练,标签通常会与输入序列对齐,并在计算损失时进行相应的移位处理。具体的移位方式取决于模型和训练框架的实现。
3.2 前向传播
模型读取输入 Token,经过 Embedding、Transformer 层和输出层,得到每个位置对应的词表预测分数,通常称为 Logits。
假设:
-
Batch Size 为 2。
-
Sequence Length 为 128。
-
词表大小为 30,000。
那么 Logits 的形状通常是:
\[ (2,128,30000) \]
三个维度分别表示:
-
2:一次处理 2 条样本。
-
128:每条样本包含 128 个 Token 位置。
-
30,000:每个位置都需要对 30,000 个词表 Token 进行预测。
3.3 计算损失
模型输出 Logits 后,会将它与正确的标签进行比较,计算交叉熵损失。
需要注意,标准语言模型训练通常会忽略不应该参与损失计算的位置,例如 Padding Token;指令微调还可以通过标签掩码,只让模型对指定部分计算损失。
例如,在对话 SFT 中,可以只计算 Assistant 回答部分的损失,而不计算 User 提问部分的损失。
3.4 反向传播和参数更新
计算 Loss 后,训练过程通常包含以下步骤:
optimizer.zero_grad() # 清空上一轮的梯度loss = model(**batch).loss # 前向传播,得到损失loss.backward() # 反向传播,计算梯度optimizer.step() # 根据梯度更新可训练参数
这段代码展示了基本训练循环。实际使用时,可能还会包含梯度累积、梯度裁剪、混合精度训练和学习率调度等操作。
四、SFT 训练数据如何组织?
数据质量通常直接影响模型最终的表现。对于指令微调,数据不只是普通文本,而是需要明确表达任务、上下文和期望输出。
4.1 常见的 JSONL 格式
JSONL 是一种常见的训练数据格式,每一行都是一个独立的 JSON 对象。
例如,采用 instruction、input、output 字段的格式:
{"instruction":"解释什么是闭包","input":"","output":"闭包是指函数能够访问其定义时所在作用域中的变量,即使外部函数已经执行完毕。"}
{"instruction":"将数字数组按升序排序","input":"[5, 2, 9, 1]","output":"[1, 2, 5, 9]"}
这种格式容易阅读和维护,但训练框架必须明确支持或转换这种数据结构。
4.2 对话式训练数据
对于 Chat 模型,也可以使用消息列表:
{
"messages": [
{
"role": "user",
"content": "什么是 TypeScript?"
},
{
"role": "assistant",
"content": "TypeScript 是 JavaScript 的超集,提供静态类型检查等能力。"
}
]
}
实际 JSONL 文件中,每条样本通常独占一行。上面为了阅读方便,使用了多行格式。
不同训练框架对字段名称和数据格式的要求可能不同,需要按照框架配置数据集。
4.3 指令微调数据需要注意什么?
第一,保证答案质量。
如果标准答案存在错误,模型就可能学习到错误的回答模式。
第二,保证任务覆盖面。
不要让数据全部集中在一种问题上。应根据目标任务,覆盖常见场景、边界条件、异常情况和不同输入形式。
第三,避免重复和数据泄漏。
大量重复样本可能让模型过度偏向某种回答方式。训练集和验证集也不应该包含相同或高度近似的样本,否则评估结果可能过于乐观。
第四,明确输出要求。
如果希望模型输出 JSON,就应该提供格式正确的示例,并覆盖字段缺失、异常输入等情况。
第五,注意数据长度。
过长的样本可能超过模型或训练配置的最大序列长度,导致截断。被截断的内容可能包括关键上下文、任务要求或标准答案。
第六,确保数据来源合规。
企业内部数据需要关注访问权限、隐私信息、敏感凭据和授权范围。训练集不应包含不必要的密码、访问令牌、个人信息或其他机密内容。
五、Full Fine-Tuning、LoRA 和 QLoRA
SFT 描述的是一种监督训练方法,而 Full Fine-Tuning、LoRA 和 QLoRA 描述的是不同的参数训练或存储方式。它们不是互相排斥的训练目标。
5.1 Full Fine-Tuning:全量微调
全量微调会更新模型的大部分或全部参数。
优点:
-
参数调整空间较大。
-
在数据、训练和评估都充分的情况下,可以适配复杂任务。
缺点:
-
显存和计算成本高。
-
训练、保存和管理模型的成本更高。
-
数据不足时仍可能出现过拟合。
5.2 LoRA:低秩适配
LoRA(Low-Rank Adaptation)是一种参数高效微调方法。
其核心思想是:冻结原模型的大部分参数,只训练额外引入的低秩矩阵。
以线性层权重矩阵为例,可以将权重更新写成:
\[ W'=W+\Delta W \]
其中:
\[ \Delta W=BA \]
-
\(W\):原始权重矩阵。
-
\(A\) 和 \(B\):可训练的低秩矩阵。
-
\(\Delta W\):需要学习的权重增量。
LoRA 通常能显著减少可训练参数的数量,因此降低训练时的显存需求,并使 Adapter 更容易保存和分发。
5.3 QLoRA:量化与 LoRA 结合
QLoRA 通常使用量化后的基础模型,并通过 LoRA 训练适配器。
它的主要思路是:
-
将基础模型以低精度形式加载。
-
冻结量化后的基础模型参数。
-
训练 LoRA 参数。
-
保存训练后的 Adapter。
量化可以降低基础模型的显存占用,但不意味着所有训练开销都会按相同比例下降。激活值、优化器状态、序列长度和批量大小等仍然会影响显存需求。
5.4 三种方式对比
| 对比维度 | Full Fine-Tuning | LoRA | QLoRA |
|---|---|---|---|
| 基础模型参数 | 通常全部更新 | 冻结大部分 | 通常冻结量化后的基础模型 |
| 可训练参数量 | 大 | 小 | 小 |
| 显存需求 | 通常较高 | 通常较低 | 通常更低 |
| 模型保存 | 完整微调模型 | Adapter | Adapter |
| 适合场景 | 资源充足、需要充分调整 | 常见的参数高效微调 | 显存受限的参数高效微调 |
对于个人实验和资源有限的场景,可以优先了解 LoRA 或 QLoRA。
六、训练中的关键参数
训练效果不仅与模型和数据有关,还受到许多超参数影响。
6.1 Learning Rate:学习率
学习率决定每次参数更新的步幅。
-
过大:训练可能不稳定,Loss 可能剧烈波动甚至出现 NaN。
-
过小:模型可能收敛缓慢,训练时间变长。
-
合适的学习率:需要根据模型、数据和微调方式进行实验。
LoRA 与全量微调的合适学习率可能不同,不应直接照搬同一个配置。
6.2 Batch Size:批量大小
Batch Size 表示一次训练处理多少条样本。
例如:
Batch Size = 4
表示一次处理 4 条样本。
Batch Size 越大,通常越需要更多显存。但实际显存占用也受到序列长度、模型规模、精度和训练实现等因素影响。
6.3 Gradient Accumulation:梯度累积
当显存不足以支持较大的 Batch Size 时,可以使用梯度累积。
假设:
-
单卡每次处理 2 条样本。
-
梯度累积步数为 8。
-
使用 1 张 GPU。
那么有效批量大小为:
\[ B_{\text{effective}} = B_{\text{device}} \times G \times D \]
其中:
-
\(B_{\text{device}}\):每张 GPU 每个微批次处理的样本数。
-
\(G\):梯度累积步数。
-
\(D\):GPU 数量。
代入示例:
\[ B_{\text{effective}}=2\times8\times1=16 \]
这意味着模型连续处理 8 个微批次,每个微批次有 2 条样本,在累积这些批次的梯度后,再进行一次优化器更新。
微批次 1:2 条样本 → 计算并累积梯度
微批次 2:2 条样本 → 计算并累积梯度
微批次 3:2 条样本 → 计算并累积梯度
...
微批次 8:2 条样本 → 计算并累积梯度
↓
更新一次参数
注意:梯度累积并不等于把 16 条样本同时放进显存。它主要是通过多次前向传播和反向传播,模拟更大的有效批量大小。
如果使用梯度累积,训练框架还需要正确处理损失缩放、最后一个不完整累积组等细节。
6.4 Epoch:训练轮数
Epoch 表示模型完整遍历训练集一次。
例如,训练集有 1,000 条样本:
-
训练 1 Epoch:完整遍历一次。
-
训练 3 Epoch:完整遍历三次。
增加 Epoch 不一定能提高模型效果。如果训练轮数过多,模型可能逐渐记住训练样本,而不是学会具有泛化能力的任务规律。
因此,需要结合验证集表现判断是否继续训练。
6.5 Max Sequence Length:最大序列长度
最大序列长度限制每条训练样本能够使用的 Token 数量。
例如:
Max Sequence Length = 1024
表示训练时通常会将样本处理到不超过 1,024 个 Token,具体是截断还是分块取决于数据处理策略。
序列长度越长,注意力计算的开销通常越高。对于标准全注意力结构,注意力计算量通常会随序列长度呈二次增长。
如果数据远远超过最大长度,直接截断可能丢失重要内容。因此,设置长度时需要先分析数据分布,并选择合适的截断、分块或过滤策略。
七、如何评估 SFT 效果?
训练 Loss 下降并不代表模型一定变得更好。
Loss 只能反映模型在当前训练目标上的表现,还需要评估模型对未见数据的泛化能力。
7.1 训练集与验证集
通常需要将数据划分为:
-
训练集: 用于更新模型参数。
-
验证集: 用于选择超参数、检查过拟合和决定训练轮数。
-
测试集: 用于最终评估,尽量不参与模型选择。
数据划分需要避免同一任务的高度相似样本同时进入训练集和测试集,否则测试结果可能无法真实反映泛化能力。
7.2 常见评估指标
| 指标 | 作用 |
|---|---|
| Training Loss | 观察训练集上的优化情况 |
| Validation Loss | 检查模型对未见数据的表现 |
| Exact Match | 判断输出是否与标准答案完全一致 |
| Accuracy | 适用于分类或有明确正确标签的任务 |
| 人工评估 | 检查答案的正确性、完整性和实用性 |
| 格式校验通过率 | 评估 JSON 等结构化输出的可靠性 |
不同任务需要选择不同的指标。例如,JSON 生成任务可以同时评估 JSON 解析成功率、Schema 通过率和字段正确率,而不是只关注文本相似度。
7.3 检查模型是否过拟合
一种常见情况是:
训练 Loss 持续下降
验证 Loss 先下降,之后开始上升
这可能表示模型开始过拟合训练集。
可以尝试:
-
增加高质量、多样化的训练样本。
-
减少训练轮数。
-
调整学习率。
-
调整 LoRA 配置。
-
改善训练集与验证集的划分方式。
八、SFT 常见问题排查
8.1 Loss 出现 NaN
NaN 表示数值计算结果不是有效的有限数值,需要及时排查。
常见原因包括:
-
学习率过高。
-
混合精度或数值稳定性问题。
-
训练数据中存在异常值或无效标签。
-
Loss 计算没有正确屏蔽 Padding Token。
-
输入长度、模型配置或训练框架配置不匹配。
-
梯度出现异常。
建议按以下顺序检查:
-
检查原始数据和预处理结果。
-
检查 Token ID、标签范围以及参与 Loss 计算的位置。
-
尝试降低学习率。
-
检查模型、精度和训练框架的兼容性。
-
在必要时使用梯度裁剪,并检查梯度是否有限。
不要仅仅因为降低了学习率后训练暂时正常,就忽略数据或标签构造中的潜在问题。
8.2 训练数据被截断
如果最大序列长度设置得太小,可能出现:
原始样本:
任务描述 + 长上下文 + 标准答案
截断之后:
任务描述 + 部分上下文
如果标准答案没有进入实际训练序列,模型就无法通过该样本学习完整的目标回答。
可以通过统计 Token 长度分布、检查截断位置,以及检查标签是否仍然包含有效目标 Token 来排查。
8.3 训练 Loss 下降,但模型回答没有改善
可能的原因包括:
-
训练集与实际使用场景不一致。
-
样本过少或重复率较高。
-
标准答案质量不高。
-
只训练了格式,而没有覆盖真正需要的任务能力。
-
评估数据不够独立。
-
模型原本已经能够完成这些任务,微调没有带来明显增益。
解决思路是先分析失败案例,再补充有针对性的高质量样本,而不是盲目增加训练轮数。
九、SFT、DPO 与 RL 的关系
SFT 是大模型后训练的重要组成部分,但并不是所有模型优化的终点。
9.1 SFT:学习标准答案
SFT 通过输入和标准答案组成的训练样本,让模型学习目标回答方式。
例如:
输入:解释 JavaScript 中的闭包。
标准答案:闭包是指函数能够访问其定义时所在词法作用域中的变量,
即使外部函数已经执行完毕……
训练目标是提高模型生成标准答案的概率。
9.2 DPO:学习回答偏好
DPO(Direct Preference Optimization,直接偏好优化)使用偏好数据,让模型学习两个回答中哪一个更符合期望。
例如:
问题:解释什么是闭包。
回答 A:闭包是一种函数相关的机制。
回答 B:闭包是指函数能够访问其定义时所在词法作用域中的变量,
即使外部函数已经执行完毕。
如果标注者更偏好回答 B,就可以将 B 作为 chosen,将 A 作为 rejected,构造偏好训练样本。
DPO 的目标不只是模仿某一个标准答案,而是调整模型,使它更倾向于生成符合偏好的回答。
9.3 RL:根据奖励信号优化
RL(Reinforcement Learning,强化学习)通过奖励信号优化模型的行为。
在大模型训练中,奖励可以来自奖励模型、规则检查器或可验证的任务结果。具体训练算法不同,实现方式也会不同。
RL 通常比基础 SFT 涉及更多训练机制,例如策略优化、奖励设计和训练稳定性控制。
9.4 三者对比
| 方法 | 主要学习信号 | 主要目标 |
|---|---|---|
| SFT | 输入与标准答案 | 学习目标任务和回答模式 |
| DPO | 偏好回答对 | 学习哪些回答更受偏好 |
| RL | 奖励信号 | 根据奖励优化模型行为 |
一种常见的学习与实践顺序是:
SFT
↓
DPO
↓
强化学习基础
↓
PPO / GRPO 等算法
这是一种便于学习的路线,并不意味着所有模型都必须严格按照这个顺序训练。实际项目需要根据目标、数据、评估结果和资源决定是否使用 DPO 或 RL。
十、总结
SFT 是大模型后训练中最基础、最常用的方法之一。
需要掌握的核心内容包括:
-
训练目标: 通过监督数据学习正确的 Token 生成方式。
-
训练机制: 前向传播、计算 Loss、反向传播和参数更新。
-
数据格式: 将任务、上下文和标准答案组织成模型支持的格式。
-
微调方式: 理解全量微调、LoRA 和 QLoRA 的区别。
-
训练参数: 理解学习率、批量大小、梯度累积、Epoch 和最大序列长度。
-
效果评估: 通过独立验证数据和任务指标判断是否真正改善。
-
常见问题: 排查 NaN、数据截断、过拟合和训练效果不佳。
-
后续学习: 在理解 SFT 的基础上,继续学习 DPO 和强化学习。
对于实际项目,最重要的并不是单纯让训练 Loss 下降,而是建立可靠的数据集、明确的评估标准和可重复的实验流程,确认微调后的模型在目标任务上确实优于原始模型。
Thanks for reading!
Related Articles
Comments
Please sign in to join the conversation.
Loading content...