案例:从 SFT 模型到 70B PPO
把前面 11 章学到的东西串起来,跑一次"从 SFT 模型起步、用 700K 偏好数据训 RM、 最后做 70B PPO"的完整流程。资源假设:32 × H100 80G,5 天集群。本章你会看到具体命令、 预期产出、踩坑应对。
12.1目标
从 Llama-3-70B-Instruct 出发,得到一个在 chat 偏好上对齐的 PPO 模型。预期:
- AlpacaEval 2.0 / Arena-Hard 分提升 5-10 分;
- 避免回答变啰嗦(length penalty 保护);
- 不要发生 reward hacking。
12.2总流程
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 0 | step 50 | step 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 模型目录,可以:
transformers.AutoModelForCausalLM.from_pretrained()加载;- vLLM 起 OpenAI 兼容 API;
- 用 llama.cpp 转 GGUF 部署到本地。
评测
用 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这章你需要带走的
- 70B RLHF 是一个 4 阶段流程:SFT → RM → PPO → 评测;
- 32 × H100 / 5 天 / ~$23K 是当前的工程量级;
- 关键监控:reward / kl / entropy / length 四条曲线;
- partial hybrid 是 70B 训练的甜点模式;
- 训完用
ckpt_ds_zero_to_universal.sh转 HF 格式; - 同一套流程换 reward + 算法 = 训推理 / 代码 / VLM 模型。