11.4 PPO算法的变种与改进
Proximal Policy Optimization (PPO) 是一个强化学习算法,具有一些变种和改进版本,以解决一些PPO原始版本的限制或提高其性能。在本节的内容中,将详细讲解这些改进。
11.4.1 PPO-Clip算法
PPO-Clip(Proximal Policy Optimization with Clipping)是一种改进的PPO算法,用于训练强化学习智能体,特别是在连续动作空间中的任务。它旨在提高PPO的稳定性和收敛性。PPO-Clip的核心思想是通过两个关键机制来控制策略更新的幅度,从而增强算法的稳定性:
- 重要性采样比率的剪切(Clipping Importance Sampling Ratio):在PPO-Clip中,为了衡量新策略和旧策略之间的相对性能,使用了重要性采样比率。具体而言,对于每个采样的状态-动作对(s, a),计算了新策略和旧策略下执行动作a的概率比值:
这里,A_t表示优势函数(Advantage Function),用于衡量在状态s下执行动作a相对于平均水平的性能。surrogate_t(s, a)表示策略改进的替代目标,用于更新策略参数。
- 策略更新:最后,PPO-Clip使用策略梯度方法来最大化剪切后的目标函数,从而更新策略的参数。通常使用梯度上升方法,如Adam优化器。
PPO-Clip算法的主要优点如下所示:
- 稳定性:通过剪切机制,PPO-Clip可以限制策略更新的幅度,从而提高训练的稳定性,减少因过大的更新步骤而导致的不稳定性。
- 收敛性:PPO-Clip通常能够更快地收敛到较好的策略,相对于一些其他策略梯度方法。
- 鲁棒性:PPO-Clip对初始策略参数的选择和超参数的选择相对较不敏感,因此更容易在不同任务上进行调优。
需要注意的是,PPO-Clip只是PPO算法的一种改进版本,实际应用时还需要根据具体任务的需求和性质来选择适当的算法。
请看下面的例子,使用一个连续动作空间(1维)的简单问题来演示 PPO-Clip 算法的用法。在这个问题中,智能体需要学会在连续状态空间中找到最大的奖励。
实例11-3:使用 PPO-Clip 算法训练一个连续动作空间的智能体(源码路径:daima\11\clip.py)
实例文件clip.py的具体实现代码如下所示:
# 定义一个简单的连续动作空间环境
class SimpleContinuousEnv:
def __init__(self):
self.state_dim = 1
self.action_dim = 1
self.state = np.random.randn(self.state_dim)
self.optimal_action = np.array([2.0]) # 最优动作
self.reward_std = 0.1 # 奖励的标准差
def reset(self):
self.state = np.random.randn(self.state_dim)
return self.state
def step(self, action):
reward = -np.sum(np.square(action - self.optimal_action)) # 最大奖励为0,与最优动作的距离越小,奖励越高
reward += np.random.randn() * self.reward_std # 添加噪声
self.state += np.random.randn(self.state_dim) * 0.01 # 随机过渡状态
return self.state, reward, False
# 定义一个简单的策略网络
class PolicyNetwork(nn.Module):
def __init__(self, input_dim, output_dim):
super(PolicyNetwork, self).__init__()
self.fc = nn.Linear(input_dim, output_dim)
def forward(self, x):
x = torch.tanh(self.fc(x))
return x
# 定义PPO-Clip代理
class PPOAgent:
def __init__(self, state_dim, action_dim):
self.policy_network = PolicyNetwork(state_dim, action_dim)
self.optimizer = optim.Adam(self.policy_network.parameters(), lr=0.001)
self.gamma = 0.99
self.eps_clip = 0.2
def select_action(self, state):
state = torch.tensor(state, dtype=torch.float32)
action_mean = self.policy_network(state)
action_std = torch.ones_like(action_mean) * 0.1 # 使用固定的标准差
action_dist = torch.distributions.Normal(action_mean, action_std)
action = action_dist.sample()
return action.item()
def update(self, states, actions, old_action_probs, advantages):
states = torch.tensor(states, dtype=torch.float32)
actions = torch.tensor(actions, dtype=torch.float32)
old_action_probs = torch.tensor(old_action_probs, dtype=torch.float32)
advantages = torch.tensor(advantages, dtype=torch.float32)
action_mean = self.policy_network(states)
action_std = torch.ones_like(action_mean) * 0.1
action_dist = torch.distributions.Normal(action_mean, action_std)
new_action_probs = action_dist.log_prob(actions)
old_action_probs = old_action_probs.view(-1, 1)
ratio = torch.exp(new_action_probs - old_action_probs)
surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1 - self.eps_clip, 1 + self.eps_clip) * advantages
loss = -torch.min(surr1, surr2).mean()
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
# 训练PPO-Clip代理
def train_ppo():
env = SimpleContinuousEnv()
num_episodes = 10000
agent = PPOAgent(env.state_dim, env.action_dim)
for episode in range(num_episodes):
state = env.reset()
states, actions, rewards, old_action_probs = [], [], [], []
while True:
action = agent.select_action(state)
new_state, reward, done = env.step(action)
states.append(state)
actions.append(action)
rewards.append(reward)
old_action_probs.append(agent.policy_network(torch.tensor(state, dtype=torch.float32)).item())
state = new_state
if done:
break
# 计算折扣回报
returns = []
discounted_reward = 0
for reward in reversed(rewards):
discounted_reward = reward + agent.gamma * discounted_reward
returns.insert(0, discounted_reward)
# 计算优势值
advantages = np.array(returns) - np.array(old_action_probs)
agent.update(states, actions, old_action_probs, advantages)
if episode % 10 == 0:
print(f"Episode {episode}: Total Reward = {sum(rewards)}")
if __name__ == "__main__":
train_ppo()
上述代码的实现流程如下所示:
(1)定义网格世界环境:GridWorld 类定义了一个简单的网格世界环境。网格世界是一个二维网格,其中包含三种类型的单元格:0(空格)、1(目标格,表示奖励+1)、-1(陷阱格,表示奖励-1)。代理的任务是通过选择动作移动到目标格,并尽量避免陷阱格。该类包括 reset 方法来初始化环境状态,以及 step 方法来执行动作并返回新状态、奖励和完成状态。
(2)定义策略网络:PolicyNetwork 类是一个简单的神经网络,它将环境状态映射到动作的概率分布。它包括两个全连接层,使用 ReLU 激活函数。这个策略网络的目标是学习如何选择在给定状态下采取哪个动作。
(3)定义PPO代理:PPOAgent 类是代理的核心部分。它包含了一个策略网络 (policy_network) 和一个优化器 (optimizer)。代理使用 PPO-Clip 算法来更新策略。重要的成员变量包括折扣因子 gamma 和剪切参数 eps_clip。代理提供了 select_action 方法来选择在给定状态下采取的动作,并提供了 update 方法来执行 PPO-Clip 算法的更新步骤。
(4)训练PPO代理:train_ppo 函数用于训练代理。它首先初始化环境和代理,然后执行一系列训练周期。每个训练周期包括以下步骤:
- 重置环境状态。
- 在当前策略下,使用 select_action 方法选择动作,并执行它们,同时收集状态、动作、奖励等信息。
- 计算折扣回报(returns)和优势值(advantages)。
- 使用 update 方法来执行 PPO-Clip 更新步骤,以改进策略网络的参数。
- 打印每个周期的总奖励。
11.4.2 PPO-Penalty算法
PPO-Penalty(Proximal Policy Optimization with Penalty)是一种强化学习算法,是对标准的PPO(Proximal Policy Optimization)的扩展。PPO-Penalty的核心思想是在PPO的损失函数中引入了一个罚项(penalty term),以进一步推动代理学习到更多的探索行为。PPO-Penalty算法的主要思想和特点如下所示:
- 目标:PPO-Penalty的目标与标准的PPO一样,即通过优化策略网络来最大化累积奖励。但是,PPO-Penalty引入了一个额外的目标,即最大化累积奖励的同时最小化罚项。
- 罚项引入:罚项的引入是PPO-Penalty的关键。这个罚项通常是一个关于策略分布的度量,例如KL散度(Kullback-Leibler divergence)。它的目的是鼓励策略网络在训练过程中不要偏离初始策略太远,以保持探索性。罚项的具体形式可以根据任务和需求进行选择。
- 优化:优化的过程涉及到两个目标:最大化累积奖励和最小化罚项。通常,这两个目标被组合成一个联合损失函数,然后使用优化算法来更新策略网络的参数。损失函数的具体形式可能因任务而异。
- 探索与稳定性:PPO-Penalty的引入可以提高探索性,因为罚项会惩罚策略变化过大。这有助于在探索和稳定性之间找到平衡,尤其在复杂任务中。
- 超参数调整:罚项的权重通常需要仔细调整,以确保在训练中取得良好的效果。不同的任务可能需要不同的罚项权重。
总的来说,PPO-Penalty是一种旨在改进PPO算法的变种,通过引入罚项来平衡探索和稳定性的需求。这个罚项可以根据具体任务和代理的需求进行设计和调整。请注意,PPO-Penalty算法的具体实现方式可能因研究论文或实验而异,因此在实际应用中需要参考相关文献或算法实现来了解具体的细节。
请看下面的实例,使用PPO算法(Proximal Policy Optimization)来训练一个智能体以在Gym环境的Pendulum-v1任务中学习控制动作。
实例11-4:训练在Pendulum-v1任务中学习控制动作的智能体(源码路径:daima\11\pen.py)
实例文件pen.py的主要实现代码如下所示:
GAMMA = 0.9 # 折扣率
EP_MAX = 1000 # episode循环次数 # 默认是1000
EP_LEN = 200 # 一个回合规定的长度 # 默认是200
A_LR = 0.0001 # actor的学习率 默认是0.0001
C_LR = 0.0002 # critic的学习率 默认是0.0002
BATCH = 32 # 缓冲池长度
A_UPDATE_STEPS = 10 # 在多少步数之后更新actor
C_UPDATE_STEPS = 10 # 在多少步数之后更新critic
S_DIM, A_DIM = 3, 1 # state维度是3, action维度是1
METHOD = [
dict(name='kl_pen', kl_target=0.01, lam=0.5), # KL penalty # 0.5
dict(name='clip', epsilon=0.2) # clip
][0] # choose the method for optimization
# METHOD[0]是Adaptive KL penalty Coefficient
# METHOD[1]是Clipped Surrogate Objective
# 结果证明,clip的这个方法更好
class Actor(nn.Module):
"""
神经网络结构
# 全连接1
# 全连接2
# ReLU
网络输出是动作的mu和sigma
"""
def __init__(self,
n_features,
n_neuron):
super(Actor, self).__init__()
self.linear = nn.Sequential(
nn.Linear(in_features=n_features,
out_features=n_neuron,
bias=True),
nn.ReLU()
)
self.mu = nn.Sequential(
nn.Linear(in_features=n_neuron,
out_features=1,
bias=True),
nn.Tanh()
)
self.sigma = nn.Sequential(
nn.Linear(in_features=n_neuron,
out_features=1,
bias=True),
nn.Softplus()
)
def forward(self, x):
y = self.linear(x)
mu = 2 * self.mu(y)
sigma = self.sigma(y)
return mu, sigma
class Critic(nn.Module):
"""
神经网络结构
# 全连接1
# 全连接2
# ReLU
输出是状态价值
"""
def __init__(self,
n_features,
n_neuron):
super(Critic, self).__init__()
self.net = nn.Sequential(
nn.Linear(in_features=n_features,
out_features=n_neuron,
bias=True),
nn.ReLU(),
nn.Linear(in_features=n_neuron,
out_features=1,
bias=True),
)
def forward(self, x):
return self.net(x)
class PPO(object):
def __init__(self,
n_features,
n_neuron,
actor_learning_rate,
critic_learning_rate,
max_grad_norm=0.5 # 梯度剪裁参数
):
self.actor_lr = actor_learning_rate
self.critic_lr = critic_learning_rate
self.actor_old = Actor(n_features, n_neuron)
self.actor = Actor(n_features, n_neuron)
self.critic = Critic(n_features, n_neuron)
self.actor_optimizer = torch.optim.Adam(params=self.actor.parameters(),
lr=self.actor_lr)
self.critic_optimizer = torch.optim.Adam(params=self.critic.parameters(),
lr=self.critic_lr)
self.max_grad_norm = max_grad_norm # 梯度剪裁参数
def update(self, s, a, r, log_old, br_next_state):
"""
:param s: np.array(buffer_s)
:param a: np.array(buffer_a)
:param r: np.array(buffer_r)
:param log_old: np.array(buffer_log_old)
:param next_state: np.array(buffer_next_state)
:return: update actor net and critic net
"""
self.actor_old.load_state_dict(self.actor.state_dict())
# 从buffer中取出state, action, reward, old_action_log_prob, next_state放在tensor上
state = torch.FloatTensor(s)
action = torch.FloatTensor(a)
discounted_r = torch.FloatTensor(r) # discounted_r是target_v
next_state = torch.FloatTensor(br_next_state)
mu_old, sigma_old = self.actor_old(state)
dist_old = Normal(mu_old, sigma_old)
old_action_log_prob = dist_old.log_prob(action).detach()
# discounted_r是target_v
target_v = discounted_r
# 优势函数advantage,也是td_error
advantage = (target_v - self.critic(state)).detach()
#advantage = (advantage - advantage.mean()) / (advantage.std()+1e-6) # sometimes helpful by movan
# update actor net,METHOD[0]是KL penalty,METHOD[1]是clip
if METHOD['name'] == 'kl_pen':
for _ in range(A_UPDATE_STEPS):
# compute new_action_log_prob
mu, sigma = self.actor(state)
dist = Normal(mu, sigma)
new_action_log_prob = dist.log_prob(action) # !!!划重点,新策略动作值的log_prob,是新策略得到的分布上找到action对应的log_prob值
new_action_prob = torch.exp(new_action_log_prob)
old_action_prob = torch.exp(old_action_log_prob)
# KL散度
kl = nn.KLDivLoss()(old_action_prob, new_action_prob)
# 计算loss
ratio = new_action_prob / old_action_prob
actor_loss = -torch.mean(ratio * advantage - METHOD['lam'] * kl)
# 梯度下降
self.actor_optimizer.zero_grad()
actor_loss.backward()
nn.utils.clip_grad_norm_(self.actor.parameters(), self.max_grad_norm) # 梯度剪裁,只解决梯度爆炸问题,不解决梯度消失问题
self.actor_optimizer.step()
if kl > 4*METHOD['kl_target']:
# this in google's paper
break
if kl < METHOD['kl_target'] / 1.5:
# 散度较小,需要弱化惩罚力度
# adaptive lambda, this is in OpenAI's paper
METHOD['lam'] /= 2
elif kl > METHOD['kl_target'] * 1.5:
# 散度较大,需要增强惩罚力度
METHOD['lam'] *= 2
# sometimes explode, this clipping is my solution
METHOD['lam'] = np.clip(METHOD['lam'], 1e-4, 10)
else:
# clipping method, find this is better (OpenAI's paper)
# update actor net
for _ in range(A_UPDATE_STEPS):
## update step as follows:
# compute new_action_log_prob
mu, sigma = self.actor(state)
n = Normal(mu, sigma)
new_action_log_prob = n.log_prob(action) # !!!划重点,新策略动作值的log_prob,是新策略得到的分布上找到action对应的log_prob值
# ratio = new_action_prob / old_action_prob
ratio = torch.exp(new_action_log_prob - old_action_log_prob)
# L1 = ratio * td_error, td_error也叫作advatange
L1 = ratio * advantage
# L2 = clip(ratio, 1-epsilon, 1+epsilon) * td_error
L2 = torch.clamp(ratio, 1-METHOD['epsilon'], 1+METHOD['epsilon']) * advantage
# loss_actor = -min(L1, L2)
actor_loss = -torch.min(L1, L2).mean()
# optimizer.zero_grad()
self.actor_optimizer.zero_grad()
# actor_loss.backward()
actor_loss.backward()
# 梯度裁剪,只解决梯度爆炸问题,不解决梯度消失问题
nn.utils.clip_grad_norm_(self.actor.parameters(), self.max_grad_norm)
# actor_optimizer.step()
self.actor_optimizer.step()
# update critic net
for _ in range(C_UPDATE_STEPS):
# critic的loss是td_error也就是advantage,可以是td_error的L1范数也可以是td_error的L2范数
critic_loss = nn.MSELoss(reduction='mean')(self.critic(state), target_v)
# optimizer.zero_grad()
self.critic_optimizer.zero_grad()
# 反向传播
critic_loss.backward()
# 梯度裁剪,只解决梯度爆炸问题,不解决梯度消失问题
nn.utils.clip_grad_norm_(self.critic.parameters(), self.max_grad_norm)
# optimizer.step()
self.critic_optimizer.step()
def choose_action(self, s):
"""
选择动作
:param s:
:return:
"""
# 状态s放在torch.tensor上
# actor net输出mu和sigma
# 根据mu和sigma采样动作
# 返回动作和动作的log概率值
s = torch.FloatTensor(s)
with torch.no_grad():
mu, sigma = self.actor(s)
# print(s, mu, sigma)
dist = Normal(mu, sigma)
action = dist.sample()
action_log_prob = dist.log_prob(action)
action = action.clamp(-2, 2)
return action.item(), action_log_prob.item()
def get_v(self, s):
"""
状态价值函数
:param s:
:return:
"""
# 状态s放在torch.tensor上
# critic net输出value
s = torch.FloatTensor(s)
with torch.no_grad():
value = self.critic(s)
return value.item()
env = gym.make('Pendulum-v1').unwrapped
env.reset(seed=0)
torch.manual_seed(0)
ppo = PPO(n_features=S_DIM, n_neuron=50,
actor_learning_rate=A_LR, critic_learning_rate=C_LR)
all_ep_r = [] # 记录每个回合的累积reward值,当前回合的累积reward值 = 上一回合reward*0.9 + 当前回合reward*0.1
for ep in range(EP_MAX):
s, info = env.reset()
buffer_s, buffer_a, buffer_r = [], [], []
buffer_log_old = [] # revised by lihan
buffer_next_state = []
ep_r = 0 # 每个回合的reward值,是回合每步的reward值的累加
for t in range(EP_LEN):
# in one episode
env.render()
# print(ep, t)
a, a_log_prob_old = ppo.choose_action(s)
s_, r, done, truncated, info = env.step([a])
buffer_s.append(s)
buffer_a.append(a)
buffer_r.append((r+8)/8) # normalize reward, find to be useful
buffer_log_old.append(a_log_prob_old)
buffer_next_state.append(s_)
s = s_
ep_r += r
# 如果buffer收集一个batch了或者episode完了,那么update ppo
if (t+1) % BATCH == 0 or t == EP_LEN - 1:
# print('update *****')
v_s_ = ppo.get_v(s_)
discounted_r = []
for r in buffer_r[::-1]:
v_s_ = r + GAMMA * v_s_
discounted_r.append(v_s_)
discounted_r.reverse()
# discounted_r是target_v
bs, ba = np.vstack(buffer_s), np.vstack(buffer_a)
br_next_state = np.vstack(buffer_next_state)
br = np.array(discounted_r)[:, np.newaxis]
blog_old = np.vstack(buffer_log_old) # revised by lihan
# 清空buffer
buffer_s, buffer_a, buffer_r = [], [], []
buffer_log_old = [] # revised by lihan
buffer_next_state = []
ppo.update(bs, ba, br, blog_old, br_next_state) # 更新PPO
if ep == 0:
all_ep_r.append(ep_r)
else:
all_ep_r.append(all_ep_r[-1]*0.9 + ep_r*0.1)
print(
'EP: %i' % ep,
"|EP_r %i" % ep_r,
("|Lam: %.4f" % METHOD['lam']) if METHOD['name']=='kl_pen' else '',
)
plt.plot(np.arange(len(all_ep_r)), all_ep_r)
plt.xlabel('Episode')
plt.ylabel('Moving averaged episode reward')
plt.show()
上述代码的实现流程如下所示:
- 设置算法的超参数,包括折扣率(GAMMA)、最大回合数(EP_MAX)、回合长度(EP_LEN)、Actor和Critic的学习率(A_LR和C_LR)、缓冲池大小(BATCH)等。
- 定义了一个Actor神经网络和一个Critic神经网络。Actor网络用于输出动作的均值(mu)和标准差(sigma),Critic网络用于估计状态的价值。
- 创建类PPO,其中包含了PPO算法的核心更新和选择动作的方法。
- 在主循环中,智能体与环境交互。它选择动作,并通过Actor网络生成动作的均值和标准差,然后从该分布中采样一个动作。智能体将观察结果、动作、奖励等存储在缓冲池中。
- 当缓冲池收集了一个批次的数据或者一个回合结束时,智能体使用PPO算法来更新Actor和Critic网络。更新过程包括计算优势函数、计算损失函数以及执行梯度下降。
- 智能体通过不断地与环境交互和更新网络参数,逐渐学习到一个更好的策略。
- 最后,绘制了回合奖励的移动平均值,以可视化训练进展,如图11-1所示。这张图显示了训练过程中的回合奖励随着训练次数的变化而变化的情况。在训练期间,智能体尝试最大化回合奖励,因此这张图可以帮助我们了解智能体的学习进展以及它在解决任务上的性能。通过观察这张图,可以判断训练是否进行得很好,以及智能体是否逐渐改进其策略以获得更高的回合奖励。如果曲线逐渐上升,表示训练效果良好。
总体来说,上述代码演示了如何使用PPO算法来训练一个智能体,使其学会在Pendulum-v1环境中控制摆杆的动作,以最大化累积奖励。通过不断地优化Actor和Critic网络,智能体可以学会更好的策略,以应对不同状态下的环境。
图11-1 回合奖励的移动平均值可视化图
11.4.3 PPO2算法
PPO2(Proximal Policy Optimization 2)是一种用于深度强化学习的算法,它是对原始PPO算法的改进和扩展。PPO2旨在提高PPO算法的稳定性和性能,特别是在处理大规模连续动作空间和高维状态空间时。PPO2算法的主要特点和改进如下所示:
- Trust Region Optimization(TRPO)的替代方法:PPO2使用了一种不同于TRPO的优化方法,它通过使用clip函数来控制策略更新的幅度,而不是使用KL散度。这种方法更加简单且易于实现,同时保持了良好的性能。
- 多步训练:PPO2允许使用多步(n-step)经验回放来更新策略和价值网络。这有助于加速训练,特别是在需要处理长期依赖关系的任务上。
- Mini-batch优化:PPO2使用小批量(mini-batch)优化来提高训练效率。它将收集的经验样本划分为小批量,并使用这些批量进行策略和价值网络的更新。
- 策略和价值网络:PPO2通常使用神经网络来表示策略和价值函数。策略网络输出动作概率分布,而价值网络用于评估状态的价值。
- 连续和离散动作空间:PPO2适用于处理连续动作空间和离散动作空间的任务。对于连续动作空间,它通常使用高斯分布来建模动作分布,而对于离散动作空间,它使用softmax函数。
- 熵正则化:PPO2支持熵正则化,以鼓励策略探索更多的动作。这有助于防止策略陷入局部最优解。
总的来说,PPO2是一种强化学习算法,旨在解决深度强化学习中的稳定性和性能问题。它已经在各种任务中取得了良好的结果,并在深度强化学习社区中得到广泛使用。例如下面是一个使用PPO2算法解决自定义环境的简单示例。在此示例中,将创建一个简单的连续动作空间环境,并使用PPO2算法来训练一个智能体以获得最大的奖励。
实例11-5:使用PPO2算法训练一个智能体以获得最大的奖励(源码路径:daima\11\ppo2.py)
实例文件ppo2.py的主要实现代码如下所示:
# 创建自定义环境
class CustomEnvironment(gym.Env):
def __init__(self):
super(CustomEnvironment, self).__init__()
self.action_space = gym.spaces.Box(low=-1, high=1, shape=(1,), dtype=np.float32)
self.observation_space = gym.spaces.Box(low=-1, high=1, shape=(1,), dtype=np.float32)
self.state = np.random.rand(1)
def reset(self):
self.state = np.random.rand(1)
return self.state
def step(self, action):
reward = -np.abs(action[0] - self.state[0])
self.state[0] += action[0]
done = False
return self.state, reward, done, {}
# 创建自定义环境实例
env = CustomEnvironment()
# 创建PPO2智能体
model = PPO("MlpPolicy", env, verbose=1)
# 训练智能体
model.learn(total_timesteps=10000)
# 评估智能体性能
obs = env.reset()
for _ in range(100):
action, _ = model.predict(obs)
obs, reward, done, _ = env.step(action)
# 关闭环境
env.close()
在上述代码中,首先定义了一个简单的自定义环境CustomEnvironment,它有一个连续的动作空间和一个连续的状态空间。然后,使用库stable-baselines3创建了一个PPO2智能体,训练它以在自定义环境中获得奖励,并评估了它的性能。执行后会输出训练PPO2算法的过程中的日志:
Using gpu device
Wrapping the env with a `Monitor` wrapper
Wrapping the env in a DummyVecEnv.
-----------------------------
| time/ | |
| fps | 853 |
| iterations | 1 |
| time_elapsed | 2 |
| total_timesteps | 2048 |
-----------------------------
-----------------------------------------
| time/ | |
| fps | 530 |
| iterations | 2 |
| time_elapsed | 7 |
| total_timesteps | 4096 |
| train/ | |
| approx_kl | 0.008373314 |
| clip_fraction | 0.0676 |
| clip_range | 0.2 |
| entropy_loss | -1.41 |
| explained_variance | 0.0035 |
| learning_rate | 0.0003 |
| loss | 1.22e+04 |
| n_updates | 10 |
| policy_gradient_loss | -0.00634 |
| std | 0.983 |
| value_loss | 2.41e+04 |
-----------------------------------------
#####省略部分输出
| loss | 84.2 |
| n_updates | 40 |
| policy_gradient_loss | -0.022 |
| std | 0.934 |
| value_loss | 258 |
-----------------------------------------
这段日志包含了每一轮训练的一些关键信息,如学习速率、策略的近似KL散度、剪切比例、熵损失、策略梯度损失、值函数损失等等,具体说明如下:
- fps:每秒处理的帧数,表示训练的速度。
- iterations:训练迭代的次数,表示已经进行了多少轮训练。
- time_elapsed:训练已经花费的时间,以秒为单位。
- total_timesteps:总的训练步数,表示已经执行了多少步训练。
- approx_kl:策略的近似KL散度,表示当前策略与旧策略之间的差异。
- clip_fraction:剪切比例,用于控制PPO算法中的策略剪切。
- entropy_loss:熵损失,表示策略的不确定性程度。
- explained_variance:解释方差,用于评估值函数的性能。
- learning_rate:学习速率,表示当前的学习速率大小。
- loss:总体损失,包括策略梯度损失和值函数损失。