Skip to content

PPO算法深入浅出:从原理到手搓三层网络

PPO (Proximal Policy Optimization,近端策略优化) 是目前深度强化学习(Deep Reinforcement Learning, DRL)中最流行的算法之一。OpenAI 在发布它时称其为“默认的首选算法”。

这篇文章旨在通过通俗易懂的语言,结合扎实的数学基础和手搓级的代码/计算推导,带你彻底搞懂 PPO。

1. 为什么我们需要 PPO?

在 PPO 出现之前,我们有 DQN(适合离散动作)、DDPG(适合连续动作)以及基础的 Policy Gradient (PG) 算法。

基础的 Policy Gradient 有一个致命的弱点:步长难以控制

  • 如果更新步长太小,训练太慢。
  • 如果更新步长太大,策略可能会更新到一个极其糟糕的区域,导致“一失足成千古恨”,模型崩塌,很难再恢复回来。

TRPO (Trust Region Policy Optimization) 试图解决这个问题,它给策略更新画了一个圈(信任区域),说:“你更新可以,但不能跑出这个圈”。TRPO 效果很好,但计算极其复杂(涉及海森矩阵的逆,计算量大)。

PPO 的核心思想很简单:我想利用 TRPO 的稳定性,但我不想算那么复杂的数学。 于是,PPO 用一个简单的“剪切(Clip)”操作,近似了 TRPO 的约束。

比喻: 假设你在学骑自行车。

  • PG 就像是蒙眼狂奔,运气好骑得快,运气不好直接冲进沟里。
  • TRPO 就像是每次只允许你调整 1 度的车把角度,安全但计算每一个角度极其精确繁琐。
  • PPO 就像是教练告诉你:“你随便调,但如果感觉要摔了(和原来的姿势差别太大),就别调了,保持现状。”

2. 数学基础“补给站”

为了看懂后面的推导,我们需要复习几个基础的数学概念。

2.1 链式求导 (Chain Rule)

这是神经网络反向传播的基石。 如果有 y=f(u)u=g(x),那么 yx 的导数是:

dydx=dydududx

例子: 设 y=sin(x2)。 令 u=x2,则 y=sin(u)dydu=cos(u), dudx=2xdydx=cos(x2)2x

2.2 Log-Derivative Trick (对数导数技巧)

在强化学习的梯度推导中,你经常看到 logπ(a|s)。这是因为:

logf(x)=f(x)f(x)

反过来就是:

f(x)=f(x)logf(x)

这在期望梯度的推导中非常有用,因为它把概率密度的梯度转化为了对数概率的梯度,方便结合期望公式 E[]

2.3 常见的激活函数导数

在手搓神经网络时,我们需要知道每一层的导数。

  1. Sigmoid: σ(x)=11+ex
    • 导数:σ(x)=σ(x)(1σ(x))
  2. Softmax (用于输出层概率):
    • yi=eziezj
    • 如果不结合 CrossEntropy,单独求导比较复杂(涉及 Jacobian 矩阵)。
    • 重点:在 PPO 中,我们通常优化的是 logπ(a|s)
    • 如果是 Softmax 输出,logyizj 的结果非常简洁:
      • i=j 时,导数为 1yi
      • ij 时,导数为 yj

3. PPO 的核心逻辑

3.1 目标函数

PPO 的核心在于它的目标函数 (Objective Function)。我们要最大化这个目标:

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

别被公式吓到了,我们拆解来看:

  1. rt(θ) (Ratio): 新策略和老策略的比值。

    rt(θ)=πθ(at|st)πθold(at|st)
    • 如果 rt>1,说明新策略比老策略更倾向于采取动作 at
    • 初始时刻,θ=θold,所以 rt=1
  2. A^t (Advantage, 优势函数): 告诉我们动作 at 有多好。

    • A>0: 动作比平均水平好,应该增加其概率。
    • A<0: 动作比平均水平差,应该减少其概率。
  3. Clip (剪切):

    • clip(r,1ϵ,1+ϵ) 限制了 r 的范围在 [0.8,1.2] 之间(假设 ϵ=0.2)。
  4. Min (取最小值): 这是 PPO 的精髓。它是一种悲观(保守)的下界估计。

    • A>0 (好事): 我们希望 r 变大(增加概率)。但如果 r 太大(超过 1.2),就被 Clip 截断了。这意味着:奖励好动作,但不要过分奖励导致策略变化太大。
    • A<0 (坏事): 我们希望 r 变小(减少概率)。但如果 r 太小(低于 0.8),也被 Clip 截断了。这意味着:惩罚坏动作,但也不要过分惩罚。

3.2 流程图解

mermaid
graph TD
    A[环境 Environment] -->|State s| B["Agent (Policy Network)"]
    B -->|Action a| A
    A -->|Reward r, Next State s'| C[收集 Trajectory]
    C -->|计算 Advantage A| D[计算 PPO Loss]
    D -->|Backpropagation| E[更新 Policy Network 参数]
    E -->|更新 Old Policy| B

4. 手搓验证:3层神经网络的 PPO 推导

这是本文的重头戏。我们将构建一个最简单的 3 层神经网络,手动模拟 PPO 的正向传播反向传播

4.1 模型结构定义

假设我们的任务是一个简单的离散动作游戏(比如向左走、向右走)。

  • 输入层 (Input): 2个神经元 (代表状态 s=[x1,x2])
  • 隐藏层 (Hidden): 2个神经元 (激活函数使用 ReLU)
  • 输出层 (Output): 2个神经元 (代表动作 Logits,经过 Softmax 变为概率)

参数设定 (简化版,不设 Bias):

  • W1: 输入到隐藏层的权重 (2×2 矩阵)
  • W2: 隐藏到输出层的权重 (2×2 矩阵)

模型图示:

mermaid
graph LR
    subgraph Input Layer
    x1((x1))
    x2((x2))
    end
    subgraph Hidden Layer
    h1((h1))
    h2((h2))
    end
    subgraph Output Layer
    o1((o1: Logit L))
    o2((o2: Logit R))
    end
    subgraph Probabilities
    p1((p1: Left))
    p2((p2: Right))
    end

    x1 --> h1
    x1 --> h2
    x2 --> h1
    x2 --> h2
    h1 --> o1
    h1 --> o2
    h2 --> o1
    h2 --> o2
    o1 --> p1
    o2 --> p2

4.2 初始状态设定

输入: s=[1.0,2.0]

权重初始化:

W1=[0.10.20.30.4]

(注意:这里假设 W1[0][0] 连接 x1h1)

W2=[0.50.60.50.6]

PPO 超参数:

  • ϵ=0.2 (Clip range)
  • 我们假设这是一个更新步骤,因此我们需要一个“旧策略”的概率。

4.3 正向传播 (Forward Pass)

第一步:计算隐藏层

h=ReLU(sW1)h1=x10.1+x20.3=1.00.1+2.00.3=0.1+0.6=0.7h2=x10.2+x20.4=1.00.2+2.00.4=0.2+0.8=1.0

经过 ReLU (正数不变):h=[0.7,1.0]

第二步:计算输出 Logits

z=hW2z1(LogitL)=h10.5+h2(0.5)=0.70.5+1.0(0.5)=0.350.5=0.15z2(LogitR)=h10.6+h2(0.6)=0.70.6+1.0(0.6)=0.420.6=0.18

z=[0.15,0.18]

第三步:计算概率 (Softmax)

p1(Left)=e0.15e0.15+e0.180.86070.8607+0.83530.86071.6960.5075p2(Right)=10.5075=0.4925

当前策略 πnew(s)=[0.5075,0.4925]

假设场景:

  • 在收集数据阶段(使用旧策略),我们在状态 s 选择了动作 Left (Index 0)
  • 旧策略在当时的概率是 πold(a=Left|s)=0.4 (注意:这里假设旧策略和新策略已经不同了,为了演示更新)。
  • 执行该动作后,计算出的优势函数 A=1.0 (这是一个好动作!)。

4.4 PPO Loss 计算

我们要计算针对动作 Left 的 Loss。

  1. 计算 Ratio:

    r=πnew(Left)πold(Left)=0.50750.4=1.26875
  2. 计算未剪切项:

    Surr1=rA=1.268751.0=1.26875
  3. 计算剪切项: ϵ=0.2,所以范围是 [0.8,1.2]r=1.26875>1.2,所以被剪切为 1.2

    Surr2=clip(r,0.8,1.2)A=1.21.0=1.2
  4. 取最小值 (PPO Objective):

    LCLIP=min(1.26875,1.2)=1.2

注意:通常我们是最小化 Loss,而 PPO 公式是最大化目标函数。所以并在深度学习框架中,我们通常定义:

Loss=LCLIP=1.2

4.5 反向传播 (Backward Pass) - 手搓梯度

我们要更新权重 W2W1。我们需要求 LossW。 因为 r 被 Clip 住了 (处于 1.2 的边界),这在数学上是一个分段函数。

  • 如果 r 处于 Clip 区域(平坦区域),梯度通常被认为是 0(或者不再鼓励进一步更新)。
  • 但是! 为了演示梯度的流动,我们假设 r 稍微小一点点,比如 r=1.1,没有触发 Clip。
    • 修正假设:令 πold=0.45,则 r=0.5075/0.451.127
    • 此时 r<1.2,未触发 Clip。
    • L=rA=1.1271.0=1.127
    • 我们要最小化 J=rA

链式法则路径:

Jrπnew(a0)zW2

Step 1: 对 r 求导

J=rAJr=A=1.0

Step 2: 对 πnew(a0) 求导

r=πnewπoldrπnew=1πold=10.452.22

Step 3: 对 Logits z 求导 (Softmax 求导) 我们需要求 πnew(a0)z。 我们选的是 a0 (Left, Index 0)。

π0z0=π0(1π0)=0.5075(10.5075)0.25π0z1=π0π1=0.50750.49250.25

Step 4: 汇总到 z 的梯度

δz0=Jz0=Jrrπnewπnewz0δz0=(1.0)(2.22)(0.25)=0.555

同理:

δz1=(1.0)(2.22)(0.25)=0.555

(注意:Logits 的梯度和为 0,这是 Softmax 的特性)

Step 5: 对 W2 求导

z=hW2JW2=hTδzh=[0.7,1.0]δz=[0.555,0.555]JW20,0(h1z0)=h1δz0=0.7(0.555)=0.3885JW21,0(h2z0)=h2δz0=1.0(0.555)=0.555

...以此类推。

更新权重: 假设学习率 α=0.1

W2new=W2oldαGradientW20,0new=0.50.1(0.3885)=0.53885

你看,因为 A>0 (好动作),我们希望增加 Left 的概率。 W20,0 连接 h1z0(Left)。增加这个权重,会增加 z0,进而增加 π(Left)。逻辑验证通过!


5. 总结

通过这篇文章,我们不仅了解了 PPO 的数学原理,还“人肉”运行了一遍神经网络的反向传播。

  1. PPO 通过 Clip 操作,在稳定性计算效率之间找到了完美的平衡。
  2. Ratio r 衡量了新旧策略的偏离程度。
  3. Advantage A 指导了更新的方向(好则加,坏则减)。
  4. 反向传播 就是利用链式法则,将“我们要怎么改”的信号,一层层传回给权重的过程。

希望这篇硬核科普能帮你彻底揭开 PPO 的神秘面纱!