让你的 AI 成为驯龙高手:该怎幺创造一个会玩 Chrome
「无法连上网际网路」。作为一个 Google Chrome 浏览器的用户,当你看到上面那个页面时,不要沮丧。换个角度一想,顺便玩个小恐龙也不错!
当你遇到打不开网页的时候,只要再点击一下这个页面,或者按下空白键,这只小恐龙就会轻轻一跳,陪你度过没有网路的时光。
这个「恐龙跳一跳」其实是藏在 Chrome 浏览器里好多年的一个彩蛋。2013 年 Chrome 开始用这个小恐龙的图像,代替令人烦恼的 404 页面。2014 年秋天,这只恐龙被正式改造成一个横版小游戏,以彩蛋的方式隐藏在新版 Chrome 浏览器里。
小恐龙被 AI后来,这个小游戏也成了不少 AI 练习的对象。
比如最近就有人在 YouTube 上传了一段影片,展示他如何用神经网络 + 遗传演算法,让一个 AI 系统独秀于浏览器之中。
↓影片就在这了↓
动图版:总而言之,一句话,这个 AI 能轻鬆玩到 2 万多分……
你能玩到几分?
大概一辈子都玩不到这个成绩吧,毕竟在 纪录 上,人类玩家的历史最高分是 18842。
怎幺做到的呢?上传这段影片的作者,并没有详细公布他用的方法,当然也没有给出一个开源的网址。但不要紧,也有别人公开分享了更多细节。
例如,GitHub 上就有一个开源的代码「IAMDinosaur」,同样也是利用神经网络+遗传演算法,来搞定这只小恐龙。(网址在此:ivanseidel/IAMDinosaur)
美中不足,上面这个项目也没有配上太详尽的解读。然而好消息是,最近有个国外的小哥 Ravi Munde,列了一份非常详尽的教学。
这个教学用的方法是强化学习中的 Q-learning,比较适合入门练习,而且对硬体的要求不高。
以下是量子位的编译。
Q-learning 了解/複习一下对动物来说,强化学习的能力是与生俱来的。拿儿童学走路来举例,如果小朋友努力迈出第一步,就会获得父母的鼓励——可能是鼓掌叫好,也可能是一块糖;但如果小朋友坚决不肯学习走路,那父母就不会给他们糖吃了。
强化学习就是依照这类激励行为而设置的。
在这个游戏中,对我们的 AI 小恐龙来说,强化学习需要让他在无监督的情况下,先认识到做出不同动作的结果,并且以获得高分为最高激励。

Ravi Munde 用 Q-learning 模拟了一个特殊函数,这个函数驱动 AI 在不同状况下做出正确的选择。
Q-learning 是强化学习的一种无模型实现,根据 Q 值对每个状态进行判断此时如果採取行动,能获得怎样的奖励。一个样本 Q 表让我们了解数据的结构。在恐龙跑酷游戏中,状态是当前的游戏截图,能採取的行动是跳或不跳 [0,1]

Ravi Munde 决定用深度神经网络来决定小恐龙何时起跳,而且要在最简单的强化学习实现基础上,引入不同参数来辅助它。
缺乏已标记的数据让强化学习非常不稳定。为了获得适用于这个游戏的数据,Munde 小哥决定,先让小恐龙自己瞎跳几千次,把每个动作的反馈记下来,然后从数据中随机挑选一些来训练模型。
但之后,Munde 小哥发现,他训练了一个倔强的模型——模型坚定的认为,跳,一定比不跳好。所以,为了让模型在训练时能在跳与不跳之间多尝试一下,他引入了一个函数ɛ来决定行动的随机性,然后再逐渐减小它的值来削减随机性,最终让模型去选择最有可能获得奖励的行动。
讚誉分布(Credit Assignment)问题 可能会让模型陷入混乱——目前获得的奖励究竟来自于过去的哪个行为呢?在恐龙跑酷游戏中,小恐龙跳到半空中后无法再次跳跃,但模型可能会在恐龙处于半空中时发出跳跃指令,这种情况就让恐龙非常容易砸到仙人掌上。
在这种情况下,「砸到仙人掌上」这个负反馈实际上是上一次做出跳跃决定的结果,而不是刚刚恐龙在半空中时做出的跳跃结果所导致的。
在面临这种问题的情况下,可以引入 贴现因子(Discount Factor)γ 来决定模型做出动作时看得多远。γ间接解决了赞誉分布问题,在这个游戏中,当γ=0.99 时,模型认识到在无障碍时随便跳会导致真的遇到障碍时自己正在半空中,无法继续跳跃。
除了这两个参数之外,后面就几乎不需要任何参数了。
你需要準备的是
- Python 3.6SeleniumOpenCVPILChromium driver for SeleniumKeras
稍微解释一下这几个工具。
构建这个 AI 模型,需要用 Python 编程。而游戏是用 JavaScript 写成的。所以,得借助一些工具才能更好地沟通。
Selenium 是一种流行的浏览器自动化工具,用于向浏览器发送操作指令,以及获取各种游戏参数。
接口的事情搞定了,还得想办法获得游戏截屏。用 Selenium 也行,但是速度很慢,截屏和处理一次大约得 1 秒钟。
用 PIL 和 OpenCV 能够更好地完成截屏和图像预先处理,可以达到 5fps 的帧率。你可能觉得还是慢,但已经足够对付这个游戏了。
游戏模组下面这个模组,实现了 Python 和浏览器(使用 Selenium)的沟通。
恐龙智能体模组这个模组在游戏模组的帮助下,用于控制小恐龙的动作。
游戏状态模组神经网络直接使用这个模组,来执行操作并获取新的状态。
预先处理游戏修改
原始的游戏相对複杂,比如游戏速度会逐渐加快,障碍物会改变,还会出现云朵、星星、地面纹理等。一次同时学习这幺多东西会消耗大量时间,甚至在训练过程中引入不必要的噪音。
为此作者修改了游戏的源代码、简化画面,去除了一些视觉元素(云、历史最佳成绩等),还有让恐龙的奔跑速度保持不变。


图像处理
原始截图的分辨率为 1200×300,包含三个通道。作者计划使用 4 个连续的屏幕截图作为模型的单一输入,也就是 1200×300×3×4。
问题是,这个小哥只有一个 i7 的 CPU 可用,所以他的电脑没办法在处理这个尺寸输入的同时玩游戏。所以,还得继续用 OpenCV 的库调整截图大小、裁剪等。最终输入图像大小为 40×20 像素,单通道,并用 Canny 突出显示边缘。
然后,堆叠 4 张图创建单个输入,也就是:40×20×4。请注意,这里小恐龙也裁减掉了,因为整个学习过程,只需要知道障碍物和与边缘的距离即可。
模型架构
现在输入有了,用模型输出来玩游戏的方法也有了,只差模型架构。
小哥选择把 3 个卷积层压平,连接到一个 512 神经元的全连接层(dense layer)上。池化层直接被砍掉了,这个东西在图像分类问题上很有用,但是玩 Dino 的时候神经网络只需要知道障碍物的位置,池化层就起不了什幺作用了。
这个模型的输出,形状和可能的操作数量一样。模型会预测各种操作的 Q 值,也叫 discounted future reward,然后我们选数值最高的那个。
下面这段代码,就能召唤一个用 TensorFlow 后端的 Keras 来搭建的模型:
开始训练接下来,就是见证奇迹的时刻!
也就是用一段代码来训练模型,这段代码的任务是:
- 从无操作开始,得到初始状态 initial state(s_t)观察玩游戏的过程,代码中的 OBSERVATION 表示步数预测一个操作的效果在 Replay Memory 中存储经验训练阶段,从 Replay Memory 里随机选择一组,用它来训练模型如果 game over 了,就重开一局
更详细的,可以看这段自带注释的代码:
将这个模型用到从 Replay Memory 里随机挑选的一批上
主体方法
调用下面的方法,就能啓动上面的训练流程:
结果这个模型,小哥用一周的时间训练了 200 万帧,其中前 100 万帧用来调整游戏参数修补 bug,后 100 万帧真正用来训练。
现在,这个模型的最好成绩是 265 分。从下面的得分和损失变化图里,能看出模型的 loss 在后 100 万帧逐渐稳定,比较低,但是会随时间波动。
目前的局限虽然这个模型后来表现还算可以了,但比人类还是差了一大截。
当然,别忘了这个小哥比较穷,他只有一个 i7 的 CPU。他认为,模型学得还不够快,得分还不够高,要怪这样几个因素:一是因为用 CPU 来学习,它总是掉帧;二是供这个 AI 来玩耍的图像实在是太小了,只有 40×20,在当前的模型架构下就可能导致了特徵的损失,还拖慢了学习速度。
如果改用 GPU,说不定……,用 GPU 究竟会不会改善,你们可以拿这份代码来试试:
ravi72munde/Chrome-Dino-Reinforcement-Learning
(原文网址在此)