前置知识: 本文假设读者具备深度学习基础(理解神经网络,反向传播),不需要 RL 背景。我会从最基础的概念开始,逐步深入到框架的技术细节和工业实践。
Agent RL 框架深度对比:从算法原理到工程实践
引言:从 SFT 到 RL,一个根本性的转变
在深入框架之前,我们需要先理解一个根本问题:为什么预训练和监督微调(SFT)已经不够用了?
预训练:学会「说什么」
预训练的核心目标是 next token prediction——给定前文,预测下一个词。这个过程让模型学会了语言的知识、语法、世界知识——本质上是在学习「说什么」。
SFT:学会「怎么说」
SFT(Supervised Fine-Tuning)用人类标注的数据,教会模型「怎么说」——遵循指令、生成合理的回答。但这带来了两个根本性的局限:
- 数据效率低: 人类标注成本极高,模型只能学到「最好的回答」,无法探索大量可能的回答空间
- 能力天花板: SFT 本质上是「模仿」,模型无法超越标注者的水平
RL:学会「什么是好」
RL(Reinforcement Learning)的核心思想是:让模型自己探索,通过奖励信号学习什么是「好」的回答。
这带来质的飞跃:
- 数据效率: 同一个 prompt 可以生成成千上万条 response,模型从中学习
- 超越模仿: 不再受限于人类标注,模型可以发现人类没想过的回答方式
- 涌现能力: DeepSeek-R1、OpenAI o1 等推理能力的涌现,都离不开 RL
关键洞察: RL 的本质不是「执行指令」,而是「定义目标,让模型自己找到最优路径」。
第一部分:RLHF 算法与流程基础
1.1 强化学习基本概念
让我们用「教狗狗握手」来理解 RL:
| RL 概念 | 比喻 | LLM 中的含义 |
|---|---|---|
| Agent | 狗狗 | LLM 本身 |
| Action | 伸爪子 | 生成一个 token |
| Reward | 零食 (+1) 或没有 (0) | 奖励模型评分 / 规则判断 |
| Policy | 狗狗的「决策脑」 | LLM 的生成逻辑 |
1.2 完整的 RLHF 训练流程
┌─────────────────────────────────────────────────────────────────────────┐
│ RLHF 完整训练流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Prompt │ ──→ │ Rollout │ ──→ │ Reward│ ──→ │ Update │ │
│ │ 准备 │ │ 生成 │ │ 计算 │ │ 策略 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ 阶段 1 阶段 2 阶段 3 阶段 4 │
│ 准备问题 LLM 生成回答 评估回答质量 更新模型参数 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
每个阶段的详细说明:
| 阶段 | 输入 | 输出 | 耗时占比 |
|---|---|---|---|
| 1. Prompt 准备 | 原始问题池 | 待生成的问题 | ~5% |
| 2. Rollout | 问题 | 生成的回答 | ~70-90% |
| 3. Reward | 回答 | 奖励分数 | ~10-20% |
| 4. Update | 奖励 | 模型参数更新 | ~5-10% |
什么是 Rollout?
Rollout 是 RL 术语,意思是「推演」——让 Agent 与环境交互,收集数据。
在 LLM 场景中:
# Rollout = 让 LLM 生成回答
prompt = "如何实现快速排序?"
# 对同一个问题,生成多个不同的回答
responses = [
"快速排序是一种分治算法...",
"我来介绍快速排序的实现...",
"快速排序的核心思想是..."
]
为什么要生成多个回答?
这是 RL 的核心思想:通过比较来学习。
问题: "2 + 3 = ?"
回答 A: "2 + 3 = 5" → 奖励: 1.0 (正确)
回答 B: "2 + 3 = 6" → 奖励: 0.0 (错误)
回答 C: "2 + 3 等于 5" → 奖励: 1.0 (正确)
→ 模型学会: 生成类似 A、C 的回答,避开 B
奖励(Reward)从哪来?
| 奖励类型 | 来源 | 例子 |
|---|---|---|
| 规则奖励 | 代码执行、格式验证 | 数学答案正确=1,错误=0 |
| Reward Model | 训练好的神经网络 | 判断回答质量 |
| 人类反馈 | 人工标注 | 成本高,很少用 |
1.3 PPO——RLHF 的开山之作
PPO(Proximal Policy Optimization)是 RLHF 的经典算法,也是 ChatGPT 背后的核心技术之一。
为什么需要 PPO?
直接用标准 RL 更新策略会有两个致命问题:
- 策略崩溃(Policy Collapse): 模型完全放飞自我,生成语法错误、逻辑混乱的文本
- 训练不稳定: 梯度更新幅度过大,模型性能急剧下降
PPO 的核心思想:保守更新
PPO 通过一个简单的 Clip 机制,确保策略不会发生剧烈变化:
# PPO 目标函数(简化)
L(θ) = E[min(r(θ) * A, clip(r(θ), 1-ε, 1+ε) * A)]
# r(θ) = π_θ(a|s) / π_OLD(a|s) # 新旧策略的概率比
# A = Advantage # 优势函数
# clip() 限制 r(θ) 在 [1-ε, 1+ε] 范围内 # ε 通常取 0.2
这就是 PPO 中 「Proximal(近端)」 的含义——步子不要迈太大。
PPO 在 LLM 中的架构
PPO 需要 四个模型:
| 模型 | 作用 | 比喻 |
|---|---|---|
| Actor | 生成 response,学习 | 做题的学生 |
| Critic | 评估「这个回答大概能得多少分」 | 评分老师 |
| Reward Model | 判断「这个回答好不好」 | 改卷老师 |
| Reference | 保持「不偏离太远」 | 锚点 |
问题: 对于百亿参数的 LLM,4 份模型权重意味着巨大的显存开销。
1.4 GRPO——工业界的主流选择
GRPO(Group Relative Policy Optimization)是 DeepSeek 在 2024 年提出的算法,迅速成为工业界主流。
GRPO 的核心 insight
PPO 需要 4 个模型的根本原因是:需要估计 value function(如果没有采取这个动作,会获得多少奖励)。
DeepSeek 发现:对于 LLM,其实可以不用显式估计 value function!
GRPO 的做法
# 1. 对每个 prompt,采样 G 个 response
responses = [policy.generate(prompt) for _ in range(G)]
# 2. 计算每个 response 的奖励
rewards = [reward_model(r) for r in responses]
# 3. 用 group 内相对排名计算 Advantage
mean_r = sum(rewards) / len(rewards)
std_r = (sum((r - mean_r)**2 for r in rewards) / len(rewards)) ** 0.5
advantages = [(r - mean_r) / std_r for r in rewards]
# 4. 直接更新策略
policy_update(advantages)
GRPO 的优势
| 对比项 | PPO | GRPO |
|---|---|---|
| 模型数量 | 4 个 | 2 个 |
| Value Function | 需要 | 不需要 |
| 显存开销 | 高 | 降低 50%+ |
| 对稀疏奖励 | 一般 | 更友好 |
GRPO 为什么会 work?
核心洞察: RL 的本质是 利用「相对比较」而非「绝对分数」。
- PPO 问:「这个回答得 85 分,好还是不好?」
- GRPO 问:「这个回答在 100 个回答里排第 10,好还是不好?」
答案是:相对比较更稳定。奖励模型的分数可能有偏,但 group 内的相对排序更 robust。
1.5 DAPO——字节的最新突破
DAPO 是字节跳动在 2025 年初开源的算法,是 GRPO 的增强版:
| 改进 | 说明 | 效果 |
|---|---|---|
| Clip-Higher | 改变 Clip 方向 | 允许策略在「好」的方向上多学一点 |
| Token-level Loss | 从 sequence 细化到 token | 更精细的梯度 |
| Dynamic Sampling | 过滤明显错误的样本 | 提高数据效率 |
关键结果: DAPO 在 AIME 2024 数学竞赛上取得了 50 分(Qwen2.5-32B),超越同期 DeepSeek-R1-Zero。
第二部分:如果让你实现一个最简单的框架
现在,让我们从第一性原理出发,思考一个问题:最简陋的 RL 训练框架应该怎么实现?
这个思考过程会帮助我们理解:为什么需要专门的框架?这些框架在解决什么问题?
2.1 最简单的实现:顺序执行
def simplest_rl_framework():
"""最 naive 的实现:每个阶段顺序执行"""
while training:
# 阶段 1: 准备问题
prompts = get_prompts(batch_size=16)
# 阶段 2: 生成回答 (GPU 跑推理)
responses = []
for prompt in prompts:
response = actor.generate(prompt)
responses.append(response)
# 阶段 3: 计算奖励 (CPU 或 GPU)
rewards = []
for response in responses:
reward = reward_fn(response)
rewards.append(reward)
# 阶段 4: 更新模型 (GPU 跑训练)
actor.update(responses, rewards)
这段代码有什么问题?
| 问题 | 后果 |
|---|---|
| 生成是串行的 | GPU 大部分时间在等待 |
| 训练要等生成完成 | GPU 利用率极低 |
| 没有并行 | 16 个 prompt 串行生成 |
2.2 问题分析:生成是瓶颈
时间线(顺序执行):
GPU 利用率: ████░░░░░░░░░ 40%
[生成...] [生成...] [生成...] [生成...] [奖励...] [训练...]
↑ GPU 跑 ↑ GPU 等待 ↑ GPU 等待
关键洞察: LLM 生成一个 response 可能需要几秒钟,而 GPU 做一次前向传播只需要几毫秒。生成是绝对的瓶颈。
2.3 改进一:批处理
def with_batching():
"""改进 1: 批量生成"""
prompts = get_prompts(batch_size=64)
# 批量生成 - GPU 并行处理
responses = actor.generate_batch(prompts)
# 批量计算奖励
rewards = reward_fn_batch(responses)
# 批量更新
actor.update_batch(responses, rewards)
效果: 批量生成可以提高 GPU 利用率,但还不够。
2.4 改进二:流水线化
def with_pipelining():
"""改进 2: 流水线化 - 多个 batch 并行"""
# 启动三个并行的 worker
prompt_worker = ThreadPool(get_prompts)
rollout_worker = ThreadPool(actor.generate)
reward_worker = ThreadPool(reward_fn)
update_worker = ThreadPool(actor.update)
# 流水线执行
queue1, queue2, queue3, queue4 = Queue(), Queue(), Queue(), Queue()
# Worker 1: 准备问题 -> queue1
# Worker 2: queue1 -> 生成 -> queue2
# Worker 3: queue2 -> 奖励 -> queue3
# Worker 4: queue3 -> 更新
# 关键: 多个 batch 同时在不同阶段
效果: 不同阶段可以并行,GPU 利用率提高。
2.5 改进三:分布式训练
对于超大模型,需要多 GPU:
def with_distributed():
"""改进 3: 分布式训练"""
# 模型并行: 不同的 GPU 跑模型的不同部分
actor = FSDP(model, device_ids=[0,1,2,3])
# 数据并行: 多个 GPU 跑同一个模型
# 梯度聚合: all_reduce
2.6 还有什么挑战?
即使做了这些优化,还有更多问题:
| 挑战 | 描述 | 解决方案 |
|---|---|---|
| 显存不够 | 4 个模型跑不动 | 3D 并行、ZeRO、Offload |
| 多节点 | 多台机器如何协同 | Ray、PyTorch DDP |
| 算法多样 | GRPO/PPO/DAPO 不同 | 灵活的模块化设计 |
| 推理框架 | vLLM vs SGLang | 统一的推理接口 |
2.7 总结:框架要解决的核心问题
经过这个思考过程,我们可以总结出 LLM RL 框架要解决的三个核心问题:
| 问题 | 目标 | 手段 |
|---|---|---|
| 吞吐量 | 让 GPU 少等待 | 批处理、流水线、异步 |
| 灵活性 | 支持不同算法 | 模块化、可插拔 |
| 扩展性 | 多卡多节点能跑 | 分布式、并行策略 |
第三部分:四大框架深度对比
这一部分基于对各框架源码和文档的深度调研,力求呈现每个框架的设计思路、实现原理和真实优缺点。
3.1 OpenRLHF——开源先驱
背景: 2024 年初开源,是最早将 Ray + vLLM 引入 RLHF 的开源框架之一
设计思路
OpenRLHF 的核心理念是 「Agent-Based Execution」——将所有 RLHF 训练流程统一为「Agent 执行」:
# OpenRLHF 的核心抽象
class AgentExecutorBase:
def execute(self, tokens):
# Token-in, Token-out
# 统一处理单轮和多轮场景
return trajectories
这个设计让 单轮 RLHF 和 多轮 Agent 共享同一套代码。
架构实现
┌─────────────────────────────────────────────────────────────┐
│ OpenRLHF Architecture │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Ray Scheduler (分布式调度) │ │
│ └──────────────────────┬──────────────────────────────┘ │
│ │ │
│ ┌──────────┬──────────┼──────────┬──────────┐ │
│ ▼ ▼ ▼ ▼ ▼ │
│ │ Actor │ Ref │ Reward │ Critic │ vLLM │ │
│ │ (训练) │ (参考) │ (奖励) │ (价值) │ (推理) │
│ └──────────┴──────────┴──────────┴──────────┘ │
│ │
│ Backend: Ray + vLLM + DeepSpeed │
└─────────────────────────────────────────────────────────────┘
关键技术点:
-
Ray 分布式调度
- Actor、Ref、Reward、Critic 分布在不同 GPU
- 支持 70B+ 参数模型
- 自动化的资源分配
-
vLLM 高效推理
- RLHF 80% 时间花在生成
- vLLM 的 Auto TP/PP 支持高吞吐生成
- PagedAttention 减少显存碎片
-
DeepSpeed 训练
- ZeRO-3 显存优化
- DeepCompile 加速
- 原生支持 HuggingFace 模型
优缺点
| 优点 | 缺点 |
|---|---|
| ✅ 简单易用,门槛低 | ❌ 吞吐量不如 verl |
| ✅ Agent 抽象优雅 | ❌ 主要支持 PPO/GRPO |
| ✅ 文档详尽 | ❌ 大规模扩展一般 |
| ✅ 社区活跃 (9.1k stars) | ❌ 需要较多 GPU 才能高效 |
适用场景
- 快速实验
- 小规模训练 (8-16 GPU)
- 学习 RLHF 原理
- 需要自定义 reward function
3.2 verl——工业级标准
背景: 字节跳动 Seed 团队开源,已在生产环境验证多年
设计思路
verl 的核心理念是 「HybridFlow」——灵活的数据流编排 + 高效的 3D 并行:
传统框架: 固定的数据流
verl: 可编程的数据流 (你定义怎么跑)
这让 verl 可以适配各种训练场景:从 GRPO 到 DAPO,从单轮对话到 Agent 训练。
架构实现
# verl 的核心抽象 - Hybrid Controller
class HybridController:
def __init__(self):
self.trainer = ...
self.rollout = ...
self.reward = ...
# 你可以自定义数据流
def forward(self, data):
# 方式 1: PPO 风格
responses = self.rollout.generate(data)
rewards = self.reward.compute(responses)
return self.trainer.ppo_update(responses, rewards)
# 方式 2: GRPO 风格
responses = self.rollout.generate_group(data)
advantages = self.compute_grpo_advantage(responses)
return self.trainer.grpo_update(advantages)
核心创新:
-
3D-HybridEngine
模型切分维度: - 数据并行: 多个 prompt 同时处理 - 流水线并行: 模型分 stages - 张量并行: 模型内部并行 关键优化: 训练 ↔ 生成 阶段切换时,动态调整显存布局 -
模块化设计
# 训练后端可选 trainer = verl.trainer.FSDPTrainer() # 或 MegatronTrainer # 推理后端可选 rollout = verl.rollout.vLLMRollout() # 或 SGLangRollout -
丰富的算法支持
- GRPO、DAPO、VAPO、PPO、PF-PPO
- ReTool (代码 Agent)
- VLA (视觉语言 Agent)
优缺点
| 优点 | 缺点 |
|---|---|
| ✅ 工业级验证 (字节生产环境) | ❌ 复杂度高 |
| ✅ 吞吐量 SOTA | ❌ 文档偏向内部 |
| ✅ 算法最全 (DAPO/VAPO) | ❌ 门槛较高 |
| ✅ 支持超大规模 (DeepSeek-671B) |
生产案例
- Seed-Thinking-v1.5: AIME 2024 86.7 分
- DAPO: AIME 2024 50 分
- DeepSeek-R1: 部分训练使用 verl
- Mind Lab: 1 万亿参数 MoE 模型训练
3.3 slime——智谱的选择
背景: 清华大学 THUDM(智谱 AI)开源,支撑 GLM 系列模型训练
设计思路
slime 的核心理念是 「SGLang-Native + 彻底解耦」:
slime = SGLang (最快推理) + Megatron (高效训练) + Data Buffer (异步解耦)
与 verl 的「可配置」不同,slime 选择了 「解耦得彻底」——Training 和 Rollout 完全独立运行。
架构实现
# slime 的核心设计
# 1. Data Buffer - 异步数据传递
class DataBuffer:
def __init__(self):
self.prompt_queue = Queue() # 待生成的问题
self.response_queue = Queue() # 生成好的回答
self.reward_queue = Queue() # 计算好的奖励
async def put_prompt(self, prompt):
await self.prompt_queue.put(prompt)
async def get_response(self):
return await self.response_queue.get()
# 2. Training - 独立进程
# 读取 response_queue,计算梯度,更新参数
# 3. Rollout - 独立进程 (SGLang)
# 读取 prompt_queue,生成 response,写入 response_queue
关键设计:
-
SGLang 深度集成
- 连续批处理 (Continuous Batching)
- Prefix Caching
- 最快的 LLM 推理框架之一
-
自定义数据生成接口
# 用户可以定义任意的 rollout 策略 class MyRollout: def generate(self, prompt): # 可以是多轮对话 # 可以是工具调用 # 可以是复杂环境交互 return response -
Megatron 训练后端
- 张量并行原生支持
- 流水线并行优化
优缺点
| 优点 | 缺点 |
|---|---|
| ✅ SGLang 推理极快 | ❌ 生态较小 |
| ✅ 灵活的数据生成 | ❌ 文档较少 |
| ✅ GLM 系列背书 | ❌ 算法支持有限 |
| ✅ 解耦设计利于调试 |
生态项目
- P1: 物理推理模型,完全 RL 训练
- TritonForge: RL 训练 GPU kernel 生成
- qqr (hilichurl): 阿里开源 Agent 进化框架
3.4 AReaL——极速代表
背景: 清华大学 IIIS + 蚂蚁集团开源,专注极致速度
设计思路
AReaL 的核心理念是 「Fully Async + boba²」——全异步流水线,目标是消除一切等待时间:
其他框架: 同步执行,GPU 大部分时间等待
AReaL: 全异步,GPU 永远在干活
架构实现
# AReaL 的异步流水线
# 阶段 1: Rollout (生成)
async def rollout_worker(prompts, model):
while True:
batch = await prompt_queue.get()
responses = await model.generate(batch)
await reward_queue.put(responses)
# 阶段 2: Reward (奖励)
async def reward_worker(reward_model):
while True:
responses = await reward_queue.get()
rewards = await reward_model.score(responses)
await advantage_queue.put((responses, rewards))
# 阶段 3: Update (更新)
async def update_worker(actor):
while True:
data = await advantage_queue.get()
await actor.update(data)
# 三个 worker 完全并行运行
boba² 加速技术:
-
动态批处理
- 根据实际生成长度动态调整 batch size
- 短序列和长序列自动分组
-
预取优化
- 提前准备下一个 batch 的数据
- 消除 I/O 等待
-
2.77× 吞吐量提升 (相比同步方案)
优缺点
| 优点 | 缺点 |
|---|---|
| ✅ 速度最快 (boba²) | ❌ 算法支持较少 |
| ✅ Agent 友好 | ❌ 主要针对推理任务 |
| ✅ 支持华为 NPU | ❌ 社区较小 |
| ✅ AReaL-lite 轻量 |
生产案例
- 235B MoE: 超越 GPT-5
- 搜索 Agent: 端到端异步训练
- 客服 Agent: 多轮对话训练
3.5 横向对比
核心架构对比
| 框架 | 调度器 | 推理引擎 | 训练后端 | 核心创新 |
|---|---|---|---|---|
| OpenRLHF | Ray | vLLM | DeepSpeed | Agent 抽象 |
| verl | 自研 | vLLM/SGLang | FSDP/Megatron | HybridFlow |
| slime | 自研 | SGLang | Megatron | 彻底解耦 |
| AReaL | 自研 | SGLang | 自研 | Fully Async |
算法支持对比
| 算法 | OpenRLHF | verl | slime | AReaL |
|---|---|---|---|---|
| PPO | ✅ | ✅ | ✅ | ✅ |
| GRPO | ✅ | ✅ | ✅ | ✅ |
| DAPO | - | ✅ | - | - |
| VAPO | - | ✅ | - | - |
| REINFORCE++ | ✅ | - | ✅ | ✅ |
| RLOO | ✅ | - | - | ✅ |
| PF-PPO | - | ✅ | - | - |
性能与效率
| 指标 | OpenRLHF | verl | slime | AReaL |
|---|---|---|---|---|
| 吞吐量 | 中 | SOTA | 高 | 很高 |
| 显存效率 | 中 | 高 | 很高 | 高 |
| 扩展性 | 中 | 极高 | 高 | 高 |
| 启动难度 | 低 | 高 | 中 | 低 |
选型决策树
需要大规模生产训练?
│
├─ YES → verl (工业验证)
│
└─ NO → 需要快速实验?
│
├─ YES → OpenRLHF (简单) 或 AReaL-lite (快速)
│
└─ NO → 需要训练 GLM?
│
├─ YES → slime
│
└─ NO → 需要 Agent 训练?
│
├─ YES → AReaL (异步) 或 verl (ReTool)
│
└─ NO → 都可以,看团队熟悉度
第四部分:从零开始跑通你的第一个 RLHF 训练
这一部分是写给完全新手的。不需要你有 GPU 集群,不需要你有模型权重,只要你有一台有显卡的电脑,就能跟着一步步做下来。
我们会解决一个问题:网上教程要么太简单(只告诉你安装命令),要么太难(假设你已经有完整基础设施)。
这篇不会告诉你所有参数什么意思,而是带你从 0 到 1 跑通第一个 RLHF 训练,然后你自己会去探索那些参数。
4.1 跑 RLHF 之前,你必须知道的 3 件事
在动手之前,先回答 3 个问题:
问题 1: 我需要什么硬件?
| 配置 | 能做什么 |
|---|---|
| 1 张 RTX 3090/4090 (24G) | 跑通流程,验证代码能work |
| 4 张 A100 80G | 可以训练 7B 模型 |
| 8+ 张 A100 80G | 可以训练 70B 模型 |
我的建议: 先用 1 张卡验证流程,不要一上来就搞多卡。
问题 2: 模型从哪里来?
你不需要自己训练模型,可以直接下载开源的:
# 方法 1: 用 HuggingFace
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-7B-Instruct")
# 方法 2: 用魔搭社区 (国内快)
# https://modelscope.cn/models/qwen/Qwen2.5-7B-Instruct
推荐模型(按难度排序):
| 模型 | 大小 | 适合 |
|---|---|---|
| Qwen2.5-1.5B | 3GB | 第一次跑,显卡一般 |
| Qwen2.5-7B | 15GB | 主流实验 |
| Llama-3-8B | 16GB | 效果更好 |
问题 3: 训练一个 RLHF 要多久?
| 模型 | GPU | 预计时间 |
|---|---|---|
| 1.5B | 1x 3090 | 1-2 小时 |
| 7B | 4x A100 | 2-4 小时 |
| 7B | 8x A100 | 30-60 分钟 |
重要的是先跑通,而不是优化时间。
4.2 环境搭建:最简单的方式
方式 A: 用 Docker(推荐)
如果你的服务器有 NVIDIA 显卡 + Docker:
# 1. 启动容器
docker run --runtime=nvidia -it --rm \
--gpus all \
--shm-size=10g \
-v $PWD:/workspace \
nvcr.io/nvidia/pytorch:24.03-py3 bash
# 2. 容器内安装 OpenRLHF
pip install openrlhf[vllm]
好处: 不用自己装 CUDA、cuDNN,不会遇到环境问题。
方式 B: 不用 Docker
如果你想本地装:
# 创建虚拟环境
conda create -n rlhf python=3.10
conda activate rlhf
# 安装 PyTorch (选择和你显卡匹配的版本)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# 安装 OpenRLHF
pip install openrlhf[vllm]
新手常见问题:
- ❌ 不要自己装 CUDA —— 用 Docker 或 conda
- ❌ 不要一上来就尝试多节点 —— 先单机跑通
4.3 第一步:准备你的数据
RLHF 训练的数据格式其实很简单。你需要两类数据:
数据 1: Prompt(问题)
就是你想让模型回答的问题。
[
{"role": "user", "content": "如何快速排序?"},
{"role": "user", "content": "什么是强化学习?"},
{"role": "user", "content": "用 Python 写一个 Hello World"}
]
保存为 prompts.jsonl(每行一个 JSON):
{"role": "user", "content": "如何快速排序?"}
{"role": "user", "content": "什么是强化学习?"}
数据 2: Reward(奖励)
有两种方式:
方式 A: 自己写奖励函数(不需要额外数据)
# reward.py
def reward_func(prompts, responses, labels=None):
"""根据规则计算奖励"""
rewards = []
for response in responses:
score = 0.0
# 规则 1: 回答不能太短
if len(response) > 20:
score += 0.3
# 规则 2: 回答中包含代码关键词
if "def " in response or "class " in response:
score += 0.4
# 规则 3: 回答长度适中
if 50 < len(response) < 500:
score += 0.3
rewards.append(score)
return rewards
方式 B: 用已有数据集(推荐新手)
# 最简单的开始方式:直接用现成数据集
from datasets import load_dataset
# 这个数据集包含各种编程问题
dataset = load_dataset("openai/gsm8k", "main") # 数学问题
dataset = load_dataset("bigcode/humaneval", "main") # 代码问题
# 取前 100 条作为 prompt
prompts = [item["question"] for item in dataset["test"][:100]]
4.4 跑通你的第一个训练
最简配置(1 张 GPU 就能跑)
python -m openrlhf.cli.train_ppo_ray \
--pretrain Qwen/Qwen2.5-1.8B-Instruct \
--prompt_data path/to/your/prompts.jsonl \
--input_key content \
--remote_rm_url path/to/your/reward.py \
--train_batch_size 32 \
--rollout_batch_size 128 \
--micro_rollout_batch_size 16 \
--micro_train_batch_size 4 \
--max_samples 1000 \
--num_epochs 1 \
--save_path ./checkpoints/rlhf_model
你会看到什么?
运行后,等 1-2 分钟(首次启动要编译),你会看到类似输出:
# 启动阶段
[INFO] Initializing model...
[INFO] Model: Qwen2.5-1.8B-Instruct
[INFO] Loading prompts from: prompts.jsonl
[INFO] Total prompts: 1000
[INFO] Initializing vLLM engine...
# 训练阶段
[INFO] Step 1/100 | Loss: 0.523 | Reward: 0.234 | KL: 0.012
[INFO] Step 10/100 | Loss: 0.456 | Reward: 0.389 | KL: 0.018
[INFO] Step 20/100 | Loss: 0.412 | Reward: 0.512 | KL: 0.021
...
[INFO] Training completed! Saving model to ./checkpoints/rlhf_model
怎么看训练是否顺利?
| 指标 | 正常范围 | 警惕 |
|---|---|---|
| Loss | 逐渐下降 | 突然飙升 |
| Reward | 逐渐上升 | 一直 0 |
| KL | 0.01 ~ 0.05 | > 0.1 |
重点: 第一次跑,不用追求效果,先看能不能跑完。
4.5 怎么评估模型变好了?
跑完后,你会得到一个微调过的模型。评估它:
方法 1: 简单测试
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("./checkpoints/rlhf_model")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen2.5-1.8B-Instruct")
# 测试一个问题
prompt = "如何实现快速排序?"
inputs = tokenizer(prompt, return_tensors="pt")
output = model.generate(**inputs, max_new_tokens=200)
response = tokenizer.decode(output[0], skip_special_tokens=True)
print(response)
方法 2: 对比基准
# 对比原始模型 vs 微调模型
original_response = original_model.generate(...)
tuned_response = tuned_model.generate(...)
print("原始回答:", original_response)
print("微调后回答:", tuned_response)
方法 3: 用评估数据集
from datasets import load_dataset
from transformers import evaluate
# 用 HumanEval 评估代码能力
accuracy = evaluate.load("humaneval")
predictions = [generate_code(item["prompt"]) for item in dataset]
results = accuracy.compute(predictions=predictions, references=dataset["test"])
print(f"Pass@{1} = {results['pass_at_k']}")
4.6 常见问题与解决
Q1: 显存不够怎么办?
Error: CUDA out of memory
按顺序尝试:
-
减少 batch size:
--micro_rollout_batch_size 8 --micro_train_batch_size 2 -
开启梯度检查点:
--gradient_checkpointing -
用更小的模型:
7B → 1.8B → 0.5B -
用量化:
--quantization 4bit
Q2: 训练一直卡着不动?
检查:
# 看 GPU 使用率
nvidia-smi
# 看进程日志
ps aux | grep python
如果 GPU 利用率 0%,可能是:
- vLLM 启动太慢(等 3-5 分钟)
- 代码有 bug(看错误日志)
Q3: Reward 一直是 0?
原因: 奖励函数设计太难,模型答不到
解决:
- 简化奖励函数(先只看回答长度)
- 增加正例比例
def reward_func(prompts, responses):
# 先用最简单的:只要回答了就给分
return [min(len(r) / 100, 1.0) for r in responses]
4.7 下一步做什么?
跑通第一个 Demo 后,你可以:
进阶 1: 换成真正的奖励模型
# 不再用自己的 reward.py,而是用训练好的 Reward Model
python -m openrlhf.cli.train_ppo_ray \
--pretrain Qwen/Qwen2.5-7B-Instruct \
--reward_pretrain openai/shepherd-2-1.8b \
...
进阶 2: 加入 SFT 热启动
# 先做 SFT,再做 RLHF
# 1. SFT
python -m openrlhf.cli.train_sft \
--pretrain Qwen/Qwen2.5-7B-Instruct \
--dataset your_sft_data.jsonl
# 2. RLHF (用 SFT 后的模型)
python -m openrlhf.cli.train_ppo_ray \
--pretrain ./checkpoints/sft_model \
...
进阶 3: 尝试不同算法
# GRPO (更简单,不需要 Reward Model)
--advantage_estimator group_norm
# REINFORCE++ (适合推理任务)
--advantage_estimator reinforce
4.8 资源汇总
入门首选
- OpenRLHF GitHub: https://github.com/OpenRLHF/OpenRLHF
- 官方文档: https://openrlhf.readthedocs.io/
模型下载
- HuggingFace: https://huggingface.co/models
- 魔搭社区: https://modelscope.cn (国内快)
学习资料
- PPO 原始论文
- DeepSeek GRPO 论文
4.9 总结
这一节我们覆盖了:
- 硬件需求: 1 张卡就能跑
- 模型来源: HuggingFace 直接下载
- 数据准备: Prompt + Reward 两类
- 完整流程: 从跑通到评估
- 常见问题: 显存、卡住、Reward=0
核心建议: 先跑通,再优化。不要一上来就调参。
总结
写到最后,分享几点我的思考。
关于算法:PPO 统治了 ChatGPT 时代,但 GRPO 用「相对比较」的智慧,让 RLHF 变得简单高效。DeepSeek 证明了一个深刻的道理:有时候,少即是多。
关于框架:我调研了四个主流框架,发现它们本质上在解决三个问题——怎么让 GPU 少等待(吞吐)、怎么支持更多算法(灵活)、怎么在大规模训练(扩展)。每个框架给出了不同的答案:OpenRLHF 追求简单、verl 追求极致性能、slime 追求解耦、AReaL 追求速度。
关于实践:框架只是工具,真正的能力来自对问题的理解。建议先跑通一个 Demo,感受数据是怎么流动的,然后再深入研究框架的细节。
关于未来:Agent RL 是下一个爆发点。Coding Agent 的发展会倒逼训练框架的进化。异步化、Agent 化、多模态是明确的方向。
希望这篇文章能帮你建立起对 LLM RL 训练的整体认知。动手实践远比纸上谈兵重要。
参考资源
- OpenRLHF GitHub · 文档
- verl GitHub · 文档
- slime GitHub · 博客
- AReaL GitHub · 文档
- DeepSeek GRPO Paper
- DAPO Paper
- VAPO Paper
本文会持续更新。