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