近端策略优化算法 (PPO) 详解
近端策略优化(Proximal Policy Optimization, PPO)是强化学习领域中一种非常流行的策略梯度算法。它的设计初衷是在保证性能提升的同时,让训练过程更加稳定和高效。相比于传统的策略梯度方法,PPO 通过限制策略更新的幅度,有效避免了模型因参数更新过大而崩溃的问题。
背景与核心思想
在强化学习中,直接优化策略往往会导致训练不稳定。如果每一步的参数更新幅度过大,模型可能会偏离当前最优解太远,甚至完全失效。TRPO(Trust Region Policy Optimization)虽然解决了这个问题,但其计算复杂度高,涉及二次规划,难以落地。
PPO 的核心思想可以概括为:限制策略更新幅度。它不改变采样数据的分布,而是通过一个裁剪(Clipping)机制,确保新策略和旧策略的概率比率不会偏离太多。这样既保留了 TRPO 的稳定性,又大大简化了实现难度。
概率比率与优势函数
PPO 引入了概率比率来衡量新旧策略的差异:
$$r_t(\theta) = \frac{\pi_\theta(a_t | s_t)}{\pi_{\theta_{\text{old}}}(a_t | s_t)}$$
其中 $\pi_\theta$ 是新策略,$\pi_{\theta_{\text{old}}}$ 是旧策略。这个比率反映了在当前状态下,新策略选择动作 $a_t$ 的概率相对于旧策略的变化程度。
同时,为了评估某个动作的好坏,我们使用优势函数 $A_t$:
$$A_t = Q(s_t, a_t) - V(s_t)$$
或者使用广义优势估计(GAE)进行近似。优势函数告诉我们,相对于平均水平,这个动作表现得好还是坏。
优化目标与损失函数
PPO 的目标函数包含三个主要部分:策略损失、值函数损失和熵正则化。
1. 裁剪策略损失
为了防止策略更新过大,PPO 使用了如下裁剪目标函数:
$$L^{CLIP}(\theta) = \mathbb{E}_t \left[ \min \left( r_t(\theta) A_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) A_t \right) \right]$$
这里的 $\epsilon$ 通常设为 0.2。clip 操作将概率比率限制在 $[1-\epsilon, 1+\epsilon]$ 区间内。如果比率超出这个范围,损失函数将不再随比率变化,从而阻止策略发生剧烈跳变。
2. 值函数损失
Critic 网络负责估计状态价值 $V(s)$,通过最小化均方误差来更新:
$$L^{VF}(\theta) = \mathbb{E}_t \left[ \left( V(s_t; \theta) - R_t \right)^2 \right]$$
其中 $R_t$ 是累计回报。这一步确保了 Critic 能更准确地预测状态价值,从而提供更可靠的优势估计。
3. 熵正则化
为了鼓励探索,防止策略过早收敛到局部最优,引入熵项:
$$L^{ENT}(\theta) = \mathbb{E}t \left[ H(\pi\theta(s_t)) \right]$$
增加熵意味着保持策略分布的不确定性,让智能体有更多尝试不同动作的机会。
总损失函数
最终的总损失函数结合了上述三项:
$$L(\theta) = L^{CLIP}(\theta) - c_1 L^{VF}(\theta) + c_2 L^{ENT}(\theta)$$
系数 $c_1$ 和 $c_2$ 用于平衡各项的重要性。
PyTorch 代码实现
下面是一个基于 PyTorch 的完整 PPO 实现示例。代码结构清晰,分为 Actor-Critic 网络、经验回放记忆类、以及主训练循环。
1. Actor-Critic 神经网络
Actor-Critic 架构共享底层特征提取层,分别输出动作概率和状态价值。
import torch
import torch.nn as nn
from torch.distributions Categorical
(nn.Module):
():
(ActorCritic, ).__init__()
.shared_layer = nn.Sequential(
nn.Linear(state_dim, ),
nn.ReLU()
)
.actor = nn.Sequential(
nn.Linear(, action_dim),
nn.Softmax(dim=-)
)
.critic = nn.Linear(, )
():
shared = .shared_layer(state)
action_probs = .actor(shared)
state_value = .critic(shared)
action_probs, state_value


