DQN(Deep Q-learning)入门教程(六)之DQN Play Flappy-bird ,MountainCar
在DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird中,我们使用q-learning算法去对Flappy Bird进行强化学习,而在这篇博客中我们将使用神经网络模型来代替Q-table,关于DQN的介绍,可以参考我前一篇博客:DQN(Deep Q-learning)入门教程(五)之DQN介绍
在这篇博客中将使用DQN做如下操作:
- Flappy Bird
- MountainCar-v0
再回顾一下DQN的算法流程:
项目地址:Github
MountainCar-v0
MountainCar的训练好的Gif示意图如下所示,汽车起始位置位于山的底部,最终目标是驶向右边山的插旗的地方,其中,汽车的引擎不能够直接驶向终点,必须借助左边的山体的重力加速度才能够驶向终点。
MountainCar-v0
由OpenAI提供,python包为gym,官网网站为https://gym.openai.com/envs/MountainCar-v0/。在Gym包中,提供了很多可以用于强化学习的环境(env):
在MountainCar-v0中,状态有2个变量,car position(汽车的位置),car vel(汽车的速度),action一共有3种: Accelerate to the Left
, Don't accelerate
,Accelerate to the Right
,然后当车达到旗帜的地方(position = 0.5)会得到\(reward = 1\)的奖励,如果没有达到则为\(-1\)。但是如果当你运行步骤超过200次的时候,游戏就会结束。详情可以参考源代码(ps:官方文档中没有这些说明)。
下面介绍一下gym中几个常用的函数:
-
env = gym.make("MountainCar-v0")
这个就是创建一个
MountainCar-v0
的游戏环境。 -
state = env.reset()
重置环境,返回重置后的state
-
env.render()
将运行画面展示在屏幕上面,当我们在训练的时候可以不使用这个来提升速度。
-
next_state, reward, done, _ = env.step(action)
执行action动作,返回下一个状态,奖励,是否完成,info。
初始化Agent
初始化Agent直接使用代码说明吧,这个还是比较简单的:
import keras
import random
from collections import deque
import gym
import numpy as np
from keras.layers import Dense
from keras.models import Sequential
class Agent():
def __init__(self, action_set, observation_space):
"""
初始化
:param action_set: 动作集合
:param observation_space: 环境属性,我们需要使用它得到state的shape
"""
# 奖励衰减
self.gamma = 1.0
# 从经验池中取出数据的数量
self.batch_size = 50
# 经验池
self.memory = deque(maxlen=2000000)
# 探索率
self.greedy = 1.0
# 动作集合
self.action_set = action_set
# 环境的属性
self.observation_space = observation_space
# 神经网路模型
self.model = self.init_netWork()
def init_netWork(self):
"""
构建模型
:return: 模型
"""
model = Sequential()
# self.observation_space.shape[0],state的变量的数量
model.add(Dense(64 * 4, activation="tanh", input_dim=self.observation_space.shape[0]))
model.add(Dense(64 * 4, activation="tanh"))
# self.action_set.n 动作的数量
model.add(Dense(self.action_set.n, activation="linear"))
model.compile(loss=keras.losses.mean_squared_error,
optimizer=keras.optimizers.RMSprop(lr=0.001))
return model
我们使用队列来保存经验,这样的话新的数据就会覆盖远古的数据。此时我们定义一个函数,专门用来将数据保存到经验池中,然后定义一个函数用来更新\(\epsilon\)探索率。
def add_memory(self, sample):
self.memory.append(sample)
def update_greedy(self):
# 小于最小探索率的时候就不进行更新了。
if self.greedy > 0.01:
self.greedy *= 0.995
训练模型
首先先看代码:
def train_model(self):
# 从经验池中随机选择部分数据
train_sample = random.sample(self.memory, k=self.batch_size)
train_states = []
next_states = []
for sample in train_sample:
cur_state, action, r, next_state, done = sample
next_states.append(next_state)
train_states.append(cur_state)
# 转成np数组
next_states = np.array(next_states)
train_states = np.array(train_states)
# 得到next_state的q值
next_states_q = self.model.predict(next_states)
# 得到state的预测值
state_q = self.model.predict_on_batch(train_states)
# 计算Q现实
for index, sample in enumerate(train_sample):
cur_state, action, r, next_state, done = sample
if not done:
state_q[index][action] = r + self.gamma * np.max(next_states_q[index])
else:
state_q[index][action] = r
self.model.train_on_batch(train_states, state_q)
大家肯定从上面的代码发现一些问题,使用了两个for循环,why?首先先说一下两个for循环分别的作用:
- 第一个for循环:得到
train_states
和next_states
,其中next_states
是为了计算Q现实。 - 第二个for循环:计算Q现实
可能有人会有一个疑问,为什么我不写成一个for循环呢?实际上写成一个for循环是完全没有问题的,很