动手学强化学习笔记-REINFORCE算法

        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算法的局限性。

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值