当前位置:   article > 正文

动手学强化学习笔记-REINFORCE算法_reinforce代码 torch

reinforce代码 torch

        REINFORCE算法是一个基于策略的方法,我们利用一个神经网络来为策略函数建模,输入某个状态,然后输出一个动作的概率分布,目标是寻找一个策略并最大化策略在环境中的期望回报,目标函数可以定义为:

        对这个目标函数求梯度,可以得到如下公式:

       其中利用蒙特卡洛方法估计,利用这个公式来更新策略

       我们采用车杆环境

导入库

  1. import gym
  2. import torch
  3. import torch.nn.functional as F
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. from tqdm import tqdm
  7. import rl_utils

定义策略网络PolicyNet,他的输入是某个状态,输出是该状态下的动作概率分布,我们利用softmax()函数来实现一个多项分布,事实上我们是利用价值的归一化来替代了动作的概率分布,简单来说,就是价值越高,那么他的概率就越大

  1. class PolicyNet(torch.nn.Module):
  2. def __init__(self,state_dim,hidden_dim,action_dim):
  3. super(PolicyNet,self).__init__()
  4. self.fc1=torch.nn.Linear(state_dim,hidden_dim)
  5. self.fc2=torch.nn.Linear(hidden_dim,action_dim)
  6. def forward(self,x):
  7. x=F.relu(self.fc1(x))
  8. #由于损失函数是有关价值的函数,所以神经网络仍然是状态-价值的映射,而我们现在是进行策略梯度更新,需要输出动作的概率分布
  9. #所以需要使用softmax,即用价值的归一化来替代动作的概率分布,也就是价值越高的动作其概率也应越大
  10. #softmax函数将向量的元素归一化为概率分布,即计算每个动作的概率,概率之和为1,便于学习
  11. return F.softmax(self.fc2(x),dim=1)

定义REINFORCE算法

  1. class REINFORCE:
  2. def __init__(self,state_dim,hidden_dim,action_dim,learning_rate,gamma,device):
  3. self.policy_net=PolicyNet(state_dim,hidden_dim,action_dim).to(device)
  4. self.optimizer=torch.optim.Adam(self.policy_net.parameters(),lr=learning_rate) #使用Adam优化器
  5. self.gamma=gamma
  6. self.device=device
  7. def take_action(self,state): #根据动作概率分布随机采样
  8. state=torch.tensor([state],dtype=torch.float).to(self.device)
  9. #probs是动作概率分布
  10. probs=self.policy_net(state)
  11. #根据动作概率分布离散化动作空间
  12. action_dist=torch.distributions.Categorical(probs)
  13. #采样
  14. action=action_dist.sample()
  15. return action.item()
  16. def update(self,transition_dict):
  17. reward_list=transition_dict['rewards']
  18. state_list=transition_dict['states']
  19. action_list=transition_dict['actions']
  20. G=0
  21. #显式地将梯度置0
  22. self.optimizer.zero_grad()
  23. for i in reversed(range(len(reward_list))): #从最后一步算起
  24. reward=reward_list[i]
  25. state=torch.tensor([state_list[i]],dtype=torch.float).to(self.device)
  26. action=torch.tensor([action_list[i]]).view(-1,1).to(self.device)
  27. #神经网络输出的是动作的概率分布,这里是对相应动作取对数,也就是log(π(θ))
  28. log_prob=torch.log(self.policy_net(state).gather(1,action))
  29. #利用蒙特卡洛采样法计算每个时刻t往后的回报G,所以前面循环要翻转
  30. G=self.gamma*G+reward
  31. #我们知道一般的梯度下降法是对损失函数求梯度,往最大梯度的负方向进行更新参数的,目的是最小化损失函数
  32. #而我们这里,损失函数是累计奖励的函数,我们希望将其最大化,而不是最小化,所以这里损失函数应该加上负号
  33. loss=-log_prob*G #每一步的损失函数
  34. loss.backward() #反向传播计算梯度
  35. self.optimizer.step() #梯度下降
'
运行

设置超参数,开始试验

  1. learning_rate=1e-3
  2. num_episodes=1000
  3. hidden_dim=128
  4. gamma=0.98
  5. device=torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
  6. env_name="CartPole-v0"
  7. env=gym.make(env_name)
  8. env.reset(seed=0)
  9. torch.manual_seed(0)
  10. state_dim=env.observation_space.shape[0]
  11. action_dim=env.action_space.n
  12. agent=REINFORCE(state_dim,hidden_dim,action_dim,learning_rate,gamma,device)
  13. return_list = []
  14. for i in range(10):
  15. with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:
  16. for i_episode in range(int(num_episodes / 10)):
  17. episode_return = 0
  18. transition_dict = {
  19. 'states': [],
  20. 'actions': [],
  21. 'next_states': [],
  22. 'rewards': [],
  23. 'dones': []
  24. }
  25. state = env.reset()
  26. done = False
  27. while not done:
  28. action = agent.take_action(state)
  29. next_state, reward, done, _ = env.step(action)
  30. transition_dict['states'].append(state)
  31. transition_dict['actions'].append(action)
  32. transition_dict['next_states'].append(next_state)
  33. transition_dict['rewards'].append(reward)
  34. transition_dict['dones'].append(done)
  35. state = next_state
  36. episode_return += reward
  37. return_list.append(episode_return)
  38. agent.update(transition_dict)
  39. if (i_episode + 1) % 10 == 0:
  40. pbar.set_postfix({
  41. 'episode':
  42. '%d' % (num_episodes / 10 * i + i_episode + 1),
  43. 'return':
  44. '%.3f' % np.mean(return_list[-10:])
  45. })
  46. pbar.update(1)

画图

  1. episodes_list = list(range(len(return_list)))
  2. plt.plot(episodes_list, return_list)
  3. plt.xlabel('Episodes')
  4. plt.ylabel('Returns')
  5. plt.title('REINFORCE on {}'.format(env_name))
  6. plt.show()
  7. mv_return = rl_utils.moving_average(return_list, 9)
  8. plt.plot(episodes_list, mv_return)
  9. plt.xlabel('Episodes')
  10. plt.ylabel('Returns')
  11. plt.title('REINFORCE on {}'.format(env_name))
  12. plt.show()

        由于采用蒙特卡洛方法进行估计,REINFORCE算法的梯度估计的方差很大,可能会造成一定程度上的不稳定,同时蒙特卡洛采样法需要在序列结束后进行更新,这同时也要求任务具有有限的步数,这是REINFORCE算法的局限性。

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号