Chapter 13
源码导读:Trainer / Strategy / Engine 三层抽象
本章给你一张"哪个文件做什么"的源码地图,把 OpenRLHF 的整套架构剥开。 目标是让你在给 OpenRLHF 提 PR 时知道改哪里。
13.1顶层目录地图
OpenRLHF/
├── openrlhf/ ★ 主代码
│ ├── cli/ 命令行入口(5 个)
│ │ ├── train_sft.py SFT 训练
│ │ ├── train_rm.py Reward Model 训练
│ │ ├── train_dpo.py DPO / IPO / SimPO 等
│ │ ├── train_ppo_ray.py ★ PPO / GRPO / RLOO 等 RL 训练
│ │ ├── serve_rm.py 把 RM 起成 HTTP service
│ │ └── lora_combiner.py merge LoRA 权重
│ ├── trainer/ 训练逻辑
│ │ ├── sft_trainer.py SFT loop
│ │ ├── rm_trainer.py RM loop
│ │ ├── dpo_trainer.py DPO loop
│ │ ├── ppo_trainer.py ★ PPO loop(单进程版,被 RayActor 调用)
│ │ ├── ppo_trainer_async.py 异步版本(multi-turn agent 用)
│ │ ├── ppo_utils/ experience / KL / replay buffer 等
│ │ └── ray/ ★ Ray actor 封装
│ │ ├── launcher.py RayActorGroup / BaseDistributedActor
│ │ ├── ppo_actor.py actor 端 Ray actor 实现
│ │ ├── ppo_critic.py critic 端 Ray actor 实现
│ │ ├── vllm_engine.py ★ vLLM rollout 的 Ray actor
│ │ └── vllm_worker_wrap.py vLLM worker 内部 patch
│ ├── models/ 模型实现
│ │ ├── actor.py Actor 类(包 HF 模型)
│ │ ├── model.py get_llm_for_sequence_regression(RM / Critic 用)
│ │ ├── loss.py ★ PolicyLoss / ValueLoss / DPOLoss
│ │ ├── ring_attn_utils.py Ring Attention 实现
│ │ └── utils.py
│ ├── datasets/ 数据加载
│ │ ├── prompts_dataset.py PPO 用的 prompt dataset
│ │ ├── reward_dataset.py RM 训练用的 preference dataset
│ │ ├── sft_dataset.py SFT 用
│ │ └── utils.py
│ └── utils/ 工具
│ ├── deepspeed/ DeepspeedStrategy + offload
│ ├── distributed_util.py stateless NCCL group
│ ├── logging_utils.py
│ └── ...
├── examples/
│ ├── scripts/ 20+ 个生产级启动脚本
│ └── python/ 自定义 reward / agent 范例
├── tests/ 单元测试
├── docs/
├── dockerfile
├── requirements.txt
├── pyproject.toml
└── setup.py
13.2五条主线代码
| 主线 | 核心文件 |
|---|---|
| 入口 + 参数解析 | cli/train_ppo_ray.py |
| Ray 编排 | trainer/ray/launcher.py |
| 主循环 | trainer/ppo_utils/experience_maker.py |
| PPO 训练 step | trainer/ray/ppo_actor.py + trainer/ppo_trainer.py |
| vLLM 集成 | trainer/ray/vllm_engine.py |
13.3从命令行到训练循环的调用栈
flowchart TB
CLI["bash train_ppo_ray_hybrid_engine.sh"]
CLI --> Py["python -m openrlhf.cli.train_ppo_ray <一长串 --args>"]
Py --> Main["train_ppo_ray.py · train(args)"]
Main --> Init["ray.init()
初始化 Ray 集群"] Main --> Groups["创建 4 个 RayActorGroup
(actor / ref / critic / reward)
+ 若干 vLLMEngine"] Groups --> EM["新建 RemoteExperienceMaker
(experience_maker.py)"] Main --> Loop["for episode / for step:"] Loop --> ME["experience = EM.make_experience(prompts)"] ME --> R1["actor → vllm 同步权重"] ME --> R2["vllm.generate() → responses"] ME --> R3["ref/reward/critic 并行 forward"] ME --> R4["KL / advantage 计算 (kl_controller.py)"] Loop --> Train["actor_group.training_step(experience)
critic_group.training_step(experience)"] Train --> PA["ppo_actor.py · DeepspeedPPOActor.training_step()"] PA --> PT["ppo_trainer.py · PolicyLoss + backward"]
初始化 Ray 集群"] Main --> Groups["创建 4 个 RayActorGroup
(actor / ref / critic / reward)
+ 若干 vLLMEngine"] Groups --> EM["新建 RemoteExperienceMaker
(experience_maker.py)"] Main --> Loop["for episode / for step:"] Loop --> ME["experience = EM.make_experience(prompts)"] ME --> R1["actor → vllm 同步权重"] ME --> R2["vllm.generate() → responses"] ME --> R3["ref/reward/critic 并行 forward"] ME --> R4["KL / advantage 计算 (kl_controller.py)"] Loop --> Train["actor_group.training_step(experience)
critic_group.training_step(experience)"] Train --> PA["ppo_actor.py · DeepspeedPPOActor.training_step()"] PA --> PT["ppo_trainer.py · PolicyLoss + backward"]
13.4cli/train_ppo_ray.py:入口
这是所有 RL 训练命令的真正入口。它做几件事:
- 解析数百个
--xxxflag; - 初始化 Ray;
- 按用户配置创建 RayActorGroup(一个 group 对应一个角色);
- 创建 vLLM engines;
- 调
trainer.fit()开始主循环。
# 简化版
def train(args):
ray.init()
# 按 args 起 4 个角色(按 colocate 配置决定是否合并 PG)
actor_group = RayActorGroup(args.actor.num_nodes, args.actor.num_gpus_per_node, PPOActor)
ref_group = RayActorGroup(args.ref.num_nodes, args.ref.num_gpus_per_node, ReferenceActor)
critic_group = RayActorGroup(args.critic.num_nodes, args.critic.num_gpus_per_node, CriticActor)
reward_group = RayActorGroup(args.reward.num_nodes, args.reward.num_gpus_per_node, RewardActor)
# 起 vLLM
vllm_engines = [LLMRayActor.remote(...) for _ in range(args.vllm.num_engines)]
# 初始化模型(每个 group 各自加载)
refs = []
refs.extend(actor_group.async_init_model_from_pretrained(args))
refs.extend(ref_group.async_init_model_from_pretrained(args))
refs.extend(critic_group.async_init_model_from_pretrained(args))
refs.extend(reward_group.async_init_model_from_pretrained(args))
ray.get(refs)
# 建 actor ↔ vllm 的 NCCL group
setup_weight_sync(actor_group, vllm_engines)
# 跑主循环
experience_maker = RemoteExperienceMaker(actor_group, critic_group, reward_group, ref_group, ...)
for episode in range(args.train.num_episodes):
for step in range(args.train.steps_per_episode):
experience = experience_maker.make_experience(prompts)
actor_group.async_run_method("training_step", experience)
critic_group.async_run_method("training_step", experience)
if step % args.ckpt.save_steps == 0:
save_checkpoint(...)
13.5trainer/ray/launcher.py:RayActorGroup 实现
| 类 | 角色 |
|---|---|
BaseDistributedActor | 所有 Ray actor 的基类,把 PyTorch distributed 信息塞进环境变量 |
RayActorGroup | N 个 actor 的包装,create placement group + 起 N 个 Ray actor |
读这个文件能理解"角色 → placement_group → N 个 Ray actor"是怎么落地的。
13.6trainer/ray/ppo_actor.py:训练 actor 主逻辑
这是 OpenRLHF 中最重要的一个文件。每个 RayActorGroup 里的 actor 都是这个类的实例。它的关键方法:
| 方法 | 做什么 |
|---|---|
init_model_from_pretrained() | 加载 HF 模型 + DeepSpeed engine 初始化 |
training_step(experience) | PPO 一步训练(forward + loss + backward + step) |
forward(sequences) | 算 log_prob(ref / actor 都用这个) |
broadcast_to_vllm() | 把当前权重 broadcast 到 vLLM |
save_checkpoint() | 分布式 save |
offload_states() / reload_states() | colocate 时的 sleep 接口 |
13.7trainer/ppo_utils/experience_maker.py:主循环
这里是"actor / ref / reward / critic 协同"真正发生的地方。RemoteExperienceMaker.make_experience() 大致:
def make_experience(self, prompts):
# 1. actor 权重同步到 vllm
self.actor_model_group.async_run_method("broadcast_to_vllm")
# 2. vllm rollout
sequences = []
for engine in self.vllm_engines:
result = engine.generate.remote(prompts)
sequences.append(ray.get(result))
sequences = concat(sequences)
# 3. 并行调 ref / reward / critic
ref_logp_ref = self.initial_model_group.async_run_method("forward", sequences)
reward_ref = self.reward_model_group.async_run_method("forward", sequences)
value_ref = self.critic_model_group.async_run_method("forward", sequences)
actor_logp_ref = self.actor_model_group.async_run_method("forward", sequences)
# 4. 等所有结果
ref_logp, reward, value, actor_logp = ray.get([ref_logp_ref, reward_ref, value_ref, actor_logp_ref])
# 5. 算 advantage + KL
advantages, returns = compute_advantages(
rewards, values, ref_logp, actor_logp,
gamma=self.gamma, lam=self.lam,
adv_estimator=self.adv_estimator,
)
return Experience(sequences, ref_logp, actor_logp, reward, value, advantages, returns)
13.8trainer/ray/vllm_engine.py:vLLM 集成
| 类 / 方法 | 作用 |
|---|---|
LLMRayActor | vLLM engine 的 Ray Actor 包装 |
LLMRayActor.generate() | 调 vllm.LLM.generate |
LLMRayActor.init_weight_update_group() | 建 actor ↔ vllm 的 NCCL group |
LLMRayActor.update_weight() | 接收 actor 广播过来的权重 |
LLMRayActor.sleep() / wake_up() | sleep mode |
vllm_worker_wrap.py 是注入到 vLLM 内部 worker 的 patch,让它能接收 NCCL broadcast 的权重。
13.9models/loss.py:所有 loss 函数
| 类 | 用途 |
|---|---|
PolicyLoss | PPO clipped objective |
ValueLoss | critic value regression loss |
DPOLoss | DPO + IPO + SimPO 系 |
SwitchBalancingLoss | MoE 负载均衡(MoE 训练用) |
新增算法(比如 GSPO / ORPO)从这里开始,加一个新的 loss class + 在 dispatch 处加 case。
13.10models/actor.py:模型抽象
OpenRLHF 的 Actor 类是 HuggingFace 模型的一层封装:
- 构造时按
--actor.model_name_or_path加载 HF 模型; forward(input_ids, attention_mask) -> (logits, logp)是核心方法;- 支持 packing samples + position_ids 跨样本边界处理;
- 支持 ring_attn / flash_attn / fused kernel。
Reference 和 Actor 用同一个类,区别只是 reference 全程 requires_grad=False。
13.11utils/deepspeed/:训练后端
| 文件 | 角色 |
|---|---|
DeepspeedStrategy | 统一的训练策略入口 |
deepspeed_utils.py | offload / reload states 等工具 |
deepspeed_config.py | 动态生成 ds_config(从 --ds.* flag) |
13.12阅读路线总结
- 跑通 ch03 单机 demo:建立感觉;
- 读
cli/train_ppo_ray.py:理解入口; - 读
trainer/ray/launcher.py:理解 RayActorGroup; - 读
trainer/ppo_utils/experience_maker.py · make_experience():理解主循环; - 读
trainer/ray/ppo_actor.py · training_step():理解训练实现; - 读
trainer/ray/vllm_engine.py:理解 vLLM 集成; - 读
models/loss.py:理解 PPO loss 实现; - 看
trainer/ppo_utils/kl_controller.py:理解自适应 KL; - 动手提一个 PR(哪怕修文档)。
13.132025–2026 新增子模块速查
13.1 给的目录树偏简化。下面是基线 commit 上有但容易忽略的新增子模块:
| 文件 / 目录 | 关键符号 (file:line) | 对应章节 |
|---|---|---|
openrlhf/trainer/ppo_trainer_async.py | 异步 PPO 主循环(与 ppo_trainer.py 镜像但 generate / train 并行) | ch08.5 / ch11.9 |
openrlhf/utils/agent.py | AgentExecutorBase:31 / AgentInstanceBase / MultiTurnAgentExecutor | ch10.13 |
openrlhf/utils/vlm_utils.py | VLM 图像加载、processor、像素张量 | ch10.14 |
openrlhf/utils/distributed_util.py | stateless_init_process_group() 给 vLLM weight sync 用 | ch06.11 |
openrlhf/utils/seqlen_balancing.py | 序列长度均衡(动态 batch 配套) | ch11.11 |
openrlhf/utils/loss_utils.py | get_loss_batch_info() 动态 batch 时 token 数统计 | ch11.11 |
openrlhf/models/ring_attn_utils.py | patch_transformers_for_ring_flash_attn:9 + reset_ring_attn_position_ids:32 | ch07.13 / ch11.12 |
13.147 个 loss 类的完整对照
ch13.9 提了 4 个核心 loss。openrlhf/models/loss.py 实际共有 7 个 loss 类:
| 类 | 用途 | 核心公式 / 特征 |
|---|---|---|
GPTLMLoss | 预训练 / SFT 因果 LM | CrossEntropyLoss(logits, labels),含 Ring Attention 兼容 |
SFTLoss | SFT 微调(支持 packing) | -per_token_logps,token / sequence 级聚合 |
PolicyLoss | PPO actor loss | min(ratio·adv, clamp(ratio)·adv),支持 PPO / GSPO / DualClip + vLLM IS 矫正 |
ValueLoss | PPO critic loss | 0.5·(values - returns)²,可选 value clip |
PairWiseLoss | Reward Model | -logsigmoid(chosen - rejected - margin),Bradley-Terry |
LogExpLoss | RM 变种 | log(1 + exp(rejected - chosen))(arXiv:2204.05862) |
DPOLoss | DPO / IPO | beta · (pi_logratios - ref_logratios),label_smoothing + IPO 变体 |
13.15建议阅读升级版(7 天路线)
| 天 | 目标 | 主要文件 |
|---|---|---|
| D1 | 跑通 ch03 最小 PPO demo | train_ppo_ray_hybrid_engine.sh |
| D2 | 读 controller 入口 | cli/train_ppo_ray.py + 9 大 args group 拆解 |
| D3 | 读 Ray 抽象 | trainer/ray/launcher.py:202 RayActorGroup + duplicate_actors |
| D4 | 读主循环 | trainer/ppo_utils/experience_maker.py:make_experience() |
| D5 | 读训练 step | trainer/ray/ppo_actor.py:training_step() + trainer/ppo_trainer.py |
| D6 | 读 vLLM 集成 | trainer/ray/vllm_engine.py + vllm_worker_wrap.py 注入 |
| D7 | 读算法 + agent | models/loss.py + utils/agent.py + ring_attn_utils |
13.16想给 OpenRLHF 提 PR?从这里入手
| 改动类型 | 对应文件 |
|---|---|
| 新 advantage estimator | trainer/ppo_utils/experience_maker.py 加 case |
| 新 loss 函数 | models/loss.py |
| 新 reward 来源类型 | trainer/ppo_utils/experience_maker.py + cli/train_ppo_ray.py |
| 新模型架构 | models/actor.py + models/model.py |
| 新数据格式 | datasets/prompts_dataset.py |
| 新采样后端(SGLang / TRT-LLM) | 仿照 trainer/ray/vllm_engine.py 写一个 |
| 新训练后端 | utils/deepspeed/ 替换或并行加 |
| 新部署模式 | trainer/ray/launcher.py placement group 策略 |
| 新 CLI flag | cli/train_ppo_ray.py args 注册 |
13.14延伸阅读
| 资源 | 用途 |
|---|---|
GitHub OpenRLHF/OpenRLHF | 主仓库 |
| openrlhf.readthedocs.io | 官方文档 |
| DeepWiki | AI 生成的代码 wiki |
| Tech Report | 系统论文 |
| verl 那本书 | 对比另一种 RLHF 框架设计(HybridFlow) |
| TRL 那本书 | 对比单机风格 RLHF |
| DeepSpeed 那本书 | OpenRLHF 训练后端的本体 |
恭喜
到这里你已经读完了 OpenRLHF 中文 tutorial 的全部 13 章。
这本书在 commit
6c6056daa522 上是精确的,对应 OpenRLHF v0.10.3。
这套框架仍在快速演进,关注新增的 VLM RLHF / Multi-Turn Agent / REINFORCE++-baseline 等方向。