导语
T_T真是光阴似箭呐,感觉这个flag刚立下不久,转眼都已经好几天了,吓得我赶紧过来写篇文章,实现这个flag,毕竟flag这东西,拖久了就倒了。
好的,废话不多,利用DQN玩"恐龙跳一跳"这一款Chrome浏览器内隐藏的彩蛋游戏,让我们愉快地开始吧~
开发工具
操作系统:Windows10
Python版本:3.6.4
相关模块:
keras模块;
TensorFlow-GPU模块;
selenium模块;
numpy模块;
pillow模块;
pyautogui模块;
matplotlib模块;
cv2模块;
以及一些Python自带的模块。
环境搭建
安装Python并添加到环境变量,pip安装需要的相关模块即可。
GPU环境配置请自行参考相关的网络资料。
愉
快
地
开
始
现在,我们正式进入今天的主题:
利用DQN玩T-Rex Rush。
关于DQN的基本原理,请自行参考我之前的文章
Python玩CartPole(DQN)
。这里不再进行赘述,我们直接开始讨论当前需要解决的问题,即如何利用DQN网络玩"恐龙跳一跳"这款游戏。
一.控制小恐龙
当我开始着手解决这个问题时,首先摆在我面前的子问题就是:
如何利用Python控制这只小恐龙呢?
第一个很自然的想法就是调用pyautogui库来实现Python自动控制键盘功能,从而达到控制小恐龙的目的。于是我开始写起了控制小恐龙的API接口类,N分钟之后,我获得了如下图所示的脚本:
是的,我写不下去了,因为利用pyautogui库并不能较为轻松地获得游戏当前的状态,只能用PIL库对当前的游戏画面进行截图,然后利用数字识别技术获取当前的分数,以及其他一些的图像处理技术来判断恐龙当前是撞死了,还是仍在"傻乎乎"地前进。这样的操作对于模型训练而言显然代价有些过大了。那么如何是好呢?
啊,游戏是隐藏在Chrome浏览器中的呀!另一个很自然的想法一闪而现,那就是利用众所周知的Selenium库实现Python与浏览器的交互功能,从而达到控制小恐龙的目的。于是,我又开始写起了控制小恐龙的API接口类,最终获得的代码如下(代码的实现建立在参考文献1作者的优秀工作之上):
通过该API接口类,我们可以很轻松地实现一个小恐龙的Agent类,用于控制小恐龙,并获取训练所需的数据。具体而言,其作用为方便神经网络控制小恐龙,并在每次完成控制小恐龙的操作之后将该控制对游戏产生的影响反馈给神经网络,作为神经网络的训练数据,具体而言,数据包括当前的分数,当前的游戏画面,小恐龙是死是活等。具体的代码实现如下(仅部分):
至此,我们解决了如何利用Python控制小恐龙的问题。为了避免遗忘,这里我还将指出一个在后续模型训练过程中的遇到的问题以及对应的解决方案。
截取游戏画面的效率低下问题。
利用Selenium对游戏进行截屏的速度极慢,后来改用了PIL,速度略有提升,但应用于DQN的实时训练,效率仍然堪忧。最后,我借鉴了参考文献3中的做法,直接利用JS来获取游戏画面,训练效率大为提高:
二.搭建网络模型
接下来我们必须解决的子问题就是:
如何搭建网络模型?
这里我选择用Keras框架来搭建一个简单的卷积网络模型,以期快速方便地检验是否可以利用DQN玩"恐龙跳一跳"这款游戏。模型结构借鉴了参考文献2,将四帧连续的游戏画面作为网络的输入,网络输出为一个二维数组,用于决定恐龙的行动方式(即跳跃或者不跳跃):
刚开始搭建模型的时候,我并没有选择加入池化层,但训练一段时间之后,我发现小恐龙固执地认为一直跳跃或者一直不跳跃可以让自己一直苟活下去。让我不得不怀疑模型存在过拟合现象,于是我增加了池化层。同时我也借鉴了参考文献1中的做法,在模型训练初期,让小恐龙不完全受神经网络的控制,而是有一定的概率随机行动,以期让训练数据更加丰富多样:
三.游戏画面预处理
在开始训练我们的模型之前,我们还需要考虑的一个子问题就是:
是否对游戏画面进行预处理?
在参考文献1中,作者使用了canny算子对游戏画面进行了预处理,因此我在训练参数中也添加了是否使用canny算子的选项:
但事实上,我并没有在训练过程中使用它,因为用JS获得的游戏画面效果和截图然后用canny算子预处理的效果几乎一致。
四.模型训练
最后,我们需要考虑的问题当然是:
如何训练这个模型呢?
事实上,训练的方式有许多种,但由于时间有限,本文仅尝试了在线实时训练的方式,并未综合测试并比较各种训练方案的优劣。
具体实现如下:
即以连续的四帧画面作为网络的输入来获取小恐龙的行动预测值(即跳还是不跳),小恐龙根据预测值行动后,Agent反馈游戏当前的状态并存储在deque数据结构中,当数据达到一定的数量时,就可以不断地随机抽取其中的一部分数据来训练当前的网络模型了,并且将新训练的网络模型实时地应用到小恐龙的行动预测中。
啊,对了,还有最后一个问题,就是小恐龙行动后获得的游戏当前状态值如何转换为网络的奖励值(Reward)。这里,我只做了简单的建模,即若行动后小恐龙阵亡,则该行动对应的奖励值为-1,或仍然苟活,则该行动对应的奖励值为0.1加上γ乘以下一个游戏画面输入网络时网络输出的较大值:
为什么?因为当前的所作所为要对未来负责啊!现在跳了,遇到障碍物跳不了怎么办,毕竟小恐龙在空中是跳不了的啊~
That's all~
完整源代码详见相关文件。
结
果
展
示
模型其实没有完全训练好T_T,我只训练了10万帧游戏画面,效果大概是这样的(现在最高能跑到2千分左右):
Loss趋势图:
参
考
文
献
https://github.com/ravi72munde/Chrome-Dino-Reinforcement-Learning/blob/master/Reinforcement%20Learning%20Dino%20Run.ipynb
https://github.com/DexHunter/DeepLearningTRexRush
https://blog.paperspace.com/dino-run/
更多
在利用DQN玩T-Rex Rush(下篇)中,我们将对模型进行优化(模型结构、训练方式等等),个人期望小恐龙能跑到1万分左右。
当然这只是一个Flag,因为优化的前提是我有时间回过头来研究这个,而且心情舒畅,想多码字和大家分享。
有心的朋友可以在国庆节提醒一下我,如果我一直没有更下篇的话。
提供的代码可能有点乱,因为我想最后都搞定了之后再整理代码的。T_T
不过我写的注释应该挺详细的。
相关文件中没有提供的训练数据,因为文件太大了,我懒得传。想自己训练的从头开始训练就是了(导入我的模型权重)。
Emmmm,就这样吧~
○
○
Charles的皮卡丘
Pikachu~
领取专属 10元无门槛券
私享最新 技术干货