LLM 课程文档

监督式微调

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

Ask a Question Open In Colab

监督式微调

监督式微调(SFT)主要用于使预训练语言模型能够遵循指令、进行对话以及使用特定的输出格式。尽管预训练模型具有令人印象深刻的通用能力,但 SFT 有助于将它们转换为类似助手的模型,以便更好地理解和响应用户提示。这通常通过对人类编写的对话和指令数据集进行训练来完成。

本页提供了使用 SFTTrainer 微调 deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B 模型的逐步指南。通过遵循这些步骤,您可以使模型更有效地执行特定任务。

何时使用 SFT

在深入实施之前,了解何时将 SFT 作为项目选择是重要的。第一步,您应该考虑使用现有指令微调模型和精心设计的提示是否足以满足您的用例。SFT 涉及大量的计算资源和工程工作,因此只有当现有模型的提示不足时才应考虑使用。

仅在以下情况下考虑 SFT: - 您需要超出提示所能达到的额外性能 - 您有一个特定的用例,其中使用大型通用模型的成本高于微调小型模型的成本 - 您需要现有模型难以处理的特定输出格式或领域特定知识

如果您确定 SFT 是必要的,则是否继续取决于两个主要因素

模板控制

SFT 允许精确控制模型的输出结构。这在您需要模型执行以下操作时特别有价值

  1. 生成特定聊天模板格式的响应
  2. 遵循严格的输出模式
  3. 在响应中保持一致的样式

领域适应

在专业领域中,SFT 通过以下方式帮助模型与领域特定要求保持一致:

  1. 教授领域术语和概念
  2. 强制执行专业标准
  3. 适当处理技术查询
  4. 遵循行业特定指南
在开始 SFT 之前,评估您的用例是否需要:- 精确的输出格式 - 领域特定知识 - 一致的响应模式 - 遵守特定指南

此评估将有助于确定 SFT 是否是满足您需求的正确方法。

数据集准备

监督式微调过程需要一个具有输入-输出对的特定任务数据集。每对应该包含

  1. 一个输入提示
  2. 预期的模型响应
  3. 任何额外的上下文或元数据

训练数据的质量对于成功的微调至关重要。让我们看看如何准备和验证数据集

训练配置

微调的成功在很大程度上取决于选择正确的训练参数。让我们探讨每个重要参数以及如何有效地配置它们

SFTTrainer 配置需要考虑几个控制训练过程的参数。让我们探讨每个参数及其目的

  1. 训练时长参数:

    • num_train_epochs:控制总训练时长
    • max_steps: epoch 的替代,设置最大训练步数
    • 更多 epoch 允许更好的学习,但有过度拟合的风险
  2. 批处理大小参数:

    • per_device_train_batch_size:决定内存使用和训练稳定性
    • gradient_accumulation_steps:允许更大的有效批处理大小
    • 更大的批处理提供更稳定的梯度,但需要更多内存
  3. 学习率参数:

    • learning_rate:控制权重更新的大小
    • warmup_ratio:用于学习率预热的训练部分
    • 过高会导致不稳定,过低会导致学习缓慢
  4. 监控参数:

    • logging_steps:指标日志记录的频率
    • eval_steps:在验证数据上评估的频率
    • save_steps:模型检查点保存的频率
从保守值开始,并根据监控进行调整:- 从 1-3 个 epoch 开始 - 最初使用较小的批处理大小 - 密切监控验证指标 - 如果训练不稳定,调整学习率

TRL 实施

现在我们了解了关键组件,让我们使用适当的验证和监控来实施训练。我们将使用 Transformers 强化学习 (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 model and tokenizer
model_name = "HuggingFaceTB/SmolLM2-135M"
model = AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path=model_name).to(
    device
)
tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path=model_name)
# Setup chat template
model, tokenizer = setup_chat_format(model=model, tokenizer=tokenizer)

# 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()
当使用包含“messages”字段的数据集(如上例)时,SFTTrainer 会自动应用模型的聊天模板,该模板从 hub 中检索。这意味着您不需要任何额外的配置来处理聊天式对话——训练器将根据模型预期的模板格式格式化消息。

打包数据集

SFTTrainer 支持示例打包以优化训练效率。此功能允许多个短示例打包到相同的输入序列中,从而在训练期间最大限度地利用 GPU。要启用打包,只需在 SFTConfig 构造函数中将 packing 设置为 True。当使用 max_steps 打包数据集时,请注意您可能会根据打包配置训练比预期更多的 epoch。您可以使用格式化函数自定义示例组合方式——这在使用具有多个字段(如问答对)的数据集时特别有用。对于评估数据集,您可以通过在 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,
)

监控训练进度

有效的监控对于成功的微调至关重要。让我们探讨在训练期间需要注意什么

理解损失模式

训练损失通常遵循三个不同的阶段

  1. 初始急剧下降:快速适应新的数据分布
  2. 逐渐稳定:随着模型微调,学习率减慢
  3. 收敛:损失值稳定,表明训练完成
SFTTrainer Training

要监控的指标

有效的监控包括跟踪定量指标和评估定性指标。可用指标包括

  • 训练损失
  • 验证损失
  • 学习率进展
  • 梯度范数
在训练期间注意这些警告信号:1. 验证损失增加而训练损失减少(过拟合)2. 损失值没有显著改善(欠拟合)3. 损失值极低(可能记忆)4. 输出格式不一致(模板学习问题)

收敛之路

随着训练的进行,损失曲线应逐渐稳定。健康训练的关键指标是训练损失和验证损失之间存在很小的差距,这表明模型正在学习可泛化的模式,而不是记忆特定示例。绝对损失值将根据您的任务和数据集而有所不同。

监控训练进度

上图显示了典型的训练进程。请注意,训练损失和验证损失最初都急剧下降,然后逐渐趋于平稳。这种模式表明模型正在有效地学习,同时保持泛化能力。

需要注意的警告信号

损失曲线中的几种模式可以指示潜在问题。下面我们说明常见的警告信号和我们可以考虑的解决方案。

SFTTrainer Training

如果验证损失的下降速度明显慢于训练损失,则您的模型可能对训练数据过度拟合。考虑

  • 减少训练步骤
  • 增加数据集大小
  • 验证数据集质量和多样性
SFTTrainer Training

如果损失没有显著改善,模型可能

  • 学习速度过慢(尝试提高学习率)
  • 难以完成任务(检查数据质量和任务复杂性)
  • 达到架构限制(考虑不同的模型)
SFTTrainer Training

极低的损失值可能表明记忆而非学习。如果出现以下情况,这尤其令人担忧

  • 模型在新颖的、类似的示例上表现不佳
  • 输出缺乏多样性
  • 响应与训练示例过于相似
在训练期间同时监控损失值和模型的实际输出。有时损失看起来不错,但模型却产生了不必要的行为。对模型响应进行定期定性评估有助于发现仅靠指标可能遗漏的问题。

我们应该注意到,我们在此概述的损失值解释主要针对最常见的情况,事实上,损失值可能会根据模型、数据集、训练参数等表现出各种行为。如果您有兴趣深入了解所概述的模式,您应该查看 Fast AI 团队的这篇博客文章。

SFT 后的评估

11.4 节中,我们将学习如何使用基准数据集评估模型。目前,我们将重点关注模型的定性评估。

完成 SFT 后,考虑以下后续操作

  1. 在保留的测试数据上彻底评估模型
  2. 验证各种输入下的模板遵循情况
  3. 测试领域特定知识的保留
  4. 监控实际性能指标
记录您的训练过程,包括:- 数据集特征 - 训练参数 - 性能指标 - 已知限制。此文档对于未来的模型迭代将非常有价值。

测验

1. SFT 中控制训练时长的参数是什么?

2. 损失曲线中的哪种模式表明可能出现过拟合?

3. gradient_accumulation_steps 的作用是什么?

4. SFT 训练期间应监控什么?

5. 训练期间健康的收敛表明什么?

💐 干得好!

您已经学会了如何使用 SFT 微调模型!要继续学习,请

  1. 尝试使用不同的参数运行 Notebook
  2. 尝试使用其他数据集
  3. 为课程材料改进做出贡献

其他资源

< > 在 GitHub 上更新