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)
这是神经网络反向传播的基石。 如果有
例子: 设
2.2 Log-Derivative Trick (对数导数技巧)
在强化学习的梯度推导中,你经常看到
反过来就是:
这在期望梯度的推导中非常有用,因为它把概率密度的梯度转化为了对数概率的梯度,方便结合期望公式
2.3 常见的激活函数导数
在手搓神经网络时,我们需要知道每一层的导数。
- Sigmoid:
- 导数:
- 导数:
- Softmax (用于输出层概率):
- 设
- 如果不结合 CrossEntropy,单独求导比较复杂(涉及 Jacobian 矩阵)。
- 重点:在 PPO 中,我们通常优化的是
。 - 如果是 Softmax 输出,
的结果非常简洁: - 当
时,导数为 - 当
时,导数为
- 当
- 设
3. PPO 的核心逻辑
3.1 目标函数
PPO 的核心在于它的目标函数 (Objective Function)。我们要最大化这个目标:
别被公式吓到了,我们拆解来看:
(Ratio): 新策略和老策略的比值。 - 如果
,说明新策略比老策略更倾向于采取动作 。 - 初始时刻,
,所以 。
- 如果
(Advantage, 优势函数): 告诉我们动作 有多好。 : 动作比平均水平好,应该增加其概率。 : 动作比平均水平差,应该减少其概率。
Clip (剪切):
限制了 的范围在 之间(假设 )。
Min (取最小值): 这是 PPO 的精髓。它是一种悲观(保守)的下界估计。
- 当
(好事): 我们希望 变大(增加概率)。但如果 太大(超过 1.2),就被 Clip 截断了。这意味着:奖励好动作,但不要过分奖励导致策略变化太大。 - 当
(坏事): 我们希望 变小(减少概率)。但如果 太小(低于 0.8),也被 Clip 截断了。这意味着:惩罚坏动作,但也不要过分惩罚。
- 当
3.2 流程图解
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| B4. 手搓验证:3层神经网络的 PPO 推导
这是本文的重头戏。我们将构建一个最简单的 3 层神经网络,手动模拟 PPO 的正向传播和反向传播。
4.1 模型结构定义
假设我们的任务是一个简单的离散动作游戏(比如向左走、向右走)。
- 输入层 (Input): 2个神经元 (代表状态
) - 隐藏层 (Hidden): 2个神经元 (激活函数使用 ReLU)
- 输出层 (Output): 2个神经元 (代表动作 Logits,经过 Softmax 变为概率)
参数设定 (简化版,不设 Bias):
: 输入到隐藏层的权重 ( 矩阵) : 隐藏到输出层的权重 ( 矩阵)
模型图示:
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 --> p24.2 初始状态设定
输入:
权重初始化:
(注意:这里假设
PPO 超参数:
(Clip range) - 我们假设这是一个更新步骤,因此我们需要一个“旧策略”的概率。
4.3 正向传播 (Forward Pass)
第一步:计算隐藏层
经过 ReLU (正数不变):
第二步:计算输出 Logits
第三步:计算概率 (Softmax)
当前策略
假设场景:
- 在收集数据阶段(使用旧策略),我们在状态
选择了动作 Left (Index 0)。 - 旧策略在当时的概率是
(注意:这里假设旧策略和新策略已经不同了,为了演示更新)。 - 执行该动作后,计算出的优势函数
(这是一个好动作!)。
4.4 PPO Loss 计算
我们要计算针对动作 Left 的 Loss。
计算 Ratio:
计算未剪切项:
计算剪切项:
,所以范围是 。 ,所以被剪切为 。 取最小值 (PPO Objective):
注意:通常我们是最小化 Loss,而 PPO 公式是最大化目标函数。所以并在深度学习框架中,我们通常定义:
4.5 反向传播 (Backward Pass) - 手搓梯度
我们要更新权重
- 如果
处于 Clip 区域(平坦区域),梯度通常被认为是 0(或者不再鼓励进一步更新)。 - 但是! 为了演示梯度的流动,我们假设
稍微小一点点,比如 ,没有触发 Clip。 - 修正假设:令
,则 。 - 此时
,未触发 Clip。 。 - 我们要最小化
。
- 修正假设:令
链式法则路径:
Step 1: 对
Step 2: 对
Step 3: 对 Logits
Step 4: 汇总到
同理:
(注意:Logits 的梯度和为 0,这是 Softmax 的特性)
Step 5: 对
...以此类推。
更新权重: 假设学习率
你看,因为
5. 总结
通过这篇文章,我们不仅了解了 PPO 的数学原理,还“人肉”运行了一遍神经网络的反向传播。
- PPO 通过 Clip 操作,在稳定性和计算效率之间找到了完美的平衡。
- Ratio
衡量了新旧策略的偏离程度。 - Advantage
指导了更新的方向(好则加,坏则减)。 - 反向传播 就是利用链式法则,将“我们要怎么改”的信号,一层层传回给权重的过程。
希望这篇硬核科普能帮你彻底揭开 PPO 的神秘面纱!