快速上手:单机跑通 PPO demo
RLHF 训练的"hello world" 是这样的:拿一个 SFT 好的模型 + 一个 reward model,
在偏好数据上跑 PPO,看到 reward 上升、KL 在控制中。这一章带你跑一次官方 demo
train_ppo_ray_hybrid_engine.sh,重点不是看明白每个 flag,而是看到 loop 真的转起来。
3.1OpenRLHF 提供的 5 类训练入口
OpenRLHF 把训练拆成 5 个独立 CLI,按 RLHF 的"准备 → 训练"流程组织:
| CLI | 对应 | 典型场景 |
|---|---|---|
python -m openrlhf.cli.train_sft | SFT | 把 base model 训成 chat model |
python -m openrlhf.cli.train_rm | Reward Model | 用 preference 数据训打分器 |
python -m openrlhf.cli.train_dpo | DPO / IPO / SimPO | offline 偏好对齐(不需要 RL) |
python -m openrlhf.cli.train_ppo_ray | PPO / GRPO / RLOO / REINFORCE++ | 在线 RLHF / 推理 RL |
python -m openrlhf.cli.serve_rm | Remote RM service | 把 RM 单独起服务给训练调 |
本章主角是 train_ppo_ray,因为它是 OpenRLHF 真正"区别于其他 RLHF 框架"的核心入口。
3.2跑通的两步
步骤 1:起 Ray head
# 启动 Ray,head 在本机
ray start --head --num-gpus=8 --dashboard-host=0.0.0.0
# 看一下资源
ray status
预期输出包含 8 GPU 资源。如果是多机,所有 worker 都要 ray start --address=<head_ip>:6379 接到 head。
步骤 2:跑 hybrid engine PPO 脚本
cd /Users/zjw/Documents/LLM/train/repos/openrlhf
bash examples/scripts/train_ppo_ray_hybrid_engine.sh
这个脚本的关键参数(不用先记住,下章会拆):
python3 -m openrlhf.cli.train_ppo_ray \
--ref.num_nodes 1 --ref.num_gpus_per_node 8 \
--reward.num_nodes 1 --reward.num_gpus_per_node 8 \
--critic.num_nodes 1 --critic.num_gpus_per_node 8 \
--actor.num_nodes 1 --actor.num_gpus_per_node 8 \
--vllm.num_engines 4 \
--vllm.tensor_parallel_size 2 \
--train.colocate_all \ # ← 所有角色共享同一组 8 卡
--vllm.gpu_memory_utilization 0.5 \
--actor.model_name_or_path OpenRLHF/Llama-3-8b-sft-mixture \
--reward.model_name_or_path OpenRLHF/Llama-3-8b-rm-700k \
--ckpt.output_dir /openrlhf/examples/final/llama3-8b-rlhf \
--train.batch_size 128 \
--rollout.batch_size 1024 \
--rollout.n_samples_per_prompt 1 \
--train.max_epochs 1 \
--data.max_len 2048 \
--ds.zero_stage 3 \
--ds.param_dtype bf16 \
--actor.adam.lr 5e-7 \
--critic.adam.lr 9e-6 \
--algo.kl.init_coef 0.01 \
--data.prompt_dataset OpenRLHF/prompt-collection-v0.1 \
--data.input_key context_messages \
--data.apply_chat_template \
--reward.normalize_enable \
--actor.gradient_checkpointing_enable \
--ds.packing_samples \
--vllm.sync_backend nccl \
--vllm.enforce_eager \
--vllm.enable_sleep \
--ds.enable_sleep \
--train.dynamic_batch_enable \
--train.max_tokens_per_gpu 16384
3.3启动后会发生什么
(8 张卡放在一组里)"] C --> D1["RayActorGroup(Actor) × 8 卡"] C --> D2["RayActorGroup(Ref) × 8 卡
(与 Actor 共置)"] C --> D3["RayActorGroup(Critic) × 8 卡"] C --> D4["RayActorGroup(Reward) × 8 卡"] C --> D5["vLLMEngine × 4 个
(每个 TP=2)"] D1 --> E["RemoteExperienceMaker
启动主循环"] D5 --> E E --> F["第 1 个 step:
① actor 权重同步到 vLLM
② vLLM rollout 生成 response
③ ref 算 ref_logp
④ reward 算 reward
⑤ critic 算 value
⑥ actor + critic 训练"] F --> G[loss / reward / kl 写 wandb]
3.4训练时盯哪几个指标
OpenRLHF 默认把日志输出到 wandb / tensorboard。重点关注的几个:
| 指标 | 正常区间 | 异常预警 |
|---|---|---|
reward/mean | 缓慢上升 | 飙升 → 检查 reward hacking |
kl_coef | 初值附近浮动 | 飙升 → policy 漂离过快 |
kl | 0.001 – 0.1 | > 1 → 策略坏了 |
actor/policy_loss | 有负值正常 | 振荡剧烈 → lr 太大 |
actor/entropy | 2 – 6 | < 1 → 熵坍塌 |
actor/clipfrac | 0.05 – 0.3 | > 0.5 → lr 过高 / clip 过紧 |
critic/value_loss | 逐步下降 | 不降 → critic 没学到 |
response_length/mean | 稳定或缓升 | 暴涨 → length hacking |
throughput/sps | — | 跌 → 网络异常 / vLLM 异常 |
3.5你看到了什么
跑了几小时后,你应该看到:
- reward 曲线缓慢上升(从 ~0.3 升到 ~0.6 这个量级,具体看 RM);
- KL 小幅波动(自适应 KL 控制器会动态调
kl_coef); - response_length 略有变化,但不应该爆涨;
- actor / critic loss 各自下降。
如果跑出"reward 涨但模型变啰嗦 / 答非所问",多半是 reward hacking — 见第 11 章。
3.6同一个入口怎么换算法
train_ppo_ray 不只是跑 PPO,它通过 --algo.* 系列参数能切到多种算法:
| 算法 | 关键改动 |
|---|---|
| PPO(默认) | 需要 critic + reward 两个角色 |
| GRPO | --algo.advantage_estimator group_norm --rollout.n_samples_per_prompt 8;不需要 critic |
| RLOO | --algo.advantage_estimator rloo --rollout.n_samples_per_prompt 4;不需要 critic |
| REINFORCE++ | --algo.advantage_estimator reinforce_plus_plus;不需要 critic |
| REINFORCE++ baseline | --algo.advantage_estimator reinforce_plus_plus_baseline(v0.10 新增) |
第 9 章会详讲这些算法的差别与公式。
3.7不用 reward model 的 demo
如果你的任务有 "可验证 reward"(数学题答案、代码单测),可以直接用自定义函数代替 RM。
仓库自带 examples/python/math_reward_func.py 是经典例子:
def reward_func(queries, prompts, labels, **kwargs) -> dict:
rewards = []
for query, prompt, label in zip(queries, prompts, labels):
response = query[len(prompt):]
pred = extract_boxed_answer(response)
is_correct = grade_answer(pred, label)
rewards.append(1.0 if is_correct else 0.0)
rewards_tensor = torch.tensor(rewards)
return {
"rewards": rewards_tensor,
"scores": rewards_tensor,
"extra_logs": {"math_accuracy": rewards_tensor.mean()},
}
跑这种"规则 reward + GRPO"的 demo(R1 风格训练):
bash examples/scripts/train_reinforce_baseline_hybrid_engine.sh
这个脚本默认用 Qwen3-4B-Thinking-2507 + dapo-math-17k,配合 examples/python/agent_func.py 做"single-turn agent"训练。第 12 章会完整复现。
3.8这章你需要带走的
- OpenRLHF 5 个 CLI 入口(sft / rm / dpo / ppo_ray / serve_rm),各管一段;
- RLHF 主循环跑在
train_ppo_ray,它自动起 Ray actor + vLLM engine; - "hybrid engine" = 所有角色 colocate 共享一组 GPU;
- 训练时盯 reward / kl / entropy / clipfrac 四件套;
- 换算法只需要改
--algo.advantage_estimator; - 有可验证 reward 时可以走自定义函数,省 RM 训练成本。