TRL(Transformer强化学习)是Hugging Face专门设计用于训练语言模型的库,支持现代方法:从监督微调(SFT)到高级技术如RLHF(人类反馈强化学习)和DPO(直接偏好优化)。
对于我们的情况——使用LoRA/QLoRA进行监督微调——我们将使用SFTTrainer,这是一个扩展了Hugging Face标准Trainer但针对文本生成任务优化的类。关键优势:
# 安装TRL(如果之前未安装)
!pip install -q trl
# 导入关键组件
from trl import SFTTrainer
from transformers import TrainingArguments
TrainingArguments定义所有训练超参数:批量大小、周期数、学习率、检查点、日志记录等。
training_args = TrainingArguments(
output_dir="./results", # 保存检查点和日志的目录
num_train_epochs=3, # 完整训练周期数
per_device_train_batch_size=4, # 每个GPU的批量大小(根据内存调整)
gradient_accumulation_steps=4, # 累积梯度以模拟更大的批量
optim="paged_adamw_8bit", # 内存高效优化器(对QLoRA至关重要)
save_steps=500, # 每500步保存检查点
logging_steps=100, # 每100步记录指标
learning_rate=2e-4, # LoRA学习率(典型值:1e-4到3e-4)
weight_decay=0.01, # L2正则化
fp16=True, # 混合精度训练(FP16)
bf16=False, # 除非GPU支持BF16(A100, H100)否则禁用
max_grad_norm=0.3, # 梯度裁剪以确保稳定性
warmup_ratio=0.03, # 线性学习率预热
lr_scheduler_type="cosine", # 余弦学习率衰减
report_to="wandb", # 向Weights & Biases报告指标(可选)
evaluation_strategy="steps", # 训练期间评估
eval_steps=500, # 每500步评估
save_total_limit=2, # 仅保留最新的2个检查点
load_best_model_at_end=True, # 结束时加载最佳模型(按评估指标)
metric_for_best_model="eval_loss", # 定义"最佳模型"的指标
greater_is_better=False, # 损失越小越好
push_to_hub=False, # 不上传到Hugging Face Hub(可选)
)
关键说明:
per_device_train_batch_size=4+gradient_accumulation_steps=4= 有效批量大小为16。optim="paged_adamw_8bit"对避免QLoRA中的OOM至关重要。fp16=True加速训练并减少内存。如果GPU支持BF16(Ampere+),使用bf16=True和fp16=False。report_to="wandb"需要免费的Weights & Biases账户。否则使用report_to="none"。
SFTTrainer期望一个带有格式化文本字段的数据集。我们将使用模块6中的format_instruction函数。
from datasets import Dataset
# 假设我们有Alpaca格式的示例
dataset_dict = {
"instruction": [
"为科技产品写一个简短的描述。",
"将以下文本概括为一句话。",
],
"input": [
"产品:带降噪功能的无线耳机。价格:$129.99。",
"生成式AI正在通过实现高质量内容的自动化创建来改变教育、娱乐和医疗保健等行业。",
],
"output": [
"通过这些高保真无线耳机享受无干扰的音乐。主动降噪功能和长达30小时的电池续航,非常适合旅行、工作或放松。仅售$129.99。",
"生成式AI正在通过自动化高质量内容的创建来革新关键行业。",
]
}
# 转换为Hugging Face数据集
dataset = Dataset.from_dict(dataset_dict)
# 应用格式化
def formatting_prompts_func(examples):
instructions = examples["instruction"]
inputs = examples["input"]
outputs = examples["output"]
texts = []
for instruction, input_text, output in zip(instructions, inputs, outputs):
text = f"### 指令:\n{instruction}\n\n### 输入:\n{input_text}\n\n### 响应:\n{output}"
texts.append(text)
return texts
# SFTTrainer将使用此函数格式化示例
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset,
formatting_func=formatting_prompts_func, # 格式化提示的函数
max_seq_length=512, # 最大序列长度
tokenizer=tokenizer,
packing=False, # 不打包序列(更适合指令调优)
dataset_text_field="text", # 包含文本的字段(如果使用formatting_func则不需要)
)
# 开始训练
trainer.train()
重要提示: 如果数据集很大,请将其分为
train_dataset和eval_dataset并都传递给训练器。这里为了简单起见,我们只使用训练。