Chapter 05
SFTTrainer 全字段拆解
SFTConfig 继承自 TrainingArguments,又加了 ~20 个自家字段。本章按"数据相关 / 训练相关 / PEFT 相关 / 性能相关"分组,告诉你哪些是必调、哪些可忽略。
5.1数据字段
| 字段 | 默认 | 含义 | 建议 |
|---|---|---|---|
dataset_text_field | None | plain text 数据用哪列 | 通常 "text" |
max_length | 1024 | 截断长度 | 2048-4096 |
packing | False | 是否打包短样本 | ★ True(数据短时收益大) |
padding_free | False | flash-attn 支持的"去 pad"模式 | 开 packing 后建议开 |
assistant_only_loss | False | 只对 assistant 算 loss | ★ True |
chat_template_path | None | 自定义 chat 模板 | tokenizer 自带就够 |
eos_token | None | 显式指定 EOS | 多数 tokenizer 已设 |
completion_only_loss | True | prompt-completion 数据 | 默认即可 |
5.2训练字段(来自 TrainingArguments)
| 字段 | SFT 推荐 | 说明 |
|---|---|---|
num_train_epochs | 1-3 | SFT 多了易过拟合 |
per_device_train_batch_size | 看显存定 | 大 model 可降到 1 |
gradient_accumulation_steps | 4-32 | 调节有效 batch size |
learning_rate | 2e-4(LoRA)/ 2e-5(全参) | LoRA 用大 lr |
lr_scheduler_type | cosine | SFT 标配 |
warmup_ratio | 0.03 | 太小训不稳 |
weight_decay | 0.01 | LoRA 不一定要 |
bf16 / fp16 | bf16 | A100/H100 默认 bf16 |
gradient_checkpointing | True(显存紧时) | 慢 25% |
optim | adamw_torch | QLoRA 用 paged_adamw_8bit |
5.3性能字段
| 字段 | 含义 | 建议 |
|---|---|---|
attn_implementation | "eager" / "sdpa" / "flash_attention_2" | flash_attention_2 |
torch_compile | 开 torch.compile | 训稳了再开 |
group_by_length | 按长度分桶 | 不开 packing 时建议开 |
dataloader_num_workers | data 加载并发 | 4-8 |
5.4评估和保存
| 字段 | 含义 |
|---|---|
eval_strategy | "no" / "steps" / "epoch" |
eval_steps | steps 模式下间隔 |
save_strategy | 同上 |
save_steps / save_total_limit | 避免占盘 |
load_best_model_at_end | 训完加载最佳 |
metric_for_best_model | "eval_loss" |
5.5logging
| 字段 | 含义 |
|---|---|
logging_steps | 10-50 |
report_to | "wandb" / "tensorboard" / "none" |
run_name | wandb 项目里的实验名 |
5.6三套典型配置
① 单卡跑通 7B LoRA
SFTConfig(
output_dir="./out",
per_device_train_batch_size=2, gradient_accumulation_steps=8,
num_train_epochs=2, learning_rate=2e-4,
max_length=2048, packing=True, assistant_only_loss=True,
bf16=True, attn_implementation="flash_attention_2",
gradient_checkpointing=True,
logging_steps=10, save_strategy="epoch",
)
② 多卡 70B QLoRA
SFTConfig(
output_dir="./out",
per_device_train_batch_size=1, gradient_accumulation_steps=16,
num_train_epochs=1, learning_rate=1e-4,
max_length=4096, packing=True, assistant_only_loss=True,
bf16=True, attn_implementation="flash_attention_2",
gradient_checkpointing=True,
optim="paged_adamw_8bit",
save_steps=500, save_total_limit=2,
report_to="wandb", run_name="qwen-70b-qlora",
)
③ 全参 7B(多卡 DeepSpeed)
SFTConfig(
output_dir="./out",
per_device_train_batch_size=4, gradient_accumulation_steps=4,
num_train_epochs=3, learning_rate=2e-5,
max_length=4096, packing=True, assistant_only_loss=True,
bf16=True, attn_implementation="flash_attention_2",
gradient_checkpointing=True,
deepspeed="ds_z3_config.json",
save_steps=500,
)
5.7SFTConfig 的 3 种 loss_type
SFT 不只是 "next token prediction"。SFTConfig(trl/trainer/sft_config.py:264–274)的 loss_type 字段提供三种选项:
| loss_type | 含义 | 什么时候用 |
|---|---|---|
"nll"(默认) | 标准负对数似然 | 常规 SFT |
"dft" | Dynamic Fine-Tuning(arXiv:2508.05629) | 论文复现,对动态权重感兴趣 |
"chunked_nll" | 分块计算 lm_head 减少 logits 显存 | 词表巨大(200K+) + 长序列时 |
chunked_nll 关键实现在 _chunked_cross_entropy_loss()(sft_trainer.py:104–212):
- 原版 NLL 一次性算
batch × seq × vocab的 logits 张量,70B 模型 + 16K 序列 + 200K 词表能瞬间吃几十 GB; chunked_nll把 hidden states 按 chunk 切,每个 chunk 单独跑 lm_head + cross entropy,逐块累加梯度;- 峰值显存降到
chunk_size × vocab,约原版 1/8 - 1/32。
5.8packing 两种实现 + v0→v1 重命名
v1 把 packing 的字符串值统一了:
packing 取值 | v0 名 | v1 名 | 实现 |
|---|---|---|---|
| True(默认 BFD) | "bfd-requeue" | "bfd_split" | Best-Fit Decreasing,O(log N) 段树查空闲位 |
| wrapped | "wrapped" | 不变 | 简单按序拼,遇 max_length 切下一条 |
BFD 比 wrapped 节省 padding 更多(实测 20-40% 训练时间),但要求所有样本 token 后再排序+装箱,启动期多花几秒。v0 旧名 "bfd-requeue" 在 v1 还能识别(向后兼容),但日志会有 deprecation warning。
5.9开箱 accelerate 配置(7 份 yaml)
仓库自带 trl/accelerate_configs/ 已经写好 7 份 yaml,accelerate launch --config_file ... 直接用:
| yaml | distributed_type | 典型场景 |
|---|---|---|
single_gpu.yaml | NO | 开发 / 调试 |
multi_gpu.yaml | MULTI_GPU | 单节点多卡 SFT,最常用 |
fsdp1.yaml | FSDP(v1) | 大模型分片(TRANSFORMER_BASED_WRAP) |
fsdp2.yaml | FSDP(v2,DTensor) | 新版 mesh-aware 分片 |
zero1.yaml | DEEPSPEED | ZeRO-1,optimizer 切分 |
zero2.yaml | DEEPSPEED | ZeRO-2,最常用 ZeRO 档位 |
zero3.yaml | DEEPSPEED | ZeRO-3,参数全切,含 zero3_save_16bit_model |
不需要自己跑 accelerate config 交互式问答。trl sft --config sft.yaml --config_file zero3.yaml 直接跑通 ZeRO-3 SFT。
5.10chat template 38 个内置模板
trl/chat_templates/ 内置 38 个 jinja 模板,覆盖主流模型族:
| 模型族 | 文件 | 特别处理 |
|---|---|---|
| Llama 3.x | llama3.jinja / llama3_1.jinja / llama3_2.jinja | tool call 单 JSON |
| Qwen 3.x | qwen3.jinja / qwen3_5_think.jinja / qwen3_5_nothink.jinja / qwen3_6.jinja | think 模式独立模板 |
| Gemma 1/2/3 | gemma.jinja / gemma3.jinja | Gemma 3 支持多模态 content blocks |
| Cohere | cohere.jinja / cohere2.jinja | Command R / R+ |
| DeepSeek-V3 | deepseekv3.jinja | tool calling 完整支持 |
| GLM-4 MoE | glm4moe.jinja | MoE 路由 token |
| GPT-OSS | gptoss.jinja | — |
每个模板都有 _training.jinja 双胞胎(如 llama3_training.jinja)。差异:
- 训练版加
{% generation %} ... {% endgeneration %}标签 → 让assistant_only_loss能精确定位 assistant 起止; - 训练版保证 prefix-preserving:append 新消息不改变此前消息的 tokenization(GRPO 多轮 rollout 必备)。
5.11这章你需要带走的
- 4 个必调字段:
max_length / packing / assistant_only_loss / learning_rate; - LoRA lr 比全参高 10×(2e-4 vs 2e-5);
- 开 packing + assistant_only_loss 是 SFT 标配;
- 性能首选 flash_attention_2 + bf16 + gradient_checkpoint(紧时)。