最近在b站上看到了很好的DQN教程及代码实例,特此开贴记录学习笔记。
首先先放上大神的视频链接:Python·Pytorch-一点一点学AI-5-人人都可以学会的强化学习DQN(Deep Q-Learning),特别详细。希望有兴趣的朋友前往b站支持。
gym常用环境:gym常用的研究问题
打开CartPole-v1,查看其源代码如图所示:
可以在描述行中看到,该环境有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型数值。
下面是源代码里面给定的相关位置的范围。
所以我们有了代码中给定奖励的方式:
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的定义:
所以,是s[0]代表位置。奖励给定的方式是:向右就给奖励,向左就不给奖励。
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()