强迫症福音!玩 Flappy Bird 怎样可以拿到 2000 高分
强化学习7日打卡营AI Studio课程主页:
https://aistudio.baidu.com/aistudio/course/introduce/1335
B站课程链接:
https://www.bilibili.com/video/BV1yv411i7xd
《Flappy Bird》相信大家都玩过或者看过,这款游戏在2014年火遍全球。其操作非常简单,只需要点击屏幕,让主角小鸟顺利地穿过水管之间的缝隙而不碰触任何障碍物。小鸟穿过的水管越多,得到的分数也就越高。
今天我们也来玩一玩这个游戏,不过我们使用强化学习的算法来让主角小鸟自己学会穿过水管躲避障碍,进而魔改环境,制作特殊的三人环境,让游戏进阶为《Flappy Paddle》。别愣着,看下去,看完我们一起划船。
学习这篇文章,你可以做出下面视频中的效果。这里在红黑两支队伍被淘汰之后,结束了录制。因为蓝色的算法可以跑很久,这里只是作为展示,所以没有继续录下去。
飞桨有众多方便好用的开发工具套件,其中PARL就是在强化学习方向的一个高性能、灵活的框架,目前已经在Github上开源。PARL支持大规模并行计算,同样提供了算法的可复现性保证。PARL的框架逻辑清晰,容易上手,从Model到Algorithm再到Agent,逐步构建智能体。同时PARL也提供了一些经典的强化学习算法代码示例,如PG、DDPG、A2C等,方便开发者的调研和验证。不仅如此,PARL还提供了比较完善的算法基类,这使得PARL的扩展性也很好,开发更为轻松快捷。
在我们这个项目中,使用的就是PARL这个开发工具套件。PARL的仓库,针对很多经典的强化学习方法也提供了对应的例子。本项目使用的DQN方法,也是在PARL的实现上的变化。
环境解析
对于强化学习问题,一般是智能体(Agent)和环境(Environment)的一个交互问题。智能体需要对环境或部分环境做出观测(Observation),并根据环境做出动作(Action),而环境对这个动作做出奖惩(Reward)。
对于观测值,我们可以通过getGameState函数得到一个观测字典,其中包含了8个字段,包括了玩家(游戏里是一个小鸟)的坐标信息、速度信息、玩家距离下一水管和再下一根水管的位置信息。当然你也可以直接使用getScreenRGB函数得到画面,并以它为观测值。这里为了简单操作,我们以观测字典为例。同时,我们也能发现这个观测值是连续的。 对于动作,我们可以通过getActionSet函数得到环境所支持的动作。在《Flappy Bird》这个游戏里,只有两个动作:1、点击屏幕让小鸟展翅高飞,2、什么都不做让小鸟自由滑翔。由此我们可以知道环境接受的动作是离散有限的。 在奖惩方面,环境是这样定义:reward = 当前帧的总分 - 前一帧的总分。总分的变化有两种情况:1、玩家通过管子,得一分。2、玩家撞天花板、地板,管子则游戏失败,扣五分。
算法选择
前一小节中,我们发现环境的观测是连续的,环境接受的动作是离散有限的。对于这种情况,可以选用Deep Q-Network(以下简称DQN)或是Policy Gradient(以下简称PG)。DQN作为查表法的扩展,把观测值从有限离散扩展到了连续空间,PG也有处理连续空间观测值的能力。两者的区别在于,DQN对观测值对应的每个动作计算Q值,并选择相应的动作;而PG则直接给出动作,省略了中间步骤。
搭建DQN及训练
这里再简单的介绍一下DQN。DQN作为Q-learning在连续观测值上的扩展,使用网络来代替传统的表格,增加了泛化的能力。DQN的训练和Q-learning一样,不断的让Q(s,a)逼近TargetQ=r+γmaxQ(s’,*)。这里利用的是神经网络的拟合能力。由于TargetQ在不断变化,DQN中使用了固定Q目标的方法,让算法更新更为平稳。除此之外,DQN中还使用了经验池的方法,提高了样本的利用率。同时这一机制也可以用来打散数据,消除样本之间的关联性。
def __init__(self, act_dim):
hid0_size = 64
hid1_size = 32
hid2_size = 16
self.fc0 = layers.fc(size=hid0_size, act='relu', name="fc0")
self.fc1 = layers.fc(size=hid1_size, act='relu', name="fc1")
self.fc2 = layers.fc(size=hid2_size, act='relu', name="fc2")
self.fc3 = layers.fc(size=act_dim, act=None, name="fc3")
def value(self, obs):
h0 = self.fc0(obs)
h1 = self.fc1(h0)
h2 = self.fc2(h1)
Q = self.fc3(h2)
return Q
def __init__(self, act_dim):
hid0_size = 64
hid1_size = 32
hid2_size = 16
self.fc0 = layers.fc(size=hid0_size, act='relu', name="catfc0")
self.fc1 = layers.fc(size=hid1_size, act='relu', name="catfc1")
self.fc2 = layers.fc(size=hid2_size, act='relu', name="catfc2")
self.fc3 = layers.fc(size=act_dim, act=None, name="catfc3")
def value(self, last_obs, obs):
oobs = fluid.layers.concat(input=[last_obs, obs], axis=-1, name='concat')
h0 = self.fc0(oobs)
h1 = self.fc1(h0)
h2 = self.fc2(h1)
Q = self.fc3(h2)
return Q
修改贴图资源,制作三人环境
现在让我们来划船吧,其实最简单的就是替换一下贴图资源,在PyGame-Learning-Environment
/ple/games/flappybird/assets文件夹中。把这个小bird换成我们的划船选手~
初始化定义三个player。 为每个player添加score和live属性及每个player对应的得分和死亡处理,以及游戏的score和结束条件。 设计新的actionset, 以能接受三个输入(实际上是一个输入包含三个Agent的三个action)。 设计新的observation。在此之前只返回一个观测值,但现在要针对每个player返回其对应的观测值。 图像绘制。在原来的基础上多绘制两个player。
成果
结果已经展示在文章的开篇视频中。这里训练了三个模型,两个隐层的模型,拿到了均分147分;拼接的模型,拿到了157分,而三个隐层的模型,则拿到了2000分左右的平均成绩。当然,针对不同的参数量的模型应该有对应的学习率等超参数层面的调整,这里仅是为了展示,并没有在这方面做更多的探索和优化。
总结
百度AI Studio课程平台
END
华为鸿蒙正式开源,进入开放原子开源基金会
中国终于有开源基金会了!既然 Star 可以刷,那我们如何判断一个开源项目的好坏?C++20 标准草案全票批准通过法律要求,伊朗开源项目维护者拒绝合并以色列开发者的 PR
觉得不错,请点个在看呀