强化学习训练流程解析:Long-CoT vs Delethink
整个流程可以看作一个清晰的"生成-评估-学习"循环,涉及多个核心文件。
第一步:配置与启动 (The Setup)
当运行一个训练脚本时,例如 examples/reproduce_rl_training/longcot_24k.sh,它最终会调用 verl/trainer/main_policy_iteration.py。
- 加载配置:
main_policy_iteration.py的核心作用是使用 Hydra 加载指定的配置文件。对于 Long-CoT,它会加载类似verl/trainer/config/r1d-1.5b_deepscaler.yaml这样的父配置文件。 - 启动训练器: 它会实例化并运行一个训练器。根据配置,这个训练器通常是
verl/trainer/ppo/ray_trainer.py中的RayPPOTrainer。注意,Delethink 有一个专门的子类RayTreetunePPOTrainer,但 Long-CoT 使用的是更基础的 PPO 训练器。
第二步:生成思考链 (Rollout - State & Action)
这是 RL 循环的核心,也是体现 Long-CoT 特点的地方。
- 训练循环入口: 训练的核心逻辑在
verl/trainer/ppo/ray_trainer.py的fit()方法中。 - 生成数据: 在
fit()方法的循环里,您会看到这样一行关键代码:
# verl/trainer/treetune_ppo/ray_trainer.py:267
gen_batch_output = self.actor_rollout_wg.generate_sequences(gen_batch)
这行代码通过 Ray 的工作组(actor_rollout_wg)调用 generate_sequences 方法,让 Actor 模型(也就是我们的 LLM 策略)根据输入的 prompt gen_batch 来生成思考链。
-
Rollout Worker 的实现: 这个调用最终会由
verl/workers/rollout/sglang_rollout/sglang_rollout.py中的SGLangRolloutWorker来处理。- 核心函数:
generate_sequences
- 核心函数:
- 关键逻辑: 在这个函数内部,它会调用 SGLang 引擎来进行文本生成:
# verl/experimental/agent_loop/delethink_agent_loop.py:111 (逻辑类似)
response: dict[str, Any] = await self.server_manager.generate(
request_id=...,
prompt_ids=prompt_ids.tolist(), # <--- 这里是关键
sampling_params=sampling_params,
)
- 状态的体现: 这里的
prompt_ids就是 Long-CoT RL 中"状态"的直接代码体现。对于 Long-CoT 来说,这个prompt_ids包含了初始问题和所有已经生成的思考文本。SGLang 引擎会处理这个完整的、越来越长的序列。 - 动作的体现:
self.server_manager.generate的执行过程就是"动作",它会生成一个新的文本片段(response_ids)。
- 二次方复杂度的根源: 在 Long-CoT 模式下,如果模型需要进行多轮思考(虽然在这个项目的实现中,一次
generate_sequences调用通常会生成完整的轨迹直到 EOS),那么下一次调用的输入将会是prompt_ids + response_ids。序列的长度不断增加,导致 SGLang 引擎内部的注意力计算和 KV Cache 呈二次方增长。这就是 OOM 错误的直接代码根源。
第三步:计算奖励 (Reward)
生成完成后,我们需要评估这个思考链的质量。
- 奖励计算入口: 回到
verl/trainer/ppo/ray_trainer.py的fit()方法中,在生成gen_batch_output之后,代码会调用奖励计算函数:
# verl/trainer/treetune_ppo/ray_trainer.py:315
reward_tensor, reward_extra_infos_dict = compute_reward(batch, self.reward_fn)
- 奖励函数的定义:
self.reward_fn是从哪里来的?它是由配置文件verl/trainer/config/r1d-1.5b_deepscaler.yaml指定的:
custom_reward_function:
path: verl/utils/reward_score/treetune_math_verify.py
- 奖励函数的实现: 这就指向了
verl/utils/reward_score/treetune_math_verify.py。这个文件里的compute_score函数就是实际的奖励函数。- 核心逻辑:
- 它接收模型生成的完整文本
solution_str。 - 使用
parse_predicted_answer_fn从文本中解析出\boxed{}里的最终答案。 - 使用
verify函数将模型答案与ground_truth进行数学等价 性比较。 - 返回 1.0(正确)或 0.0(错误)。这就是稀疏的、终端的奖励信号。
- 它接收模型生成的完整文本
- 核心逻辑:
第四步:学习与更新 (PPO)
拿到奖励后,模型需要根据这个反馈进行学习。
- 计算优势 (Advantage): 在
fit()方法中,紧接着奖励计算,代码会调用compute_advantage函数。
# verl/trainer/treetune_ppo/ray_trainer.py:393
batch = compute_advantage(
batch,
adv_estimator=self.config.algorithm.adv_estimator, # 通常是 'gae'
...
)
这个函数(位于 verl/trainer/ppo/ray_trainer.py)使用 GAE (Generalized Advantage Estimation) 算法,根据刚刚计算出的奖励,为整个生成序列中的每一步(每个 token)估算一个"优势值",告诉模型哪些动作是"好"的,哪些是"