强化学习 PPO 算法详解:原理、公式与核心代码
一、PPO 是什么
PPO 全称为:
Proximal Policy Optimization
中文通常称为:
近端策略优化算法。
PPO 是一种经典的策略梯度强化学习算法,属于:
On-Policy 算法。
也就是说,PPO 主要使用当前策略采集的数据进行训练,然后重新与环境交互获取新的数据。
PPO 可以用于:
离散动作空间
连续动作空间
因此在机器人控制、自动驾驶、游戏智能体等任务中都有较广泛的应用。
PPO 最核心的思想是:
策略可以更新,但是一次不能变化得太大。
如果策略一次更新幅度过大,可能导致训练过程变得不稳定。
因此 PPO 引入了:
Clipping(裁剪)机制
限制新策略和旧策略之间的变化幅度。
整体思想可以概括为:
当前策略与环境交互
↓
采集一批数据
↓
计算 Advantage
↓
比较新策略和旧策略
↓
Clip 限制策略更新幅度
↓
更新 Actor 和 Critic
二、PPO 的 Actor-Critic 结构
PPO 通常采用 Actor-Critic 结构。
其中:
Actor:负责选择动作
Critic:负责评价当前状态
Actor 根据状态输出一个动作概率分布:
πθ(a∣s)\\pi_\\theta(a|s)πθ(a∣s)
其中:
s:当前状态
a:动作
θ:Actor 网络参数
PPO 的 Actor 通常使用随机策略。
例如在离散动作空间中:
向左:0.2
直行:0.6
向右:0.2
然后根据这个概率分布采样一个动作。
整体过程为:
State
↓
Actor
↓
Action Distribution
↓
Sample
↓
Action
Critic 根据当前状态输出:
V(s)V(s)V(s)
整体过程:
State
↓
Critic
↓
V(s)
其中 V(s) 表示:
从当前状态开始,按照当前策略继续执行时,未来累计回报的期望。
因此:
Actor:决定应该做什么
Critic:评价当前状态有多好
三、为什么需要 PPO
普通策略梯度算法的基本思想是:
某个动作表现得好
↓
提高以后选择这个动作的概率
某个动作表现得差
↓
降低以后选择这个动作的概率
但是存在一个问题:
如果策略一次更新幅度太大,训练可能变得不稳定。
例如:
旧策略采集了一批数据
↓
使用这批数据更新 Actor
↓
Actor 参数变化过大
↓
新策略与旧策略差距过大
↓
训练稳定性下降
PPO 的解决方法就是:
允许策略学习
但是限制一次更新的幅度
因此 PPO 引入新旧策略的:
Probability Ratio
再利用:
Clip
对这个比例进行限制。
四、PPO 的概率比率
假设旧策略为:
πθold(at∣st)\\pi_{\\theta_{old}}(a_t|s_t)πθold(at∣st)
当前正在优化的新策略为:
πθ(at∣st)\\pi_\\theta(a_t|s_t)πθ(at∣st)
PPO 定义新旧策略的概率比率:
ρt(θ)=πθ(at∣st)πθold(at∣st)\\rho_t(\\theta)=\\frac{\\pi_\\theta(a_t|s_t)}{\\pi_{\\theta_{old}}(a_t|s_t)}ρt(θ)=πθold(at∣st)πθ(at∣st)
这个比例反映:
新策略选择当前动作的概率,相比旧策略发生了多大变化。
例如:
旧策略选择动作 a 的概率:
0.4
新策略选择动作 a 的概率:
0.44
那么:
ratio = 0.44 / 0.4
= 1.1
如果:
ratio = 1
表示新旧策略对这个动作的选择概率没有变化。
如果:
ratio > 1
表示新策略更倾向于选择这个动作。
如果:
ratio < 1
表示新策略降低了这个动作的选择概率。
实际代码中通常使用 Log Probability 计算:
ratio = torch.exp(
new_log_prob – old_log_prob
)
因为:
πθ(a∣s)πθold(a∣s)=exp(logπθ(a∣s)−logπθold(a∣s))\\frac{\\pi_\\theta(a|s)}{\\pi_{\\theta_{old}}(a|s)}=\\exp\\left(\\log\\pi_\\theta(a|s)-\\log\\pi_{\\theta_{old}}(a|s)\\right)πθold(a∣s)πθ(a∣s)=exp(logπθ(a∣s)−logπθold(a∣s))
五、Advantage 优势函数
仅仅知道新旧策略的变化还不够。
PPO 还需要知道:
当前执行的这个动作,到底比原本预期更好还是更差?
这就是:
Advantage Function
优势函数。
记作:
AtA_tAt
可以简单理解为:
Advantage > 0
说明这个动作表现比预期更好
应该提高这个动作的概率
Advantage < 0
说明这个动作表现比预期更差
应该降低这个动作的概率
PPO 中通常使用 GAE:
Generalized Advantage Estimation
来计算 Advantage。
首先计算 TD Error:
δt=rt+γ(1−dt)V(st+1)−V(st)\\delta_t=r_t+\\gamma(1-d_t)V(s_{t+1})-V(s_t)δt=rt+γ(1−dt)V(st+1)−V(st)
然后从后向前计算:
At=δt+γλ(1−dt)At+1A_t=\\delta_t+\\gamma\\lambda(1-d_t)A_{t+1}At=δt+γλ(1−dt)At+1
其中:
r_t:当前奖励
γ:折扣因子
λ:GAE 参数
d_t:当前状态是否终止
得到 Advantage 后,可以进一步计算 Critic 的学习目标:
Rt=At+V(st)R_t=A_t+V(s_t)Rt=At+V(st)
六、PPO 最核心的 Clip 机制
PPO 最重要的部分就是:
Clipped Objective
定义:
LCLIP(θ)=Et[min(ρt(θ)At,clip(ρt(θ),1−ϵ,1+ϵ)At)]L^{CLIP}(\\theta)=E_t\\left[\\min\\left(\\rho_t(\\theta)A_t,\\operatorname{clip}(\\rho_t(\\theta),1-\\epsilon,1+\\epsilon)A_t\\right)\\right]LCLIP(θ)=Et[min(ρt(θ)At,clip(ρt(θ),1−ϵ,1+ϵ)At)]
其中:
ρ_t:
新旧策略概率比率
A_t:
Advantage
ε:
Clip 范围
例如:
clip_epsilon = 0.2
那么裁剪范围就是:
[0.8, 1.2]
即:
[1−ϵ,1+ϵ][1-\\epsilon,1+\\epsilon][1−ϵ,1+ϵ]
为什么需要 Clip?
假设某个动作:
Advantage > 0
说明这个动作表现得比较好。
因此我们希望:
提高以后选择这个动作的概率
但是不能无限提高。
如果:
ratio = 1.5
而:
epsilon = 0.2
那么 PPO 的裁剪边界为:
1.2
这样可以避免策略为了追求更高收益而一次改变过大。
同样,当:
Advantage < 0
PPO 也会限制动作概率下降得过多。
所以 PPO 的核心思想可以记成:
策略应该学习
但是每次不要走得太远
七、PPO 的 Actor 和 Critic Loss
1. Actor Loss
PPO 希望最大化:
LCLIP(θ)L^{CLIP}(\\theta)LCLIP(θ)
但是 PyTorch 优化器默认执行的是最小化 Loss,因此:
Lactor=−LCLIP(θ)L_{actor}=-L^{CLIP}(\\theta)Lactor=−LCLIP(θ)
对应代码:
ratio = torch.exp(
new_log_prob – old_log_prob
)
surr1 = ratio * advantages
surr2 = torch.clamp(
ratio,
1 – self.clip_epsilon,
1 + self.clip_epsilon
) * advantages
actor_loss = –torch.min(
surr1,
surr2
).mean()
其中:
surr1:
没有进行裁剪的策略目标
surr2:
经过 Clip 限制后的策略目标
2. Critic Loss
Critic 负责预测:
V(st)V(s_t)V(st)
目标值为:
RtR_tRt
因此最基本的 Critic Loss 为:
Lcritic=(V(st)−Rt)2L_{critic}=\\left(V(s_t)-R_t\\right)^2Lcritic=(V(st)−Rt)2
对应代码:
values = self.critic(states)
critic_loss = F.mse_loss(
values,
returns
)
Critic 的作用就是:
让预测的 V(s)
逐渐接近真实的 Return
八、Entropy 的作用
PPO 中通常还会使用:
Entropy Bonus
也就是熵奖励。
如果 Actor 很快变成:
动作 A:0.99
动作 B:0.005
动作 C:0.005
说明策略几乎只会选择动作 A。
如果过早出现这种情况:
探索能力下降
↓
可能过早收敛
↓
难以发现更好的策略
因此可以利用 Entropy 鼓励 Actor 保留一定随机性。
常见的总体损失形式为:
L=Lactor+c1Lcritic−c2H(π)L=L_{actor}+c_1L_{critic}-c_2H(\\pi)L=Lactor+c1Lcritic−c2H(π)
其中:
L_actor:Actor Loss
L_critic:Critic Loss
H(π):策略熵
c1:Critic Loss 系数
c2:Entropy 系数
Entropy 越大,通常说明策略分布越具有随机性,因此可以帮助智能体保持一定探索能力。
九、PPO 完整训练流程
PPO 一般不会像 DDPG、TD3 那样长期使用 Replay Buffer 保存大量历史数据。
它通常先使用当前策略采集一批 Rollout 数据,然后利用这一批数据进行若干轮优化。
完整流程可以表示为:
1. 使用当前 Actor 与环境交互
↓
2. 保存 State、Action、Reward
Done、Log Probability、Value
↓
3. 获得一批 Rollout 数据
↓
4. 计算 Advantage 和 Return
↓
5. 使用当前策略重新计算 Log Probability
↓
6. 计算新旧策略 Probability Ratio
↓
7. 使用 Clip 计算 Actor Loss
↓
8. 计算 Critic Loss
↓
9. 计算 Entropy
↓
10. 更新 Actor 和 Critic
↓
11. 对当前数据训练若干 Epoch
↓
12. 重新与环境交互采集新数据
核心过程:
State
│
▼
Actor
│
▼
Action
│
▼
Environment
│
▼
Reward
│
▼
Advantage
│
▼
Probability Ratio
│
▼
Clip
│
▼
Actor Loss
十、PPO 核心 PyTorch 训练代码
下面给出 PPO 最核心的更新逻辑。
假设已经计算得到:
states
actions
old_log_probs
returns
advantages
核心代码如下:
def update(
self,
states,
actions,
old_log_probs,
returns,
advantages
):
# Advantage 标准化
advantages = (
advantages – advantages.mean()
) / (
advantages.std() + 1e-8
)
# 对当前 Rollout 数据训练多个 Epoch
for _ in range(self.update_epochs):
# =========================
# Actor
# =========================
dist = self.actor(states)
new_log_probs = dist.log_prob(actions)
# 连续多维动作时对动作维度求和
if new_log_probs.dim() > 1:
new_log_probs = new_log_probs.sum(
dim=–1,
keepdim=True
)
# 新旧策略概率比率
ratio = torch.exp(
new_log_probs – old_log_probs
)
# PPO Clip
surr1 = ratio * advantages
surr2 = torch.clamp(
ratio,
1 – self.clip_epsilon,
1 + self.clip_epsilon
) * advantages
# Entropy
entropy = dist.entropy()
if entropy.dim() > 1:
entropy = entropy.sum(dim=–1)
entropy = entropy.mean()
# Actor Loss
actor_loss = (
–torch.min(
surr1,
surr2
).mean()
– self.entropy_coef * entropy
)
# =========================
# Critic
# =========================
values = self.critic(states)
critic_loss = F.mse_loss(
values,
returns
)
# =========================
# 更新 Actor
# =========================
self.actor_optimizer.zero_grad()
actor_loss.backward()
self.actor_optimizer.step()
# =========================
# 更新 Critic
# =========================
self.critic_optimizer.zero_grad()
critic_loss.backward()
self.critic_optimizer.step()
这段代码对应 PPO 最核心的几个过程:
torch.exp(new_log_probs – old_log_probs)
→ 计算新旧策略 Probability Ratio
torch.clamp(…)
→ 限制策略更新幅度
torch.min(surr1, surr2)
→ PPO Clip Objective
advantages
→ 判断动作比预期更好还是更差
critic_loss
→ 更新状态价值函数
entropy
→ 保持一定探索能力
十一、PPO 与 DDPG、TD3 的区别
PPO、DDPG 和 TD3 都可以用于连续控制,但是训练方式存在明显区别。
| 算法类型 | On-Policy | Off-Policy | Off-Policy |
| 策略 | 随机策略 | 确定性策略 | 确定性策略 |
| Actor | 有 | 有 | 有 |
| Critic | V(s) | Q(s,a) | Twin Q(s,a) |
| 长期 Replay Buffer | 不使用 | 使用 | 使用 |
| 核心机制 | Clip | 确定性策略梯度 | Twin Critic + Delay + Smoothing |
| 主要探索方式 | 策略分布采样 | Action Noise | Action Noise |
PPO 最核心的特点是:
限制策略每次更新的幅度
DDPG 的核心是:
Actor 直接生成连续动作
TD3 则是在 DDPG 的基础上,通过双 Critic、延迟策略更新和目标策略平滑进一步提高训练稳定性。
十二、PPO 核心公式总结
PPO 最重要的公式可以总结为以下几个。
1. Probability Ratio
ρt(θ)=πθ(at∣st)πθold(at∣st)\\rho_t(\\theta)=\\frac{\\pi_\\theta(a_t|s_t)}{\\pi_{\\theta_{old}}(a_t|s_t)}ρt(θ)=πθold(at∣st)πθ(at∣st)
2. TD Error
δt=rt+γ(1−dt)V(st+1)−V(st)\\delta_t=r_t+\\gamma(1-d_t)V(s_{t+1})-V(s_t)δt=rt+γ(1−dt)V(st+1)−V(st)
3. GAE
At=δt+γλ(1−dt)At+1A_t=\\delta_t+\\gamma\\lambda(1-d_t)A_{t+1}At=δt+γλ(1−dt)At+1
4. PPO Clip Objective
LCLIP(θ)=Et[min(ρt(θ)At,clip(ρt(θ),1−ϵ,1+ϵ)At)]L^{CLIP}(\\theta)=E_t\\left[\\min\\left(\\rho_t(\\theta)A_t,\\operatorname{clip}(\\rho_t(\\theta),1-\\epsilon,1+\\epsilon)A_t\\right)\\right]LCLIP(θ)=Et[min(ρt(θ)At,clip(ρt(θ),1−ϵ,1+ϵ)At)]
5. Actor Loss
Lactor=−LCLIP(θ)L_{actor}=-L^{CLIP}(\\theta)Lactor=−LCLIP(θ)
6. Critic Loss
Lcritic=(V(st)−Rt)2L_{critic}=\\left(V(s_t)-R_t\\right)^2Lcritic=(V(st)−Rt)2
十三、总结
PPO 是一种经典的 On-Policy 策略优化算法。
它采用 Actor-Critic 结构:
Actor
负责产生动作概率分布
Critic
负责估计状态价值 V(s)
PPO 最核心的问题是:
如何在优化策略的同时,避免新策略一次偏离旧策略太远?
PPO 的解决方法是:
计算新旧策略概率比率
↓
Probability Ratio
↓
结合 Advantage 判断更新方向
↓
使用 Clip 限制更新幅度
↓
更新 Actor
PPO 最核心的目标函数为:
LCLIP(θ)=Et[min(ρt(θ)At,clip(ρt(θ),1−ϵ,1+ϵ)At)]L^{CLIP}(\\theta)=E_t\\left[\\min\\left(\\rho_t(\\theta)A_t,\\operatorname{clip}(\\rho_t(\\theta),1-\\epsilon,1+\\epsilon)A_t\\right)\\right]LCLIP(θ)=Et[min(ρt(θ)At,clip(ρt(θ),1−ϵ,1+ϵ)At)]
整个 PPO 的训练过程可以概括为:
当前策略采集数据
↓
计算 Advantage
↓
计算新旧策略 Probability Ratio
↓
使用 Clip 限制更新范围
↓
更新 Actor
↓
更新 Critic
↓
重新采集数据
理解 PPO 最重要的是理解三个问题:
Advantage 表示什么?
为什么需要比较新策略和旧策略?
为什么 PPO 要使用 Clip 限制策略更新?
理解这三个问题以及 PPO 的完整训练流程,就基本掌握了 PPO 的核心原理。




