执行摘要
- 一句话:修复完全异步训练中部分rollout恢复时routed_experts拼接错误,确保路由专家与生成模型版本一致。
- 推荐动作:该PR值得精读,特别是对于从事异步训练和MoE模型开发的工程师。关注点包括:1) 如何在部分rollout中处理模型版本差异;2) 路由专家拼接的设计决策(切片而非替换);3) review中关于张量类型的讨论,展示了实际环境中数据类型的保证。
功能与动机
PR body指出,在完全异步训练中,模型权重可能在部分rollout迭代间更新,而之前#6030的修复简单替换了整个routed_experts序列,导致最新调用的路由专家反映的是新模型权重,而非实际生成早期token的旧模型。这会导致训练不稳定,表现为actor/ppo_kl指标波动剧烈。作者通过实验验证,保留每个迭代实际生成token的路由专家能使PPO KL散度更平滑。
实现拆解
- 导入依赖调整:在
verl/experimental/fully_async_policy/agent_loop/agent_loop.py中添加import torch,为后续张量拼接提供支持。
- 核心逻辑重构:修改
generate方法中routed_experts的处理逻辑:
- 当
output.routed_experts不为空且生成了新token时(len(output.token_ids) > 0)才进行处理。
- 如果是首次迭代(
final_output.routed_experts is None),则直接使用完整的output.routed_experts(覆盖prompt和第一批token)。
- 如果是后续迭代,则切片
output.routed_experts[-len(output.token_ids):]获取新生成token的路由专家,并通过torch.cat与现有的final_output.routed_experts拼接。
- 添加
len(output.token_ids) > 0的防护,避免[-0:]切片错误。
- 注释更新:将原注释替换为说明模型版本差异和保留每个迭代路由的原因。
- 无测试配套:由于变更需要MoE模型、异步部分rollout和多GPU环境,作者通过实验验证而非单元测试。
关键文件:
verl/experimental/fully_async_policy/agent_loop/agent_loop.py(模块 异步策略;类别 source;类型 core-logic;符号 generate): 这是唯一变更的文件,包含了修复routed_experts拼接逻辑的核心实现。
关键符号:generate
关键源码片段
verl/experimental/fully_async_policy/agent_loop/agent_loop.py
这是唯一变更的文件,包含了修复routed_experts拼接逻辑的核心实现。
async def generate(
self,
request_id: str,
prompt_ids: list[int],
sampling_params: dict,
image_data: Optional[list[Any]] = None,
video_data: Optional[list[Any]] = None,
) -> TokenOutput:
# ... 初始化 final_output 等代码 ...
while True:
# 1. 生成token
output = await super().generate(
request_id=request_id,
prompt_ids=prompt_ids + final_output.token_ids,
sampling_params=sampling_params,
image_data=image_data,
video_data=video_data,
)
# 2. 合并输出到final_output
final_output.token_ids.extend(output.token_ids)
if output.log_probs is not None:
final_output.log_probs.extend(output.log_probs)
# 在部分rollout恢复时,模型版本可能不同,因此保留现有路由,
# 仅追加新生成token的路由专家。
if output.routed_experts is not None and len(output.token_ids) > 0:
if final_output.routed_experts is None:
# 首次迭代:使用完整的routed_experts(覆盖prompt和第一批token)
final_output.routed_experts = output.routed_experts
else:
# 后续迭代:切片新生成token的路由专家并与现有路由拼接
final_output.routed_experts = torch.cat(
[final_output.routed_experts, output.routed_experts[-len(output.token_ids) :]],
dim=0,
)
if output.num_preempted is not None:
final_output.num_preempted += output.num_preempted
final_output.stop_reason = output.stop_reason
# ... 其余代码(更新max_new_tokens、检查停止原因等) ...
评论区精华
reviewer gemini-code-assist[bot] 建议使用 torch.as_tensor 确保输入为张量,避免 output.routed_experts 可能返回 numpy.ndarray 或列表导致的 TypeError。作者 NoonePauseferg 回复指出,在当前上下文中 routed_experts 始终是 torch 张量(sglang 和 vllm 引擎均返回张量),因此未采纳该建议。最终 wuxibin89 批准了 PR,但未进一步讨论。
- routed_experts拼接时的张量类型安全 (correctness): 作者未采纳建议,认为现有实现已保证类型安全。
风险与影响
- 风险:1. 回归风险:修改了
routed_experts 的拼接逻辑,如果切片索引计算错误或张量维度不匹配,可能导致下游路由重放(router replay)功能异常。
2. 性能风险:使用 torch.cat 拼接张量可能增加内存开销,但影响较小,因为 routed_experts 通常规模不大。
3. 兼容性风险:依赖 torch 导入,如果环境中未安装 torch 或版本不兼容,可能导致运行时错误。
4. 测试覆盖不足:PR body 指出由于需要复杂环境,未添加单元测试,仅通过实验验证,可能存在未覆盖的边缘情况。
- 影响:1. 对用户影响:使用完全异步训练、部分rollout和MoE模型的用户将受益于更稳定的训练过程,PPO KL散度波动减小。
2. 对系统影响:修复了路由专家与生成模型版本不一致的问题,提升了异步训练中路由重放的准确性。
3. 对团队影响:改进了 #6030 的修复,展示了在异步训练中模型权重更新对路由专家一致性的重要性,为后续类似问题提供参考。
- 风险标记:核心路径变更, 缺少测试覆盖, 依赖新增导入
关联脉络
- PR #6030 [fsdp, sft] feat: add Gemma4 FSDP SFT support: PR body 提到本 PR 是对 #6030 的改进,但 #6030 实际是关于 Gemma4 FSDP SFT 支持,可能引用错误。更相关的可能是历史 PR 中 #6029(修复完全异步策略中 routed_experts 问题),但未在上下文中明确提及。
- PR #6029 [fully_async] fix: replace routed_experts on partial rollout resume i…: 历史 PR 分析显示 #6029 修复了完全异步策略中部分rollout恢复时 routed_experts 重复拼接问题,与本 PR 主题高度相关,可能为同一问题线的后续修复。
参与讨论