LLM 课程文档
监督式微调
并获得增强的文档体验
开始使用
监督式微调
监督式微调 (SFT) 主要用于调整预训练语言模型,使其能够遵循指令、进行对话和使用特定的输出格式。虽然预训练模型具有令人印象深刻的通用能力,但 SFT 有助于将它们转化为更像助手的模型,从而更好地理解和响应用户提示。这通常通过在人工编写的对话和指令数据集上进行训练来完成。
本页面提供了一个逐步指南,介绍如何使用 SFTTrainer
微调 deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
模型。通过遵循这些步骤,您可以使模型更有效地执行特定任务。
何时使用 SFT
在深入实施之前,重要的是要了解何时 SFT 是您项目的正确选择。作为第一步,您应该考虑使用现有的指令调整模型和精心设计的提示是否足以满足您的用例。SFT 涉及大量的计算资源和工程工作,因此只有在提示现有模型被证明不足时才应考虑采用。
如果您确定 SFT 是必要的,那么是否继续进行取决于两个主要因素
模板控制
SFT 允许精确控制模型的输出结构。当您需要模型执行以下操作时,这一点尤其有价值
- 以特定的聊天模板格式生成响应
- 遵循严格的输出模式
- 在响应中保持一致的样式
领域自适应
在专门领域工作时,SFT 通过以下方式帮助模型与特定领域的规范对齐
- 教授领域术语和概念
- 执行专业标准
- 适当处理技术查询
- 遵循行业特定指南
此评估将有助于确定 SFT 是否是满足您需求的正确方法。
数据集准备
监督式微调过程需要使用输入-输出对进行结构化的特定于任务的数据集。每对数据应包含
- 输入提示
- 预期的模型响应
- 任何额外的上下文或元数据
您的训练数据质量对于成功进行微调至关重要。让我们看看如何准备和验证您的数据集
训练配置
微调的成功很大程度上取决于选择正确的训练参数。让我们探讨每个重要参数以及如何有效地配置它们
SFTTrainer 配置需要考虑几个控制训练过程的参数。让我们探讨每个参数及其用途
训练时长参数:
num_train_epochs
:控制总训练时长max_steps
:epochs 的替代方案,设置最大训练步数- 更多的 epochs 可以实现更好的学习,但也存在过拟合的风险
批量大小参数:
per_device_train_batch_size
:决定内存使用量和训练稳定性gradient_accumulation_steps
:允许更大的有效批量大小- 更大的批量提供更稳定的梯度,但需要更多的内存
学习率参数:
learning_rate
:控制权重更新的大小warmup_ratio
:用于学习率预热的训练部分- 过高可能导致不稳定,过低会导致学习缓慢
监控参数:
logging_steps
:指标记录的频率eval_steps
:在验证数据上评估的频率save_steps
:模型检查点保存的频率
使用 TRL 实现
现在我们了解了关键组件,让我们通过适当的验证和监控来实现训练。我们将使用 Transformers Reinforcement Learning (TRL) 库中的 SFTTrainer
类,该类构建于 transformers
库之上。这是一个使用 TRL 库的完整示例
from datasets import load_dataset
from trl import SFTConfig, SFTTrainer
import torch
# Set device
device = "cuda" if torch.cuda.is_available() else "cpu"
# Load dataset
dataset = load_dataset("HuggingFaceTB/smoltalk", "all")
# Configure trainer
training_args = SFTConfig(
output_dir="./sft_output",
max_steps=1000,
per_device_train_batch_size=4,
learning_rate=5e-5,
logging_steps=10,
save_steps=100,
eval_strategy="steps",
eval_steps=50,
)
# Initialize trainer
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
processing_class=tokenizer,
)
# Start training
trainer.train()
打包数据集
SFTTrainer 支持示例打包,以优化训练效率。此功能允许将多个短示例打包到同一个输入序列中,从而最大限度地提高训练期间的 GPU 利用率。要启用打包,只需在 SFTConfig 构造函数中设置 packing=True
即可。当将打包数据集与 max_steps
结合使用时,请注意,根据您的打包配置,您可能会训练比预期更多的 epochs。您可以使用格式化函数自定义示例的组合方式 - 这在处理具有多个字段(如问答对)的数据集时尤其有用。对于评估数据集,您可以通过在 SFTConfig 中设置 eval_packing=False
来禁用打包。以下是自定义打包配置的基本示例
# Configure packing
training_args = SFTConfig(packing=True)
trainer = SFTTrainer(model=model, train_dataset=dataset, args=training_args)
trainer.train()
当打包具有多个字段的数据集时,您可以定义自定义格式化函数,将这些字段组合成一个输入序列。此函数应接收示例列表,并返回包含打包后的输入序列的字典。以下是自定义格式化函数的示例
def formatting_func(example):
text = f"### Question: {example['question']}\n ### Answer: {example['answer']}"
return text
training_args = SFTConfig(packing=True)
trainer = SFTTrainer(
"facebook/opt-350m",
train_dataset=dataset,
args=training_args,
formatting_func=formatting_func,
)
监控训练进度
有效的监控对于成功的微调至关重要。让我们探讨在训练期间需要注意的事项
理解损失模式
训练损失通常遵循三个不同的阶段
- 初始急剧下降:快速适应新的数据分布
- 逐渐稳定:学习率随着模型微调而减慢
- 收敛:损失值趋于稳定,表明训练完成

要监控的指标
有效的监控包括跟踪定量指标和评估定性指标。可用的指标包括
- 训练损失
- 验证损失
- 学习率进展
- 梯度范数
收敛路径
随着训练的进行,损失曲线应逐渐趋于稳定。健康训练的关键指标是训练损失和验证损失之间的小差距,这表明模型正在学习可泛化的模式,而不是记忆特定的示例。绝对损失值将因您的任务和数据集而异。
监控训练进度
上面的图表显示了典型的训练过程。请注意训练损失和验证损失最初是如何急剧下降,然后逐渐趋于平稳。这种模式表明模型正在有效地学习,同时保持泛化能力。
需要注意的警告信号
损失曲线中的几种模式可能表明潜在问题。下面我们说明常见的警告信号以及我们可以考虑的解决方案。

如果验证损失的下降速度明显慢于训练损失,则您的模型可能过度拟合训练数据。考虑
- 减少训练步数
- 增加数据集大小
- 验证数据集的质量和多样性

如果损失没有显示出显著的改善,则模型可能
- 学习速度过慢(尝试提高学习率)
- 难以应对任务(检查数据质量和任务复杂度)
- 达到架构限制(考虑使用不同的模型)

极低的损失值可能表明是记忆而不是学习。如果出现以下情况,则尤其令人担忧
- 模型在新颖的类似示例上表现不佳
- 输出缺乏多样性
- 响应与训练示例过于相似
我们应该注意到,我们在此处概述的损失值的解释旨在针对最常见的情况,事实上,损失值的行为方式可能会因模型、数据集、训练参数等而异。如果您有兴趣探索更多关于概述模式的信息,您应该查看 Fast AI 人员的这篇博文:Fast AI。
SFT 后的评估
在 11.4 节中,我们将学习如何使用基准数据集评估模型。现在,我们将重点关注模型的定性评估。
完成 SFT 后,请考虑以下后续操作
- 在保留的测试数据上彻底评估模型
- 验证各种输入中的模板一致性
- 测试特定领域的知识保留
- 监控真实世界的性能指标
测验
1. 在 SFT 中,哪些参数控制训练时长?
2. 损失曲线中的哪种模式表明可能存在过拟合?
3. gradient_accumulation_steps 用于什么?
4. 在 SFT 训练期间应该监控什么?
5. 什么表明训练期间的健康收敛?
💐 干得漂亮!
您已经学会了如何使用 SFT 微调模型!要继续学习
- 尝试使用不同参数的 notebook
- 尝试其他数据集
- 为课程材料贡献改进