首页
/ vLLM 异步强化学习:基于 pause/resume 的生成—训练并行与权重热更新指南

vLLM 异步强化学习:基于 pause/resume 的生成—训练并行与权重热更新指南

2026-09-06 19:19:03作者:谭伦延

本技术指南讲解 vLLM 仓库 docs/training/async_rl.md 中定义的**异步强化学习(Async RL)**支持体系:如何在训练与推理并行运行的同时,通过 pause_generation / resume_generation 安全地把训练侧新权重同步进正在出词的推理引擎。读完本文,你将掌握三种暂停模式的取舍、KV 缓存一致性语义、HTTP 层等价端点,以及一套可直接照搬的权重同步循环,并能在仓库源码与示例中找到对应的实现证据。

一、为什么需要 Async RL:one-off pipelining

标准的 RL 训练循环里,生成(rollout)与训练(training)是串行交替的:策略模型先跑出一批样本,训练器基于这批样本更新一轮权重,然后继续下一轮生成。这个过程中,生成阶段训练加速卡闲置、训练阶段推理加速卡闲置,GPU 利用率因此受限于单侧负载。

文档提出的一对一流水线(one-off pipelining)思路是:把生成与训练拆成两条并行协程,让推理引擎在旧样本上训练的同时,持续产出新样本,由此获得更高的 GPU 利用率与训练吞吐。

但并行重叠立刻引入一个核心难点:权重必须在请求还在飞行(in-flight)的半途被换入推理引擎。如果只是简单粗暴地覆盖权重,正在解码的请求可能读到"前半段用旧权重、后半段用新权重"以外的混乱状态(例如 KV cache 与权重不一致)。这正是 vLLM 引入 pause/resume API 的原因。

二、核心 API:pause_generation 与 resume_generation

为了在引擎运行期间安全更新权重,vLLM 在推理引擎上暴露了 pause_generationresume_generation 两个异步方法,让训练器协调出一个干净的权重同步窗口,且不丢失进行中的工作。它们定义在 vllm/v1/engine/async_llm.pyAsyncLLM 类中,对应协议见 vllm/engine/protocol.py

pause_generation

Python 侧调用方式如下(AsyncLLM 的签名位于 async_llm.py#L874-L917):

await engine.pause_generation(mode="keep", clear_cache=True)

实际签名中还包含一个已废弃的历史参数 wait_for_inflight_requests: bool | None = None(传入后内部会触发 DeprecationWarning 并把 mode 置为 "wait"),新代码应直接使用 mode

mode 参数决定如何处理进行中的请求,其取值类型为 Literal["abort", "wait", "keep"](见 vllm/v1/engine/init.py#L31):

Mode 行为
"abort" 立即中止所有 in-flight 请求并返回部分结果(默认)
"wait" 等待所有 in-flight 请求结束再暂停
"keep" 冻结队列中的请求,调用 resume_generation 后继续生成

clear_cache 参数控制暂停结束后是否清空 KV cache 与 prefix cache:

await engine.pause_generation(mode="keep", clear_cache=True)

在引擎内核 vllm/v1/engine/core.pypause_scheduler 实现中,三种模式对应两条调度状态路径:

  • "abort":先调用 scheduler.finish_requests(None, RequestStatus.FINISHED_ABORTED) 立刻终止全部请求,再把调度器置为 PAUSED_NEW(新请求只排队、不进入 step()),可选清空缓存后完成。
  • "wait":置为 PAUSED_NEW(新请求排队但调度器继续 step),直到 in-flight 请求排空,可选清空缓存。
  • "keep":直接置为 PAUSED_ALL,返回一个在输出队列清空时完成的 Future——请求既不被中止也不被推进,而是冻结在调度队列里等待恢复。

无论哪种模式,暂停期间新到达的生成/编码请求都不会被调度,直到调用 resume_generation。暂停完成后内核还会通过 collective_rpc("synchronize_device") 做一次设备同步,确保设备真正空闲(对应 core.py#L853-L886)。另外需要注意:"wait" 模式在 in-process engine 模式下不可用(pause_scheduler 会直接抛出 ValueError)。

resume_generation

await engine.resume_generation()

resume_generation 会把调度器状态从暂停态改回 UNPAUSED(见 async_llm.py#L919-L921core.py#L890-L892),随后用 mode="keep" 冻结的请求会继续解码。引擎还额外提供了只读状态查询 is_paused(),可判断当前是否处于任一暂停态。

三、HTTP 端点:dev-mode 下的等价格令

设置环境变量 VLLM_SERVER_DEV_MODE=1 后,vLLM HTTP 服务器会挂载一批与上述 API 等价的端点。路由实现在 vllm/entrypoints/serve/dev/rlhf/api_router.py

端点 方法 说明
/pause?mode=keep POST 暂停生成;mode 支持 abort / wait / keep,另有 clear_cache 查询参数
/resume POST 恢复生成
/abort_requests POST 不暂停调度器,直接中止 in-flight 请求:body 为 {} 中止全部,或 {"request_ids": [...]} 中止指定请求
/weight_info GET 返回最新已提交的 weight_version

以 curl 为例:

# 冻结进行中的请求(keep 模式)
curl -X POST "http://localhost:8000/pause?mode=keep"

# 训练器完成权重同步后恢复生成
curl -X POST "http://localhost:8000/resume"

# 中止全部 in-flight 请求(不暂停调度器)
curl -X POST http://localhost:8000/abort_requests -d '{}'

# 查询当前已提交的权重版本
curl http://localhost:8000/weight_info

路由层还额外暴露了 /is_paused(GET,返回 {"is_paused": bool})。注意 /pause 中传入非法的 mode 值会被拒绝并返回 400。/abort_requests 在未提供 request_ids 时会遍历 AsyncLLM.output_processor 中跟踪的全部请求(含并行采样父请求),按内部 ID 中止。

补充说明:带 VLLM_SERVER_DEV_MODE=1 的同一套 dev 路由同时承载了完整的权重转移控制面(/init_weight_transfer_engine/start_weight_update/update_weights/finish_weight_update/update_weight_version/get_world_size 等)。pause/resume 只是其中与"调度生命周期"相关的两个端点。完整端点表可查阅 docs/training/weight_transfer/README.md

数据并行(DP)下的注意点

文档给出了一条重要的数据并行使用约束:

  • 当使用 vLLM 内部负载均衡(即 data_parallel_backend="ray")时,pause/resume 会由系统自动在所有 DP rank 上统一处理,单次调用即可
  • 当使用外部负载均衡(即多个相互独立的 vLLM 实例位于代理之后)时,你必须在权重更新前后,逐个实例地发送 pause 与 resume 请求。

仓库中 examples/features/pause_resume/data_parallel_pause_resume.py 提供了面向 HTTP 端点的封装示例,其中 pause_generation(base_url, mode="keep")resume_generation(base_url) 演示了逐实例调用的写法。

四、典型异步 RL 循环:权重同步五步走

把上述 API 组装起来,一个典型的异步 RL 权重同步循环如下:

  1. 用当前策略启动 rollout 生成;
  2. 一旦训练器有新权重待同步,以 mode="keep" 暂停生成;
  3. 把更新后的权重从训练器同步到推理引擎(见 Weight Transfer 文档);
  4. 恢复生成——被冻结的 in-flight 请求用新权重继续;
  5. 循环往复。

其中最关键的技术洞察是权重边界与 token 的对应关系

  • mode="keep" 冻结的请求,暂停前产出的 token 来自旧权重,恢复后产出的 token 来自新权重
  • 单个请求的产物因此被明确切分为"旧权重段 + 新权重段",这是后续校验阶段能精确对账的前提。

clear_cache 则决定 KV 缓存在暂停窗口内的去留:

clear_cache 语义
True 暂停后丢弃此前缓存的 key-value 条目。恢复后产出的全部 token 均由新权重从头计算,上下文与权重严格一致
False 保留已有 KV cache 条目。恢复后上下文中的部分 token 仍反映旧权重(即存在 stale KV cache),换取更快的恢复速度

实现上,暂停完成的收尾逻辑 _finish_pause(clear_cache)(见 core.py#L853-L858)会在 clear_cache=True 时重置 KV cache、prefix cache、多模态 cache 与 encoder cache,并同步设备。异步入口 async_llm.py#L908-L910 还会在暂停前主动清空多模态 cache(renderer.clear_mm_cache_async()),并在暂停完成后小睡 20ms,保证 in-flight 请求的最终输出先于 pause_generation 返回,便于调用方按直觉顺序处理事件。

五、完整示例拆解:rlhf_async_new_apis.py

文档末尾指出的参考示例是 examples/rl/rlhf_async_new_apis.py。它以真实可运行的形态把本文所有概念串了起来:vllm.AsyncLLMEngine + Ray 进程管理 + NCCL 权重传输 + 中途中止/恢复 + 结果校验。

脚本整体划分为两条独立 GPU 流水线:

  • 训练侧TrainModel 是一个 @ray.remote(num_gpus=1) actor,用 Hugging Face AutoModelForCausalLM 加载 Qwen/Qwen3-1.7B 训练模型;
  • 推理侧:自定义子类 MyLLM(vllm.AsyncLLMEngine) 以 Ray 作为 distributed_executor_backend,加载基础模型 Qwen/Qwen3-1.7B-Base(权重传输配置为 WeightTransferConfig(backend="nccl"))。

关键流程分两个阶段:

Phase 1:并发请求 + 中途换权重

  1. 定义 PAUSE_TOKEN_THRESHOLD = 10,采样参数为贪心解码(temperature=0),max_tokens = PAUSE_TOKEN_THRESHOLD + N_NEW_TOKENSN_NEW_TOKENS = 100);
  2. 通过 do_generate 为一批 13 条 prompt 各发起一个远端生成任务(gen_futures),同时在 pause_after_n_tokens 协程中轮询"是否有请求越过 10 token 阈值";
  3. 一旦越过阈值就调用 super().pause_generation(mode="keep") 冻结全部 in-flight 请求,随后小睡 5 秒,并把 _generation_paused 置位——后续生成循环据此记录 pause_token_index(即"换权重前已产出的 token 数");
  4. 调用训练 actor 的 broadcast_weights(),由 trainer 侧引擎通过 send_weights() 驱动"初始化 → 开始 → 传输 → 结束"四阶段协议,经 NCCL broadcast 把训练模型权重覆盖到推理 worker(该机制的架构细节见 docs/training/weight_transfer/README.md,NCCL 后端说明见 nccl.md);
  5. 调用 resume_generation() 恢复,收集结果后按 pause_idx 切分:all_token_ids[:pause_idx] 为旧权重段、all_token_ids[pause_idx:] 为新权重段,分别解码打印。

Phase 2:用全新 vLLM 实例做正确性校验

验证阶段的思路是:用 prompt + 旧权重段 token 作为输入,在一个直接加载 V2 训练模型的全新 MyLLM 实例上重新贪心解码,将其输出与 Phase 1 中"新权重段"做逐 token 比对。这种比对依赖批次不变性(batch-invariant)生成——即输出与请求被如何批处理无关。示例通过 Ray runtime env 设置 VLLM_BATCH_INVARIANT=1 开启该特性;批次不变性目前要求 NVIDIA compute capability 9.0 及以上的 GPU(H100/H200、B100/B200)。ROCm 平台上由于存在残余非确定性,示例放宽到 90% 的通过率(MIN_PASS_RATE = 0.9),同时注入固定 seed、关闭 prefix caching、max_num_seqs=1 等确定性设置;而真正的权重同步故障会造成约 0% 通过率而非 90%+,因此该阈值足以区分"实现缺陷"与"平台抖动"。

最终脚本断言 pass_rate >= MIN_PASS_RATE,否则以详细的首个发散 token 信息宣告失败。这套"换权重后输出 ≈ 全新加载同权重实例的输出"的验证模式,是判断权重同步是否真正生效的黄金标准。

六、把机制放进更大的 RL 生态

pause/resume 只是 vLLM RL 训练体系里"调度生命周期"的一环,与之配套的仓库资源还有:

  • 权重传输四阶段协议:初始化(init_weight_transfer_engine)→ 开始(start_weight_update)→ 传输(update_weights,可多次调用以支持分块)→ 结束(finish_weight_update,可提交 weight_version)。协议由 trainer 侧 TrainerWeightTransferEngine 驱动,推理侧 worker 被动响应,详见 docs/training/weight_transfer/README.md
  • 多种传输后端:NCCL(训练/推理分 GPU)、IPC(CUDA IPC handle,同卡共置)、sparse_nccl(checkpoint 坐标稀疏补丁)、sharded_rdt(NIXL/Ray Direct Transport,MoE 专家并行等超大模型场景)。传输后端的选择只由推理侧 WeightTransferConfig(backend=...) 声明,训练侧则由各 TrainerInitInfo 子类自带的 backend ClassVar 决定。
  • HTTP 训练流程示例examples/rl/ 目录下还提供了 rlhf_http_ipc.pyrlhf_http_nccl.py(通过 HTTP 端点 + IPC/NCCL 权重通道驱动、并在其中使用 /pause/resume 的完整示例),以及面向 FSDP/EP 的 rlhf_ipc_fsdp_ep.pyrlhf_nccl_fsdp_ep.pyrlhf_sharded_rdt_small_ep.pyrlhf_sparse_nccl.py 等进阶变体,可作为理解数据并行与分片权重同步的补充素材。
  • 分层(layerwise)与采样掩码:若训练按层推进或需要控制 token 级别的训练目标,可进一步参考 docs/training/layerwise.mddocs/training/sampling_mask.md

结语

异步 RL 的价值在于让训练与生成始终并行运转;而要把"并行"变成"安全",关键就在于 vLLM 提供的 pause/resume 调度原语。本文覆盖的 abort / wait / keep 三种模式、clear_cache 的一致性语义、dev-mode HTTP 端点与数据并行注意事项,共同构成了构建 RLHF / GRPO 等在线 RL 训练闭环的工程基础。动手实践时,建议先运行 examples/rl/rlhf_async_new_apis.py 观察"同一请求旧/新权重分界"的实际输出,再对照 async_llm.pycore.py 中的调度状态机,即可完整建立起从 API 到内核的认知链路。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388