Chapter 05

训练配置 SFTTrainer

把模型、数据、超参组装成一次训练。理解 batch、梯度累积、学习率、warmup、bf16 这些旋钮各自的作用。

5.1 SFTTrainer 是什么

Unsloth 复用 HuggingFace TRL 库的 SFTTrainer——它封装了监督微调(SFT)的完整训练循环:取 batch、前向、算 loss、反向、更新、记录日志、保存 checkpoint。你只需传入模型、分词器、数据集和一组超参(SFTConfig/TrainingArguments)。

SFT(Supervised Fine-Tuning)
监督微调:用带标准答案的"问-答"对训练,让模型模仿期望输出。SFTTrainer 就是为它服务的。
batch size(批大小)
一次前向/反向处理的样本数。越大越稳但越占显存。字段名 per_device_train_batch_size
gradient accumulation(梯度累积)
累计多个小 batch 的梯度再更新一次,等效放大 batch 而不增显存。有效 batch = batch × 累积步数。
learning rate(学习率)
参数每步更新的幅度。LoRA 常用 1e-4 ~ 2e-4,太大训崩、太小学不动。
warmup(预热)
训练初期让学习率从 0 线性升到目标值的若干步,避免一开始梯度剧烈震荡。
bf16 / fp16
混合精度训练。bf16 数值范围大、更稳(Ampere+ 支持);老卡用 fp16。省显存、提速。
gradient checkpointing(梯度检查点)
前向不存全部激活,反向时重算,用算力换显存。Unsloth 的 "unsloth" 模式最省。
optimizer(优化器)
更新参数的算法。推荐 adamw_8bit,用 8bit 存优化器状态,进一步省显存。

5.2 组装 SFTTrainer

from trl import SFTTrainer, SFTConfig

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = train_ds,
    eval_dataset = eval_ds,
    dataset_text_field = "text",      # 第 4 章生成的字段
    max_seq_length = 2048,
    packing = False,                  # 短样本可设 True 提速
    args = SFTConfig(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,   # 有效 batch = 2×4 = 8
        warmup_steps = 5,
        num_train_epochs = 1,             # 或用 max_steps 控制
        learning_rate = 2e-4,
        logging_steps = 1,
        optim = "adamw_8bit",
        weight_decay = 0.01,
        lr_scheduler_type = "linear",
        seed = 3407,
        bf16 = True,                    # 老卡改 fp16=True
        output_dir = "outputs",
    ),
)

5.3 有效 batch 与显存的权衡

显存不够就把 per_device_train_batch_size 调小,同时把 gradient_accumulation_steps 调大,保持有效 batch 不变。二者乘积决定"每次真正更新看了多少样本"。

显存batch_sizegrad_accum有效 batch
8GB (T4)188
16GB248
24GB (3090)4416
40GB (A100)8216

5.4 epoch vs max_steps

控制训练时长有两种方式:num_train_epochs(跑几遍数据)或 max_steps(跑多少步)。快速验证流程时用 max_steps=60 之类的小值先跑通;正式训练用 1-3 个 epoch。SFT 一般 1-3 epoch 足够,太多轮容易过拟合。

学习率经验值

LoRA/QLoRA:1e-4 ~ 2e-4;继续预训练(第 9 章)用更小如 5e-5。配 linearcosine 调度器都行,warmup 取总步数的 3%-5%。

5.5 只对回答计算 loss(可选进阶)

默认训练会对整段文本算 loss,包括用户的问题部分。若只想让模型学"回答"、不学"复述问题",可用 TRL 的 train_on_responses_only 屏蔽掉指令部分的 loss:

from unsloth.chat_templates import train_on_responses_only

trainer = train_on_responses_only(
    trainer,
    instruction_part = "<|im_start|>user\n",
    response_part = "<|im_start|>assistant\n",
)
为什么这么做

只对 assistant 回答算 loss,模型把"学习预算"全花在生成好答案上,通常能提升指令跟随质量,尤其是多轮对话数据。

5.6 小结与下一步