PPO 近端策略优化


文档摘要

PPO 近端策略优化 本节摘要:A2C 每次更新后就扔掉滚动。PPO 用一个裁剪的重要性比率把策略梯度包起来,让你能在同一份数据上跑 10+ 个 epoch 而不让策略爆炸。Schulman et al. (2017),九年后仍是默认策略梯度算法。本节讲透它的核心:重要性比率 ;裁剪代理目标 (典型 ε=0.2)如何在「好的一侧」给梯度盖平顶、「坏的一侧」盖平地;完整训练循环(收集 → 算 GAE → 冻结旧策略 → K 个 epoch 小批量更新 → 丢弃);以及三大诊断——平均 KL、裁剪比例、解释方差。你会在 4×4 GridWorld 上看到它比 A2C 样本效率更高,并理解为什么 RLHF 几乎都用 PPO 做策略优化器。

PPO 近端策略优化

本节摘要:A2C 每次更新后就扔掉滚动。PPO 用一个裁剪的重要性比率把策略梯度包起来,让你能在同一份数据上跑 10+ 个 epoch 而不让策略爆炸。Schulman et al. (2017),九年后仍是默认策略梯度算法。本节讲透它的核心:重要性比率 r_t(θ) = π_θ(a_t|s_t) / π_{θ_old}(a_t|s_t);裁剪代理目标 min(r·A, clip(r, 1-ε, 1+ε)·A)(典型 ε=0.2)如何在「好的一侧」给梯度盖平顶、「坏的一侧」盖平地;完整训练循环(收集 → 算 GAE → 冻结旧策略 → K 个 epoch 小批量更新 → 丢弃);以及三大诊断——平均 KL、裁剪比例、解释方差。你会在 4×4 GridWorld 上看到它比 A2C 样本效率更高,并理解为什么 RLHF 几乎都用 PPO 做策略优化器。

对应原课程:Phase 9 · Lesson 08 · ppo(原英文 phases/09-reinforcement-learning/08-ppo/docs/en.md)。

学习目标

阅读完本节,你应当能够:

  1. 写出重要性比率 r_t(θ) = π_θ(a_t|s_t) / π_old(a_t|s_t) 并说明它的数值含义(r=2 表示什么)。
  2. 推导裁剪代理目标 min(r·A, clip(r, 1-ε, 1+ε)·A),解释四象限(优势正/负 × 比率大小)下梯度的行为。
  3. 写出 PPO 的完整训练循环:收集 N×T 转移 → GAE → 冻结旧策略 → K epoch 小批量更新 → 丢弃。
  4. 监控三大诊断(平均 KL、裁剪比例、解释方差)并据此调 K_EPOCHSLR
  5. 说明 PPO 为何是 RLHF 的默认策略优化器,以及它如何演化出 DPO 与 GRPO。

一、问题与直觉

A2C(第 07 节)是 on-policy 的:梯度 E_{π_θ}[A · ∇ log π_θ] 要求当前 π_θ 采样的数据。更新一次,π_θ 变了;刚用的数据就成了 off-policy。再用它,你的梯度就有偏。

滚动很贵。Atari 上,8 环境 × 128 步 = 1024 转移,要十几秒环境时间。一次梯度步后扔掉,太浪费。

TRPO(Schulman 2015)是第一个修复:约束每次更新让新旧策略的 KL 散度低于 δ。理论上干净,但每次更新要解一次共轭梯度。2026 年没人跑 TRPO。

PPO(Schulman et al. 2017)用一个简单的裁剪目标替换硬信赖域约束。多一行代码。每滚动 10 个 epoch。无共轭梯度。理论保证够用。九年后,从 MuJoCo 到 RLHF,它仍是默认策略梯度算法。

重要性比率

r_t(θ) = π_θ(a_t | s_t) / π_{θ_old}(a_t | s_t)

这是新策略相对「采集数据时那个策略」的似然比。r_t = 1 表示没变;r_t = 2 表示新策略采取 a_t 的概率是旧策略的两倍。

裁剪代理目标

L^{CLIP}(θ) = E_t [ min( r_t(θ) A_t, clip(r_t(θ), 1-ε, 1+ε) A_t ) ]

两项:

  • 若优势 A_t > 0 且比率想涨过 1 + ε,裁剪把梯度抹平——别把好动作推到旧概率之上 以外。
  • 若优势 A_t < 0 且比率想涨过 1 - ε(意思是相对其裁剪后的降低,我们会让坏动作更可能),裁剪封顶梯度——别把坏动作压到 以下。

min 处理另一方向:若比率朝有利方向移动,你仍得到梯度(不在会伤你的一侧裁剪)。

典型 ε = 0.2。把目标作为 r_t 的函数画出来:一个分段线性函数,「好的一侧」有平顶,「坏的一侧」有平地。

完整 PPO 损失

L(θ, φ) = L^{CLIP}(θ) - c_v · (V_φ(s_t) - V_t^{target})² + c_e · H(π_θ(·|s_t))

与 A2C 同结构。三个系数,通常 c_v = 0.5c_e = 0.01ε = 0.2

训练循环

  1. N 个并行环境收集 N × T 转移,每环境 T 步。
  2. 算优势(GAE),作为常量冻结。
  3. π_{θ_old} 冻结为当前 π_θ 的快照。
  4. K 个 epoch,对每个 (s, a, A, V_target, log π_old(a|s)) 小批量:
    • r_t(θ) = exp(log π_θ(a|s) - log π_old(a|s))
    • L^{CLIP} + 价值损失 + 熵。
    • 梯度步。
  5. 丢弃滚动,回到第 1 步。

K = 10、小批量 64 是标准超参。PPO 鲁棒:在 ±50% 内具体数字很少要紧。

KL 惩罚变体

原论文还提了一个用自适应 KL 惩罚的替代:L = L^{PG} - β · KL(π_θ || π_old),β 按观测到的 KL 调。裁剪版成了主流;KL 版在 RLHF 里延续(那里对参考策略的 KL 是你本来就一直想要的独立约束)。

💡 PPO 的精髓是「允许数据被复用,但不许走太远」。r_t 衡量「走多远」,clip(·, 1±ε) 是「不许超过 ε」。这个简单约束换来的是 10× 的样本效率——这是它在 RLHF 里活到 2026 年的根本原因:LLM 的每次滚动都要跑完整段生成,太贵了,扔不起。

二、从零实现

Step 1:在滚动时记录 log π_old(a | s)

for step in range(T): probs = softmax(logits(theta, state_features(s))) a = sample(probs, rng) s_next, r, done = env.step(s, a) buffer.append({ "s": s, "a": a, "r": r, "done": done, "v_old": value(w, state_features(s)), "log_pi_old": log(probs[a] + 1e-12), }) s = s_next

快照在滚动时取一次,更新 epoch 里不变。

Step 2:算 GAE 优势(第 07 节)

与 A2C 同。跨 batch 归一化。

Step 3:裁剪代理更新

for _ in range(K_EPOCHS): for mb in minibatches(buffer, size=64): for rec in mb: x = state_features(rec["s"]) probs = softmax(logits(theta, x)) logp = log(probs[rec["a"]] + 1e-12) ratio = exp(logp - rec["log_pi_old"]) adv = rec["advantage"] surrogate = min( ratio * adv, clamp(ratio, 1 - EPS, 1 + EPS) * adv, ) # 反传 -surrogate,加价值损失,减熵 grad_logpi = onehot(rec["a"]) - probs if (adv > 0 and ratio >= 1 + EPS) or (adv < 0 and ratio <= 1 - EPS): pg_grad = 0.0 # 被裁剪 else: pg_grad = ratio * adv for i in range(N_ACTIONS): for j in range(N_FEAT): theta[i][j] += LR * pg_grad * grad_logpi[i] * x[j]

「裁剪 → 零梯度」这个模式是 PPO 的核心。如果新策略已经朝有利方向飘得太远,更新就停。

Step 4:价值与熵

加上对 critic 目标的标准 MSE 与 actor 上的熵正则,与 A2C 同。

Step 5:诊断

每次更新盯三件事:

  • 平均 KL E[log π_old - log π_θ]。应留在 [0, 0.02]。若冲过 0.1,降 K_EPOCHSLR
  • 裁剪比例——比率落在 [1-ε, 1+ε] 外的样本占比。应 ~0.1-0.3。若 ~0,裁剪从不触发 → 升 LRK_EPOCHS;若 ~0.5+,你在过拟合滚动 → 降它们。
  • 解释方差 1 - Var(V_target - V_pred) / Var(V_target)。critic 质量指标,应随 critic 学习爬向 1。

💡 裁剪比例是 PPO 的「体温计」。它直接告诉你「数据复用有没有过头」。0.1~0.3 是健康区间;持续 >0.4 说明策略飘离旧策略太远,该降 K 或 LR;接近 0 说明你太保守,该激进点。

三、框架对比

PPO 是 2026 年跨多个领域的默认 RL 算法:

用例 PPO 变体
MuJoCo / 机器人控制 PPO + 高斯策略 + GAE(0.95)
Atari / 离散博弈 PPO + 分类型策略 + 滚动 128 步
LLM 的 RLHF PPO + 对参考模型的 KL 惩罚 + 响应末尾的 RM 奖励
大规模博弈智能体 IMPALA + PPO(AlphaStar、OpenAI Five)
推理 LLM GRPO(第 12 节)——无 critic 的 PPO 变体
仅偏好数据 DPO——PPO+KL 的闭式塌缩,无需在线采样

PPO 的损失形状——裁剪代理 + 价值 + 熵——是 DPO、GRPO 与几乎所有 RLHF 流水线的脚手架。

与 stable-baselines3 / HuggingFace TRL 的对照

stable-baselines3PPO 是控制任务的标准实现,默认带 GAE、优势归一化、裁剪、熵正则、学习率线性衰减。LLM 场景则用 HuggingFace TRL 的 PPOTrainer——它把 PPO 适配到序列生成:奖励来自响应末尾的奖励模型,价值头挂在策略主干上,且内置对参考模型的自适应 KL。第 09 节我们会看到它的完整用法。两套实现都把本节手写骨架工程化,值得对照阅读。

四、可复用产物

本节产出一个可复用 skill(位于原课程 outputs/skill-ppo-trainer.md)。骨架:

--- name: ppo-trainer description: 为给定环境产出 PPO 训练配置与诊断计划。 version: 1.0.0 phase: 9 lesson: 8 tags: [rl, ppo, policy-gradient] --- 给定一个环境与训练预算,输出: 1. 滚动大小。N 环境 × T 步。 2. 更新计划。K epoch、小批量大小、LR 调度。 3. 代理参数。ε(裁剪)、c_v、c_e,优势归一化开。 4. 优势。GAE(λ),给出显式 γ 与 λ。 5. 诊断计划。KL、裁剪比例、解释方差阈值与告警。 拒绝 K > 30 或 ε > 0.3(不安全的信赖域)。 拒绝任何没有优势归一化或 KL/裁剪监控的 PPO。 标记裁剪比例持续 > 0.4 为漂移。

五、练习

  1. 基础。 在 4×4 GridWorld 上跑 PPO,ε=0.2, K=4,在匹配环境步数下对比 A2C(每滚动一 epoch)的样本效率。
  2. 进阶。K ∈ {1, 4, 10, 30},画回报 vs 环境步,并跟踪每次更新的平均 KL。这个任务上 K 多大时 KL 爆?
  3. 挑战。 把裁剪代理换成自适应 KL 惩罚(KL > 2·目标 时 β 翻倍,KL < 目标/2 时 β 减半)。对比最终回报、稳定性与「无裁剪」程度。

六、常见陷阱

  • 裁剪系数没调好。 ε = 0.2 是事实标准。降到 0.1 更新太保守;0.3+ 招致不稳。
  • epoch 太多。 K > 20 常因策略飘离 π_old 太远而不稳。封顶 epoch,尤其对大网络。
  • 没归一化奖励。 大奖励尺度吃掉裁剪范围。算优势前归一化奖励(running std)。
  • 忘了优势归一化。 每 batch 零均值单位方差归一化是标准操作。跳过它在多数 benchmark 上毁掉 PPO。
  • 学习率没衰减。 PPO 受益于 LR 线性衰减到零。常数 LR 常更差。
  • 重要性比率的数学错误。 永远用 exp(log_new - log_old) 保证数值稳定,不要 new / old
  • 梯度符号错。 最大化代理 = 最小化 -L^{CLIP}。符号反是最常见的 PPO bug。

七、关键术语速查

术语 俗称 实际含义
重要性比率 「r_t(θ)」 π_θ(a|s) / π_old(a|s);相对采集数据时策略的偏离
裁剪代理 「PPO 的主技巧」 min(r·A, clip(r, 1-ε, 1+ε)·A);有利一侧过裁剪后梯度为零
信赖域 「TRPO / PPO 的意图」 限制每次更新的 KL 以保证单调改进
KL 惩罚 「软信赖域」 替代 PPO:L - β · KL(π_θ || π_old),自适应 β
裁剪比例 「裁剪多常触发」 诊断——应 0.1-0.3;偏离说明没调好
多 epoch 训练 「数据复用」 每滚动 K epoch;用方差换样本效率
准 on-policy 「基本是 on-policy」 PPO 名义上 on-policy,但 K>1 epoch 安全地用了略 off-policy 的数据
PPO-KL 「另一个 PPO」 KL 惩罚变体;用于 RLHF,那里对参考的 KL 本就是约束

本节要点回顾

  1. PPO 解决 A2C 的数据浪费:用裁剪重要性比率允许同一份滚动跑 K 个 epoch 而不爆。
  2. 重要性比率 r_t = π_θ/π_old 衡量新旧策略偏离;r=1 无变化,r=2 新策略概率翻倍。
  3. 裁剪代理 min(r·A, clip(r,1±ε)·A):好动作过 1+ε 后梯度抹平,坏动作过 1-ε 后封顶;ε=0.2 是标准。
  4. 训练循环:收集 N×T → GAE 冻结 → 旧策略快照 → K epoch 小批量 → 丢弃;K=10、batch=64 标准。
  5. 完整损失 = 裁剪代理 + 价值 MSE + 熵正则,与 A2C 同骨架。
  6. 三大诊断:平均 KL(留 [0,0.02])、裁剪比例(0.1-0.3)、解释方差(爬向 1)。
  7. 鲁棒:±50% 内具体数字很少要紧;但要线性衰减 LR、归一化优势、用 exp(log_new-log_old)
  8. 是 RLHF 默认策略优化器,也是 DPO/GRPO 的脚手架——理解它就理解了大半 LLM 对齐管线。

下一节,我们把 PPO 用在语言模型上——奖励建模 + RLHF,把 GPT-3 变成 ChatGPT 的三段式配方:SFT → 奖励模型 → 带 KL 惩罚的 PPO。这是大模型对齐的根基。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U