Skip to content

PPO

Proximal Policy Optimization

PPO 是什么

想办法让策略变好,但又不能一下子改太猛。

在强化学习里,我们想学一个策略 πθ(as),也就是“在状态 s 下采取动作 a 的概率”。如果每次更新都特别激进,策略很容易直接崩掉;如果每次更新都特别保守,学习又会太慢。PPO 就是在这两者之间找平衡。

所以你可以把 PPO 理解成:

一个“带刹车”的策略梯度方法。

TIP

PPO 是一种 on-policy 的 actor-critic 强化学习算法,可以看成是对传统策略梯度方法的一种“保守更新”改造。

它从旧策略采样轨迹,使用 value function 和 GAE 估计 advantage,再通过 importance ratio 构造 surrogate objective。

为了防止同一批数据被反复优化时造成过大的策略漂移,PPO 不直接最大化 rt(θ)A^t,而是使用 clipped objective:min(rtA^t, clip(rt,1ϵ,1+ϵ)A^t)

其中 clip 的作用不是完全禁止大更新,而是在更新过大时移除额外收益,从而让训练更稳定。

实践中,PPO 还会同时训练 value function,并加入 entropy bonus 鼓励探索,因此完整目标通常由 policy loss、value loss 和 entropy 三部分构成。

和 TRPO 相比,PPO 放弃了更复杂的 trust-region 约束求解,换来了更简单、更易实现、但通常仍然很稳的训练方式。

PPO 的核心不是“直接让奖励变大”,而是:用旧策略采到的数据,谨慎地调整新策略,让好动作概率上升、坏动作概率下降,但不要一次改太猛。

从策略梯度讲起

PPO 不是凭空出现的,它是从最基础的 policy gradient 一路长出来的。

强化学习里最根本的目标是最大化策略的期望回报:

J(θ)=Eτπθ[R(τ)].

这里:

  • θ 是策略参数;
  • τ 是一整条轨迹;
  • R(τ) 是这条轨迹的总回报。
  • τπθ 表示“轨迹是由当前策略采样出来的”,所以策略一变,采到的数据分布也会变。这就是后面 PPO 为什么要小心更新的根源。

也就是说,我们真正想做的是:

调整参数 θ,让策略平均拿到更高的回报。

策略梯度方法告诉我们,可以直接对这个目标做梯度上升。最基本的 policy gradient 形式是:

θJ(θ)=Eτπθ[tθlogπθ(atst)Φt].

这里的 Φt 可以取很多种形式,最常见的是 return、reward-to-go、或者 advantage。实际中最常见的写法是 advantage form:

θJ(θ)E[tθlogπθ(atst)A^t].

这个式子表达的意思其实很朴素:

  • 如果一个动作比平均水平更好,也就是 A^t>0,那就增加它的概率;
  • 如果一个动作比平均水平更差,也就是 A^t<0,那就降低它的概率。

所以 policy gradient 的精神就是:

好动作多做,坏动作少做。

TIP

θπθ(as)=πθ(as)θlogπθ(as)

“概率本身的梯度” 可以写成 “概率 × log 概率的梯度”。

在 RL 中,这么写可以把求和变成期望:

aθπθ(as)Q(s,a)

利用恒等式变成:

aπθ(as)θlogπθ(as)Q(s,a)

也就是:

Eaπθ[θlogπθ(as)Q(s,a)]

这就很方便了:我们不用枚举所有动作,只要从策略里采样动作,就能估计这个梯度。

为什么不能直接一直优化这个目标

看起来很自然,但这里有一个大坑。

如果我们直接把 policy gradient 写成一个“损失函数”,比如

LPG(θ)=E^t[logπθ(atst)A^t],

然后拿着同一批轨迹反复做很多步梯度上升,往往会出事。

原因是:

这批数据是旧策略采出来的,不是新策略采出来的。

一旦你更新了参数,当前策略就不是生成这批数据的那个策略了。此时你再继续拿同一批旧数据狠狠干,就会越来越偏离真实目标,最后可能把策略推崩。

PPO 原论文在背景部分就明确指出:对同一批轨迹做多步优化,经验上经常会导致破坏性的超大策略更新。PPO 想解决的正是这个问题。

Advantage 到底是什么

advantage function 的定义是:

Aπ(st,at)=Qπ(st,at)Vπ(st).

它表示:

这个动作到底比“当前策略在这个状态下的平均表现”好多少。

其中 Qπ(s,a) 表示“在状态 s 采取动作 a 后,未来大概能拿多少分”。

Vπ(s) 表示“在状态 s 按当前策略正常发挥,未来平均能拿多少分”。

所以:

  • Aπ(s,a)>0,说明这个动作比平均水平好;
  • Aπ(s,a)<0,说明这个动作比平均水平差;
  • Aπ(s,a)=0,说明它大概就是平均水平。

这也是为什么 advantage 特别适合拿来做 policy update:它直接告诉你“该鼓励还是该打压”。

TIP

比如某个状态下平均能得 10 分,而你选的动作最后预计能得 14 分,那么 advantage 是 4,说明这个动作值得鼓励。

如果预计只能得 7 分,那么 advantage 是 -3,说明这个动作要被压低概率。

Value function 和 baseline 为什么会出现

如果你直接用整条轨迹的总回报做权重,方差通常很大,学习会很抖。

一个经典技巧是加 baseline。最常见的 baseline 就是 value function:

Vπ(st)=E[未来回报st].

于是我们不再问“这次回报有多大”,而是问:

这次结果比我原本预期的好还是差?

这就把 return 变成了 advantage,也就是

advantagereturnbaseline.

这样做的好处是:

不改变期望梯度,但通常能显著降低方差。

直觉上也很好理解:

  • 如果结果和预期差不多,那就别太激动;
  • 真正值得大改策略的,是那些“明显比预期好”或“明显比预期差”的动作。

GAE 是怎么来的

PPO 实际应用里最常用的 advantage 估计方法之一就是 GAE,也就是 Generalized Advantage Estimation

先定义 TD residual

δtV=rt+γV(st+1)V(st).

这个量可以理解成:

一步现实结果 - 一步价值预测。

更通俗的讲:

真实看到的一步结果 − 原本对当前状态的预测

其中 rt+γV(st+1) 是“我走了一步以后,得到奖励,再加上下个状态未来值”。

V(st) 是“我原本以为当前状态值多少钱”。

所以 δtV>0 表示结果比预期好,δtV<0 表示结果比预期差。

如果 value function 很准,那么 δtV 就已经是一个不错的局部 advantage 信号。

但只看一步会偏;看很长的回报又会方差大。所以 GAE 采取折中:把很多步的 TD 误差按指数衰减加起来。

GAE 的定义是:

A^tGAE(γ,λ)=l=0(γλ)lδt+lV.

就是把很多步的 TD residual 加起来。

GAE 公式推导

先定义 k 步 advantage 估计:

A^t(k)=l=0k1γlδt+lV.

把前几项展开看一下:

A^t(1)=δtV,A^t(2)=δtV+γδt+1V,A^t(3)=δtV+γδt+1V+γ2δt+2V.

GAE 的想法是,把这些不同步长的估计做指数加权平均:

A^tGAE(γ,λ)=(1λ)(A^t(1)+λA^t(2)+λ2A^t(3)+).

把上面的展开式代进去,然后把同类项收集,就会得到:

A^tGAE(γ,λ)=l=0(γλ)lδt+lV.

这就是 GAE 论文给出的简洁形式。

λ 到底在控制什么

如果 λ=0,那就只有一步 TD:

A^t=δtV.

它方差小,但偏差可能大。

如果 λ=1,那就更接近长回报:

A^t=l=0γlδt+lV.

它偏差更小,但方差更大。

所以 λ 控制的就是 bias-variance trade-off。

这也是为什么 PPO 实现里常看到像 γ=0.99λ=0.95 这样的组合:它们是在“稳”和“准”之间做折中。

从旧策略到新策略:为什么会出现 ratio

PPO 真正的关键转折点在这里。

我们想更新策略,但手里拿到的数据是旧策略 πθold 采出来的。那要如何评价“新策略 πθ 在这批旧数据上会怎样”呢?

对同一个动作,新策略比旧策略更想做,还是更不想做?

这时会用到一个非常经典的东西:importance sampling ratio

定义:

rt(θ)=πθ(atst)πθold(atst).

这个比值很好理解:

  • 如果 rt(θ)>1,说明新策略更想做这个动作;
  • 如果 rt(θ)<1,说明新策略更不想做这个动作;
  • 如果 rt(θ)=1,说明新旧策略在这个动作上的态度一样。

于是,旧策略数据上的 surrogate objective 可以写成:

LCPI(θ)=E^t[rt(θ)A^t].

这通常叫 surrogate objective

这个目标的直觉

如果 A^t>0,说明这个动作好,那我们希望提高它在新策略下的概率,于是希望 rt 变大。

如果 A^t<0,说明这个动作差,那我们希望降低它的概率,于是希望 rt 变小。

听起来完全合理。

但问题是:

如果你只顾着把这个目标做大,很容易把策略改得太远。

因为这个 surrogate objective 本质上只是“在旧策略附近”的一个局部近似。离得太远,它就不可靠了。

TRPO 为什么会出现

在 PPO 之前,一个很有代表性的思路是 TRPO,也就是 Trust Region Policy Optimization

TRPO 的想法是:

既然 surrogate objective 只在旧策略附近可靠,那我就强行限制每次更新不能离旧策略太远。

它写成约束优化就是:

maxθLθold(θ)s.t.D¯KL(πθold,πθ)δ.

也就是说,一边想让 surrogate objective 变大,一边要求新旧策略之间的平均 KL divergence 不超过阈值 δ

这个想法很漂亮,但工程上比较重,因为它涉及约束优化、二阶近似、共轭梯度等技巧,实现复杂。

PPO 的核心想法:别搞那么重,直接 clip

PPO 的核心思想就是:

我也想让新策略别离旧策略太远,但我不想像 TRPO 那样搞一个重型约束优化。

于是 PPO 提出一个更简单的做法:直接在 objective 里把 ratio 卡住。

PPO-Clip 的核心目标是:

LCLIP(θ)=E^t[min(rt(θ)A^t,clip(rt(θ),1ϵ,1+ϵ)A^t)].

这里的 ϵ 是一个小超参数,比如 0.10.2

这个式子看起来吓人,但其实只是在做一件事:

如果 ratio 改得太多,就不给你继续赚这部分收益。

clip 到底在干什么

这是 PPO 最容易看懵、但其实最该弄懂的一步。

先看 A^t>0 的情况。

这说明这个动作是好动作。我们当然希望新策略提高它的概率,所以想让 rt(θ) 变大。

如果没有限制,最自然就是一直把 rt 往上推。

但 PPO 说:

可以往上推,但推到 1+ϵ 以后,再继续推,objective 不再继续奖励你。

因为这时候:

clip(rt,1ϵ,1+ϵ)=1+ϵ.

于是目标就最多只会按 (1+ϵ)A^t 来算。也就是说,继续变大已经没有额外好处了。

再看 A^t<0 的情况。

这说明这个动作是坏动作,我们希望新策略降低它的概率,也就是让 rt 变小。

PPO 同样说:

可以往下压,但压到 1ϵ 以后,再继续压,objective 也不再继续奖励你。

所以不管是奖励好动作还是惩罚坏动作,PPO 都在做同一件事:

限制单次更新不要太激进。

用一个小数字例子理解

假设旧策略下某动作概率是 0.2,新策略想把它提高到 0.3

那么

r=0.30.2=1.5.

如果 ϵ=0.2,那允许区间只有 [0.8,1.2]

这时如果该动作的 advantage 为正,本来 rA=1.5A,但 clip 以后最多只按

1.2A

来算。

也就是说,PPO 会告诉你:

这动作是好,但你别一次把它的概率抬太多。

反过来,如果它是坏动作,旧概率 0.2,新概率被压到 0.1,那么

r=0.10.2=0.5.

如果 A^<0,理论上你会很想继续压。但 PPO 只允许你最低按 0.8 这条线吃收益,超出这部分不会继续奖励。

所以 clip 真正干的事不是“完全禁止大更新”,而是:

取消大更新带来的额外激励。

为什么公式里要取 min

对于:

min(rtA^t, clip(rt,1ϵ,1+ϵ)A^t)

为什么是取最小值?

答案是:

因为 PPO 想要一个偏保守、偏悲观的目标。

原论文就明确说,这个 clipped objective 是 unclipped objective 的一个 lower bound,也就是一种 pessimistic bound。

它的思路是:

  • 如果修改方向对我们有利,但改得太多,那就按 clip 后的更保守版本算;
  • 如果修改方向对我们不利,那就老老实实把不利后果算进去。

宁可保守一点,也不要把策略一步推飞。

PPO-Penalty 和 PPO-Clip 的关系

PPO 其实有两个主要变体。

一个是 PPO-Penalty,写法更接近 TRPO 的软约束版本:

LKLPEN(θ)=E^t[rt(θ)A^tβDKL(πθold(st),πθ(st))].

也就是直接把 KL penalty 加到目标里。

另一个就是最常见的 PPO-Clip。OpenAI Spinning Up 也明确说,实际大家最常讨论和使用的是 PPO-Clip。

直觉上可以这样区分:

  • PPO-Penalty:用 KL 项“罚”你走太远;
  • PPO-Clip:直接把 ratio 的可盈利区间截住。

工程上大家更爱 PPO-Clip,因为更简单、也通常更稳。

PPO 里的 actor 和 critic 到底各干什么

PPO 通常是一个 actor-critic 方法。

其中:

  • actor 就是策略网络 πθ(as)
  • critic 就是价值网络 Vϕ(s)

两者分工很明确。

策略网络负责“怎么选动作”。

价值网络负责“现在这个状态大概值多少钱”,也就是帮助估计 advantage。

所以 critic 不是直接输出动作,它更像一个老师,负责告诉 actor:

你刚才那个动作,和平均水平相比,到底是赚了还是亏了。

这也是为什么 PPO 和 GRPO 最大的区别之一,就是 GRPO 删掉了 PPO 里的 critic/value model,改成组内相对奖励来构造 advantage。

13. PPO 的完整训练目标

在真实实现里,PPO 通常不只有 policy loss。

原论文给出的组合目标是:

LtCLIP+VF+S(θ)=E^t[LtCLIP(θ)c1LtVF(θ)+c2S[πθ](st)].

其中:

  • LCLIP 是策略更新项;
  • LVF 是 value function 的回归误差;
  • S[πθ] 是 entropy bonus,用来鼓励探索。

value loss 通常是平方误差:

LtVF(θ)=(Vθ(st)Vttarg)2.

entropy bonus 为什么有用

如果策略太快变得特别确定,探索就会变差。

entropy 大表示策略还比较随机,说明它还在探索;entropy 太小表示策略已经很“死板”了。于是加一项 entropy bonus,就是在告诉模型:

先别那么快把所有概率压成 0 和 1,保留一点探索。

PPO 的完整训练流程

如果把 PPO 忘掉一切细节,只记训练循环,可以记成下面这几步。

先用当前旧策略 πθold 跑环境,收集一批轨迹数据。

然后用这批轨迹计算:

  • reward-to-go;
  • value targets;
  • advantage,通常用 GAE。

接着固定这批旧数据,计算旧策略下的 log-prob,形成 importance ratio:

rt(θ)=πθ(atst)πθold(atst).

然后对 clip objective 做多轮小步 SGD 或 Adam 更新。

与此同时,再训练 value network 去拟合 return 或 value target。

最后,把更新后的策略当成新的 old policy,再去环境里采下一批数据。

为什么 PPO 是 on-policy

因为它主要使用的是“当前旧策略刚采出来的数据”,而不是像 DQN、SAC 那样长时间反复使用一个大的 replay buffer。

所以 PPO 属于典型的 on-policy 方法。

这类方法一般优点是训练更稳定、理论更顺;缺点是样本利用率通常不如很多 off-policy 方法。

你可以把 PPO 理解成什么

如果非要找一个特别通俗的比喻,PPO 很像:

你每次根据最近一次考试结果调整学习策略,但调整幅度不能太大。

如果某种做题方法最近明显有效,那下次更倾向于继续用;

如果某种方法最近明显无效,那下次就少用。

但你不会因为一次考试表现好,就把整套学习方法完全推翻重来。PPO 的 clip 就是在干这个事:

允许改,但别一次改过头。

总结

PPO = 用 clipped objective 限制策略每次不要改太猛的 actor-critic 策略梯度方法。

importance ratio:

rt(θ)=πθ(atst)πθold(atst).

PPO-Clip 核心目标:

LCLIP(θ)=E^t[min(rt(θ)A^t,clip(rt(θ),1ϵ,1+ϵ)A^t)].

GAE:

A^tGAE(γ,λ)=l=0(γλ)lδt+lV,δtV=rt+γV(st+1)V(st).