完整实战:从 base 模型到对齐模型
本章把前 11 章的 TRL 用法拼成一条完整工作流,目标是用 Qwen2.5-7B 训出一个能用的对齐 chat 模型。给出实际命令、时间预算、关键 checkpoint。
12.1三阶段全景
路径 A(实用主义):SFT → DPO。两天搞定,效果通常够用。
路径 B(追极致):SFT → RM → GRPO 或 PPO。四天,能拿到 R1 风格推理能力。
12.2阶段 1:SFT
数据
# 选 Capybara(5k)+ tulu-3-sft-mixture(一部分)
from datasets import load_dataset, concatenate_datasets
ds1 = load_dataset("trl-lib/Capybara", split="train")
ds2 = load_dataset("allenai/tulu-3-sft-mixture", split="train[:50000]")
sft_ds = concatenate_datasets([ds1, ds2]).shuffle(seed=42)
训练
accelerate launch examples/scripts/sft.py \
--model_name_or_path Qwen/Qwen2.5-7B \
--dataset_name trl-lib/Capybara \
--output_dir ./sft_out \
--per_device_train_batch_size 2 \
--gradient_accumulation_steps 16 \
--num_train_epochs 2 \
--learning_rate 2e-4 \
--max_length 4096 \
--packing \
--assistant_only_loss \
--bf16 \
--attn_implementation flash_attention_2 \
--gradient_checkpointing \
--use_peft --lora_r 16 --lora_alpha 32 \
--logging_steps 10 --save_strategy epoch
预算(8×A100-80GB):~30k samples × 2 epoch ≈ 18 小时。
12.3阶段 2:DPO
accelerate launch examples/scripts/dpo.py \
--model_name_or_path ./sft_out/final \
--dataset_name trl-lib/ultrafeedback_binarized \
--output_dir ./dpo_out \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 8 \
--num_train_epochs 1 \
--learning_rate 5e-6 \
--beta 0.1 \
--max_length 2048 \
--bf16 \
--gradient_checkpointing \
--use_peft --lora_r 16
预算:60k 偏好对 × 1 epoch ≈ 8 小时(8×A100)。
评估
# 训完用 MT-Bench / AlpacaEval / IFEval 跑一遍
git clone https://github.com/EleutherAI/lm-evaluation-harness
lm_eval --model hf --model_args pretrained=./dpo_out/final --tasks alpaca_eval --batch_size 8
12.4阶段 3(可选):GRPO 训推理能力
想做"会推理"的版本(R1 风格):
accelerate launch examples/scripts/grpo.py \
--model_name_or_path ./sft_out/final \
--dataset_name openai/gsm8k --dataset_config_name main \
--output_dir ./grpo_out \
--per_device_train_batch_size 1 \
--num_generations 8 \
--max_prompt_length 512 --max_completion_length 1024 \
--learning_rate 1e-6 \
--beta 0.04 \
--bf16 \
--use_vllm --vllm_mode colocate \
--use_peft --lora_r 16 \
--num_train_epochs 1
预算:~5k prompt × G=8 ≈ 18 小时(8×A100)。
12.5合并 LoRA、导出 HF 格式
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B", torch_dtype="bfloat16")
model = PeftModel.from_pretrained(base, "./dpo_out/final")
model = model.merge_and_unload()
model.save_pretrained("./qwen2.5-7b-dpo-merged")
AutoTokenizer.from_pretrained("./dpo_out/final").save_pretrained("./qwen2.5-7b-dpo-merged")
推上 HF Hub:
huggingface-cli upload your-name/qwen2.5-7b-dpo-aligned ./qwen2.5-7b-dpo-merged
12.6部署
# vLLM 部署
vllm serve ./qwen2.5-7b-dpo-merged --tensor-parallel-size 2 --port 8000
# 测试
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "qwen2.5-7b-dpo-merged",
"messages": [{"role":"user","content":"Hello"}]}'
12.7整条 pipeline 的时间 / 成本
| 阶段 | 耗时 | 云成本(8×A100 按 $20/h) |
|---|---|---|
| SFT | ~18 h | $360 |
| DPO | ~8 h | $160 |
| GRPO(可选) | ~18 h | $360 |
| 合并 + 部署 | ~1 h | $20 |
| 路径 A 总 | ~27 h | $540 |
| 路径 B 总 | ~45 h | $900 |
12.8第四条路径:Online DPO(SFT → 在线偏好)
2024-08 之后 TRL 加了 online preference 系(trl/experimental/online_dpo/)。它的 niche:不需要预先收集 chosen/rejected 数据,训练时实时让 reward 模型打分生成偏好对。
from trl.experimental.online_dpo import OnlineDPOTrainer, OnlineDPOConfig
cfg = OnlineDPOConfig(
output_dir="./online_dpo_out",
beta=0.1,
learning_rate=5e-7,
use_vllm=True, # ★ rollout 用 vLLM
vllm_mode="colocate",
num_generations=2, # 每条 prompt 采 2 个回复,RM 选 chosen/rejected
)
trainer = OnlineDPOTrainer(
model=model,
reward_model=rm, # ★ 用 RM 实时打分而不是离线 chosen/rejected
args=cfg,
train_dataset=ds, # 只有 prompt 列
)
trainer.train()
同族还有 NashMDTrainer(博弈论 nash equilibrium)和 XPOTrainer(exploration policy optimization),都继承 OnlineDPOTrainer,差别只在 chosen/rejected 选法上。
12.9第五条路径:RLOO(GRPO 之外的"无 critic RL")
RLOOTrainer(trl/trainer/rloo_trainer.py:105)是 GRPO 之外的另一条 critic-less RL 路径,2024 论文 2402.14740 的实现:
| 维度 | GRPO | RLOO |
|---|---|---|
| baseline 估计 | group 内 z-score | leave-one-out(baseline = 其他 N-1 个的 reward 均值) |
| 无偏 vs 有偏 | 有偏(z-score 归一) | 无偏 |
| 变种数量 | 多(pass@k / Dr.GRPO / GFPO / ...) | 少(设计简单) |
| 实战收敛 | 更稳,主流 | 更快但波动大 |
| vLLM 支持 | ✅ | ✅ |
"该选哪个?"实战经验:先 GRPO;如果不收敛 / 太慢,尝试 RLOO;如果还想再快但接受波动,看 GFPO(trl/experimental/gfpo/)。
12.10多 reward 加权的实战 recipe
ch07.4 提了"format reward + correctness reward"组合。完整 R1 风格 recipe:
def format_reward(completions, **kw):
"""要求 <think>思考</think><answer>答案</answer>"""
return [1.0 if ("<think>" in c[0]["content"] and "<answer>" in c[0]["content"]) else 0.0
for c in completions]
def length_reward(completions, **kw):
"""惩罚太长的 think 块"""
return [max(0, 1 - len(c[0]["content"]) / 4096.0) for c in completions]
from trl.rewards import accuracy_reward
cfg = GRPOConfig(
output_dir="./r1_repro",
reward_weights=[1.0, 0.2, 0.1], # ★ 三个 reward 的权重
num_generations=8,
beta=0.001, # R1 论文用很小的 KL
use_vllm=True, vllm_mode="colocate",
)
trainer = GRPOTrainer(
model=base_model,
reward_funcs=[accuracy_reward, format_reward, length_reward],
args=cfg,
train_dataset=ds,
)
trainer.train()
三个 reward 的角色:
- accuracy(1.0):主导,最终答案是否正确;
- format(0.2):辅助,鼓励用 think/answer 标签结构思考;
- length(0.1):惩罚,避免无限输出 think 内容。
这是 Open-R1(2025-01)公开的几个关键配方之一。
12.11这章你需要带走的
- 实用路径:SFT (LoRA) → DPO (LoRA),2 天 + ~$540;
- 推理增强路径:再加一阶段 GRPO,多 1 天 + ~$360;
- TRL 提供
examples/scripts/已经把所有 trainer 包装成 CLI; - 训完 merge LoRA 推 HF Hub,用 vLLM 部署。