强化学习系列(5):近端策略优化算法(PPO)及其进阶应用
一、近端策略优化算法(PPO)原理
提出背景
策略梯度算法虽然在很多场景下能有效学习最优策略,但存在一些问题,比如训练过程中策略更新可能过大,导致训练不稳定,难以收敛到好的策略。PPO算法旨在解决这些问题,通过限制新策略和旧策略之间的差异,使得策略能够更稳定、更高效地更新,从而提升训练效果。
核心思想
PPO基于重要性采样(Importance Sampling)的思想,利用旧策略收集的样本数据来训练新策略,同时通过一些约束机制来控制新策略和旧策略之间的距离,避免策略更新幅度过大。它主要有两种实现方式,PPO1(基于策略比值的裁剪)和PPO2(基于KL散度的约束)。
PPO1(策略比值裁剪)
- 策略比值计算:定义策略比值 (r_t(\theta) = \frac{\pi_{\theta}(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}),其中(\pi_{\theta}(a_t|s_t))是新策略在状态 (s_t) 下采取动作 (a_t) 的概率,(\pi_{\theta_{old}}(a_t|s_t))是旧策略对应的概率。这个比值反映了新旧策略在同一状态下选择相同动作的概率变化情况。
- 裁剪目标函数:PPO1的目标函数通过对优势函数 (A_t) 和策略比值 (r_t) 进行裁剪来构建。具体来说,裁剪后的目标函数为:
[
L^{CLIP}(\theta) = \mathbb{E}_t[\min(r_t(\theta)A_t, \text{clip}(r_t(\theta), 1 - \epsilon, 1 + \epsilon)A_t)]
]
这里的(\epsilon)是一个超参数,用于控制裁剪的范围,通过这种裁剪方式,限制了策略更新的幅度,使得新策略不会偏离旧策略太远。
PPO2(KL散度约束)
- KL散度概念:KL散度(Kullback-Leibler Divergence)用于衡量两个概率分布之间的差异。在PPO2中,通过约束新旧策略之间的KL散度来控制策略更新的幅度。
- 目标函数构建:在训练过程中,会设定一个KL散度的目标值(\delta),然后在优化目标函数的同时,要保证新旧策略的KL散度接近这个目标值。目标函数形式相对复杂一些,涉及到对策略的对数似然、优势函数以及KL散度相关项的综合考量,通过优化算法(如梯度下降等)来调整策略参数,使得既能提升策略性能(基于优势函数)又能满足KL散度的约束。
二、PPO的多种实现形式及应用特点
PPO1的特点及应用场景
- 特点:
- 实现相对简单直观,通过直接裁剪策略比值来控制策略更新,容易理解和编码实现。
- 对超参数(\epsilon)比较敏感,不同的(\epsilon)取值会影响策略更新的幅度和训练的稳定性,需要根据具体问题进行适当调优。
- 应用场景:在一些离散动作空间的简单游戏环境(如CartPole、Acrobot等)中表现较好,能够较快地收敛到较好的策略,而且由于其相对简单的结构,在入门学习和初步验证想法时比较常用。
PPO2的特点及应用场景
- 特点:
- 基于KL散度的约束更加灵活和理论性强,能够根据实际情况动态调整策略更新的幅度,相比于PPO1在处理复杂问题时可能更具优势。
- 不过其实现相对复杂一些,涉及到计算和控制KL散度,需要对概率分布、优化算法等有更深入的理解。
- 应用场景:在连续动作空间的控制问题(如机器人的复杂运动控制、自动驾驶中的决策控制等)以及复杂的Atari游戏等环境中应用广泛,能够更好地应对策略更新过程中的复杂变化,提升训练的稳定性和最终策略的质量。
对比与选择
在实际应用中,如果问题相对简单,对训练速度和简单性有要求,可以优先尝试PPO1;而对于复杂的、对策略稳定性和精度要求较高的场景,尤其是连续动作空间或者高维状态空间的问题,PPO2往往能发挥更好的作用。当然,具体的选择还需要通过实验对比来确定,结合不同场景下的收敛速度、最终策略性能等指标来综合考量。
三、PPO算法应用代码示例
离散动作空间(以CartPole环境为例)
import gymnasium as gym
import torch
import torch.nn as nn
import torch.optim as optim
# 定义策略网络(Actor)
class PolicyNetwork(nn.Module):
def __init__(self, input_size, output_size):
super(PolicyNetwork, self).__init__()
self.fc1 = nn.Linear(input_size, 64)
self.fc2 = nn.Linear(64, 64)
self.fc3 = nn.Linear(64, output_size)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
return torch.softmax(self.fc3(x), dim=1)
# PPO1训练过程关键代码片段
def train_ppo1(policy_net, optimizer, num_episodes, gamma, epsilon):
env = gym.make('CartPole-v1')
for episode in range(num_episodes):
state, _ = env.reset()
states, actions, rewards, log_probs = [], [], [], []
done = False
while not done:
state = torch.tensor(state, dtype=torch.float).unsqueeze(0)
action_probs = policy_net(state)
action = torch.multinomial(action_probs, num_samples=1).item()
log_prob = torch.log(action_probs[0][action])
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
states.append(state)
actions.append(action)
rewards.append(reward)
log_probs.append(log_prob)
state = next_state
returns = []
G = 0
for r in reversed(rewards):
G = r + gamma * G
returns.insert(0, G)
returns = torch.tensor(returns)
advantages = returns - torch.stack([policy_net(s)[0][a] for s, a in zip(states, actions)])
for _ in range(10): # 多次更新
for s, a, old_log_prob, adv in zip(states, actions, log_probs, advantages):
action_probs = policy_net(s)
new_log_prob = torch.log(action_probs[0][a])
ratio = torch.exp(new_log_prob - old_log_prob)
clipped_ratio = torch.clamp(ratio, 1 - epsilon, 1 + epsilon)
loss = -torch.min(ratio * adv, clipped_ratio * adv).mean()
optimizer.zero_grad()
loss.backward()
optimizer.step()
env.close()
连续动作空间(以机器人关节控制简化示例)
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
# 定义策略网络(输出均值和方差,用于采样连续动作,这里假设高斯分布)
class ContinuousPolicyNetwork(nn.Module):
def __init__(self, input_size, output_size):
super(ContinuousPolicyNetwork, self).__init__()
self.fc1 = nn.Linear(input_size, 64)
self.fc2 = nn.Linear(64, 64)
self.fc_mean = nn.Linear(64, output_size)
self.fc_std = nn.Linear(64, output_size)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
mean = self.fc_mean(x)
std = torch.sigmoid(self.fc_std(x)) # 限制方差为正
return mean, std
# PPO2训练过程关键代码片段(简化示意,实际还需处理KL散度等更精细部分)
def train_ppo2(policy_net, optimizer, num_episodes, gamma):
env = gym.make('SomeRobotEnv-v0') # 假设的机器人控制环境
for episode in range(num_episodes):
state, _ = env.reset()
states, actions, rewards, log_probs = [], [], [], []
done = False
while not done:
state = torch.tensor(state, dtype=torch.float).unsqueeze(0)
mean, std = policy_net(state)
action = torch.normal(mean, std).squeeze(0).detach().numpy()
log_prob = torch.distributions.Normal(mean, std).log_prob(torch.tensor(action).unsqueeze(0)).sum()
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
states.append(state)
actions.append(action)
rewards.append(reward)
log_probs.append(log_prob)
state = next_state
returns = []
G = 0
for r in reversed(rewards):
G = r + gamma * G
returns.insert(0, G)
returns = torch.tensor(returns)
advantages = returns - torch.stack([policy_net(s)[0][a] for s, a in zip(states, actions)])
for _ in range(10): # 多次更新
for s, a, old_log_prob, adv in zip(states, actions, log_probs, advantages):
mean, std = policy_net(s)
new_log_prob = torch.distributions.Normal(mean, std).log_prob(torch.tensor(a).unsqueeze(0)).sum()
ratio = torch.exp(new_log_prob - old_log_prob)
loss = -ratio * adv.mean() # 这里简化,实际要结合KL散度等约束
optimizer.zero_grad()
loss.backward()
optimizer.step()
env.close()
四、PPO与其他策略梯度算法性能对比
实验设置
选取多个具有代表性的强化学习环境,包括离散动作空间的Atari游戏(如Breakout、Pong等)以及连续动作空间的机器人控制模拟环境(如MuJoCo中的一些任务)。分别使用PPO(PPO1和PPO2)、REINFORCE、A2C、A3C等策略梯度算法进行训练,设置相同的训练轮数、超参数范围(如学习率、折扣因子等尽量保持一致,仅调整各算法特有的参数),记录每个算法在不同环境下的平均得分(对于游戏)或者任务完成指标(对于机器人控制)、收敛速度、策略稳定性等性能指标。
实验结果分析
- 平均得分方面:在大部分环境中,PPO算法尤其是PPO2在复杂的Atari游戏和连续动作空间的机器人控制任务上往往能取得较高的平均得分,相比REINFORCE、A2C等算法,它能够更稳定地优化策略,找到更优的动作选择方式,从而获得更多的奖励。不过在一些简单的离散动作空间环境(如CartPole)中,PPO与其他算法的差距可能不太明显,因为这些简单问题相对容易收敛,各算法都能较快找到较好的策略。
- 收敛速度方面:PPO通常收敛速度较快,特别是在处理复杂环境时,得益于其对策略更新幅度的有效控制,不会像REINFORCE等算法那样容易出现因策略更新过大而导致的训练波动,能够稳步朝着最优策略方向前进,减少了不必要的反复调整过程,所以在给定的训练轮数内能更快地达到较好的性能表现。
- 策略稳定性方面:PPO的优势较为突出,通过限制新旧策略差异,无论是PPO1的裁剪机制还是PPO2的KL散度约束,都使得策略在更新过程中保持相对稳定,不会出现突然变差或者难以收敛的情况,这对于实际应用中需要可靠策略的场景(如机器人控制、自动驾驶等)尤为重要。
五、常见问题及解决思路
1. PPO训练中出现策略过早收敛到局部最优怎么办?
- 增加探索机制:可以适当提高策略的探索程度,比如在离散动作空间中增加动作选择的随机性(如采用更大的(\epsilon)值在(\epsilon)-greedy策略中,或者采用更复杂的探索策略如基于熵的探索等);在连续动作空间中,可以增加动作采样时的噪声强度或者采用更具探索性的分布形式来采样动作。
- 调整优化参数和网络结构:尝试减小学习率,避免过快地收敛到局部最优;同时可以考虑增加网络的复杂度(如增加层数、神经元数量等),以便能够更好地拟合复杂的策略空间,有更多机会找到全局最优解,但要注意避免过拟合问题。
2. 在连续动作空间应用中KL散度难以控制怎么办?
- 优化KL散度计算和约束方式:仔细检查KL散度的计算代码是否正确,确保新旧策略的概率分布表示准确,并且在约束KL散度时,可以尝试采用更灵活的动态调整机制,比如根据训练过程中KL散度的实际变化情况,自适应地调整KL散度的目标值(\delta),使其既能有效约束策略更新又不会过于限制导致训练缓慢。
- 调整策略网络输出表示:对于连续动作采样相关的网络输出(如均值和方差等),可以尝试不同的激活函数或者参数化方式,确保输出的分布合理,使得KL散度更容易被控制在合适的范围内,例如对输出的方差进行更合理的变换或约束。
六、下期预告:深度确定性策略梯度(DDPG)算法及其应用
在**强化学习系列(6)**中,您将学习到:
- 深度确定性策略梯度(DDPG)算法的原理和独特之处,理解它是如何结合深度神经网络与确定性策略来处理连续动作空间问题的。
- DDPG的关键组件,包括Actor-Critic结构、经验回放机制以及目标网络更新等方面的详细内容。
- 通过实际代码示例展示DDPG算法在复杂的机器人控制、物理模拟等连续动作空间场景中的应用,并对比分析其与PPO等算法在这些场景下的性能表现。
欢迎继续关注本系列,一起深入探索强化学习的精彩世界,期待您在评论区分享您的学习心得和疑问哦! 🔔
1456

被折叠的 条评论
为什么被折叠?



