Chapter 12

案例:从 SFT 模型到 70B PPO

📌 commit 6c6056daa522 4 阶段端到端复现 — 5 天集群

把前面 11 章学到的东西串起来,跑一次"从 SFT 模型起步、用 700K 偏好数据训 RM、 最后做 70B PPO"的完整流程。资源假设:32 × H100 80G,5 天集群。本章你会看到具体命令、 预期产出、踩坑应对。

12.1目标

Llama-3-70B-Instruct 出发,得到一个在 chat 偏好上对齐的 PPO 模型。预期:

12.2总流程

flowchart LR S0([Llama-3-70B-Instruct]) S0 --> S1[阶段 1
SFT 数据增强
~ 1 天] S1 --> S2[阶段 2
训练 70B Reward Model
~ 1.5 天] S2 --> S3[阶段 3
70B PPO 主训
~ 2 天] S3 --> S4[阶段 4
评测 + 导出 HF
~ 0.5 天] S4 --> END([对齐后的 70B 模型])

12.3阶段 1:SFT(如果需要)

如果 base 模型已经 SFT 过(如 Llama-3-Instruct),可以跳过这步。如果只有 base 模型:

# 32 × H100 / SFT 训练
python -m openrlhf.cli.train_sft \
  --pretrain meta-llama/Llama-3-70B \
  --dataset OpenRLHF/Llama-3-8b-sft-mixture \
  --apply_chat_template \
  --max_len 4096 \
  --train_batch_size 256 \
  --micro_train_batch_size 1 \
  --learning_rate 5e-6 \
  --max_epochs 1 \
  --bf16 \
  --zero_stage 3 \
  --gradient_checkpointing \
  --packing_samples \
  --save_path ./ckpt/llama3_70b_sft \
  --save_steps 200 \
  --logging_steps 1 \
  --use_wandb

预期:1 天,loss 从 ~2 降到 ~1。

12.4阶段 2:训练 70B Reward Model

python -m openrlhf.cli.train_rm \
  --pretrain ./ckpt/llama3_70b_sft \
  --dataset OpenRLHF/preference_dataset_mixture2_and_safe_pku \
  --apply_chat_template \
  --max_len 4096 \
  --train_batch_size 256 \
  --micro_train_batch_size 1 \
  --learning_rate 9e-6 \
  --max_epochs 1 \
  --bf16 \
  --zero_stage 3 \
  --gradient_checkpointing \
  --packing_samples \
  --save_path ./ckpt/llama3_70b_rm \
  --use_wandb

预期:1.5 天。RM 在验证集上 pair accuracy 应该 > 70%(一般 72%-78% 算正常)。

12.5阶段 3:70B PPO 主训

这是核心。资源 32 × H100,用 fully distributed 模式:

# 起 Ray
ray start --head --num-gpus=8
# 在其他 3 节点 ray start --address=...

# 提交训练
ray job submit --address="http://<head>:8265" \
   --runtime-env-json='{"working_dir": "/openrlhf"}' \
   -- python3 -m openrlhf.cli.train_ppo_ray \
   \
   --actor.num_nodes 1 --actor.num_gpus_per_node 8 \
   --ref.num_nodes 1 --ref.num_gpus_per_node 8 \
   --critic.num_nodes 1 --critic.num_gpus_per_node 8 \
   --reward.num_nodes 1 --reward.num_gpus_per_node 8 \
   --vllm.num_engines 1 --vllm.tensor_parallel_size 8 \
   \
   --train.colocate_actor_ref \
   --train.colocate_critic_reward \
   \
   --actor.model_name_or_path ./ckpt/llama3_70b_sft \
   --reward.model_name_or_path ./ckpt/llama3_70b_rm \
   --ckpt.output_dir ./ckpt/llama3_70b_rlhf \
   --ckpt.path ./ckpt/llama3_70b_rlhf/ds_ckpt \
   --ckpt.save_hf \
   --ckpt.save_steps 50 \
   --ckpt.max_num 3 \
   \
   --train.batch_size 256 \
   --rollout.batch_size 1024 \
   --rollout.n_samples_per_prompt 1 \
   --train.micro_batch_size 1 \
   --rollout.micro_batch_size 4 \
   --train.max_epochs 1 \
   --train.num_episodes 1 \
   \
   --data.max_len 4096 \
   --data.max_samples 100000 \
   --rollout.max_new_tokens 2048 \
   --rollout.temperature 1.0 \
   --rollout.top_p 0.95 \
   \
   --ds.zero_stage 3 \
   --ds.param_dtype bf16 \
   --ds.packing_samples \
   --ds.tensor_parallel_size 2 \
   \
   --actor.adam.lr 5e-7 \
   --actor.adam.weight_decay 0.01 \
   --critic.adam.lr 9e-6 \
   \
   --algo.advantage_estimator gae \
   --algo.gamma 1.0 \
   --algo.lam 0.95 \
   --algo.kl.init_coef 0.01 \
   --algo.kl.use_adaptive \
   --algo.kl.target 6.0 \
   \
   --actor.gradient_checkpointing_enable \
   --critic.gradient_checkpointing_enable \
   \
   --vllm.gpu_memory_utilization 0.6 \
   --vllm.enable_sleep \
   --ds.enable_sleep \
   --vllm.sync_backend nccl \
   --vllm.enforce_eager \
   \
   --train.dynamic_batch_enable \
   --train.max_tokens_per_gpu 8192 \
   --rollout.max_tokens_per_gpu 16384 \
   \
   --data.prompt_dataset OpenRLHF/prompt-collection-v0.1 \
   --data.input_key context_messages \
   --data.apply_chat_template \
   --reward.normalize_enable \
   \
   --use_wandb \
   --wandb_project openrlhf-70b-ppo

预期:2 天,约 100-200 训练 step。监控 reward / KL / entropy 三条曲线。

12.6训练曲线长什么样

指标step 0step 50step 200
reward/mean 0.2 0.5 0.7
kl 0.0 0.05 0.08
kl_coef (adaptive) 0.01 0.012 0.011
actor/entropy 4.5 4.0 3.8
actor/clipfrac 0.0 0.12 0.15
response_length/mean 180 200 220

如果你的 reward 飙升 + length 暴涨 + entropy 跌穿 → reward hacking,立即停训。

12.7阶段 4:评测 + 导出

DS checkpoint 转 HF

训练中间用的是 DeepSpeed shard 格式,最后用 OpenRLHF 自带工具转 HF:

bash examples/scripts/ckpt_ds_zero_to_universal.sh \
  ./ckpt/llama3_70b_rlhf/ds_ckpt \
  ./ckpt/llama3_70b_rlhf_hf

得到 ./ckpt/llama3_70b_rlhf_hf/ 就是标准 HF 模型目录,可以:

评测

用 vLLM serve 起服务:

vllm serve ./ckpt/llama3_70b_rlhf_hf \
  --tensor-parallel-size 8 \
  --port 8000

用 lm-eval-harness / AlpacaEval 跑:

# AlpacaEval 2.0
alpaca_eval evaluate_from_model \
  --model_configs 'pretrained=http://localhost:8000/v1' \
  --annotators_config alpaca_eval_gpt4_turbo_fn

# Arena-Hard
python show_result.py \
  --model-list llama3_70b_rlhf llama3_70b_sft

12.8关键决策点 / 调参经验

问题本案例的选择替代
用哪种部署模式 partial hybrid(colocate_actor_ref + colocate_critic_reward)32 卡富裕时改 fully distributed
vLLM 切法 1 engine × tp 8 显存够时 2 engine × tp 4
actor lr 5e-7 不稳定可降到 3e-7
critic lr 9e-6 RM 初始化好可降到 5e-6
kl init_coef 0.01 + adaptive 更激进可 0.005
n_samples_per_prompt 1(PPO) 切 GRPO 时 4-8
batch / rollout 比 train_batch 256 / rollout_batch 1024 = 1:4样本利用率敏感时 1:8
max_new_tokens 2048 chat 任务 2K 足够;推理任务 8K+
save_steps 50 训得不稳时缩到 20

12.9资源 / 时间预算

阶段资源时间预估成本(云)
阶段 1: SFT(如需) 32 × H100 1 天 ~ $5,000
阶段 2: RM 训练 32 × H100 1.5 天 ~ $7,500
阶段 3: PPO 32 × H100 2 天 ~ $10,000
阶段 4: 评测 + 导出 8 × H100 0.5 天 ~ $600
合计 ~ 5 天~ $23,000

对比:自己写一份 PPO 训练代码 + 调通的时间,至少 1-2 月工程师工时。

12.10常见失败 + 修复

失败现象修复
训练第 1 天 reward 上涨但模型变啰嗦 reward hacking。停训,检查 RM;加 length penalty
KL 飙升到 100+ kl init_coef 太小。改成 0.05;用 adaptive
某节点 GPU 利用率为 0 Ray placement_group 配置错;查 ray list placement-groups
每步训练时间从 60s 跳到 200s 显存碎片。重启训练,--ckpt.load_enable resume
训完模型 perplexity 反而升 KL 太低,policy 漂离严重。下次训用更严的 KL 约束

12.11把这个流程发散出去

诉求本流程的改造
R1 风格推理模型 RM 换成 math_reward_func;algorithm 切 GRPO;rollout.n_samples_per_prompt 8
代码生成模型 reward 换成"运行测试通过率";max_new_tokens 加到 8K
VLM RLHF 用 train_vlm_math_hybrid_engine.sh;agent_func 接图像
长上下文模型 开 ring_attn_size=4-8;增大 max_len 到 32K
用 GPT-4 当 judge reward.remote_url 指向 GPT-4 wrapper service

12.12第二条路径:ProRL V2 SOTA 推理 recipe

2026-02 OpenRLHF 团队公布的 ProRL V2 是1.5B 模型在推理任务上的开源 SOTA 配方,对小模型 RL 训练做的几个特殊改进现在收纳在 examples/scripts/train_prorlv2_math_hybrid_engine.sh。关键 flag 摘录:

python -m openrlhf.cli.train_ppo_ray \
    --algo.advantage.estimator reinforce_baseline \
    --algo.advantage.is_correction_enable \
    --algo.is.mode icepop \
    --algo.dynamic_filtering_enable \
    --algo.dynamic_filtering_range 0 1 \
    --actor.eps_clip_low_high 0.2 0.27 \
    --actor.token_level_loss_enable \
    --reward.stop_properly_penalty_coef 0.0 \
    --rollout.n_samples_per_prompt 16 \
    --rollout.temperature 1.0 \
    --algo.kl_coef 0.001 \
    --train.colocate_all \
    --vllm.enable_sleep \
    --ds.enable_sleep
flag为什么这么配
reinforce_baseline 对奖励尺度鲁棒,小模型不易崩
icepop IS 矫正 16 样本时 vLLM / training logp 数值差异较大
动态过滤 + 范围 [0, 1] 过滤全对(reward=1 group)和全错(reward=0 group)→ 留有信号样本
非对称 clip 0.2/0.27 探索上界放宽 35%,鼓励长 CoT 出现
token_level_loss 长 response 每个 token 都贡献梯度
stop_properly_penalty = 0 关掉"未停止"惩罚,让长 CoT 自由产生
n_samples = 16, temp = 1.0 大采样数 + 高熵 → 多样性高,过滤后仍有足够 group
kl_coef = 0.001 很小的 KL,允许 policy 大幅偏离

"我能在 7B 上用这套配方吗?" 可以,但 kl_coef 需要调大(0.01–0.04)防止大模型过偏离;其余字段保留。这套配方对"有可信规则 reward 的推理任务" 特别管用——R1 风格的复现就是这条路。

12.13长 CoT 涌现的人工可见信号

RL 训推理模型时看 reward 一个数字不够,关键看以下协同:

指标早期涌现中异常征兆
response_length/mean ~512 缓慢稳定升到 2K-4K 暴涨到 max_len = length hacking
reward/mean ~0.2 稳定升到 0.6+ 飞涨 0.95+ = reward hacking
actor/entropy 3-4 缓降到 1-2 < 0.3 = 策略坍塌
kl/mean (vs ref) ~0 稳定增长到 5-20 突破 100 = 立刻减 lr
样本里 "wait / actually / let me reconsider" 频率~0显著出现 不出现 = 没学到反思

最后一行 grep 样本 token 频率是"R1 涌现"最强的人工可见信号。OpenRLHF 不自动统计,但你可以在 reward function 里附带 extra_logs 统计这些关键词。

12.14这章你需要带走的