源码导读:调用栈与文件地图
13.1 顶层目录地图
verl/
├── trainer/ 训练入口 + Controller 算法主控
│ ├── main_ppo.py ★ 默认启动入口(async 风格)
│ ├── main_ppo_sync.py ★ 同步 PPO 入口(TransferQueue / agent loop)
│ ├── main_eval.py 离线评测入口
│ ├── main_generation_server.py 独立 generation server
│ ├── sft_trainer.py / sft_trainer_ray.py SFT 单进程 / Ray 入口
│ ├── ppo/
│ │ ├── ray_trainer.py ★ RayPPOTrainer.fit() 主循环(1769 行)
│ │ ├── core_algos.py ★ 算法核心(13 个 advantage / 11 个 policy loss)
│ │ ├── metric_utils.py metrics 计算与聚合
│ │ ├── utils.py Role enum / need_reference_policy
│ │ └── ...
│ ├── distillation/ 在线蒸馏 loss
│ ├── config/ ★ Hydra YAML 层叠(algorithm/actor/critic/ref/rollout/...)
│ ├── constants_ppo.py PPO_RAY_RUNTIME_ENV
│ └── runtime_env.yaml Ray runtime 默认环境变量
├── single_controller/ Controller + WorkerGroup 抽象
│ ├── base/ 平台无关基类
│ │ ├── worker.py Worker 基类、@register 装饰器
│ │ ├── worker_group.py WorkerGroup / NaiveWorkerGroup / ResourcePool / ClassWithInitArgs
│ │ └── decorator.py Dispatch 枚举(8 种)+ dispatch/collect 函数
│ └── ray/ Ray 实现(RayResourcePool / RayWorkerGroup / func_generator)
├── workers/ Worker 实现
│ ├── engine/ 训练引擎抽象(EngineRegistry)
│ │ ├── base.py BaseEngine / EngineRegistry
│ │ ├── fsdp/ FSDPEngine / FSDPEngineWithLMHead
│ │ ├── megatron/ MegatronEngine{,WithLMHead,WithValueHead}
│ │ ├── torchtitan/ TorchTitanEngine
│ │ ├── veomni/ VeOmniEngine
│ │ ├── automodel/ AutomodelEngine(HF 直跑 / vexact)
│ │ └── mindspeed/ 华为昇腾
│ ├── rollout/ 采样后端
│ │ ├── base.py BaseRollout + _ROLLOUT_REGISTRY
│ │ ├── vllm_rollout/ vLLM ServerAdapter + BucketedWeightSender
│ │ ├── sglang_rollout/ SGLang ServerAdapter + PD 分离
│ │ └── trtllm_rollout/ TensorRT-LLM ServerAdapter
│ ├── reward_manager/ reward 计算
│ │ ├── abstract.py AbstractRewardManager
│ │ ├── registry.py REWARD_MANAGER_REGISTRY
│ │ ├── naive.py / batch.py / dapo.py / prime.py
│ ├── engine_workers.py ★ TrainingWorker / ActorRolloutRefWorker
│ ├── utils/losses.py ppo_loss / value_loss
│ └── config/ ★ ActorConfig / CriticConfig / RolloutConfig / EngineConfig
├── protocol.py ★ DataProto / DataProtoFuture / BatchData
├── base_config.py BaseConfig (frozen dataclass)
├── models/ transformer 模型实现(多模态 / Megatron 包装)
├── checkpoint_engine/ 多后端 checkpoint(nccl / hccl / kimi / mooncake / nixl)
├── model_merger/ 训完导出 HF 格式
├── third_party/ 第三方集成(vLLM patch 等)
├── tools/ 工具框架(BaseTool / tool_registry)
├── utils/ 通用工具 + reward_score + zero_to_fp32 等
├── experimental/ 2024-2026 新特性温床
│ ├── agent_loop/ 多轮工具调用 agent
│ ├── fully_async_policy/ 2025-08 全异步训练
│ ├── one_step_off_policy/ 一步 off-policy
│ ├── transfer_queue/ 零拷贝零 padding 数据通道
│ ├── reward_loop/ 在线 reward model
│ ├── teacher_loop/ 在线蒸馏 teacher
│ └── separation/ 架构变体
└── recipe/ 子模块:业界 recipe 集合(独立仓 verl-recipe)
13.2 五条主线代码
| 主线 | 核心文件 |
|---|---|
| 启动入口 | trainer/main_ppo.py |
| 控制主循环 | trainer/ppo/ray_trainer.py |
| 算法核心 | trainer/ppo/core_algos.py |
| Worker 实现 | workers/actor/dp_actor.py、workers/rollout/vllm_rollout.py |
| RPC 框架 | single_controller/ray/base.py |
13.3 从命令行到训练循环的调用栈
RayPPOTrainer(config)"] RT --> WG1["init_workers():
建 ActorRolloutRefWorkerGroup"] RT --> WG2["建 CriticWorkerGroup(PPO 时)"] RT --> WG3["建 RewardModelWorkerGroup(model reward 时)"] WG1 --> Init["WorkerGroup.init_model()
每个 worker 加载模型"] RT --> Fit["trainer.fit()"] Fit --> Loop["for epoch / for batch:
generate → reward → advantage → update"]
13.4 ray_trainer.py:算法主循环
这是 verl 最重要的一个文件。它的 fit() 函数大约 200-300 行,是算法工程师工作的主战场:
# 伪代码(接近真实结构)
def fit(self):
for epoch in range(total_epochs):
for batch in self.train_dataloader:
# 1. rollout:actor 生成 response
gen_output = self.actor_rollout_wg.generate_sequences(batch)
# 2. reference logp
ref_log_prob = self.actor_rollout_wg.compute_ref_log_prob(gen_output)
# 3. critic value(PPO only)
if self.use_critic:
values = self.critic_wg.compute_values(gen_output)
# 4. reward
reward_tensor = self.reward_fn(gen_output)
# 5. advantage(grpo / gae / rloo / ...)
gen_output = compute_advantage(
gen_output,
adv_estimator=self.config.algorithm.adv_estimator,
...
)
# 6. 训练 actor
for ppo_epoch in range(ppo_epochs):
self.actor_rollout_wg.update_actor(gen_output)
# 7. 训练 critic
if self.use_critic:
self.critic_wg.update_critic(gen_output)
# 8. 保存 / 评测
if step % save_freq == 0:
self._save_checkpoint(...)
if step % test_freq == 0:
self._evaluate(...)
整个 RLHF 训练循环就这 8 步。改算法 = 改这 8 步中的某一两步。
13.5 single_controller/:RPC 透明化的实现
| 文件 | 说明 |
|---|---|
base/worker.py | 所有 Worker 的基类,含 @register 装饰器 |
base/decorator.py | @register(dispatch_mode=...) 的实现 |
base/dispatch.py | 5 种 dispatch_mode(DP/All-to-all/...) |
ray/base.py | RayWorkerGroup + RayResourcePool |
ray/megatron.py | Megatron 的特殊 ResourcePool 打包 |
13.6 workers/actor/dp_actor.py:训练 actor 主循环
FSDP actor 的 update_actor() 大致结构:
def update_actor(self, data: DataProto) -> DataProto:
self._enter_train_mode() # weight resharding 回训练布局
for micro_batch in chunked(data):
# 前向:算 new logp
logits = self.model(micro_batch.input_ids)
log_prob = compute_log_prob(logits, micro_batch.labels)
# ratio
ratio = (log_prob - micro_batch.old_log_prob).exp()
# policy loss(PPO clip)
policy_loss = -torch.min(
ratio * micro_batch.advantages,
ratio.clamp(1-eps, 1+eps) * micro_batch.advantages,
).mean()
# KL loss(可选)
kl_loss = compute_kl_loss(log_prob, micro_batch.ref_log_prob)
loss = policy_loss + self.kl_loss_coef * kl_loss
loss.backward()
self.optimizer.step()
读懂这个函数 = 读懂 verl 训练核心。
13.7 workers/rollout/vllm_rollout.py:采样后端封装
| 方法 | 做什么 |
|---|---|
__init__ | 启动 vLLM LLM engine |
generate_sequences() | 从 DataProto 取 prompt → vLLM 生成 → 包成 DataProto |
sync_model_weights() | actor 权重 → vLLM 权重布局 |
offload_model_weights() | rollout 完,把 vLLM 内存释放 |
新接入一个采样后端 = 模仿这个文件写一个新的 SGLangRollout 或 MyRollout。
13.8 workers/sharding_manager/:训推权重对接
| 文件 | 说明 |
|---|---|
fsdp_vllm.py | FSDP ↔ vLLM TP 布局变换 |
megatron_vllm.py | Megatron ↔ vLLM 变换(含 EP) |
fsdp_sglang.py | FSDP ↔ SGLang |
遇到新模型架构 rollout 报形状错,多半要在这里加 special case。
13.9 trainer/ppo/core_algos.py:算法核心
| 函数 | 说明 |
|---|---|
compute_gae_advantage_return | GAE 经典实现 |
compute_grpo_outcome_advantage | GRPO 组内归一 |
compute_rloo_outcome_advantage | RLOO leave-one-out |
compute_reinforce_plus_plus_outcome_advantage | REINFORCE++ |
compute_policy_loss | PPO clip policy loss |
kl_penalty | kl / abs / mse / low_var_kl / full |
新算法 = 在这里加一个 compute_my_advantage + dispatch 一行 case。
13.10 阅读路线总结
- 跑通 ch03 单机 GRPO 例子:先有手感;
- 读
trainer/ppo/ray_trainer.py · fit():理解主循环 8 步; - 读
trainer/ppo/core_algos.py:理解算法侧; - 读
single_controller/ray/base.py:理解 RPC 透明化; - 读
workers/actor/dp_actor.py:理解训练实现; - 读
workers/rollout/vllm_rollout.py:理解推理对接; - 看一遍
workers/sharding_manager/fsdp_vllm.py:理解 hybrid engine 的"魔法"。
13.11 想给 verl 提 PR?从这些地方入手
| 改动类型 | 对应文件 |
|---|---|
| 新 advantage 算法 | trainer/ppo/core_algos.py 加函数 + dispatch |
| 新 reward 函数 | utils/reward_score/<new>.py + 注册到 __init__.py |
| 新采样后端 | workers/rollout/<new>_rollout.py |
| 新训练后端 | workers/engine/<new>_engine.py + 对应 actor/critic 实现 |
| 支持新模型架构 | workers/sharding_manager/ 加变换规则 |
| 新 worker 角色(如 evaluator) | 新建 workers/<new>_worker.py |
| 新数据集预处理 | examples/data_preprocess/<dataset>.py |
| 新配置字段 | trainer/config/ YAML + dataclass |
13.12 延伸阅读
| 资源 | 用途 |
|---|---|
GitHub verl-project/verl | 主仓库 |
GitHub verl-project/verl-recipe | R1 / DAPO 等业界 recipe |
GitHub verl-project/verl-omni | 扩散与多模态后训练 |
GitHub verl-project/vexact | "零失配" rollout |
| verl.readthedocs.io | 官方文档 |
| deepwiki.com/verl-project/verl | AI 生成的代码 wiki |
| HybridFlow 论文 | 理论基础 |
| 本目录 OpenRLHF 那本书 | 对比另一种 RLHF 框架设计 |
| 本目录 TRL 那本书 | 对比单机风格 RLHF |
13.13 RayPPOTrainer.fit() 真实代码地图
fit() 的伪代码 13.4 已经给过,下表是真实 line 锚点。基于 verl/trainer/ppo/ray_trainer.py(commit 9c38b8bb1876):
| 序 | 行号 | marker | 做什么 |
|---|---|---|---|
| 1 | 1361 | fit() 入口 | 整个 PPO 训练循环 |
| 2 | 1466 | "gen" | 调 async_rollout_manager.generate_sequences(),rollout |
| 3 | 1517 | "reward" | _compute_reward_colocate() 或 extract_reward(batch) |
| 4 | 1532–1541 | "old_log_prob" | bypass mode 复用 rollout logp;decoupled 调 _compute_old_log_prob() |
| 5 | 1577 | "RefPolicy" | _compute_ref_log_prob()(如果 use_reference_policy) |
| 6 | 1583 | "values" | _compute_values()(如果 use_critic,即 GAE) |
| 7 | 1587 | "adv" | compute_advantage(),按 13 种 estimator 之一 |
| 8 | 1599 | "apply_kl_penalty" | 如果 algorithm.use_kl_in_reward=True |
| 9 | 1615 | rollout IS 矫正 | decoupled mode 应用 importance ratio |
| 10 | 1624 | compute_advantage 实际调度 | dispatch 到具体 estimator 函数 |
| 11 | 1636 | "update_critic" | critic mini-batch 更新(如果 use_critic) |
| 12 | 1642 | critic warmup | warmup 阶段只更新 critic 不动 actor |
| 13 | ~1660 | "update_actor" | actor mini-batch 更新 |
| 14 | 1669–1670 | checkpoint 触发 | 步数到 / ESI 即将到期 |
| 15 | 1674 | checkpoint_manager.update_weights() | actor 权重同步到 rollout |
| 16 | 1711–1746 | metrics 聚合 | compute_data_metrics / timing_metrics / throughout_metrics / variance_proxy_metrics / spec_decode_metrics |
13.14 ActorRolloutRefWorker.update_weights() 完整路径
ch05.10 介绍过 update_weights 的两种 mode。源码在 verl/workers/engine_workers.py:659–738,按行号展开 naive 模式 5 步:
| 行 | 动作 |
|---|---|
| 689 | 判定 mode:naive vs 其他(checkpoint-engine 路径) |
| 699 | rollout.resume(tags=["weights"]) —— 唤醒 vLLM/SGLang server,腾出权重空间 |
| 703–704 | actor.engine.get_per_tensor_param(layered_summon, base_sync_done) 生成 (name, tensor) 流 |
| 709 | LoRA 时 sleep_level=1(base 留 GPU),否则 =2 |
| 721 | rollout.update_weights(per_tensor_param, peft_config=..., global_steps=...) |
| 734 | rollout.release() —— 释放 rollout 端的临时内存 |
对应到 vLLM ServerAdapter(vllm_rollout.py):
| 行 | 动作 |
|---|---|
| 61 | ServerAdapter 类定义 |
| 85–88 | 计算 replica_rank / rollout_rank |
| 91–95 | sleep_level 决策 |
| 108 | ZMQ IPC handle 协商 |
| 119 | _execute_method() —— Ray RPC dispatcher |
| 150–162 | resume() / release() 走 collective_rpc 到 vllm server |
| 165–192 | update_weights() 用 BucketedWeightSender 流式发权重 |
13.15 EngineRegistry 与所有训练后端的"找类"
"我用 strategy: fsdp,最后实例化的到底是哪个类?"答:
EngineRegistry._registry[model_type][backend][device]
常见组合的实际类(来自 verl/workers/engine/__init__.py:14–65):
| 用户角色 | strategy | 实际实例 |
|---|---|---|
| actor | fsdp | FSDPEngineWithLMHead |
| actor | megatron | MegatronEngineWithLMHead |
| critic | fsdp | FSDPEngineWithValueHead(若存在;否则 base FSDPEngine 加 value head wrapper) |
| critic | megatron | MegatronEngineWithValueHead |
| ref | fsdp / megatron | 同 actor 但 forward_only=True |
| actor | torchtitan | TorchTitanEngine |
| actor | veomni | VeOmniEngine(多模态) |
| actor | automodel | AutomodelEngine(vexact / 调试) |
13.16 跟着测试读 API
verl 自带丰富 unit tests,是"我想看这个 feature 怎么用最小化"的最快入口。下表是几份高密度的:
| 测试文件 | 示范了什么 |
|---|---|
tests/test_protocol.py | DataProto chunk / concat / select_idxs 全套用法 |
tests/single_controller/test_worker_group.py | WorkerGroup / Dispatch 各模式 |
tests/single_controller/test_register_decorator.py | @register 装饰器各 dispatch_mode 的真实行为 |
tests/workers/engine/test_fsdp_engine.py | FSDPEngine 构造 + train/eval batch 最小例 |
tests/workers/rollout/test_vllm_rollout.py | vLLM rollout 单独跑(绕过整个 trainer) |
tests/workers/reward_manager/test_naive_reward.py | 规则 reward 的样本流 |
tests/trainer/ppo/test_core_algos.py | 13 种 advantage estimator 的数值正确性 |
tests/tools/test_base_tool.py | BaseTool 接口 |
跑测试的标准命令:cd repos/verl && pytest tests/test_protocol.py -xv。-x 在第一个失败处停,-v 看每条 test 名字。
13.17 阅读路线(升级版)
整合本书所有锚点,给一个目标导向的 7 天阅读路线:
| 天 | 目标 | 主要文件 |
|---|---|---|
| D1 | 跑通最小 GRPO | ch03 步骤 + examples/grpo_trainer/run_qwen3_4b_fsdp.sh |
| D2 | 理解 controller 装配 | main_ppo.py:39 / 52 / 111 (TaskRunner) |
| D3 | 啃完主循环 | ray_trainer.py:1361 fit() + 13.13 line 表 |
| D4 | RPC 透明化机制 | single_controller/base/decorator.py:26 + ray/base.py:48 func_generator |
| D5 | worker 内的实际计算 | workers/engine_workers.py:76 TrainingWorker / 434 ActorRolloutRefWorker |
| D6 | 训推权重桥接 | workers/engine/base.py:150 get_per_tensor_param + workers/rollout/vllm_rollout/vllm_rollout.py:165 update_weights |
| D7 | 算法细节 + 新特性 | trainer/ppo/core_algos.py 13 个 estimator + 11 个 loss;扫一眼 experimental/ |
跑完这 7 天,verl 90% 的代码不再陌生。剩下 10%(多模态 / VLA / verl-omni)是 niche,到需要时再读对应子目录。
13.18 想给 verl 提 PR?升级版索引
| 改动类型 | 对应文件 / 注册点 |
|---|---|
| 新 advantage estimator | trainer/ppo/core_algos.py + @register_adv_est("name") |
| 新 policy loss | core_algos.py + @register_policy_loss("name") |
| 新 reward manager | workers/reward_manager/<name>.py + @register("name") |
| 新 reward 规则 | utils/reward_score/<name>.py + 在 __init__.py 加 data_source 映射 |
| 新 rollout 后端 | workers/rollout/<name>_rollout/ + 在 rollout/base.py:83 _ROLLOUT_REGISTRY 注册 |
| 新 training 后端 | workers/engine/<name>/ + 在 engine/__init__.py 导出 + EngineRegistry.register |
| 新 worker 角色 | workers/<new>_worker.py + trainer/ppo/utils.py Role enum |
| 新工具 | tools/<name>_tool.py 继承 BaseTool |
| 新模型支持(kernel inject) | third_party/vllm/ 或 third_party/sglang/ 加 patch |
| 新算法 recipe | verl-recipe 独立仓而非主仓 |
9c38b8bb1876 上是精确的;verl 高速演进,
关注 verl-project 组织下的新仓库(recipe / vexact / verl-omni)来跟进前沿。