🏠 总目录📚 本教程 09 · 策略梯度
📑 本页目录(点开跳转)

09 · 策略梯度

30 分钟 | ⭐ 换一条路:不估价值,直接优化策略


🎯 一句话

前八章都是「先估出 Q,再由 Q 推出策略」。 这一章直接把策略本身当成一个神经网络,用梯度下降优化它这条路是 PPO、RLHF 的起点。


🚪 一、为什么要换路(三个理由)

理由 说明
动作连续时 max 算不了 「方向盘转 13.7 度」有无穷多个动作,没法遍历取 max
最优策略可能本身是随机的 石头剪刀布;基于价值的方法只能输出确定性策略
有时策略比价值简单得多 「看到障碍就绕开」很好描述,但要精确算出每个状态值多少分,难得多

💡 第三点值得多想一秒你要的是策略,价值只是达到策略的手段。 既然如此 —— 为什么不直接优化你要的那个东西?


🎯 二、目标函数

把策略参数化成 $\pi_\theta(a|s)$(一个神经网络),目标是最大化期望回报:

$$J(\theta) = \mathbb{E}_{\tau\sim\pi_\theta}\big[R(\tau)\big]$$

其中 τ 是一条完整轨迹(trajectory),R(τ) 是它的总回报。

   ⭐ 难点:τ 的分布【依赖 θ】
      → 你不能像监督学习那样,把数据当成固定的
      → 怎么对"分布本身依赖参数的期望"求导?

⭐⭐ 三、策略梯度定理

$$\nabla_\theta J(\theta) = \mathbb{E}\Big[\sum_t \nabla_\theta\log\pi_\theta(a_t|s_t)\cdot R(\tau)\Big]$$

💡 人话翻译

让「导致高回报的动作」的概率变大,让「导致低回报的动作」的概率变小。

∇log π(a|s) :把这个动作概率【调大】的方向
→ 结果好,就朝这个方向多走 → 结果差,R 是负的,就朝反方向走
📐 推导(想看再点,核心只有一个小技巧)

$$\nabla_\theta J = \nabla_\theta\int p_\theta(\tau)R(\tau)d\tau = \int \nabla_\theta p_\theta(\tau)\,R(\tau)d\tau$$

关键技巧:对数导数技巧(log-derivative trick)

$$\nabla_\theta p_\theta(\tau) = p_\theta(\tau)\,\nabla_\theta\log p_\theta(\tau)$$

(因为 $\nabla\log p = \nabla p / p$)

代回去:

$$\nabla_\theta J = \int p_\theta(\tau)\nabla_\theta\log p_\theta(\tau)R(\tau)d\tau = \mathbb{E}\big[\nabla_\theta\log p_\theta(\tau)R(\tau)\big]$$

这一步把「对分布求导」变回了「在分布下求期望」 —— 于是可以用采样估计了。

再把轨迹概率展开:

$$p_\theta(\tau) = p(s_0)\prod_t \pi_\theta(a_t|s_t)\,P(s_{t+1}|s_t,a_t)$$

取对数后,所有不含 θ 的项(初始分布、转移概率)求导都变成 0

$$\nabla_\theta\log p_\theta(\tau) = \sum_t \nabla_\theta\log\pi_\theta(a_t|s_t)$$

⭐⭐ 注意这个结果有多惊人:环境的转移概率 P 被完全消掉了。 这意味着策略梯度完全不需要知道环境模型。

🔑 上面折叠块里最后那句话是这一章的精华推导过程中,环境的转移概率 P 自动消失了。 这就是策略梯度能用在"完全不懂环境"的场景(比如 RLHF)的原因。


🎬 四、REINFORCE:最朴素的实现

   ① 用当前策略跑完一整个回合
   ② 算出每一步的回报 G_t
   ③ 对每一步做梯度上升:θ ← θ + α·∇log π(a_t|s_t)·G_t
   ④ 重复
import torch

def reinforce_update(policy, opt, states, actions, rewards, gamma=0.99):
    # 从后往前算折扣回报
    G, returns = 0, []
    for r in reversed(rewards):
        G = r + gamma * G
        returns.insert(0, G)
    returns = torch.tensor(returns, dtype=torch.float32)
    returns = (returns - returns.mean()) / (returns.std() + 1e-8)   # ⭐ 标准化,见下

    logp = policy(torch.stack(states)).log_prob(torch.stack(actions))
    loss = -(logp * returns).sum()          # ⭐ 负号:优化器做的是最小化
    opt.zero_grad(); loss.backward(); opt.step()

⚠️ 两个新手必踩的坑

说明
忘了负号 PyTorch 的优化器最小化损失,而我们要最大化回报 ⭐
以为这是监督学习的交叉熵 形式很像,但权重 returns 是变量不是常数,含义完全不同

💀 五、致命问题:方差大得离谱

   REINFORCE 是【无偏】的,但方差大到几乎没法用:

   · R(τ) 是整条轨迹的回报 → 一局的运气全算进去了
   · 同一个动作,这局赢了给 +100,下局输了给 −100
     → 梯度方向来回摆  💀
   · 需要海量样本才能平均掉噪声

三个降方差的办法(一个比一个重要)

① 只用「未来」的回报(reward-to-go)

   ❌ 用整条轨迹的 R(τ)
   ✅ 用 G_t = 从 t 时刻【往后】的回报

   ⭐ 道理:t 时刻的动作,影响不了 t 之前已经发生的奖励
      把它们算进来纯粹是噪声

② 减去基线(baseline)⭐

$$\nabla J = \mathbb{E}\big[\nabla\log\pi(a_t|s_t)\cdot(G_t - b(s_t))\big]$$

   ⭐ 关键性质:只要 b 不依赖动作 a,减去它【不改变期望】,但能大幅降方差

   最常用的基线:b(s) = V(s)   ← 这个状态的平均水平

   直觉:
   考试考了 80 分,好不好?
   · 班级平均 60 → 好,+20   ✅ 应该鼓励
   · 班级平均 95 → 差,−15   ❌ 应该抑制

   ⭐ 没有基线时,所有正回报的动作都会被鼓励 —— 哪怕它其实比平均差

③ 优势函数 A(s,a)

$$A(s,a) = Q(s,a) - V(s)$$

💡 人话这个动作比该状态的平均水平好多少。

优势函数是从这里通向第 10、11 章的桥A 就是「G_t − 基线」的正式版本,PPO 优化的正是它。


🆚 六、基于价值 vs 基于策略

基于价值(DQN) 基于策略(PG)
学什么 Q 函数 策略本身
连续动作 ✅ ⭐
随机策略
收敛性 可能震荡 更稳定(但容易到局部最优
样本效率 高(能经验回放) (on-policy,用完就丢)⭐
方差

🔑 看最后两行 —— 这正是第 10、11 章要解决的Actor-Critic 用价值函数降方差;PPO 让 on-policy 数据能多用几次。 两者合起来,就补上了策略梯度的两个短板。


🤖 七、和语言模型的关系(提前看一眼)

   语言模型就是一个策略:π(下一个token | 已生成的内容)

   ⭐ 词表 5 万个 token → 5 万个动作
      DQN 要对 5 万个动作取 max,还要估准每一个的 Q —— 不现实
      策略梯度只需要 log π(token),而这【正是语言模型本来就在算的东西】⭐⭐

   → 这就是 RLHF 必须走策略梯度这条路的原因

💡 注意这个巧合有多美妙log π(a|s) 就是语言模型的对数概率,训练时本来就要算。 策略梯度几乎是为语言模型量身定做的。


🔗 八、和站内其他章的关系

相关的地方 这里的位置
第 2 章随机策略可微 这一章兑现了那个伏笔
第 8 章 DQN 只能离散动作 换路的直接原因
数学原理 01 MLE log π 的形式和它一样,但权重不同
ML基础 08反向传播 梯度怎么算
语言模型的 logits 就是策略网络的输出

✅ 检查点

  1. 为什么要从「基于价值」换到「基于策略」?(三个理由)
  2. 策略梯度定理用一句人话怎么说?
  3. 推导里的关键技巧是什么?推导结果中最惊人的一点是什么?
  4. REINFORCE 的两个代码坑是什么?
  5. REINFORCE 的致命问题是什么?
  6. reward-to-go 为什么能降方差?
  7. 基线为什么能"降方差但不改变期望"?用考试的例子说明它的作用。
  8. 优势函数是什么?它通向哪一章?
  9. 为什么 RLHF 必须走策略梯度这条路?
👀 答案
  1. 动作连续时没法对所有动作取 max ②最优策略可能本身是随机的 ③有时策略比价值简单得多——你要的本来就是策略,价值只是手段。
  2. 让「导致高回报的动作」概率变大,让「导致低回报的动作」概率变小。
  3. 对数导数技巧:∇p = p·∇log p,把"对分布求导"变回"在分布下求期望"。最惊人的是:推导中环境转移概率 P 被完全消掉了 —— 所以策略梯度不需要知道环境模型。
  4. 忘了负号(优化器最小化,我们要最大化)②以为是监督学习的交叉熵——形式像但权重 returns 是变量不是常数
  5. 方差大到几乎没法用。R(τ) 把一局的运气全算进去,同一个动作这局 +100 下局 −100,梯度方向来回摆。
  6. 因为 t 时刻的动作影响不了 t 之前已发生的奖励,把它们算进来纯粹是噪声。
  7. 因为基线 b 不依赖动作 a,在期望里它乘 ∇log π 的和为 0。作用:考 80 分,班均 60 就该鼓励(+20),班均 95 就该抑制(−15)。没有基线时所有正回报动作都被鼓励,哪怕它其实比平均差
  8. A(s,a) = Q(s,a) − V(s),即这个动作比该状态平均水平好多少。它是"G_t − 基线"的正式版本,通向第 10、11 章 —— PPO 优化的正是它
  9. 因为词表 5 万个 token,DQN 要对 5 万个动作取 max 还要估准每个 Q,不现实;而策略梯度只需要 log π(token)这正是语言模型本来就在算的东西

🛑 可以停在这里

走神救援

换路:不估价值,直接把策略当神经网络优化。三个理由:连续动作没法取max、最优策略可能本身随机、策略常比价值简单(你要的本来就是策略,价值只是手段)。⭐⭐策略梯度定理人话:让导致高回报的动作概率变大,低回报的变小∇J = E[∇log π(a|s) · R] —— ∇logπ 是"把这动作概率调大的方向",R 是"这次结果多好"。推导关键是对数导数技巧 ∇p = p·∇log p(把对分布求导变回求期望);⭐最惊人的是环境转移概率 P 被完全消掉了 → 策略梯度不需要环境模型REINFORCE 两个坑:忘负号、误以为是交叉熵(权重是变量不是常数)。💀致命问题:方差大(一局运气全算进去,同一动作这局+100下局−100)。三个降方差:①reward-to-go(t 时刻动作影响不了之前的奖励)②⭐减基线(b 不依赖动作所以不改变期望;考80分班均60该鼓励、班均95该抑制 —— 没基线时所有正回报动作都被鼓励哪怕它比平均差)③⭐优势函数 A=Q−V,PPO 优化的正是它PG 的两个短板(样本效率低、方差高)正是第10、11章要补的。⭐RLHF必须走这条路:词表5万个token,DQN要取max不现实,而 log π(token) 正是语言模型本来就在算的东西

下一节 👉 10-Actor-Critic.md

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