强化学习——Actor Critic Method

作者: EastSmith
日期: 2021.05
摘要: 展示 CartPole-V0 环境中 Actor-Critic 方法的一个实现。

一、介绍

本案例展示了CartPole-V0环境中Actor-Critic方法的一个实现。

Actor Critic Method(演员–评论家算法)

当代理在环境中执行操作和移动时,它将观察到的环境状态映射到两个可能的输出:

  • 推荐动作:动作空间中每个动作的概率值。代理中负责此输出的部分称为actor(演员)。

  • 未来预期回报:它预期在未来获得的所有回报的总和。负责此输出的代理部分是critic(评论家)。

演员和评论家学习执行他们的任务,这样演员推荐的动作就能获得最大的回报。

CartPole-V0

在无摩擦的轨道上,一根杆子系在一辆手推车上。agent(代理)必须施加力才能移动手推车。每走一步,杆子就保持直立,这是奖励。因此,agent(代理)必须学会防止杆子掉下来。

二、环境配置

本教程基于Paddle 2.1 编写,如果你的环境不是本版本,请先参考官网安装 Paddle 2.1 。

  1. import gym, os
  2. from itertools import count
  3. import paddle
  4. import paddle.nn as nn
  5. import paddle.optimizer as optim
  6. import paddle.nn.functional as F
  7. from paddle.distribution import Categorical
  8. print(paddle.__version__)
  1. 2.1.0

三、实施演员-评论家网络

这个网络学习两个功能:

  • 演员Actor:它将环境的状态作为输入,并为其动作空间中的每个动作返回一个概率值。

  • 评论家Critic:它将的环境状态作为输入,并返回对未来总回报的估计。

  1. device = paddle.get_device()
  2. env = gym.make("CartPole-v0") ### 或者 env = gym.make("CartPole-v0").unwrapped 开启无锁定环境训练
  3. state_size = env.observation_space.shape[0]
  4. action_size = env.action_space.n
  5. lr = 0.001
  6. class Actor(nn.Layer):
  7. def __init__(self, state_size, action_size):
  8. super(Actor, self).__init__()
  9. self.state_size = state_size
  10. self.action_size = action_size
  11. self.linear1 = nn.Linear(self.state_size, 128)
  12. self.linear2 = nn.Linear(128, 256)
  13. self.linear3 = nn.Linear(256, self.action_size)
  14. def forward(self, state):
  15. output = F.relu(self.linear1(state))
  16. output = F.relu(self.linear2(output))
  17. output = self.linear3(output)
  18. distribution = Categorical(F.softmax(output, axis=-1))
  19. return distribution
  20. class Critic(nn.Layer):
  21. def __init__(self, state_size, action_size):
  22. super(Critic, self).__init__()
  23. self.state_size = state_size
  24. self.action_size = action_size
  25. self.linear1 = nn.Linear(self.state_size, 128)
  26. self.linear2 = nn.Linear(128, 256)
  27. self.linear3 = nn.Linear(256, 1)
  28. def forward(self, state):
  29. output = F.relu(self.linear1(state))
  30. output = F.relu(self.linear2(output))
  31. value = self.linear3(output)
  32. return value

四、训练模型

  1. def compute_returns(next_value, rewards, masks, gamma=0.99):
  2. R = next_value
  3. returns = []
  4. for step in reversed(range(len(rewards))):
  5. R = rewards[step] + gamma * R * masks[step]
  6. returns.insert(0, R)
  7. return returns
  8. def trainIters(actor, critic, n_iters):
  9. optimizerA = optim.Adam(lr, parameters=actor.parameters())
  10. optimizerC = optim.Adam(lr, parameters=critic.parameters())
  11. for iter in range(n_iters):
  12. state = env.reset()
  13. log_probs = []
  14. values = []
  15. rewards = []
  16. masks = []
  17. entropy = 0
  18. env.reset()
  19. for i in count():
  20. # env.render()
  21. state = paddle.to_tensor(state,dtype="float32",place=device)
  22. dist, value = actor(state), critic(state)
  23. action = dist.sample([1])
  24. next_state, reward, done, _ = env.step(action.cpu().squeeze(0).numpy())
  25. log_prob = dist.log_prob(action);
  26. # entropy += dist.entropy().mean()
  27. log_probs.append(log_prob)
  28. values.append(value)
  29. rewards.append(paddle.to_tensor([reward], dtype="float32", place=device))
  30. masks.append(paddle.to_tensor([1-done], dtype="float32", place=device))
  31. state = next_state
  32. if done:
  33. if iter % 10 == 0:
  34. print('Iteration: {}, Score: {}'.format(iter, i))
  35. break
  36. next_state = paddle.to_tensor(next_state, dtype="float32", place=device)
  37. next_value = critic(next_state)
  38. returns = compute_returns(next_value, rewards, masks)
  39. log_probs = paddle.concat(log_probs)
  40. returns = paddle.concat(returns).detach()
  41. values = paddle.concat(values)
  42. advantage = returns - values
  43. actor_loss = -(log_probs * advantage.detach()).mean()
  44. critic_loss = advantage.pow(2).mean()
  45. optimizerA.clear_grad()
  46. optimizerC.clear_grad()
  47. actor_loss.backward()
  48. critic_loss.backward()
  49. optimizerA.step()
  50. optimizerC.step()
  51. paddle.save(actor.state_dict(), 'model/actor.pdparams')
  52. paddle.save(critic.state_dict(), 'model/critic.pdparams')
  53. env.close()
  54. if __name__ == '__main__':
  55. if os.path.exists('model/actor.pdparams'):
  56. actor = Actor(state_size, action_size)
  57. model_state_dict = paddle.load('model/actor.pdparams')
  58. actor.set_state_dict(model_state_dict )
  59. print('Actor Model loaded')
  60. else:
  61. actor = Actor(state_size, action_size)
  62. if os.path.exists('model/critic.pdparams'):
  63. critic = Critic(state_size, action_size)
  64. model_state_dict = paddle.load('model/critic.pdparams')
  65. critic.set_state_dict(model_state_dict )
  66. print('Critic Model loaded')
  67. else:
  68. critic = Critic(state_size, action_size)
  69. trainIters(actor, critic, n_iters=201)
  1. Iteration: 0, Score: 32
  2. Iteration: 10, Score: 43
  3. Iteration: 20, Score: 11
  4. Iteration: 30, Score: 18
  5. ...

五、效果展示

在训练的早期:

https://ai-studio-static-online.cdn.bcebos.com/d8826cc5bb8a4106bdd871a7f35c449d90029a3ae3f6465aa373c614baa78a9f

在训练的后期 https://ai-studio-static-online.cdn.bcebos.com/88b967da1ba74e049b3ff28dd9083d1e527ba734dc064a798374f99199f84086

六、总结

  • Actor-Critic,其实是用了两个网络: 一个输出策略,负责选择动作,这个网络称为Actor;一个负责计算每个动作的分数,这个网络称为Critic。

  • 可以形象地想象为,Actor是舞台上的舞者,Critic是台下的评委,Actor在台上跳舞,一开始舞姿并不好看,Critic根据Actor的舞姿打分。Actor通过Critic给出的分数,去学习:如果Critic给的分数高,那么Actor会调整这个动作的输出概率;相反,如果Critic给的分数低,那么就减少这个动作输出的概率。

  • Actor-Critic方法结合了值函数逼近(Critic)和策略函数逼近(Actor),它从与环境的交互中学习到越来越精确的Critic(评估),能够实现单步更新,相对单纯的策略梯度,Actor-Critic能够更充分的利用数据。