临汾人才招聘网:DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird

admin 5个月前 (05-30) 科技 49 0

在上一篇博客中,我们详细的对Q-learning的算法流程举行了先容。同时我们使用了\(\epsilon-贪心法\)防止陷入局部最优。

那么我们可以想一下,最后我们获得的效果是什么样的呢?由于我们思量到了所有的(\(\epsilon-贪心法\)导致的)情形,因此最终我们将会获得一张如下的Q-Table表。

Q-Table \(a_1\) \(a_2\)
\(s_1\) \(q(s_1,a_1)\) \(q(s_1,a_2)\)
\(s_2\) \(q(s_2,a_1)\) \(q(s_2,a_2)\)
\(s_3\) \(q(s_3,a_1)\) \(q(s_3,a_2)\)

当agent运行到某一个场景\(s\)时,会去查询已经训练好的Q-Table,然后从中选择一个最大的\(q\)对应的action。

训练内容

这一次,我们将对Flappy-bird游戏举行训练。这个游戏的先容我就不多说了,可以看一下维基百科的先容。

游戏就是控制一只穿越管道,然后可以获得分数,对于小鸟来说,他只有两个动作,跳or不跳,而我们的目的就是使小鸟穿越管道获得更多的分数。

前置准备

由于我们的目的是来学习“强化学习”的,以是我们不能能说自己去弄一个Flappy-bird(固然自己弄也可以),这里我们直接使用一个已经写好的Flappy-bird。

PyGame-Learning-Environment,是一个Python的强化学习环境,简称PLE,下面时他Github上面的先容:

PyGame Learning Environment (PLE) is a learning environment, mimicking the Arcade Learning Environment interface, allowing a quick start to Reinforcement Learning in Python. The goal of PLE is allow practitioners to focus design of models and experiments instead of environment design.

PLE hopes to eventually build an expansive library of games.

然后关于FlappyBird的文档先容在这里,文档的先容照样蛮清晰的。安装步骤如下所示,推荐在Pipenv的环境下安装,不外你也可以直接clone我的代码然后然后凭据reademe的步骤举行使用。

git clone https://github.com/ntasfi/PyGame-Learning-Environment.git
cd PyGame-Learning-Environment/
pip install -e .

需要的库如下:

  • pygame
  • numpy
  • pillow

函数说明

在官方文档有几个的函数在这里说下,由于等下我们需要用到。

  • getGameState():获得游戏当前的状态,返回值为一个字典:

    1. player y position.
    2. players velocity.
    3. next pipe distance to player
    4. next pipe top y position
    5. next pipe bottom y position
    6. next next pipe distance to player
    7. next next pipe top y position
    8. next next pipe bottom y position

    部门数据示意如下:

  • reset_game():重新开始游戏

  • act(action):在游戏中执行一个动作,参数为动作,返回执行后的分数。

  • game_over():若是游戏竣事,则返回True,否者返回False。

  • getActionSet():获得游戏的动作聚集。

我们的窗体巨细默认是288*512,其中鸟的速率在-20到10之间(最小速率我并不知道,然则经由考察,并没有小于-20的情形,而最大的速率在源代码内里已经说明好了为10)

Coding Time

在前面我们说,通过getGameState()函数,我们可以获得几个关于环境的数据,在这里我们选择如下的数据:

  • next_pipe_dist_to_player:
  • player_y与next_pipe_top_y的差值
  • 的速率

然则我们可以想一想,next_pipe_dist_to_player一共会有若干种的取值:由于窗体巨细为288*512,则取值的局限大约是0~288,也就是说它大约有288个取值,而关于player_y与next_pipe_top_y的差值,则大概有1024个取值。这样很难让模子收敛,因此我们将数值举行简化。其中简化的思绪来自:GitHub

首先我们建立一个Agent类,然后逐渐向内里添加功效。

class Agent():

    def __init__(self, action_space):
        # 获得游戏支持的动作聚集
        self.action_set = action_space

        # 建立q-table
        self.q_table = np.zeros((6, 6, 6, 2))

        # 学习率
        self.alpha = 0.7
        # 励衰减因子
        self.gamma = 0.8
        # 贪心率
        self.greedy = 0.8

至于为什么q-table的巨细是(6,6,6,2),其中的3个6划分代表next_pipe_dist_to_playerplayer_y与next_pipe_top_y的差值的速率,其中的2代表动作的个数。也就是说,表格中的state一共有$6 \times6 \times 6 $种,表格的巨细为\(6 \times6 \times 6 \times 2\)

缩小状态值的局限

我们界说一个函数get_state(s),这个函数专门提取游戏中的状态,然后返回举行简化的状态数据:

    def get_state(self, state):
        """
        提取游戏state中我们需要的数据
        :param state: 游戏state
        :return: 返回提取好的数据
        """
        return_state = np.zeros((3,), dtype=int)
        dist_to_pipe_horz = state["next_pipe_dist_to_player"]
        dist_to_pipe_bottom = state["player_y"] - state["next_pipe_top_y"]
        velocity = state['player_vel']
        if velocity < -15:
            velocity_category = 0
        elif velocity < -10:
            velocity_category = 1
        elif velocity < -5:
            velocity_category = 2
        elif velocity < 0:
            velocity_category = 3
        elif velocity < 5:
            velocity_category = 4
        else:
            velocity_category = 5

        if dist_to_pipe_bottom < 8:  # very close or less than 0
            height_category = 0
        elif dist_to_pipe_bottom < 20:  # close
            height_category = 1
        elif dist_to_pipe_bottom < 50:  # not close
            height_category = 2
        elif dist_to_pipe_bottom < 125:  # mid
            height_category = 3
        elif dist_to_pipe_bottom < 250:  # far
            height_category = 4
        else:
            height_category = 5

        # make a distance category
        if dist_to_pipe_horz < 8:  # very close
            dist_category = 0
        elif dist_to_pipe_horz < 20:  # close
            dist_category = 1
        elif dist_to_pipe_horz < 50:  # not close
            dist_category = 2
        elif dist_to_pipe_horz < 125:  # mid
            dist_category = 3
        elif dist_to_pipe_horz < 250:  # far
            dist_category = 4
        else:
            dist_category = 5

        return_state[0] = height_category
        return_state[1] = dist_category
        return_state[2] = velocity_category
        return return_state

更新Q-table

更新的数学公式如下:

\[{\displaystyle Q^{new}(s_{t},a_{t})\leftarrow \underbrace {Q(s_{t},a_{t})} _{\text{旧的值}}+\underbrace {\alpha } _{\text{学习率}}\cdot \overbrace {{\bigg (}\underbrace {\underbrace {r_{t}} _{\text{奖励}}+\underbrace {\gamma } _{\text{奖励衰减因子}}\cdot \underbrace {\max _{a}Q(s_{t+1},a)} _{\text{estimate of optimal future value}}} _{\text{new value (temporal difference target)}}-\underbrace {Q(s_{t},a_{t})} _{\text{旧的值}}{\bigg )}} ^{\text{temporal difference}}} \]

下面是更新Q-table的函数代码:

def update_q_table(self, old_state, current_action, next_state, r):
    """

    :param old_state: 执行动作前的状态
    :param current_action: 执行的动作
    :param next_state: 执行动作后的状态
    :param r: 奖励
    :return:
    """
    next_max_value = np.max(self.q_table[next_state[0], next_state[1], next_state[2]])

    self.q_table[old_state[0], old_state[1], old_state[2], current_action] = (1 - self.alpha) * self.q_table[
        old_state[0], old_state[1], old_state[2], current_action] + self.alpha * (r + next_max_value)

选择最佳的动作

然后我们就是凭据q-table对应的Q值选择最大的那一个,其中第一个代表(也就是0)跳跃,第2个代表不执行任何操作。

选择的示意图如下:

临汾人才招聘网:DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird 第1张

代码如下所示:

def get_best_action(self, state, greedy=False):
    """
    获得最佳的动作
    :param state: 状态
    :是否使用ϵ-贪心法
    :return: 最佳动作
    """
	
    # 获得q值
    jump = self.q_table[state[0], state[1], state[2], 0]
    no_jump = self.q_table[state[0], state[1], state[2], 1]
    # 是否执行计谋
    if greedy:
        if np.random.rand(1) < self.greedy:
            return np.random.choice([0, 1])
        else:
            if jump > no_jump:
                return 0
            else:
                return 1
    else:
        if jump > no_jump:
            return 0
        else:
            return 1

更新\(\epsilon\)

这个比较简单,早年面的博客中,我们知道\(\epsilon\)是随着训练次数的增添而削减的,有很多种计谋可以选择,这里乘以\(0.95\)吧。

def update_greedy(self):
    self.greedy *= 0.95

执行动作

在官方文档中,若是小鸟没有殒命奖励为0,越过一个管道,奖励为1,殒命奖励为-1,我们稍微的对其举行改变:

def act(self, p, action):
    """
    执行动作
    :param p: 通过p来向游戏发出动作下令
    :param action: 动作
    :return: 奖励
    """
    # action_set示意游戏动作集(119,None),其中119代表跳跃
    r = p.act(self.action_set[action])
    if r == 0:
        r = 1
    if r == 1:
        r = 10
    else:
        r = -1000
    return r

main函数

最后我们就可以执行main函数了。

if __name__ == "__main__":
    # 训练次数
    episodes = 2000_000000
    # 实例化游戏工具
    game = FlappyBird()
    # 类似游戏的一个接口,可以为我们提供一些功效
    p = PLE(game, fps=30, display_screen=False)
    # 初始化
    p.init()
    # 实例化Agent,将动作集传进去
    agent = Agent(p.getActionSet())
    max_score = 0
	
    for episode in range(episodes):
        # 重置游戏
        p.reset_game()
        # 获得状态
        state = agent.get_state(game.getGameState())
        agent.update_greedy()
        while True:
            # 获得最佳动作
            action = agent.get_best_action(state)
            # 然后执行动作获得奖励
            reward = agent.act(p, action)
            # 获得执行动作之后的状态
            next_state = agent.get_state(game.getGameState())
            # 更新q-table
            agent.update_q_table(state, action, next_state, reward)
            # 获得当前分数
            current_score = p.score()
            state = next_state
            if p.game_over():
                max_score = max(current_score, max_score)
                print('Episodes: %s, Current score: %s, Max score: %s' % (episode, current_score, max_score))
                # 保留q-table
                if current_score > 300:
                    np.save("{}_{}.npy".format(current_score, episode), agent.q_table)
                break

部门的训练的效果如下:

临汾人才招聘网:DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird 第2张

总结

emm,说实话,我也不知道效果会怎么样,由于训练的时间比较长,我不想放在我的电脑上面跑,然后我就放在树莓派上面跑,然则树莓派性能比较低,导致训练的速率比较慢。然则,我照样以为我的方式有点问题get_state()函数中简化的方式,我感受不是稀奇的合理,若是列位有好的看法,可以在谈论区留言哦,然后配合学习。

项目地址:https://github.com/xiaohuiduan/flappy-bird-q-learning

参考

  • Use reinforcement learning to train a flappy bird NEVER to die
  • PyGame-Learning-Environment
  • https://github.com/BujuNB/Flappy-Brid-RL
,

Allbet

www.cfelectron.com欢迎进入欧博开户平台(Allbet Gaming),欧博开户平台开放欧博(Allbet)开户、欧博(Allbet)代理开户、欧博(Allbet)电脑客户端、欧博(Allbet)APP下载等业务

Sunbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:临汾人才招聘网:DQN(Deep Q-learning)入门教程(四)之Q-learning Play Flappy Bird

网友评论

  • (*)

最新评论

文章归档

站点信息

  • 文章总数:642
  • 页面总数:0
  • 分类总数:8
  • 标签总数:1010
  • 评论总数:267
  • 浏览总数:7544