DQN小车爬山——pytorch实现

  最近在b站上看到了很好的DQN教程及代码实例,特此开贴记录学习笔记。
  首先先放上大神的视频链接:Python·Pytorch-一点一点学AI-5-人人都可以学会的强化学习DQN(Deep Q-Learning),特别详细。希望有兴趣的朋友前往b站支持。

gym常用环境

gym常用环境:gym常用的研究问题
打开CartPole-v1,查看其源代码如图所示:
DQN小车爬山——pytorch实现_第1张图片
DQN小车爬山——pytorch实现_第2张图片
可以在描述行中看到,该环境有4个观测值,分别是车的位置,车速,杆的角度,杆的偏转速度。2个动作分别为车向左和右走。
另外可通过以下代码查看:

import gym
env = gym.make('CartPole-v0')
print('观测空间 = {}'.format(env.observation_space))
print('动作空间 = {}'.format(env.action_space))
print('观测范围 = {}~{}'.format(env.observation_space.low,env.observation_space.high))
print('动作数 = {}'.format(env.action_space.n))

输出结果:

观测空间 = Box(4,)
动作空间 = Discrete(2)
观测范围 = [-4.8000002e+00 -3.4028235e+38 -4.1887903e-01 -3.4028235e+38]~[4.8000002e+00 3.4028235e+38 4.1887903e-01 3.4028235e+38]
动作数 = 2

运行结果告诉我们,观测空间是形状为(2,)的浮点型np.array,而动作空间是取的int型数值。

小车爬山问题

  • 这里我们考虑小车爬山问题,通过上述描述,可知小车爬山有两个状态:位置和速度。三个动作:向左,向右。。。。。所以神经网络输入为2维,输出为3维。
  • 通过环境选择动作的时候,采用的是 ε \varepsilon ε-greedy方法。即刚开始是随机选择动作,即完全探索,随机学习次数的增多,agent的行为应该越来越优化,所以 ε \varepsilon ε的值会越来越小。
  • 这里坐标的设置可能与我们想的不同,如下图所示,在x轴方向看。

DQN小车爬山——pytorch实现_第3张图片
下面是源代码里面给定的相关位置的范围。
DQN小车爬山——pytorch实现_第4张图片
所以我们有了代码中给定奖励的方式:

s_, r , done , info = env.step(a)               ##环境返回值,可查看step函数
        r = s_[0] + 0.5                        ##因为初始点位置为-0.5,+0.5保证奖励r为0。
        if s_[0] > -0.5:                      ##如果位置向右,就给奖励。
            r = s_[0] + 0.5
            if s_[0] > 0.5:                  ###到达目标,给5的奖励  
                r = 5
        else:
            r = 0                          ##在左边,不得分

源代码中对state的定义:
1

所以,是s[0]代表位置。奖励给定的方式是:向右就给奖励,向左就不给奖励。

  • DQN设置了记忆库,反复试验然后存储数据,存满后,然后随机采样。此外还设置了目标网络和当前网络和延迟更新。原因可见我的另外一篇博客。[DQN学习笔记]。
  • 此外,代码中关于gather函数的用法,可见注释和这篇博文:pytorch之gather函数
    好了,以上就是pytorch实现DQN的细节。代码如下:
import torch
import torch.nn as nn
import numpy as np
import gym
import torch.autograd
import random


class MyNet(nn.Module):
    def __init__(self):
        super(MyNet,self).__init__()
        self.fc = nn.Sequential(
            nn.Linear(2,24),    ##两个输入
            nn.ReLU(),
            nn.Linear(24,24),
            nn.ReLU(),
            nn.Linear(24,3)    ##三个输出
        )
        self.mls = nn.MSELoss()
        self.opt = torch.optim.Adam(self.parameters(),lr = 0.001)
    def forward(self,x):
        return self.fc(x)
env = gym.envs.make('MountainCar-v0')
env = env.unwrapped
net = MyNet()   #实例化
net2 = MyNet()


store_count = 0
store_size = 2000
decline = 0.6   # epsilo
learn_time = 0
updata_time = 20  #目标值网络更新步长
gama = 0.9
b_size = 1000
store = np.zeros((store_size,6))    ###[s,a,s_,r],其中s占两个,a占一个,r占一个
start_study = False

for i in range(50000):
    s = env.reset()  ##
    while True:
        ###根据 state 产生动作
        if random.randint(0,100) < 100 * (decline ** learn_time):  # 相当于epsilon
            a = random.randint(0,2)
        else:
            out = net(torch.Tensor(s)).detach()    ##detch()截断反向传播的梯度,[r1,r2]
            a = torch.argmax(out).data.item()      ##[取最大,即取最大值的index]
        s_, r , done , info = env.step(a)               ##环境返回值,可查看step函数
        r = s_[0] + 0.5
        if s_[0] > -0.5:
            r = s_[0] + 0.5
            if s_[0] > 0.5:
                r = 5
        else:
            r = 0
        # r = abs(s_[0]-(-0.5))

        store[store_count % store_size][0:2] = s    ##覆盖老记忆
        store[store_count % store_size][2:3] = a
        store[store_count % store_size][3:5] = s_
        store[store_count % store_size][5:6] = r
        store_count +=1
        s = s_
  #####反复试验然后存储数据,存满后,就每次取随机部分采用sgd
        if store_count > store_size:
            if learn_time % updata_time ==0:
                net2.load_state_dict(net.state_dict())  ##延迟更新

            index = random.randint(0,store_size - b_size - 1)
            b_s = torch.Tensor(store[index:index + b_size,0:2])
            b_a = torch.Tensor(store[index:index + b_size, 2:3]).long()  ##  因为gather的原因,索引值必须是longTensor
            b_s_ = torch.Tensor(store[index:index + b_size, 3:5])
            b_r = torch.Tensor(store[index:index + b_size, 5:6 ])  #取batch数据

            q = net(b_s).gather(1,b_a) #### 聚合形成一张q表    根据动作得到的预期奖励是多少
            q_next = net2(b_s_).detach().max(1)[0].reshape(b_size,1)  #值和索引,延迟更新
            tq = b_r+gama * q_next
            loss = net.mls(q,tq)
            net.opt.zero_grad()
            loss.backward()
            net.opt.step()

            learn_time += 1
            if not start_study:   
                print('start_study')
                start_study  = True
                break
        if done:
            print(i)
            break

        env.render()

你可能感兴趣的:(强化学习)