推理引擎升级强化学习模块,强调正确性先于优化的方法论。
简而言之:修复四个问题后,vLLM V1 的表现与作为参照的 vLLM V0 一致。这四个问题分别是:处理后的 rollout logprobs、V1 特有的运行时默认值、inflight 权重更新路径,以及用于最终投影的 fp32 lm_head。我们先修复 backend 行为,再修改 RL objective。
参照实验使用 vLLM 0.8.5,V1 实验使用 vLLM 0.18.1。图 1 展示了最终结果。红色曲线是最初的 V1 实验,绿色曲线则是应用下文所述修复后的最终 V1 实验。
vLLM V1 对 V0 engine 进行了大规模重写。因此,我们刻意将迁移目标限定在一个较窄的范围内:
最初可观察到的异常出现在:
clamp_log_ratio_new_old_indicator
这些指标来自一次 GSPO 训练实验,GSPO 也是本次实验采用的 objective。同一类不一致也可能出现在 PPO、GRPO 或任何将 rollout 侧 logprobs 作为优化目标一部分的在线 RL 系统中。
最初的 V1 实验清楚地暴露了这个问题。在训练早期,trainer 侧的 logprobs 和 reward 就开始偏离 V0 参照实验。
trainer 指标中也出现了相同的模式。在最初的对比中,clip rate 是最容易看出问题的信号。
我们将可能的原因分成三个层面:
一开始,我们过早地怀疑了第三类原因。真正有效的诊断方式,是先将前两类问题视为 backend 行为问题,并优先排查和排除它们。
第一个问题属于语义层面。vLLM V1 默认返回原始模型输出对应的 logprobs,也就是在 temperature scaling、penalties 和 top-k/top-p filtering 等 logits 后处理之前计算的 logprobs。PipelineRL 预期的则是 sampler 实际使用的、经过处理后的分布所对应的 logprobs。
需要设置:
logprobs-mode=processed_logprobs
这样便消除了 rollout logprobs 中明显的均值偏移。不过,相较于已知表现正确的参照实验,训练曲线依然存在差距,因此下一个问题必然出在推理路径上。
policy ratio 图直接说明了这一点。为 V1 启用 processed_logprobs 后,三次实验的平均 policy ratio 在整个过程中都极其接近 1.0。这证明均值偏差已经得到修复。剩余的不一致则体现在 clip rate、KL、entropy 以及后续的训练行为中。
早期的 V1 实验将 engine 版本变更与 V1 的运行时默认值混在了一起:
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 还需要处理重复前缀、并发请求、async scheduling 和 inflight 权重更新。
如果缓存策略忽略权重更新边界,那么 prefix-cache 命中可能会复用权重更新前计算出的状态。禁用 prefix caching,可以从一致性对比中消除一个仅存在于 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 更接近旧的 inflight 更新模型clear_cache=False 与 V0 wrapper 的行为一致,后者会在更新时保留缓存状态lag 是一个很有用的运行时诊断指标。与修正后的 V1 实验相比,最初的 V1 路径在训练后期持续表现出更高的 lag。
lm_head上述 V1 backend 修复消除了明显的迁移问题,但要实现最终的一致性,还需要让计算 logits 的数值路径保持一致。trainer 使用 fp32 lm_head 完成最终投影,rollout backend 也必须匹配这一行为。
MiniMax-M1 技术报告中出现过一个密切相关的问题:他们的 RL 实验存在训练与推理之间的 token probability 不一致。最终,他们将问题定位到 LM output head,并通过使用 fp32 计算该 head 解决了问题。
这很重要,因为 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 的语义 bug,但训练结果仍然不一致。truncated importance sampling、importance-ratio reweighting 等 objective 侧修正方法都是有用的工具。如果 rollouts 本身就是有意保持陈旧、采用异步方式生成,或者来自一个无法与 trainer 侧 policy 保持等价的 backend,那么添加某种修正通常是正确的做法。
但这里的首要问题是推理正确性。迁移到 V1 后,rollout backend 返回的 logprobs 和运行时行为破坏了 trainer 的假设。如果此时就加入 objective 侧修正,便会将两个问题混在一起:
这两个问题必须分开处理。否则,objective 侧修正可能会掩盖错误的推理 backend 行为,让训练曲线变得更难解释。
当前的 objective 仍有改进空间。恢复推理一致性之后,下一步就是常规的 async/off-policy 清理工作:
这次迁移带来的主要经验其实更加具体:先修复 backend 正确性,再针对剩余的不一致添加修正。
Voice Agent 能应对双语客户吗?在语码转换语音上评测前沿 ASR
EVA-Bench Data 2.0:3 个领域、121 个工具、213 个场景
· 注册或登录后发表评论