🏠 总目录📚 本教程 11 · PPO
📑 本页目录(点开跳转)

11 · PPO

30 分钟 | ⭐⭐ 现在实际在用的那一个


🎯 一句话

策略梯度有一个要命的毛病:步子迈大一点,策略就崩了,而且再也回不来。 PPO 用一个极其简单的裁剪操作解决了它 —— 简单到几行代码, 但它是 ChatGPT、机器人控制、游戏 AI 背后共同的那个算法。

1−ε11+ε优势 A > 0(这动作是好的)优势 A < 0(这动作是差的)封顶:再涨也没奖励封底:再跌也没额外惩罚新旧策略的概率比 r = π_new / π_old只要策略走得太远,梯度就被【剪平】——直接变成 0⭐ 这就是 PPO 全部的秘密:不让一次更新把策略推太远因为策略一变,数据分布就变了 —— 走太远,手上这批数据就不再适用
横轴是新旧策略的概率比。一旦超出 [1−ε, 1+ε],目标就被剪平 —— 梯度直接变 0。⭐ 这就是 PPO 的全部秘密:不让一次更新把策略推太远。因为策略一变数据分布就变,走太远手上这批数据就不再适用。

💥 一、先看清那个毛病

   监督学习:参数走错了 → 下一批数据还是原来那批 → 能纠正回来 ✅

   强化学习:参数走错了 → 策略变差
            → 【采到的数据也跟着变差】⭐
            → 用更差的数据继续训练
            → 更差…… 💀 【正反馈崩溃】

🔑 这就是 RL 和监督学习最本质的区别之一你的模型决定了你的训练数据。 一次糟糕的更新不只是"这一步没学好",而是污染了后面所有的数据

   ⭐ 所以核心诉求变成了:
     「每次更新,别让新策略离旧策略太远」

📏 二、怎么衡量"离得远"

TRPO 的答案(PPO 的前身):用 KL 散度做硬约束

$$\max_\theta \mathbb{E}\Big[\frac{\pi_\theta(a|s)}{\pi_{old}(a|s)}A\Big] \quad \text{s.t.}\quad \text{KL}(\pi_{old}\,\|\,\pi_\theta) \le \delta$$

🔗 KL 散度到底是什么、为什么它能当「两个策略离多远」的尺子数学原理 01b · KL 散度。 ⭐ 去那里主要是为了一件在这里会反复咬你的事:KL 不对称,$\text{KL}(p\|q)\ne\text{KL}(q\|p)$, 而且两个方向的行为完全相反(一个逼你「铺开、别漏掉任何峰」,一个逼你「找一个峰钻进去」)。 TRPO 这里写的是 $\text{KL}(\pi_{old}\|\pi_\theta)$,第六节 RLHF 那个惩罚项写的是反过来的方向—— 顺序不是随便写的,谁在前谁在后决定了它到底在罚什么

   ✅ 有理论保证(单调改进)
   ❌ 但要解一个带约束的优化问题:需要二阶导、共轭梯度、线搜索
   → 实现复杂,跑得慢 💀

PPO 的答案别做约束优化了,直接把目标函数裁掉。


✂️ 三、裁剪目标(PPO-Clip)

先定义概率比

$$r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{old}(a_t|s_t)}$$

💡 人话新策略选这个动作的概率,是旧策略的几倍。 r = 1 表示没变,r = 1.5 表示新策略更倾向于选它了。

目标函数

$$L = \mathbb{E}\Big[\min\big(r_tA_t,\ \text{clip}(r_t, 1-\epsilon, 1+\epsilon)A_t\big)\Big]$$

(通常 ε = 0.2)

💡 用两种情况把它讲清楚

   情况 ①:A > 0(这个动作比预期好,想提高它的概率)

      r 从 1 涨到 1.2  → 目标跟着涨 ✅ 鼓励
      r 涨到 1.5       → clip 到 1.2 → 【目标不再增加】⭐
      → 再怎么推高这个动作的概率,也拿不到更多好处
      → 【失去了继续迈大步的动力】

   情况 ②:A < 0(这个动作比预期差,想降低它的概率)

      r 从 1 降到 0.8  → 目标涨 ✅
      r 降到 0.5       → clip 到 0.8 → 目标不再涨
      → 同样刹住了
   ⭐ 那个 min 的作用:
     它让目标函数成为真实目标的一个【悲观下界】
     → 只在"改进有限"的方向上给奖励,
       在"步子太大"的方向上不给任何额外好处

🔑 PPO 的精妙之处在于它的朴素不是"禁止"迈大步(那需要约束优化),而是"迈大步没有奖励" —— 用一个 min 和一个 clip 就把二阶优化问题变成了普通的梯度下降。


🔁 四、为什么能重复利用数据

   ⭐ 有了概率比 r,同一批数据可以【重复训练多轮】(通常 3~10 轮)

   因为:即使策略已经更新过了,
        r 会如实反映"现在离采样时那个策略有多远"
        → clip 会自动刹住

   ✅ 这大幅提升了 on-policy 算法的样本效率

💡 这是 PPO 实用性的关键: 纯 on-policy 算法(如 REINFORCE)每批数据只能用一次,样本效率极低。 PPO 靠重要性采样比 + 裁剪,让数据能重复用几轮,同时不会跑飞。


🔨 五、完整实现

# 采样阶段(用旧策略跑一批,记下 logp 和优势)
import torch
import torch.nn as nn
import torch.nn.functional as F
with torch.no_grad():
    old_logp = dist.log_prob(actions)
    advantages = compute_gae(rewards, values, dones, gamma=0.99, lam=0.95)
    advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8)  # ⭐ 标准化

# 训练阶段(同一批数据跑 K 轮)
for _ in range(K_EPOCHS):
    for batch in minibatches(data):
        logp  = policy(batch.states).log_prob(batch.actions)
        ratio = torch.exp(logp - batch.old_logp)          # ⭐ 在 log 空间做除法

        s1 = ratio * batch.adv
        s2 = torch.clamp(ratio, 1 - eps, 1 + eps) * batch.adv
        policy_loss = -torch.min(s1, s2).mean()           # ⭐ 核心就这三行

        value_loss  = F.mse_loss(critic(batch.states), batch.returns)
        entropy     = dist.entropy().mean()

        loss = policy_loss + 0.5 * value_loss - 0.01 * entropy
        opt.zero_grad(); loss.backward()
        nn.utils.clip_grad_norm_(params, 0.5)             # ⭐ 梯度裁剪,必加
        opt.step()

⚠️ 六个真正影响成败的实现细节

细节 为什么
优势标准化 不做的话不同批次的优势量级差很多,学习率没法统一
梯度裁剪(0.5) RL 的梯度偶尔会爆炸
ratio 在 log 空间算 exp(logp − old_logp) 比直接除稳定得多
熵奖励系数 太小 → 过早收敛;太大 → 学不到东西
K_EPOCHS 不能太大 太多轮会让新旧策略偏离过远,clip 也救不回来
观测归一化 连续控制任务里几乎是必需的

💡 有一篇著名的论文专门研究这件事("实现细节对 PPO 有多重要"): 结论是 PPO 的效果很大程度上来自这些工程细节,而不只是那个 clip 公式这也是复现 RL 论文特别难的原因。


🎯 六、PPO 在 RLHF 里的样子

   语言模型的 RLHF 里,PPO 的各个部件对应:

   ├─ 状态 s     = 到目前为止的对话 + 已生成的 token
   ├─ 动作 a     = 下一个 token ⭐
   ├─ 策略 π     = 语言模型本身
   ├─ 奖励 r     = 奖励模型给整个回复打的分(只在结尾给)⭐
   ├─ Critic     = 一个额外的价值头
   └─ ⭐ 额外一项:KL 惩罚

        r_total = 奖励模型分数 − β·KL(π ‖ π_SFT)
                                    ↑ 别离原始模型太远

🔑 为什么需要那个 KL 惩罚: 只优化奖励模型的分数,模型会去钻奖励模型的漏洞 (生成一些人类觉得莫名其妙、但奖励模型给高分的文本)——这叫奖励攻击KL 惩罚把它拴在原始模型附近。

💡 注意这里有两层"别走太远"clip 管的是"这一步别更新太多",KL 惩罚管的是"整体别偏离 SFT 模型太远"。 两者解决不同的问题。

🔗 第 12 章会把整个流程串起来。


📊 七、超参速查

超参 常用值
clip ε 0.2(RLHF 里常用 0.2 或更小)
γ 0.99(RLHF 里常用 1.0)
GAE λ 0.95
K_EPOCHS 3–10(RLHF 里常用 1–4)⭐
学习率 3e-4(控制)/ 1e-6 ~ 1e-5(RLHF)⭐
熵系数 0.01(RLHF 里常设 0)
梯度裁剪 0.5

⚠️ 注意 RLHF 的学习率比常规 RL 小两三个数量级 —— 因为你是在微调一个已经很好的模型,不是从零学。


🔗 和站内其他章的关系

相关的地方 这里的位置
第 9 章 策略梯度 PPO 是它的稳定化版本
第 10 章 GAE PPO 的标配优势估计 ⭐
第 10 章 熵奖励 PPO 里同样保留
全景导论第 4 章 RLHF 本章是它的算法内核
第 13 章 DPO 试图完全绕开 PPO 的路线

✅ 检查点

  1. RL 里"一次糟糕的更新"为什么比监督学习严重得多?
  2. TRPO 的做法是什么?它的问题在哪?
  3. 概率比 r 的含义是什么?r=1.5 表示什么?
  4. 用 A>0 和 A<0 两种情况解释 clip 怎么起作用。
  5. 那个 min 的作用是什么?
  6. PPO 为什么能重复利用同一批数据?
  7. 六个实现细节里,哪两个最容易被忽略?
  8. RLHF 里 PPO 的状态、动作、奖励分别是什么?
  9. 为什么 RLHF 需要 KL 惩罚?它和 clip 解决的是同一个问题吗?
  10. 为什么 RLHF 的学习率比常规 RL 小那么多?
👀 答案
  1. 因为你的模型决定了你的训练数据。参数走错 → 策略变差 → 采到的数据也跟着变差 → 用更差的数据继续训练 → 正反馈崩溃。监督学习里数据是固定的,能纠正回来。
  2. KL 散度做硬约束(最大化代理目标,约束 KL ≤ δ)。问题:要解带约束的优化问题,需要二阶导、共轭梯度、线搜索,实现复杂且慢。
  3. 新策略选这个动作的概率是旧策略的几倍。r=1.5 表示新策略更倾向于选它了(概率涨了 50%)。
  4. A>0(想提高概率):r 涨到 1.2 内目标跟着涨,超过就被 clip 到 1.2,目标不再增加 → 失去继续迈大步的动力A<0(想降低概率):r 降到 0.8 内目标涨,再低就被 clip 住,同样刹车。
  5. 让目标函数成为真实目标的悲观下界——只在"改进有限"的方向给奖励,在"步子太大"的方向不给任何额外好处。
  6. 因为概率比 r 会如实反映"现在离采样时那个策略有多远",即使已经更新过几轮,clip 也会自动刹住。这大幅提升了 on-policy 的样本效率。
  7. 优势标准化(不做的话不同批次量级差很多,学习率无法统一)和 K_EPOCHS 不能太大(太多轮会让新旧策略偏离过远,clip 也救不回来)。
  8. 状态 = 到目前为止的对话+已生成 token;动作 = 下一个 token;奖励 = 奖励模型给整个回复的打分(只在结尾给)。
  9. 因为只优化奖励分数,模型会去钻奖励模型的漏洞(生成人类觉得莫名其妙但奖励模型给高分的文本)——奖励攻击不是同一个问题clip 管"这一步别更新太多",KL 惩罚管"整体别偏离 SFT 模型太远"
  10. 因为是在微调一个已经很好的模型,不是从零学。常规 RL 用 3e-4,RLHF 用 1e-6~1e-5。

🛑 可以停在这里

走神救援

RL 的要命毛病:你的模型决定了你的训练数据——参数走错 → 策略变差 → 采到的数据也跟着变差 → 正反馈崩溃(监督学习里数据固定,能纠正)。诉求变成"每次更新别让新策略离旧策略太远"。TRPO 用 KL 硬约束(有理论保证但要二阶导+共轭梯度,复杂又慢);⭐PPO 的答案:别做约束优化,直接把目标裁掉概率比 r = π_new/π_old(新策略选这动作的概率是旧的几倍);目标 L = min(r·A, clip(r,1−ε,1+ε)·A),ε=0.2。两种情况:A>0 时 r 涨过 1.2 就被裁 → 目标不再增加 → 失去迈大步的动力A<0 时 r 降过 0.8 同样刹车。min 让目标成为真实目标的悲观下界。⭐PPO 的精妙在于朴素:不是"禁止"迈大步,而是"迈大步没有奖励"——把二阶优化变成普通梯度下降。⭐能重复利用同一批数据 3~10 轮(r 如实反映离采样策略多远,clip 自动刹住)→ 大幅提升 on-policy 样本效率。六个实现细节比公式更影响成败:⭐优势标准化、梯度裁剪 0.5、ratio 在 log 空间算、熵系数、⭐K_EPOCHS 不能太大、观测归一化——有论文专门证明 PPO 的效果很大程度来自这些细节,这也是复现 RL 论文特别难的原因RLHF 里:状态=已生成的对话、动作=下一个 token、奖励=奖励模型给整个回复打分(只在结尾给);⭐额外的 KL 惩罚 r = RM分数 − β·KL(π‖π_SFT),防奖励攻击(模型钻奖励模型漏洞);⭐注意两层"别走太远":clip 管"这一步别更新太多",KL 管"整体别偏离 SFT 太远"。⚠️RLHF 学习率 1e-6~1e-5,比常规 RL 小两三个数量级(在微调一个已经很好的模型)。

下一节 👉 12-RLHF全流程.md ⭐⭐

打卡记录保存在你的浏览器里,首页能看到总进度