verl 整体架构:HybridFlow、single-controller、RayWorkerGroup 和 DataProto 边界
verl 是面向 LLM post-training 的 RL 训练框架。它最值得学习的地方,不只是 PPO/GRPO 的公式,而是怎样把复杂 RL 控制流、分布式训练 worker、高吞吐 rollout engine、reward 计算、配置系统和 checkpoint/weight sync 放进同一条可扩展的数据流里。
先验知识
读架构前,需要先建立几个概念:
- Ray 可以把 Python class 变成远端 actor。driver 调用
actor.method.remote(),真正执行发生在远端进程。 - LLM 训练 worker 往往不是一个普通 Python 对象,而是一组 GPU 进程,内部还有 data parallel、tensor parallel、pipeline parallel。
- RL post-training 的控制流比 SFT 更复杂。一次 step 包含生成、打分、logprob、value、advantage、更新和权重同步。
- verl 选择让一个 driver 写“算法控制流”,把重计算派发给远端 worker。这就是 single-controller 的直觉。
DataProto是 driver 和 worker 之间的主要数据边界。worker 内部可转成TensorDict或更底层结构。
本页原先不适合小白的地方
原说明给了架构大图,但还缺这些小白入口:
- 没有解释“不懂 Ray 也能读”的最低模型。初学者需要先知道 worker group 是“远端多 worker 的本地代理”。
- HybridFlow 容易被误解成某个具体类。它更像一种设计思想:单进程表达 RL 控制流,多进程执行模型计算。
@register、dispatch、collect、RayWorkerGroup的链条需要连到 trainer 的actor_rollout_wg.compute_log_prob(batch)。- DataProto 边界需要明确:driver 用 DataProto 组织样本,worker 内部常用 TensorDict 和 engine-specific batch。
一句话架构
一个 Python driver 负责写训练逻辑;single_controller 把 driver 上的 wg.method(batch) 转成多 Ray worker 的远端调用;actor/critic/ref/reward/rollout 角色由 worker group 承载;DataProto 负责跨模块传 batch;checkpoint engine 负责把训练后的 actor 权重同步给 rollout server。
Hydra config
-> main_ppo.py TaskRunner
-> RayPPOTrainer
-> RayWorkerGroup / RewardLoopManager / LLMServerManager
-> DataProto flows through rollout, reward, logprob, value, update
-> CheckpointEngineManager syncs actor weights to rolloutHybridFlow 的直觉
docs/hybrid_flow.rst 把 RL 训练拆成两层 flow:
- control flow:先 rollout,再 reward,再 advantage,再 update。这是 RL 算法逻辑。
- computation flow:每个阶段内部的模型 forward/backward/optimizer step。这通常是多 GPU 程序。
verl 的选择是:control flow 留在一个 driver 进程里表达,computation flow 交给分布式 worker 执行。
这带来的好处:
- 新算法可以主要改 trainer loop,不必重写 FSDP/Megatron/vLLM/SGLang 的内部。
- 同一套 trainer 控制流可以复用不同模型训练后端。
- 调试时可以在 driver 上看 batch 字段如何一步步长出来。
代价:
- driver 和 worker 之间要搬数据。
- 每个 worker 方法都要声明 dispatch/collect 规则。
- DataProto 字段必须严格对齐,否则跨进程问题很难查。
不懂 Ray 时怎么理解 RayWorkerGroup
你可以先把 RayWorkerGroup 理解成:
它是 driver 本地的代理对象,背后管理 N 个远端 Ray actor。你调用一次
wg.method(batch),它负责把 batch 切成 N 份,发给 N 个 worker,等结果回来再拼成一个 batch。
真实链路是:
trainer 调用 actor_rollout_wg.compute_log_prob(batch_td)
-> RayWorkerGroup 上动态绑定的 compute_log_prob
-> dispatch_fn 切分参数
-> execute_fn 调用每个 Ray actor 的远端方法
-> collect_fn 拼接返回结果
-> trainer 得到 TensorDict/DataProto 结果@register 做了什么
worker 方法上会有装饰器:
@register(dispatch_mode=make_nd_compute_dataproto_dispatch_fn(mesh_name="actor"))
def compute_log_prob(self, data: TensorDict) -> TensorDict:
...@register 本身不执行远端调用。它只是给方法挂上元数据:
{
"dispatch_mode": ...,
"execute_mode": ...,
"blocking": ...
}WorkerGroup._bind_worker_method() 扫描 worker class,发现带 MAGIC_ATTR 的方法,就把它绑定成 worker group 上的同名方法。真正执行远端调用的是 RayWorkerGroup.func_generator() 生成的函数。
dispatch/execute/collect 三步
以 Dispatch.DP_COMPUTE_PROTO 为例:
- dispatch:
DataProto.chunk(world_size),把全局 batch 切给每个 worker。 - execute:
RayWorkerGroup.execute_all(),对每个 Ray actor 调用远端方法。 - collect:
DataProto.concat(output),把 worker 输出拼回全局 batch。
新源码里很多训练方法用的是:
make_nd_compute_dataproto_dispatch_fn(mesh_name="actor")
make_nd_compute_dataproto_dispatch_fn(mesh_name="ref")
make_nd_compute_dataproto_dispatch_fn(mesh_name="train")这比简单按 world_size 切更细:worker 先注册自己在某个 mesh 下的 data parallel rank,dispatch 时按 mesh 的 DP rank mapping 切数据,collect 时只从应该产出结果的 rank 收集。
worker role
RayPPOTrainer 里常见角色:
| role | 职责 |
|---|---|
| ActorRollout / ActorRolloutRef | actor 训练、rollout 权重源、可选 reference |
| Critic | value model 训练和推理 |
| RefPolicy | 冻结 reference policy,某些配置下单独存在 |
| RewardModel | 模型 reward 的资源池或 worker |
| TeacherModel | distillation 路径的 teacher |
当前统一模型引擎里:
ActorRolloutRefWorker可以组合 actor、rollout、ref、checkpoint engine。TrainingWorker承载 critic 和 reward model 一类训练/推理 worker。RewardLoopManager负责 reward 计算流程,可能使用规则函数、模型或远端/沙盒逻辑。LLMServerManager和AgentLoopManager负责 async rollout 生成。
colocated hybrid worker
init_workers() 里会把同一 resource pool 上的多个 role 通过 create_colocated_worker_cls(class_dict=...) 组合,再 wg_dict.spawn(prefix_set=...) 拆出不同 role 的 worker group 代理。
直觉上:
同一批 Ray actor 进程里可以暴露 actor、rollout、ref 等多种方法
driver 仍然拿到 actor_rollout_wg / ref_policy_wg 等不同代理这减少了重复 CUDA/distributed context,也方便 actor 到 rollout 的快速权重同步。但它也带来显存生命周期复杂度,所以你会在 trainer 里看到:
self.checkpoint_manager.sleep_replicas()
self.checkpoint_manager.update_weights(global_steps)这些动作本质上是在训练阶段和 rollout 阶段之间协调显存和权重。
DataProto 边界
verl 的数据边界可以这样看:
driver / trainer:
DataProto
worker group dispatch:
DataProto 或 TensorDict 被 chunk 到 worker
worker 内部:
TensorDict
no-padding/padding 转换
engine-specific batch
worker 返回:
TensorDict 或 DataProto
collect 后回到 driverray_trainer.py 里 _compute_old_log_prob() 是最好的例子:
batch_td = batch.to_tensordict()
batch_td = left_right_2_no_padding(batch_td)
output = self.actor_rollout_wg.compute_log_prob(batch_td)
log_probs = no_padding_2_padding(tu.get(output, "log_probs"), batch_td)
old_log_prob = DataProto.from_tensordict({"old_log_probs": log_probs})所以不要把 DataProto 当成模型内部格式。它更像跨模块协议。
和普通 PyTorch 训练的区别
普通 SFT:
dataloader -> model forward -> loss -> backward -> optimizer stepverl PPO/GRPO 主路径:
dataloader
-> rollout generate
-> reward
-> old/ref logprob
-> value
-> advantage
-> critic update
-> actor update
-> rollout weight sync多出来的每一步都可能跨进程、跨 GPU、跨推理服务。verl 的架构就是为这些边界服务的。
源码实现怎么读
推荐路线:
- 先读
docs/hybrid_flow.rst,理解 control flow 和 computation flow 为什么拆开。 - 读
docs/single_controller.rst,用generate_sequences的例子理解@register -> bind -> dispatch -> execute -> collect。 - 读
verl/single_controller/base/decorator.py,看Dispatch、register()、make_nd_compute_dataproto_dispatch_fn()。 - 读
verl/single_controller/base/worker_group.py,看_bind_worker_method()。 - 读
verl/single_controller/ray/base.py,看RayWorkerGroup._init_with_resource_pool()、spawn()、execute_all_async()、func_generator()。 - 读
verl/workers/engine_workers.py,看 worker 如何注册actor、ref、trainmesh,并暴露compute_log_prob()、update_actor()、train_mini_batch()。 - 回到
verl/trainer/ppo/ray_trainer.py,把init_workers()和fit()串起来。
本节参考与延伸阅读
- 源码:
verl/trainer/ppo/ray_trainer.py,重点读RayPPOTrainer.init_workers()和fit()。 - 源码:
verl/single_controller/base/decorator.py、worker_group.py、worker.py。 - 源码:
verl/single_controller/ray/base.py。 - 源码:
verl/workers/engine_workers.py、verl/protocol.py。 - 官方 docs:
docs/hybrid_flow.rst、docs/single_controller.rst、docs/examples/ppo_code_architecture.rst、docs/workers/ray_trainer.rst。 - 论文/网页:HybridFlow: A Flexible and Efficient RLHF Framework, arXiv:2409.19256;Ray 官方文档中 actor 和 placement group 相关章节。