中文内容
vLLM V0 到 V1:RL 中先确保正确性,再进行修正
迁移目标 失败模式 V1 后端修复 Logprob 语义 运行时默认值 飞行中权重更新 剩余差距:fp32 lm_head 消融实验 为什么我们首先修复后端正确性 PipelineRL 使用 vLLM 作为生成 rollout 的推理引擎。推理引擎对 token 进行采样并返回 token logprobs;训练器使用这些 logprobs 来计算策略比率、KL、裁剪率、熵和奖励。logprobs 计算方式中的任何差异都可能改变训练动态。这就是我们在从 vLLM V0 迁移到 V1 期间需要消除的训练-推理不匹配。
TL;DR。vLLM V1 在我们修复四项内容后,与我们的 vLLM V0 参考版本相匹配:处理后的 rollout logprobs、V1 特定的运行时默认值、飞行中权重更新路径,以及用于最终投影的 fp32 lm_head。我们先修复了后端行为,然后才更改 RL 目标。
参考运行使用 vLLM 0.8.5;V1 运行使用 vLLM 0.18.1。图 1 展示了最终结果。红色曲线是最初的 V1 尝试,绿色曲线是经过下文所述修复后的最终 V1 运行。
迁移目标
vLLM V1 是对 V0 引擎的大规模重写。因此,我们的迁移目标刻意限定得很窄:
- 验证 V1 是否以训练器预期的形式返回 rollout logprobs
- 针对 V0 参考版本重新运行相同的工作负载
- 仅在后端一致性恢复后评估目标层面的变化
最先可见的症状出现在:
- 正文:clamp_log_ratio_new_old_indicator
- 正文:kl_new_old
- 熵
- 奖励
这些指标来自一次 GSPO 训练运行,这是本实验所使用的目标函数。同一类不匹配也可能出现在 PPO、GRPO,或任何将 rollout 侧 logprobs 作为优化目标一部分的在线 RL 系统中。
初始的 V1 运行清楚地显示了这个问题。训练器侧的 logprobs 和 reward 在训练早期就偏离了 V0 参考。
训练器指标中也出现了相同的模式。在初始比较中,裁剪率是最容易读取的信号。
失效模式
我们将可能的原因分为三层:
- 语义不匹配:后端返回的 logprobs 与训练器预期的含义不同。
- 推理路径不匹配:后端在缓存、调度或请求处理方面使用不同的运行时默认设置,因此相同的提示会遵循不同的执行路径。
- 目标不匹配:RL 目标需要针对仍然存在的陈旧程度或后端不匹配进行校正。
我们最初过早地怀疑了第三类问题。有用的诊断来自于先将前两类视为后端行为问题,并首先将它们排除。
V1 后端修复
Logprob 语义
第一个问题是语义层面的。vLLM V1 默认从原始模型输出返回 logprobs,也就是在温度缩放、惩罚项以及 top-k/top-p 过滤等 logits 后处理之前。PipelineRL 期望的是采样器所使用的处理后分布中的 logprobs。
所需设置为:
- 正文:logprobs-mode=processed_logprobs
这消除了 rollout logprobs 中明显的均值偏移。训练曲线相对于已知良好的参考仍然显示出差距,因此下一个问题必定位于推理路径中。
policy-ratio 图直接显示了这一点。一旦为 V1 启用 processed_logprobs,三个运行中的平均 policy ratio 都始终极其接近 1.0。这证明均值偏差已被修复。剩余的不匹配体现在 clip rate、KL、entropy 以及下游训练行为中。
运行时默认设置
早期的 V1 运行将引擎版本与 V1 运行时默认设置混用:
- prefix caching,在早期运行中未设置,因此应用了 vLLM 0.18.1 的默认设置
- 异步调度,在早期运行中未设置,因此应用了 vLLM 0.18.1 的默认值
- 一个临时的 disable-cascade-attn 覆盖项,通过启动时 kwarg 透传进行设置,并且位于已提交配置中的一致性方案之外
对于一致性运行,我们明确做出了这些选择:
vllm_config:
use_v1: true
vllm_kwargs:
logprobs-mode: processed_logprobs
enable-prefix-caching: false
async-scheduling: false
Prefix caching 值得单独说明。对于固定模型状态而言,它通常是一种保持正确性的推理优化。在这个在线 RL 设置中,相对于 V0 参考路径,它是 V1 独有的缓存生命周期和复用差异。actor 还在处理重复前缀、并发请求、异步调度以及进行中的权重更新。
当前缀缓存策略忽略权重更新边界时,前缀缓存命中可以复用权重更新前计算出的状态。禁用前缀缓存从一致性比较中移除了一个仅 V1 存在的自由度。
飞行中权重更新
权重同步也必须与在线 RL 更新模型相匹配。一种选择是让 V1 比 V0 更严格,即在每次更新时清空请求并清除缓存。但那会回答另一个问题。我们首先需要验证 V1 是否能够匹配现有的 V0 行为。
V0 实际所做的更接近于:
- 在引擎边界处阻塞执行
- 加载新的权重
- 在未显式使缓存状态失效的情况下继续运行
最接近的 V1 类比是:
await engine.pause_generation(mode="keep", clear_cache=False)
await engine_client.collective_rpc_async(
"receive_weight_update",
args=(request.model_dump_json(),),
)
await engine.resume_generation()
有两个细节很重要:
- mode="keep" 比 wait 或 abort 更接近旧的进行中更新模型
- clear_cache=False 与 V0 包装器行为一致,后者在更新时保留缓存状态不变
滞后是一个有用的运行时诊断指标。与修正后的 V1 运行相比,初始 V1 路径在训练后期带有更多持续性滞后。
剩余差距:fp32 lm_head
上述 V1 后端修复消除了明显的迁移问题,但要实现最终一致性,仍需要匹配用于计算 logits 的数值路径。trainer 使用 fp32 lm_head 进行最终投影。rollout 后端必须匹配这一行为。
MiniMax-M1 技术报告中也出现了一个密切相关的问题:他们的 RL 运行显示训练/推理 token 概率不匹配,他们将其追溯到 LM 输出头,并通过以 fp32 计算该输出头来修复。
这一点很重要,因为 RL 更新会直接使用 token 的 logprobs。logits 的微小变化可能会体现在 policy ratios、KL 和 clipping 中。因此,最终投影的精度是在线 RL 正确性边界的一部分。ScaleRL 论文随后将 fp32 logits/head 计算纳入其 RL 方案,并通过消融实验表明它是大规模 RL 的一项有用设计选择。
在包含 fp32 lm_head 路径后,reward 对最终一致性结果给出了一个简洁的视图。在图 6 中,最终的 V1 运行轨迹与 V0 参考一致;最初的 V1 尝试则产生了明显不同的 reward 曲线。
消融实验
阴性结果很重要,因为它们排除了常见解释。
- 仅使用 processed_logprobs:修复了语义 logprob 错误;训练不匹配仍然存在。
- 批次不变性:在一项单独测试中,不匹配仍然存在,并伴随更高的滞后、更高的裁剪率以及 NCCL 复杂性。
- 将第一次 V1 运行视为公平基线:第一次 V1 运行启用了多个仅限 V1 的默认设置,因此这是一次存在混杂因素的迁移比较。
为什么我们首先修复后端正确性
目标侧校正(如截断重要性采样、重要性比率重加权及相关方法)是有用的工具。如果 rollout 有意保持陈旧、异步生成,或由无法与训练器侧策略等价的后端生成,那么通常应当加入某种形式的校正。
这里的首要问题是推理正确性。迁移到 V1 后,rollout 后端返回的 logprobs 和运行时行为打破了训练器假设。此时加入目标侧校正会把两个问题混在一起:
- 推理后端是否生成了正确的 logprobs?
- 在给定正确 logprobs 的情况下,目标函数是否仍然需要离策略或异步校正?
这些问题需要分开。否则,目标函数侧的校正可能会补偿推理后端行为的异常,这会使训练曲线更难解释。
当前目标函数仍然可以改进。在恢复推理一致性之后,下一步改进就是常规的异步/离策略清理:
- 保留 rollout 时显式的行为策略 logprobs
- 在优化时于训练器端重新计算旧策略的对数概率
- 将后端不匹配校正与策略更新比率分离
- 跟踪校正项的 ESS 等诊断指标,并与训练器聚合指标并列记录
此次迁移的主要经验更为具体:先修复后端正确性问题,然后再为仍然存在的不匹配添加校正。
社区
· 注册或登录以发表评论
