赞
踩
REINFORCE算法是一个基于策略的方法,我们利用一个神经网络来为策略函数建模,输入某个状态,然后输出一个动作的概率分布,目标是寻找一个策略并最大化策略在环境中的期望回报,目标函数可以定义为:
对这个目标函数求梯度,可以得到如下公式:
其中利用蒙特卡洛方法估计,利用这个公式来更新策略
我们采用车杆环境
导入库
- import gym
- import torch
- import torch.nn.functional as F
- import numpy as np
- import matplotlib.pyplot as plt
- from tqdm import tqdm
- import rl_utils
定义策略网络PolicyNet,他的输入是某个状态,输出是该状态下的动作概率分布,我们利用softmax()函数来实现一个多项分布,事实上我们是利用价值的归一化来替代了动作的概率分布,简单来说,就是价值越高,那么他的概率就越大
- class PolicyNet(torch.nn.Module):
- def __init__(self,state_dim,hidden_dim,action_dim):
- super(PolicyNet,self).__init__()
- self.fc1=torch.nn.Linear(state_dim,hidden_dim)
- self.fc2=torch.nn.Linear(hidden_dim,action_dim)
-
- def forward(self,x):
- x=F.relu(self.fc1(x))
- #由于损失函数是有关价值的函数,所以神经网络仍然是状态-价值的映射,而我们现在是进行策略梯度更新,需要输出动作的概率分布
- #所以需要使用softmax,即用价值的归一化来替代动作的概率分布,也就是价值越高的动作其概率也应越大
- #softmax函数将向量的元素归一化为概率分布,即计算每个动作的概率,概率之和为1,便于学习
- return F.softmax(self.fc2(x),dim=1)
定义REINFORCE算法
- class REINFORCE:
- def __init__(self,state_dim,hidden_dim,action_dim,learning_rate,gamma,device):
- self.policy_net=PolicyNet(state_dim,hidden_dim,action_dim).to(device)
- self.optimizer=torch.optim.Adam(self.policy_net.parameters(),lr=learning_rate) #使用Adam优化器
- self.gamma=gamma
- self.device=device
-
- def take_action(self,state): #根据动作概率分布随机采样
- state=torch.tensor([state],dtype=torch.float).to(self.device)
- #probs是动作概率分布
- probs=self.policy_net(state)
- #根据动作概率分布离散化动作空间
- action_dist=torch.distributions.Categorical(probs)
- #采样
- action=action_dist.sample()
- return action.item()
-
- def update(self,transition_dict):
- reward_list=transition_dict['rewards']
- state_list=transition_dict['states']
- action_list=transition_dict['actions']
-
- G=0
- #显式地将梯度置0
- self.optimizer.zero_grad()
- for i in reversed(range(len(reward_list))): #从最后一步算起
- reward=reward_list[i]
- state=torch.tensor([state_list[i]],dtype=torch.float).to(self.device)
- action=torch.tensor([action_list[i]]).view(-1,1).to(self.device)
- #神经网络输出的是动作的概率分布,这里是对相应动作取对数,也就是log(π(θ))
- log_prob=torch.log(self.policy_net(state).gather(1,action))
- #利用蒙特卡洛采样法计算每个时刻t往后的回报G,所以前面循环要翻转
- G=self.gamma*G+reward
- #我们知道一般的梯度下降法是对损失函数求梯度,往最大梯度的负方向进行更新参数的,目的是最小化损失函数
- #而我们这里,损失函数是累计奖励的函数,我们希望将其最大化,而不是最小化,所以这里损失函数应该加上负号
- loss=-log_prob*G #每一步的损失函数
- loss.backward() #反向传播计算梯度
- self.optimizer.step() #梯度下降
'运行
设置超参数,开始试验
- learning_rate=1e-3
- num_episodes=1000
- hidden_dim=128
- gamma=0.98
- device=torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-
- env_name="CartPole-v0"
- env=gym.make(env_name)
- env.reset(seed=0)
- torch.manual_seed(0)
- state_dim=env.observation_space.shape[0]
- action_dim=env.action_space.n
- agent=REINFORCE(state_dim,hidden_dim,action_dim,learning_rate,gamma,device)
-
- return_list = []
- for i in range(10):
- with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:
- for i_episode in range(int(num_episodes / 10)):
- episode_return = 0
- transition_dict = {
- 'states': [],
- 'actions': [],
- 'next_states': [],
- 'rewards': [],
- 'dones': []
- }
- state = env.reset()
- done = False
- while not done:
- action = agent.take_action(state)
- next_state, reward, done, _ = env.step(action)
- transition_dict['states'].append(state)
- transition_dict['actions'].append(action)
- transition_dict['next_states'].append(next_state)
- transition_dict['rewards'].append(reward)
- transition_dict['dones'].append(done)
- state = next_state
- episode_return += reward
- return_list.append(episode_return)
- agent.update(transition_dict)
- if (i_episode + 1) % 10 == 0:
- pbar.set_postfix({
- 'episode':
- '%d' % (num_episodes / 10 * i + i_episode + 1),
- 'return':
- '%.3f' % np.mean(return_list[-10:])
- })
- pbar.update(1)
画图
- episodes_list = list(range(len(return_list)))
- plt.plot(episodes_list, return_list)
- plt.xlabel('Episodes')
- plt.ylabel('Returns')
- plt.title('REINFORCE on {}'.format(env_name))
- plt.show()
-
- mv_return = rl_utils.moving_average(return_list, 9)
- plt.plot(episodes_list, mv_return)
- plt.xlabel('Episodes')
- plt.ylabel('Returns')
- plt.title('REINFORCE on {}'.format(env_name))
- plt.show()
由于采用蒙特卡洛方法进行估计,REINFORCE算法的梯度估计的方差很大,可能会造成一定程度上的不稳定,同时蒙特卡洛采样法需要在序列结束后进行更新,这同时也要求任务具有有限的步数,这是REINFORCE算法的局限性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。