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 accelerateAccelerate 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_statesnext_states,其中next_states是为了计算Q现实。
  • 第二个for循环:计算Q现实

可能有人会有一个疑问,为什么我不写成一个for循环呢?实际上写成一个for循环是完全没有问题的,很

版权声明:本文为xiaohuiduan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xiaohuiduan/p/13021970.html