← 返回信息流
技术博客Hugging Face Blog·28 天前

vLLM V0 到 V1:强化学习中的正确性先于修正

原标题:vLLM V0 to V1: Correctness Before Corrections in RL

速览

vLLM 从 V0 升级到 V1,在强化学习场景下确立了“正确性优先”的开发原则。该版本旨在解决 V0 中存在的潜在错误,确保模型训练结果的准确性。这一转变对于提升强化学习应用的稳定性和可靠性具有重要意义。

AI 深度解读

vLLM V0 到 V1:在强化学习修正之前,先确保正确性

背景

vLLM V1 是对 V0 引擎的一次重大重写。随着大模型推理引擎的快速迭代,从 V0 迁移到 V1 的过程中,保持训练与推理后端行为的一致性(Parity)至关重要,尤其是在涉及在线强化学习(Online RL)的场景中。

本文记录了 Hugging Face 团队在将 vLLM 从 0.8.5 版本迁移到 0.18.1 版本(V1)时遇到的挑战。迁移的目标非常明确且狭窄:

  1. 验证 V1 返回的 rollout logprobs(对数概率)是否符合 Trainer 的预期格式。
  2. 使用相同的负载重新运行 V0 参考实验。
  3. 只有在恢复后端一致性后,才评估目标函数层面的变化。

然而,初始的 V1 运行结果显示,Trainer 侧的 logprobs 和奖励(Reward)在训练早期就偏离了 V0 的参考值。这种不匹配在 GSPO(一种用于该实验的目标函数)的训练指标中表现得尤为明显,具体体现在 clamp_log_ratio_new_old_indicatorkl_new_oldentropyreward 等指标上。同样的不匹配模式也可能出现在 PPO、GRPO 或任何将 rollout 侧 logprobs 视为优化目标一部分的在线 RL 系统中。

核心内容

团队将潜在原因分为三个层次:语义不匹配、推理路径不匹配和目标函数不匹配。起初团队过早怀疑是第三类(目标函数)问题,但正确的诊断方法是将前两类视为后端行为问题并优先排除。

1. Logprob 语义修复

第一个问题是语义层面的。默认情况下,vLLM V1 从原始模型输出中返回 logprobs,即在 logits 后处理(如温度缩放、惩罚、top-k/top-p 过滤)之前。然而,PipelineRL 期望的是采样器使用的经过处理的分布的 logprobs。

解决方案: 必须设置以下配置:

logprobs-mode=processed_logprobs

这一设置消除了 rollout logprobs 中明显的均值偏移。启用 processed_logprobs 后,策略比率(policy-ratio)图显示,所有三次运行的平均策略比率都极其紧密地保持在 1.0 附近,这确立了均值偏差的修复。然而,训练曲线与已知良好的参考值之间仍存在差距,表明下一个问题出在推理路径上。

2. 运行时默认值修复

早期的 V1 运行混合了引擎版本与 V1 的运行时默认值,导致行为不一致:

  • 前缀缓存(Prefix Caching): 早期运行中未设置,应用了 vLLM 0.18.1 的默认值。
  • 异步调度(Async Scheduling): 早期运行中未设置,应用了 vLLM 0.18.1 的默认值。
  • 临时覆盖: 通过启动时 kwarg 透传设置了 disable-cascade-attn,这不在提交的配置的一致性食谱中。

为了确保一致性,团队在配置中显式做出了以下选择:

vllm_config:
  use_v1: true
vllm_kwargs:
  logprobs-mode: processed_logprobs
  enable-prefix-caching: false
  async-scheduling: false

关于前缀缓存的特别说明: 通常,前缀缓存是一种在固定模型状态下保持正确性的推理优化。但在本在线 RL 设置中,它与 V0 参考路径相比,在缓存生命周期和重用方面存在 V1 特有的差异。Actor 还处理重复的前缀、并发请求、异步调度和运行中权重更新。如果缓存策略忽略权重更新边界,前缀缓存命中可能会重用权重更新前计算的状态。禁用前缀缓存消除了比较中的一大 V1 特有自由度。

3. 运行中权重更新(Inflight Weight Updates)

权重同步也必须匹配在线 RL 的更新模型。V0 实际上执行的操作更接近于:

  1. 在引擎边界暂停执行。
  2. 加载新权重。
  3. 在不显式使缓存状态失效的情况下恢复。

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"waitabort 更贴近旧的运行中更新模型。
  • clear_cache=False 匹配 V0 包装器的行为,即在更新时保留缓存状态。

运行时延迟(Lag)是一个有用的诊断指标。初始 V1 路径在训练后期表现出比修正后的 V1 运行更持久的延迟。

4. 剩余差距:fp32 lm_head

上述后端修复消除了明显的迁移问题,但最终的一致性仍需匹配用于计算 logits 的数值路径。Trainer 使用 fp32 lm_head 进行最终投影,Rollout 后端必须匹配这一行为。

这是一个密切相关的问题,出现在 MiniMax-M1 的技术报告中:他们的 RL 运行显示出训练/推理 token 概率不匹配,他们将其追溯到 LM 输出头,并通过以 fp32 计算头部来修复。

为什么这很重要: RL 更新直接消耗 token logprobs。logits 的微小变化会在策略比率、KL 散度和裁剪中显现出来。因此,最终投影的精度是在线 RL 正确性表面的一部分。ScaleRL 论文后来也将 fp32 logits/head 计算包含在其 RL 食谱中,并将其作为大规模 RL 的有用设计选择进行了消融实验。

加入 fp32 lm_head 路径后,奖励(Reward)提供了最终一致性结果的紧凑视图。最终 V1 运行跟踪了 V0 参考值,而初始 V1 尝试产生了明显不同的奖励曲线。

关键要点

  • 语义优先: vLLM V1 默认返回处理前的 logprobs,必须显式设置 logprobs-mode=processed_logprobs 以匹配 Trainer 期望的采样分布概率。
  • 默认值陷阱: V1 引入了新的运行时默认值(如前缀缓存、异步调度)。在迁移时必须显式禁用或配置这些选项(如 enable-prefix-caching: false),以避免因缓存生命周期和重用策略差异导致的状态不一致。
  • 权重更新同步: 运行中权重更新需使用 pause_generation(mode="keep", clear_cache=False) 以匹配 V0 的行为,即保留缓存状态并避免显式失效,从而减少运行时延迟。
  • 数值精度一致性: 在线 RL 对 logprobs 极其敏感。必须确保 Rollout 后端使用与 Trainer 相同的 fp32 lm_head 进行最终投影,微小的 logits 差异会显著影响策略比率和 KL 散度。
  • 排除法诊断: 当训练指标出现偏差时,应先排除后端行为(语义、推理路径、数值精度)问题,再考虑目标函数修正。过早应用目标函数修正可能会掩盖后端错误,使训练曲线难以解释。
  • 初始运行不可作为基准: 初始 V1 运行通常启用了多个 V1 特有的默认值,是一个混淆的迁移比较,不能作为公平基线。

意义与影响

这篇文章强调了在大型基础设施重构(如 vLLM V0 到 V1)中,正确性先于修正的重要性。

  1. 分离关注点: 团队明确指出,不应将“推理后端是否产生正确的 logprobs”与“给定正确的 logprobs,目标函数是否仍需要离线/异步修正”这两个问题混为一谈。如果后端行为不正确,添加目标函数层面的修正(如截断重要性采样、重要性比率重加权)只会掩盖问题,导致训练曲线难以解读。
  2. 在线 RL 的敏感性: 在线强化学习系统对推理后端的细微变化极其敏感。即使是看似微小的实现差异(如 logprobs 的计算时机、缓存策略、浮点精度),都可能导致训练发散或性能下降。
查看原文 →huggingface.co