0. 一句话定位
分布式 RL 训练 = 用 Ray 做编排,把 Actor(策略)/ Critic(价值)/ Reference(参考模型,算 KL)/ Reward(打分) 四个角色拆到不同 GPU 池,中间插一个 vLLM 或 SGLang 的 Rollout Engine 专门负责生成,再用高速 Weight Sync 把参数从训练侧广播回推理侧——这是 RLHF、RLAIF、偏好微调、机器人 VLA 偏好对齐共同的工程基座。
它不是什么新算法,而是把”一个模型在一张卡上又当演员又当裁判”这件蠢事彻底拆开。
1. 为什么 RLHF 不能”一锅炖”
PPO-RLHF 的教科书实现(TRL / HF Trainer 早期版本)是单进程顺序跑:
同一份参数 fp 前向生成 → reward 打分 → 同一份参数反向更新
这在 <1B 模型上还能扛,7B 以上必须拆,原因有三个浪费:
- 同一份参数被当成两个角色用:既是 Actor(要梯度),又是 Reference(要冻结算 KL)。两份都要常驻显存,浪费一倍。
- 推理与训练抢同一组 SM:生成 256 个 token 的 response 是纯推理负载(吃带宽、batch 越大越好),梯度下降是计算负载(吃算力)。硬塞在一起,两边都不在最佳工作点。
- 时间分布极度不均:一个 PPO step 里 rollout(生成)占大约 80% 的时间,DeepSpeed 训练只占 20%。用同一批卡顺序做,等于让 80% 的卡在训练阶段闲着。
一句话:rollout 和训练是两种完全不同的负载,必须异步。
2. 四角色解耦 + vLLM Rollout Engine
图:四个角色各占独立 GPU 池,靠 Ray Object Store 共享 experience buffer;每轮更新后必须把权重广播回推理引擎。
这个结构没有任何一行是”算法创新”,全是工程。它要回答的问题是:怎么让 80% 的时间花在最有价值的那件事(生成)上,而不浪费卡。
3. 一个 PPO step 的六阶段流水线
① Rollout generation : vLLM 生成 response(最长的一段)
② Reward scoring : RM 或规则校验器打分
③ Reference forward + KL : 冻结模型对每条样本算 logp_ref
④ GAE advantage : Critic 估计优势
⑤ Critic / Policy gradient : 反向更新
⑥ Weight sync back to vLLM : 参数广播回推理引擎,进入下一轮
真正的难点在 ③ 和 ⑥:③ 决定了你需要多少显存(Reference 的权重和 KV),⑥ 决定了你的流水线会不会”卡在等同步”。
4. PPO 与 GRPO 的损失
PPO 的核心四行:
A_t = R_t - V(s_t) # 优势(实际用 GAE)
rho_t = pi_theta(a_t|s_t) / pi_old(a_t|s_t) # importance ratio,必须 >= 0
L_CLIP = E[ min( rho_t*A_t, clip(rho_t, 1-eps, 1+eps)*A_t ) ]
L_KL = -beta * E[ log pi_theta(a_t) - log pi_ref(a_t) ]
L_PPO = L_CLIP - c1 * L_VF + c2 * L_KL
其中 eps 默认 0.2;beta 随实测 KL 自适应上调(KL 大了说明漂移太多,要拉回来);c1、c2 是 value 与 KL 的系数;Reference 在算 logp_ref 时必须处于冻结状态。
GRPO 的价值在于把 Critic 整个删掉:不训 value head,而是对同一个 prompt 采样一组(group)答案,用组内相对优劣当优势:
A_i = (r_i - mean(r_group)) / std(r_group) # 组内标准化,替代 Critic
少一整个 GPU 池、少一份显存、少一个训练目标。代价是 group size 要 >= 4,否则组内方差估计不稳。
5. Weight Sync:两条路径
def sync_weights_to_vllm(actor, vllm):
for name, param in actor.named_parameters():
if param.requires_grad:
# 走 side-channel NCCL process group,GPU -> GPU 零拷贝广播
vllm.collective_rpc("load_weights", param.data)
| 路径 | 机制 | 评价 |
|---|---|---|
| Ray Object Store | GPU → CPU → 序列化 → CPU → GPU | 通信量 O(N × 参数量),好在配置简单 |
| NCCL broadcast | GPU → GPU 直通,广播 tensor | 所有 serious 框架的默认选择 |
vLLM 0.7+ 暴露了 WorkerExtension 与 collective_rpc,训练侧用 StatelessProcessGroup 建一条旁路 NCCL 组,即可做到零拷贝广播。
6. 实测收益
| 框架 | 结果 |
|---|---|
| OpenRLHF | 70B 模型 RLHF 全程 PPO 收敛,相对 TRL 原版约 3× 提速 |
| veRL(字节 Seed) | Qwen2.5-32B 上 PPO RLHF 一个迭代从 1.5 小时降到 25 分钟 |
| DeepSeek V4 自研 CANN-GRPO | KL 校正与奖励建模融合进单次前向,RLHF 迭代周期 7 天 → 19 小时,SWE-bench 42.1 → 58.2(+38.2%) |
| DeepSeek V4 + Miles | MegaMoE 把通信与计算融成单 GPU kernel,rollout 路径 1.96× 提速 |
| AReaL(蚂蚁) | 完全异步 rollout + replay buffer,70B 推理模型 RL 训练利用率 >80% |
| NeMo-RL(NVIDIA) | 70B+ 全栈 RLHF 训练,GB300 上的 RTX-only pipeline |
把这些放在一起看,能读出一个趋势:RL 训练的效率提升,绝大部分来自”把 rollout 和训练解耦”这一件事,而不是新的损失函数。
7. 四个常见坑
- Sync gap(同步空窗):Weight sync 太久,就会训练用 v1 权重、推理用 v2 权重,policy gradient 直接失真。建议每个 mini-batch 后立即同步。
- Actor 显存爆炸:Reference 不能 unload 到 CPU,否则算 KL 时 I/O 阻塞,整条流水线等它。
- vLLM 不 sleep:切换 rollout / train 阶段时要把 vLLM engine
enable_sleep,把显存让给训练,否则 OOM。 - GRPO 组内方差爆炸:group size < 4 时组内优势估计方差过大,训练抖动。要么加大 group,要么退回 PPO + Critic。
8. 与机器人 VLA 的关系
这套基础设施不止服务文本 RLHF,机器人策略的 RLAIF(用 reward model 做偏好对齐) 走的是同一套:
VLA 生成轨迹 → reward model 打分 → 组内相对优劣(GRPO)→ 更新策略 → 同步回推理侧
- Octo、π0 都已支持在自定义 reward model 上做 RLAIF;
- GRPO 是 VLA 微调的高效算法参考:去掉 Critic 后,27M 的 Octo-Small 微调时间可从 2 小时压到 约 20 分钟;
- 对工厂侧的多场景并行训练更是刚需:白天训练 + 推理跑机器人,晚上集中 rollout 收数据,同一组 64 张卡做到”一鱼多吃”。这套用法正是 OpenRLHF / veRL 的主线场景,机器人公司可以白嫖大部分 boilerplate。
9. 总结
| 维度 | 答案 |
|---|---|
| 要解决什么 | rollout 与训练是两种负载,硬塞在一起浪费 80% 的卡 |
| 怎么解决 | 四角色解耦 + 独立 Rollout Engine + NCCL Weight Sync |
| 最大收益来自 | 解耦本身,而不是新损失函数(veRL 1.5h → 25min) |
| 要不要 Critic | 可选。GRPO 去掉 Critic 换更简单,代价是 group size >= 4 |
| 与 VLA 的关系 | RLAIF 与文本 RLHF 共用同一套基础设施,GRPO 可直接用于 VLA 微调 |
一句话记住:RLHF 的瓶颈从来不在算法,而在”让生成和训练各就各位”。分布式 RL 训练做的就是把这件事工程化。
系列导航:本篇是本系列开篇。相关背景见 fa4-deepseek-v4(V4 的训练与压缩)与 sys8-parallel-strategies-pd-disaggregation(并行切分与 PD 分离)。
💬 留言
NaphJohn/LLM-blog尚未启用 Discussions:请在 GitHub 仓库 Settings → General → Features 勾选 Discussions 后刷新本页,评论区即自动显示。