Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

5.5 Actor–Critic 与 PPO

上一节的结尾留下了一个明显的缺口:基线选什么。两种极端都不好用——用整局回报 GtG_t 当「优势」的无偏估计(REINFORCE),方差大到信噪比接近 1;用一个学出来的值函数当基线,方差骤降但引入了偏差(值函数本身在漂移)。这一节的起点就是把这两种极端中间的路走通:学策略的 Actor 与学价值的 Critic 互相喂饭,前者用 Critic 给出的优势更新自身,后者用前者采集的数据修正价值——这就是 Actor–Critic。

但 Actor–Critic 只解决了「优势怎么算」,没有解决一个更致命的工程问题:步长。策略梯度对步长极其敏感——步长太大,一次更新就能把策略推进一个「所有动作都变差」的区域;而策略一坏,后续采样的数据全废,你甚至无法判断是哪里坏了(这正是 RL 难调试的核心)。TRPO(Trust Region Policy Optimization)用「限制新旧策略的 KL 距离」优雅地解决了它,代价是要解带约束的优化问题、需要二阶信息。PPO(Proximal Policy Optimization)则用一个四行的裁剪目标达到几乎相同的效果,只需一阶梯度——它因此成为今天最通用的默认算法,也是 5.6 里 RLHF 第一阶段的执行器。

这一节的组织:优势函数与 Actor–Critic(5.5.1)→ GAE(5.5.2,把偏差—方差做成一根旋钮)→ TRPO 的信任域(5.5.3,讲清「为什么必须限幅」)→ PPO 的裁剪目标(5.5.4,本讲最该会推的一段)→ 熵正则与稳定性诊断(5.5.5–5.5.6)→ 一个能跑的最小更新步骤(5.5.7)。

5.5.1 优势函数与 Actor–Critic

定义 5.5.1 · 优势函数(advantage)

Aπ(s,a)=Qπ(s,a)−Vπ(s),Vπ(s)=∑aπ(a∣s)Qπ(s,a).A^{\pi}(s,a)=Q^{\pi}(s,a)-V^{\pi}(s),\qquad V^{\pi}(s)=\sum_a\pi(a\mid s)Q^{\pi}(s,a). Aπ(s,a)A^\pi(s,a) 度量「在该状态下执行 aa 比按策略平均好多少」。两条基本性质:

  • ∑aπ(a∣s)Aπ(s,a)=0\sum_a\pi(a\mid s)A^\pi(s,a)=0:在策略的分布下,优势的均值为零(这是基线无偏性的直接推论,定理 5.4.3);
  • Aπ(s,a)=0A^\pi(s,a)=0 的动作是「平均水平」的动作;只有 A>0A\gt0 的动作才值得抬高概率。
优势与 QQ 的差别:把「状态有多好」除掉

QQ 的数值里混了两部分:这个状态本身好不好(比如「离终点只有一步」)与这个动作选得对不对。前者对同一状态下的所有动作是公共项,对排序毫无贡献,却会主导梯度的大小——在值很大的状态里,所有动作的权重都很大,学习率实际被放大;在值很小的状态里,学习几乎停滞。

减去 V(s)V(s) 就是把这层「公共背景」除掉,只留下「差异」。这也是为什么优势归一化(把一个 batch 的 A^\hat A 标准化到均值 0、方差 1)在实践中几乎总是有用:它让不同状态、不同时间的梯度尺度可比。

Actor–Critic 与 A2C:两个网络,各管一件事

结构:

  • Actor:策略网络 πθ(a∣s)\pi_\theta(a\mid s),负责「演」,按策略梯度更新;
  • Critic:值网络 Vϕ(s)V_\phi(s),负责「评」,用 TD 学习拟合回报,并向 Actor 提供优势估计。

最常用的优势估计是「一步 TD 残差」: A^t=rt+1+γVϕ(st+1)−Vϕ(st)=δt,\hat A_t=r_{t+1}+\gamma V_\phi(s_{t+1})-V_\phi(s_t)=\delta_t, 它既有 Critic 的低方差,又只需走一步就能更新(不必等整局结束)。Critic 的损失就是让 VϕV_\phi 逼近「回报估计」: LV(ϕ)=Et[(Vϕ(st)−R^t)2],R^t=A^t+Vϕ(st) (用同一批数据算出的“不变的目标”).\mathcal L_V(\phi)=\mathbb E_t\bigl[\bigl(V_\phi(s_t)-\hat R_t\bigr)^2\bigr],\qquad \hat R_t=\hat A_t+V_\phi(s_t)\ \text{(用同一批数据算出的“不变的目标”)}.

A2C(Advantage Actor–Critic):把上面这件事并行化——同时跑 NN 个环境副本,把 N×TN\times T 步数据凑成一个 batch 做一次更新(A3C 是它的异步版本)。并行环境的作用不是提速,而是降方差:不同副本的随机性互相抵消,相当于把 batch 变大。

优势估计 公式 偏差 方差 何时用
蒙特卡洛 A^t=Gt−Vϕ(st)\hat A_t=G_t-V_\phi(s_t) 无偏(VϕV_\phi 准确时) 大(含整局随机性) 回合短、奖励稠密
一步 TD A^t=δt=rt+1+γVϕ(st+1)−Vϕ(st)\hat A_t=\delta_t=r_{t+1}+\gamma V_\phi(s_{t+1})-V_\phi(s_t) 依赖 VϕV_\phi 质量 小 通用默认,A2C 的主力
nn 步 A^t(n)=∑l=0n−1γlδt+l\hat A_t^{(n)}=\sum_{l=0}^{n-1}\gamma^l\delta_{t+l} 随 nn 减小 随 nn 增大 想要更强信号时
GAE A^tGAE=∑l≥0(γλ)lδt+l\hat A_t^{\text{GAE}}=\sum_{l\ge0}(\gamma\lambda)^l\delta_{t+l} 由 λ\lambda 连续调节 由 λ\lambda 连续调节 PPO 的事实标准(5.5.2)

5.5.2 GAE:把偏差—方差做成一根旋钮

上面那张表里,「nn 步」是一个离散的选择:n=1,5,20,…n=1,5,20,\dots。GAE 的做法是把所有 nn 的估计按指数权重混起来——于是不必再挑 nn,只要调一个连续的 λ∈[0,1]\lambda\in[0,1]。

定理 5.5.1 · 广义优势估计(GAE)

记 δt=rt+1+γV(st+1)−V(st)\delta_t=r_{t+1}+\gamma V(s_{t+1})-V(s_t)(用 Critic 估计)。定义 nn 步优势 A^t(n)=∑l=0n−1γlδt+l(n≥1),A^t(∞)=Gt−V(st).\hat A_t^{(n)}=\sum_{l=0}^{n-1}\gamma^{l}\delta_{t+l}\qquad(n\ge1),\qquad \hat A_t^{(\infty)}=G_t-V(s_t). 对所有 nn 取指数加权平均(权重 (1−λ)λn−1(1-\lambda)\lambda^{n-1},λ∈[0,1)\lambda\in[0,1)): A^tGAE(γ,λ)=(1−λ)∑n≥1λn−1A^t(n)= ∑l≥0(γλ)l δt+l \hat A_t^{\text{GAE}(\gamma,\lambda)}=(1-\lambda)\sum_{n\ge1}\lambda^{n-1}\hat A_t^{(n)} =\boxed{\ \sum_{l\ge0}(\gamma\lambda)^{l}\,\delta_{t+l}\ } 等价地,它满足一个从后往前的递归: A^t=δt+γλ A^t+1,A^T=0 (超出轨迹末端的项为 0).\hat A_t=\delta_t+\gamma\lambda\,\hat A_{t+1},\qquad \hat A_{T}=0\ \text{(超出轨迹末端的项为 0)}. 两个极端:λ=0\lambda=0 时 A^t=δt\hat A_t=\delta_t(一步 TD,偏差大、方差小);λ=1\lambda=1 时 A^t=∑lγlδt+l=Gt−V(st)\hat A_t=\sum_l\gamma^l\delta_{t+l}=G_t-V(s_t)(蒙特卡洛,偏差小、方差大)。

为什么 γλ\gamma\lambda 会「一起」出现,以及怎么调 λ\lambda

闭式里的 (γλ)l(\gamma\lambda)^l 有两个来源:γl\gamma^l 来自折扣(第 ll 步之后的奖励本来就该打折),λl\lambda^l 来自我们故意给远处的 TD 残差打的额外折扣。因此调 λ\lambda 就是在调「多信任远期数据」:

  • λ→0\lambda\to0:几乎只信一步之内的信息,估计主要来自 Critic——如果 Critic 学得差,梯度方向就被带偏(偏差);
  • λ→1\lambda\to1:几乎完全信实际回报,估计无偏但每条轨迹的噪声全部进来(方差);
  • 实践中的默认是 λ=0.95\lambda=0.95 上下(与 γ=0.99\gamma=0.99 搭配,γλ≈0.94\gamma\lambda\approx0.94,有效记忆约 11−γλ≈17\frac{1}{1-\gamma\lambda}\approx17 步)。

实现提示:闭式看起来要算无穷项,但递归式只需一次从后往前的循环(代码见 5.5.7)。它同时也回答了「为什么 PPO 里总能看到 λ\lambda 这个超参数」——它就是偏差—方差的旋钮,没有更深的含义(更紧凑的版本见附录 C.11)。

推导(指数加权求和 → 闭式)

把 nn 步优势展开为双重求和,并交换求和次序(n≥1n\ge1,0≤l≤n−10\le l\le n-1): (1−λ)∑n≥1λn−1∑l=0n−1γlδt+l=(1−λ)∑l≥0γlδt+l∑n≥l+1λn−1=(1−λ)∑l≥0γlδt+l⋅λl1−λ=∑l≥0(γλ)lδt+l,(1-\lambda)\sum_{n\ge1}\lambda^{n-1}\sum_{l=0}^{n-1}\gamma^{l}\delta_{t+l} =(1-\lambda)\sum_{l\ge0}\gamma^{l}\delta_{t+l}\sum_{n\ge l+1}\lambda^{n-1} =(1-\lambda)\sum_{l\ge0}\gamma^{l}\delta_{t+l}\cdot\frac{\lambda^{l}}{1-\lambda} =\sum_{l\ge0}(\gamma\lambda)^{l}\delta_{t+l}, 其中用到几何级数 ∑n≥l+1λn−1=λl1−λ\sum_{n\ge l+1}\lambda^{n-1}=\frac{\lambda^{l}}{1-\lambda}(λ<1\lambda\lt1)。

递归式的证明:把闭式在 tt 处拆出 l=0l=0 项: A^t=δt+∑l≥1(γλ)lδt+l=δt+γλ∑l≥0(γλ)lδt+l+1=δt+γλ A^t+1. ■\hat A_t=\delta_t+\sum_{l\ge1}(\gamma\lambda)^l\delta_{t+l}=\delta_t+\gamma\lambda\sum_{l\ge0}(\gamma\lambda)^{l}\delta_{t+l+1}=\delta_t+\gamma\lambda\,\hat A_{t+1}.\ \blacksquare

∎
例 5.5.1 · 手算 GAE:λ\lambda 如何改变同一个序列的估计

给定 γ=0.99\gamma=0.99、λ=0.95\lambda=0.95,四步的 TD 残差为 δ0=1.0,δ1=−0.8,δ2=0.6,δ3=−0.4.\delta_0=1.0,\quad \delta_1=-0.8,\quad \delta_2=0.6,\quad \delta_3=-0.4 . 求 A^0\hat A_0 在 λ=0\lambda=0、λ=0.95\lambda=0.95、λ=1\lambda=1 三种设置下的值,并解释差异。

解

记 γλ=0.99×0.95=0.9405\gamma\lambda=0.99\times0.95=0.9405。用递归 A^t=δt+γλA^t+1\hat A_t=\delta_t+\gamma\lambda\hat A_{t+1}(A^4=0\hat A_4=0):

(a) λ=0\lambda=0:A^t=δt\hat A_t=\delta_t,故 A^0=1.0\hat A_0=1.0。

(b) λ=0.95\lambda=0.95(自后往前): A^3=−0.4,A^2=0.6+0.9405×(−0.4)=0.2238,\hat A_3=-0.4,\quad \hat A_2=0.6+0.9405\times(-0.4)=0.2238, A^1=−0.8+0.9405×0.2238=−0.5895,A^0=1.0+0.9405×(−0.5895)=0.4455.\hat A_1=-0.8+0.9405\times0.2238=-0.5895,\quad \hat A_0=1.0+0.9405\times(-0.5895)=0.4455 .

(c) λ=1\lambda=1(此时 γλ=0.99\gamma\lambda=0.99): A^3=−0.4,A^2=0.6+0.99×(−0.4)=0.204,\hat A_3=-0.4,\quad \hat A_2=0.6+0.99\times(-0.4)=0.204, A^1=−0.8+0.99×0.204=−0.598,A^0=1.0+0.99×(−0.598)=0.408.\hat A_1=-0.8+0.99\times0.204=-0.598,\quad \hat A_0=1.0+0.99\times(-0.598)=0.408 .

解释:三者对 A^0\hat A_0 的估计从 1.0(只信第一步)降到 0.445(λ=0.95\lambda=0.95)再到 0.408(完全信实际回报)。差别来自后面的负残差:越信任远期数据,越会把「后面变差了」这件事计入当前优势。注意 λ=0.95\lambda=0.95 与 λ=1\lambda=1 在这里只差 0.037——说明 λ\lambda 的作用是「平滑」,而不是「换一个量级」;它的价值主要体现在方差上(λ=1\lambda=1 会把整条轨迹的噪声全带进来)。这也提示一个实用判据:如果 λ\lambda 从 0.95 调到 1.0 后回报曲线剧烈振荡,说明 Critic 尚可、但轨迹噪声太大;反过来若调到 0 反而更好,说明 Critic 比实际回报更可信(不常见,通常出现在奖励极度稀疏时)。■\blacksquare

∎

5.5.3 TRPO:为什么必须限制步长

策略梯度与监督学习最大的工程差异是失败是不可逆的。监督学习一步走坏,下一批数据仍然是那个固定的数据集,梯度还在;RL 里一步走坏,后续采样的数据全部来自坏策略——你可能再也采不到「好策略才会遇到的状态」,学习信号随之消失,错误被永久固化。

TRPO 的信任域(trust region)

用重要性采样把「新策略的回报」写成一个可以用旧数据估计的替代目标(CPI, conservative policy iteration): LCPI(θ)=Es∼dπold, a∼πold[πθ(a∣s)πθold(a∣s) A^πold(s,a)].L^{\text{CPI}}(\theta)=\mathbb E_{s\sim d^{\pi_{\text{old}}},\,a\sim\pi_{\text{old}}}\Bigl[\frac{\pi_\theta(a\mid s)}{\pi_{\theta_{\text{old}}}(a\mid s)}\,\hat A^{\pi_{\text{old}}}(s,a)\Bigr]. 注意状态分布仍是旧的 dπoldd^{\pi_{\text{old}}}——这是「替代」两字的由来:它只是真实目标的一阶近似。TRPO 在这上面加一个硬约束: max⁡θ LCPI(θ)s.t.Es[KL(πθold(⋅∣s) ∥ πθ(⋅∣s))]≤δ.\max_\theta\ L^{\text{CPI}}(\theta)\qquad \text{s.t.}\qquad \mathbb E_s\bigl[\mathrm{KL}\bigl(\pi_{\theta_{\text{old}}}(\cdot\mid s)\,\|\,\pi_\theta(\cdot\mid s)\bigr)\bigr]\le\delta . 为什么用 KL 而不是参数距离:策略的行为只由输出分布决定,参数走多远并不直接对应行为变化(softmax 的饱和区可以让参数变化很大而分布几乎不动,反之亦然)。KL 是行为层面的度量,因此更可靠。

代价:这个约束优化在实现上要做二阶近似(用 Fisher 信息矩阵)、共轭梯度求解方向、再做线搜索保证约束满足。它能用,但工程上不轻——这正是 PPO 出现的动机。

为什么「步长」是 RL 的独有难题

监督学习的步长只需满足「损失下降」,因为数据分布固定、目标函数固定。RL 里步长还额外控制数据分布的变化速度:策略变太快,旧的样本(以及 Critic 的估计)就迅速失效。更麻烦的是,失败看起来像成功——回报可能因为随机性暂时上升,而策略已经不可恢复。

因此 RL 的稳定化技巧可以分成三类,分别对应三个不稳定源:限幅(PPO / TRPO,控制策略变化)、降噪(基线、GAE、并行环境、优势归一化)、限速(学习率衰减、梯度裁剪、定期重新初始化 Critic)。看一个 RL 实现时,先找出它这三类里各做了什么。

5.5.4 PPO:用裁剪替代 KL 约束

PPO 裁剪目标在 A>0 与 A<0 两种情形下的形状(ε=0.2) 左右两面板分别画出优势为正与为负时,未裁剪项 ρA(蓝虚线)、裁剪项(红线)随重要性比 ρ 的变化。ρ 超出 [1−ε, 1+ε] 后目标被拉平,梯度归零,阻止策略一步走太远。 0.511.5-0重要性比 ρ = π_θ/π_θold每项贡献 ρ=1 1+ε 1−ε A > 0(好动作) 奖励提高其概率,但 ρ 超过 1+ε 后不再加码 未裁剪 ρA裁剪后 min(ρA, clip·A) 0.511.5-0重要性比 ρ = π_θ/π_θold每项贡献 ρ=1 1+ε 1−ε A < 0(坏动作) 压低其概率,但 ρ 低于 1-ε 后不再追加惩罚 未裁剪 ρA裁剪后 min(ρA, clip·A)
PPO 的信任域 intuition(本教程计算绘制,ε=0.2)。红线是实际目标min(ρA, clip(ρ, 1−ε, 1+ε)·A):当新旧策略比 ρ 越出 [1−ε, 1+ε],目标拉平、梯度归零——一步更新被硬性限幅,这是 PPO 比 REINFORCE 稳定的全部秘密(见 5.5 节)。
定义 5.5.2 · PPO 的裁剪替代目标

设 πθold\pi_{\theta_{\text{old}}} 是采样时用的策略,A^t\hat A_t 是优势估计(通常是 GAE),定义概率比 ρt(θ)=πθ(at∣st)πθold(at∣st)(ρt=1 表示与采样时完全一致).\rho_t(\theta)=\frac{\pi_\theta(a_t\mid s_t)}{\pi_{\theta_{\text{old}}}(a_t\mid s_t)}\quad(\rho_t=1\ \text{表示与采样时完全一致}). PPO 最大化的是裁剪替代目标 LCLIP(θ)=Et[min⁡(ρt(θ)A^t,  clip(ρt(θ), 1−ε, 1+ε)A^t)],L^{\mathrm{CLIP}}(\theta)=\mathbb E_t\Bigl[\min\Bigl(\rho_t(\theta)\hat A_t,\ \ \mathrm{clip}\bigl(\rho_t(\theta),\,1-\varepsilon,\,1+\varepsilon\bigr)\hat A_t\Bigr)\Bigr], 其中 ε\varepsilon 是超参数(论文与社区默认常取 0.20.2;等价地,也可以用「单侧减半」的参数化写成 ε\varepsilon 与 0.5ε0.5\varepsilon)。

裁剪到底裁掉了什么

ρt\rho_t 衡量新策略相对老策略「偏爱这个动作的程度变了多少」:ρt=1\rho_t=1 表示没变,ρt=5\rho_t=5 表示把这个动作的概率抬到了 5 倍。

  • 当 A^t>0\hat A_t\gt0(好动作):目标希望 ρt\rho_t 越大越好,但被 min⁡\min 与 clip\mathrm{clip} 卡在 1+ε1+\varepsilon——抬到一定程度就够了,再抬不给额外收益;
  • 当 A^t<0\hat A_t\lt0(坏动作):目标希望 ρt\rho_t 越小越好,但被卡在 1−ε1-\varepsilon——压低到一定程度也不再多要。

于是「越过裁剪区间的更新」在继续外推的方向上拿不到梯度(梯度为 0),策略不会因为一个 batch 的噪声被一步推飞。这就是它替代 TRPO 的原因:同样的「别走太远」的约束,却只需一阶梯度、实现简单、对超参数鲁棒。

它也是「近似 on-policy」的:ρt\rho_t 是重要性采样比,修正了「用老策略数据估计新策略目标」的偏差,因此同一批数据可以重复做几个 epoch 的梯度上升(论文常用 3–10),样本效率比 REINFORCE 高不少;但数据仍来自 πθold\pi_{\theta_{\text{old}}},跑完这几轮后必须重新采样。

推导(分情形:哪两个象限梯度为 0,哪两个象限被“拉回”)

记 f(ρ)=min⁡(ρA, clip(ρ,1−ε,1+ε)A)f(\rho)=\min\bigl(\rho A,\ \mathrm{clip}(\rho,1-\varepsilon,1+\varepsilon)A\bigr),其中 AA 视为与该样本的 θ\theta 无关(优势用旧数据估计,是常数)。注意 clip\mathrm{clip} 关于 ρ\rho 在区间外是常数,故分四个象限讨论。

情形 ①:A>0A\gt0 且 ρ>1+ε\rho\gt1+\varepsilon。 此时 clip=1+ε<ρ\mathrm{clip}=1+\varepsilon\lt\rho,所以 clip⋅A=(1+ε)A<ρA\mathrm{clip}\cdot A=(1+\varepsilon)A\lt\rho A,min⁡\min 取常数项: f=(1+ε)A⟹∂f∂ρ=0.f=(1+\varepsilon)A\quad\Longrightarrow\quad \frac{\partial f}{\partial \rho}=0 . 梯度为 0:不再奖励「继续抬高概率」。

情形 ②:A<0A\lt0 且 ρ<1−ε\rho\lt1-\varepsilon。 此时 clip=1−ε>ρ\mathrm{clip}=1-\varepsilon\gt\rho。因 A<0A\lt0,乘以更大的正数得到更负的值:(1−ε)A<ρA(1-\varepsilon)A\lt\rho A,min⁡\min 仍取常数项: f=(1−ε)A⟹∂f∂ρ=0.f=(1-\varepsilon)A\quad\Longrightarrow\quad \frac{\partial f}{\partial \rho}=0 . 梯度为 0:不再奖励「继续压低概率」。

情形 ③:A>0A\gt0 且 ρ<1−ε\rho\lt1-\varepsilon。 此时 clip=1−ε\mathrm{clip}=1-\varepsilon,比较 1−ε1-\varepsilon 与 ρ\rho 得 (1−ε)A>ρA(1-\varepsilon)A\gt\rho A,于是 min⁡\min 取 f=ρAf=\rho A: ∂f∂ρ=A>0.\frac{\partial f}{\partial \rho}=A\gt0 . 梯度仍然为「抬高 ρ\rho」——方向恰好是把自己拉回区间。

情形 ④:A<0A\lt0 且 ρ>1+ε\rho\gt1+\varepsilon。 由 clip=1+ε<ρ\mathrm{clip}=1+\varepsilon\lt\rho 与 A<0A\lt0 得 (1+ε)A>ρA(1+\varepsilon)A\gt\rho A,故 min⁡\min 取 f=ρAf=\rho A: ∂f∂ρ=A<0.\frac{\partial f}{\partial \rho}=A\lt0 . 梯度仍然为「压小 ρ\rho」——同样是把自己拉回区间。

情形 ⑤:1−ε≤ρ≤1+ε1-\varepsilon\le\rho\le1+\varepsilon:clip=ρ\mathrm{clip}=\rho,两项相等,f=ρAf=\rho A,梯度 =A=A(这就是带重要性权重的普通策略梯度)。

作为下界的意义:因为 min⁡\min 恒取两个候选里较小的那个,故 LCLIP(θ) ≤ Et[ρtA^t]=LCPI(θ)(逐点成立),L^{\mathrm{CLIP}}(\theta)\ \le\ \mathbb E_t[\rho_t\hat A_t]=L^{\mathrm{CPI}}(\theta)\quad\text{(逐点成立)}, 即裁剪目标始终是无裁剪替代目标的保守下界——它不会高估「一步更新能带来的改善」。把这一点与情形 ①②③④ 合起来看,就得到 PPO 的全部行为:区间内正常优化;越界后「继续外推」的梯度被归零,而「回到区间」的梯度保留。这就是「软信任域」:TRPO 用硬约束禁止越界,PPO 允许越界但不再给你奖励。

注意:这个推导说明的是「一旦越界,目标函数对该样本的梯度为 0」,不等于「该样本对参数 θ\theta 完全没有梯度贡献」——因为 ρt\rho_t 本身通过 πθ\pi_\theta 依赖 θ\theta,只是链式法则中 ∂f∂ρ=0\frac{\partial f}{\partial\rho}=0 这一环断开了。工程上因此仍要用 clip_fraction 监控有多少样本落在越界区。■\blacksquare

∎
例 5.5.2 · 裁剪目标的分情形数值验算(ε=0.2\varepsilon=0.2)

对以下四种组合,写出 LCLIPL^{\mathrm{CLIP}} 的取值与当前梯度分支(是否被截断):

组合 A^\hat A ρ\rho ρA^\rho\hat A clip(ρ,0.8,1.2)A^\mathrm{clip}(\rho,0.8,1.2)\hat A min⁡\min 取值 梯度分支
(a) +2+2 1.51.5 3.03.0 2.42.4 2.42.4 截断(∂f/∂ρ=0\partial f/\partial\rho=0)
(b) +2+2 0.50.5 1.01.0 1.61.6 1.01.0 活跃,方向为抬高 ρ\rho(拉回)
(c) −2-2 1.51.5 −3.0-3.0 −2.4-2.4 −3.0-3.0 活跃,方向为压低 ρ\rho(拉回)
(d) −2-2 0.50.5 −1.0-1.0 −1.6-1.6 −1.6-1.6 截断(∂f/∂ρ=0\partial f/\partial\rho=0)
解

逐格验算(ε=0.2\varepsilon=0.2,故裁剪区间 [0.8,1.2][0.8,1.2]):

(a) A^=+2,ρ=1.5>1.2\hat A=+2,\rho=1.5\gt1.2:未裁剪项 3.03.0,裁剪项 1.2×2=2.41.2\times2=2.4,min⁡=2.4\min=2.4。属情形①:梯度为 0——尽管「事实上的目标值」仍是正的(2.4),但继续抬高 ρ\rho 不再带来任何收益。

(b) A^=+2,ρ=0.5<0.8\hat A=+2,\rho=0.5\lt0.8:未裁剪项 1.01.0,裁剪项 0.8×2=1.60.8\times2=1.6,min⁡=1.0\min=1.0。属情形③:梯度 =A^=+2>0=\hat A=+2\gt0,即鼓励增大 ρ\rho(从 0.5 向区间靠拢)。

(c) A^=−2,ρ=1.5>1.2\hat A=-2,\rho=1.5\gt1.2:未裁剪项 −3.0-3.0,裁剪项 1.2×(−2)=−2.41.2\times(-2)=-2.4,min⁡=−3.0\min=-3.0。属情形④:梯度 =A^=−2<0=\hat A=-2\lt0,即鼓励减小 ρ\rho(从 1.5 向区间靠拢)。

(d) A^=−2,ρ=0.5<0.8\hat A=-2,\rho=0.5\lt0.8:未裁剪项 −1.0-1.0,裁剪项 0.8×(−2)=−1.60.8\times(-2)=-1.6,min⁡=−1.6\min=-1.6。属情形②:梯度为 0——已经压低到这个坏动作的幅度,不再追加惩罚。

读法:把 A^\hat A 当「方向」、把 ρ\rho 当「已走多远」。(a)(d) 是「走太远且方向一致」→ 停手;(b)(c) 是「方向一致但走到反向区间去了」→ 继续走。这正是「软信任域」的四格图。■\blacksquare

∎
PPO 的完整目标与超参数

实践中的 PPO 从来不是只优化裁剪项,而是三项相加(符号约定:最大化目标、最小化其负值):

LPPO(θ,ϕ)=Et[min⁡(ρtA^t, clip(ρt,1−ε,1+ε)A^t)]⏟策略(Actor)− c1Et[(Vϕ(st)−R^t)2]⏟价值(Critic)+ c2Et[H(πθ(⋅∣st))]⏟熵正则.L^{\text{PPO}}(\theta,\phi)=\underbrace{\mathbb E_t\bigl[\min(\rho_t\hat A_t,\ \mathrm{clip}(\rho_t,1{-}\varepsilon,1{+}\varepsilon)\hat A_t)\bigr]}_{\text{策略(Actor)}} -\ c_1\underbrace{\mathbb E_t\bigl[\bigl(V_\phi(s_t)-\hat R_t\bigr)^2\bigr]}_{\text{价值(Critic)}} +\ c_2\underbrace{\mathbb E_t\bigl[\mathcal H\bigl(\pi_\theta(\cdot\mid s_t)\bigr)\bigr]}_{\text{熵正则}} .

熵正则:H(π)=−∑aπ(a∣s)log⁡π(a∣s)\mathcal H(\pi)=-\sum_a\pi(a\mid s)\log\pi(a\mid s) 度量策略的随机性。加它有两个作用:维持探索(防止策略过早坍缩到单一动作、熵归零后再也回不来)与正则化(抑制策略过度自信)。系数 c2c_2 常取 00–0.010.01:太小则熵塌缩,太大则拒绝收敛(回报曲线上表现为「一直很随机、学不快」)。

超参数 PPO 论文(MuJoCo 设置)与社区默认(以 Stable-Baselines3 的 PPO 默认值为参照) 作用与调法
裁剪系数 ε\varepsilon 0.20.2 越小越保守;0.10.1–0.30.3 是常见范围
折扣 γ\gamma 0.990.99 与任务时间尺度匹配(5.2)
GAE λ\lambda 0.950.95 偏差—方差旋钮(5.5.2)
学习率 3×10−43\times10^{-4}(Adam) 偏大是 PPO 不稳定的首要原因
rollout 长度 TT / 并行环境数 NN T=2048T=2048(单环境 SMDP)或 N×T≈2048N\times T\approx2048–81928192 决定一次更新的数据量与方差
minibatch / epochs 64 / 10 epochs 越大越省样本、越容易越界(看 clip_fraction)
价值系数 c1c_1 0.50.5 太小 Critic 学不动,太大梯度被价值项主导
熵系数 c2c_2 0.00.0(论文)/0.00.0–0.010.01(社区) 熵塌缩时上调
梯度裁剪 max-norm 0.50.5 抑制单批异常更新
优势归一化 开(batch 级标准化) 让不同任务的学习率可比
KL\mathrm{KL} 早停阈值 常见 0.0150.015–0.030.03(社区经验值) 超过阈值就丢弃本批剩余 epoch

5.5.5 稳定性诊断:看什么指标、异常时怎么办

PPO 相对「稳定」不等于「不会坏」。它坏的时候通常不会给出明显报错,只会悄悄变差(或看起来在变好)。下面这张表是本讲最实用的工程清单之一。

PPO 的六个诊断指标
指标 正常表现 异常与含义 处理
近似 KL E[ρ−1−log⁡ρ]\mathbb E[\rho-1-\log\rho] 每批更新后约 10−310^{-3}–10−210^{-2} 持续 >0.03&gt;0.03:策略每批变化过大 降学习率、降 epochs、降 ε\varepsilon、开 KL 早停
裁剪比例(越界样本占比) 约 5%–20% 长期 >50%\gt50\%:更新被大量截断,学不动;接近 0:裁剪几乎没起作用 调 ε\varepsilon;检查优势尺度是否过大
策略熵 缓慢下降 突然塌到 ~0:探索死锁;一直不降:c2c_2 太大或任务没学会 调 c2c_2;检查奖励是否有信号
价值损失 平缓、与回报同尺度 爆炸:Critic 目标尺度太大 奖励缩放、调 c1c_1、检查 done 掩码
解释方差(explained variance,1−Var(R−V)/Var(R)1-\mathrm{Var}(R-V)/\mathrm{Var}(R)) 上升趋近 1 <0&lt;0:Critic 还不如「用均值预测」 Critic 单独多训几步;检查观测归一化
回报与回合长度分布 多种子下均值上升、方差收敛 只有单条曲线好:不能下结论(5.7) 补种子;报分布而不是最优值
本节常见误解
  1. 「PPO 是 on-policy,所以同一批数据只能用一次」:严格策略梯度确实是;PPO 用 ρ\rho 做了重要性修正,因此可以在同一批数据上跑几个 epoch。但能跑几轮是有上限的——KL / 裁剪比例就是那个上限的度量。跑太多轮 ⇒ 数据严重 off-policy ⇒ 训练崩塌。
  2. 「裁剪对所有越界情形都起作用」:不是。5.5.4 的推导 表明 4 个越界象限里只有 2 个梯度归零(A>0A\gt0 且 ρ>1+ε\rho\gt1+\varepsilon;A<0A\lt0 且 ρ<1−ε\rho\lt1-\varepsilon),另 2 个仍然活跃且方向是「拉回」。
  3. 「ρ\rho 的裁剪等价于把概率比硬截断」:被裁剪的是目标函数,不是策略概率本身。策略概率依旧可以变到任意值(只是没有奖励激励),这与「把概率截断到 0」完全不同——后者会导致 log⁡0\log 0 与熵塌缩。
  4. 「优势不归一化也行」:优势的尺度随奖励设计变化,不归一化等于让有效学习率随任务漂移。归一化是几乎零成本的稳定器(但要注意:归一化会改变乘性尺度,若奖励的绝对水平有意义,应先确认它不会破坏语义)。
  5. 「TRPO 已过时,不用理解」:PPO 的裁剪是 TRPO 约束的一阶近似版本。不理解信任域,就无法回答「ε\varepsilon 到底在限制什么」——这也是面试里区分「背过 PPO」与「理解 PPO」的常见分水岭。
  6. 「PPO 能直接用于所有 RL 问题」:它对奖励尺度与回合结构很敏感。稀疏奖励(5.1)、连续控制的精确动作、需要深度探索的任务,往往需要额外的探索机制或换用其它算法族。

5.5.6 代码:PPO 的一次更新(GAE + 裁剪 + 诊断)

下面的片段把本节所有公式落成代码:GAE 的从后往前递归、裁剪目标、价值与熵项,以及可以直接打印的诊断量。完整训练循环(含环境与多种子评估)在 5.7。

import torch
import torch.nn as nn

def compute_gae(rewards, values, dones, last_value, gamma=0.99, lam=0.95):
    """rewards/values/dones 是长度 T 的张量;last_value 由 Critic 对最后一步的 s' 估计给出。"""
    T = len(rewards)
    adv = torch.zeros(T + 1)                       # adv[T] = 0:轨迹末端的后继优势视为 0
    for t in reversed(range(T)):                   # ← GAE 的递归必须从后往前
        next_val = last_value if t == T - 1 else values[t + 1]
        nonterm = 1.0 - dones[t]                   # 终止后不再自举(同 5.3 的 done 掩码)
        delta = rewards[t] + gamma * next_val * nonterm - values[t]
        adv[t] = delta + gamma * lam * nonterm * adv[t + 1]
    adv = adv[:T]
    returns = adv + values                         # 价值回归的目标:Â + V(“不变的目标”)
    return adv.detach(), returns.detach()

def ppo_update(policy, value_fn, optimizer, batch,
               eps_clip=0.2, vf_coef=0.5, ent_coef=0.01, max_grad_norm=0.5):
    """batch = (s, a, logp_old, adv, ret),均为同一批 rollout 上算好的量。"""
    s, a, logp_old, adv, ret = batch
    adv = (adv - adv.mean()) / (adv.std() + 1e-8)          # 优势归一化:让尺度与任务无关

    dist = torch.distributions.Categorical(logits=policy(s))
    logp = dist.log_prob(a)
    ratio = torch.exp(logp - logp_old)                     # ρ = π_θ / π_θold
    unclipped = ratio * adv
    clipped = torch.clamp(ratio, 1.0 - eps_clip, 1.0 + eps_clip) * adv
    policy_loss = -torch.min(unclipped, clipped).mean()    # 最大化目标 ⇒ 取负最小化

    value_loss = nn.functional.mse_loss(value_fn(s).squeeze(-1), ret)
    entropy = dist.entropy().mean()
    loss = policy_loss + vf_coef * value_loss - ent_coef * entropy

    optimizer.zero_grad(); loss.backward()
    nn.utils.clip_grad_norm_(list(policy.parameters()) + list(value_fn.parameters()), max_grad_norm)
    optimizer.step()

    # 诊断量:k3 估计的近似 KL(非负、无偏)与裁剪比例
    with torch.no_grad():
        approx_kl = ((ratio - 1.0) - (logp - logp_old)).mean().item()
        clip_frac = ((ratio - 1.0).abs() > eps_clip).float().mean().item()
    return {"loss": loss.item(), "approx_kl": approx_kl,
            "clip_frac": clip_frac, "entropy": entropy.item()}

if __name__ == "__main__":
    B, OBS, ACT = 64, 8, 4
    shared = nn.Sequential(nn.Linear(OBS, 64), nn.Tanh())           # 共享躯干(常见做法)
    policy = nn.Sequential(shared, nn.Linear(64, ACT))              # Actor 头
    value_fn = nn.Sequential(shared, nn.Linear(64, 1))               # Critic 头
    opt = torch.optim.Adam(list(policy.parameters()) + list(value_fn.parameters()), lr=3e-4)
    s = torch.randn(B, OBS); a = torch.randint(0, ACT, (B,))
    with torch.no_grad():
        logp_old = torch.distributions.Categorical(logits=policy(s)).log_prob(a)
    adv = torch.randn(B); ret = torch.randn(B)
    info = ppo_update(policy, value_fn, opt, (s, a, logp_old, adv, ret))
    assert 0.0 <= info["approx_kl"] < 1.0 and 0.0 <= info["clip_frac"] <= 1.0
    print(info)

# 判据(每次更新后打印,直接用它们排查):
# 1) approx_kl 应在 1e-3 ~ 1e-2 量级;> 0.03 说明这一步走得太远;
# 2) clip_frac 应在 5%~20%;长期 > 50% 说明更新几乎被截断(学不动);
# 3) entropy 应缓慢下降;若第一轮就接近 0,说明策略塌缩,检查奖励与 entropy 系数。

三点读代码的提示:

  1. logp_old 必须用采样时的策略算、并用 no_grad() 固定:它是 ρ\rho 的分母。若在更新循环里重算(或在多 epoch 之间更新),比率就不再是「新旧策略之比」,裁剪的前提失守。
  2. returns = adv + values 是 PPO 里最容易被写错的一处:Critic 的回归目标应当是「优势估计 + 当前价值估计」,它在一批数据内是常数,绝不能对 ret 求梯度(代码里用 .detach() 保证)。
  3. 诊断量不是装饰:approx_kl 与 clip_frac 是 PPO 的「仪表盘」。工业实现(如 RLHF 训练框架)通常还把它们写进日志并对 approx_kl 设早停阈值——这是 PPO 能在生产环境里长期跑下去的关键工程手段。
即时自测 · 5.5

Q1 写出优势函数的定义,并说明「优势均值为 0」这一性质来自哪条更基本的引理。

Q2 在 GAE 中,γλ\gamma\lambda 为什么会一起出现?若把 λ\lambda 从 0.950.95 调到 1.01.0,偏差与方差各朝哪个方向变化?

Q3 用例 5.5.2的记号,给定 A^=−1.5\hat A=-1.5、ρ=0.6\rho=0.6、ε=0.2\varepsilon=0.2,min⁡\min 取哪一项?梯度是否被截断?

Q4 训练日志显示:approx_kl = 0.08、clip_frac = 0.62、entropy = 0.02。三个数字合起来说明什么?给出两条优先处理措施。

参考要点

Q1 Aπ(s,a)=Qπ(s,a)−Vπ(s)A^\pi(s,a)=Q^\pi(s,a)-V^\pi(s),其中 Vπ(s)=∑aπ(a∣s)Qπ(s,a)V^\pi(s)=\sum_a\pi(a\mid s)Q^\pi(s,a)。故 ∑aπ(a∣s)Aπ(s,a)=0\sum_a\pi(a\mid s)A^\pi(s,a)=0——本质上就是定理 5.4.3 用的那条引理:任何只依赖状态的量在策略梯度期望里可自由加减(这里是减去 VV)。

Q2 γl\gamma^l 来自折扣(第 ll 步以后的奖励本来就该打折),λl\lambda^l 是人为给远处 TD 残差打的折扣,二者在闭式里天然乘在一起成为 (γλ)l(\gamma\lambda)^l。λ→1\lambda\to1:更接近蒙特卡洛 ⇒ 偏差减小、方差增大;λ→0\lambda\to0:更依赖 Critic ⇒ 方差减小、偏差增大(当 Critic 不准时偏差显著)。

Q3 ρ=0.6<0.8=1−ε\rho=0.6\lt0.8=1-\varepsilon,裁剪项为 0.8×(−1.5)=−1.20.8\times(-1.5)=-1.2,未裁剪项为 0.6×(−1.5)=−0.90.6\times(-1.5)=-0.9;min⁡(−0.9,−1.2)=−1.2\min(-0.9,-1.2)=-1.2,即取裁剪项。属情形②(A^<0\hat A\lt0 且 ρ<1−ε\rho\lt1-\varepsilon):∂f/∂ρ=0\partial f/\partial\rho=0,梯度被截断——坏动作的概率已经压得够低了,不再追加惩罚(继续压会破坏探索与熵)。

Q4 approx_kl=0.08 远超常见阈值(0.015–0.03)⇒ 每批更新走得太远;clip_frac=0.62 说明六成样本的更新被截断 ⇒ 有效学习信号被大量丢弃,训练会表现为「loss 在动但回报不涨」;entropy=0.02 逼近 0 ⇒ 策略已接近确定性,探索死锁(一旦塌缩很难恢复)。优先措施:① 立刻把学习率降到原来的 1/31/3–1/101/10,并把每批的 epochs 从 10 降到 3–5(或加 KL 早停);② 上调熵系数 c2c_2(例如从 0.01 提到 0.03)并检查奖励尺度是否需要缩放。两项做完再观察 approx_kl 是否回到 10−210^{-2} 量级、entropy 是否回升到 0.3 以上。

延伸阅读
  • Schulman, Moritz, Levine, Jordan & Abbeel, High-Dimensional Continuous Control Using Generalized Advantage Estimation(2016, arXiv:1506.02438)——GAE 原文(含 γλ\gamma\lambda 的最优性与实验)。
  • Schulman, Levine, Abbeel, Jordan & Moritz, Trust Region Policy Optimization(2015, arXiv:1502.05477)——TRPO 与替代目标的理论(单调改进的界)。
  • Schulman, Wolski, Dhariwal, Radford & Klimov, Proximal Policy Optimization Algorithms(2017, arXiv:1707.06347)——PPO 原文(裁剪目标、KL 惩罚变体、超参数表)。
  • Mnih et al., Asynchronous Methods for Deep Reinforcement Learning(2016, arXiv:1602.01783)——A3C / A2C 的并行环境做法。
  • Stable-Baselines3 文档的「PPO」页——逐项列出社区默认超参数,与本节表格互为参照(工程文档,非论文)。
  • OpenAI Spinning Up 的 Intro to Policy Optimization 与 Trust Region 两节——把 TRPO→PPO 的动机讲得很清楚,适合作为本节的第二遍材料。

本节小结:Actor–Critic 把「学策略」与「学价值」分成两个网络互相喂饭,优势函数 A=Q−VA=Q-V 是它们之间的接口;GAE 用 γλ\gamma\lambda 把「单步 TD(低方差高偏差)」与「蒙特卡洛(高方差低偏差)」连成一根可调的旋钮,成为 PPO 的事实标准。TRPO 用 KL 硬约束限制策略变化幅度,代价是二阶优化;PPO 用裁剪替代目标在越界时把「继续外推」的梯度归零、同时保留「拉回区间」的梯度,用一阶实现近似同样的效果,并配以价值损失与熵正则。它是今天的默认算法——也是 5.6 里 RLHF 第一阶段的执行器。下一节处理两个更现实的约束:不能交互(离线 RL)与写不出奖励(RLHF 与偏好优化)。