嗨玩手游网

打开浏览器就能玩:适合「摸鱼」的轻量小游戏推荐

不少人上班都会偷偷摸摸「摸鱼」,毕竟一时摸鱼一时爽,一直摸鱼一直爽,但如果「摸鱼」的内容是玩游戏,就称得上是「老虎嘴里拔牙」了。在办公设备上运行各种大型游戏,很可能会遇到老板走到背后却来不及响应切换到其他页面的尴尬情形。

所以我收集并整理了相对轻量、用浏览器就能玩的游戏,写出了这篇摸鱼指南。希望大家适度「摸鱼」,劳逸结合。

注:游戏截图为拼图,可点击查看大图预览。

Chrome 小恐龙

说起网页小游戏,Chrome 浏览器在无法连接到网络的时候,大家一定见过下面这个可爱的小恐龙。

游戏非常简单:只需要在合适的时机按下空格,控制小恐龙跳过一个个竖立在地面上的仙人掌即可。后期小恐龙会越跑越快,你还能够看见空中飞的乌鸦,它也是一个需要躲避的障碍物。游戏通过存活时间获得积分,活的时间越久积分越高;另外因为可以连跳,所以如果你运气够好,有时一直按住空格不松手反而能够一直越过障碍物。

断网后随便访问一个页面都可以打开这个小游戏,在联网状态下我们也可以在 Chrome 地址栏里输入 chrome://dino/ 来跳转打开,手机上同样适用,起身倒咖啡的时候也能来一把!

另外根据评论区以及少数派作者群内的提醒,iOS 14 版本的 Chrome 浏览器还提供了桌面恐龙小组件:点按并按住主屏幕,然后点击左上角的 + 号图标,即可查找并添加 Chrome 小组件。

之后你就能快速点击小组件来游玩小恐龙游戏了。

让我们网上冲浪吧

既然你 Google 的 Chrome 都有个小恐龙,那我微软自然也不会落后。这款游戏只能在基于Chromium 的 Edge 版本上运行,同样是离线小游戏。

在任何状态下我们都可以通过在地址栏输入 edge://surf/ 的方式来进行游玩,具体需要我们做的是通过方向键左、右来控制角色移动,躲避海面上的障碍物;方向键上可以让人物停止不动,方向键下则是直线前进。

你的小人一开始拥有三条生命,每次撞到障碍物后会丢失一条生命,不过中途也会有红色的 ❤ 来续命;另外如果有幸捡到闪电 ⚡,按下 F 键可以快速加速(虽然个人建议不要这么使用,因为很容易触礁);中途也会有加速器,穿过它也会获得加速;游戏途中偶尔还会遇见机器人和你一起冲浪——他们也会触礁。

相比 Chrome 小恐龙,微软还把 Edge 这个小游戏做得很完善,在游戏开始页面的右上角我们还能看到专门的游戏设置页面。

在设置页面中我们可以切换不同的游戏模式:让我们网上冲浪吧 、计时赛、之字形等三个游戏模式,也能够开启高可见性模式,让你清晰的知道那些障碍物是不可撞击,那些是可撞击的。

红色是可穿过的障碍物,碰到黑色则丢失生命值

Vivaldia

这是 Vivaldi 浏览器自带的一款游戏,游戏类似天天跑酷,画面是像素风格的,看上去有点赛博朋克的味道。

游戏中我们需要使用上下左右控制方向,使用键盘 N 和 M 来分别发射向上和向前的子弹解决敌人,同时按下则是 45° 斜角发射;游戏过程中会遇到金币、地雷、天空中的无人机以及裂缝等等挑战。

相比上面两款浏览器,Vivaldi 这款游戏进一步有了世界观设定,下面是游戏的官方介绍:

Vivaldia 一觉醒来后发现,自己的和平城市已经被邪恶的机器所接管,这些机器想要窃取每个人的身份并将人们变成数字。如果人类不反击,他们的未来将变得暗无天日。面对逆境,维沃蒂亚成长壮大并击败了敌人。她为拯救这座城市的人类而战。维沃蒂亚骑着她的控制论忍者单轮摩托车 “庞克之轮”,超越了大型邪恶机器并超越了它们。有时,看起来胜利似乎是一场不可能的战斗,但通过勇气和力量,维沃蒂亚胜利了。邪恶已被制服,人类可以夺回他们的城市和未来。维沃蒂亚赢得了人民的心。

想玩这款游戏的方法也很类似:下载最新版 Vivaldi 浏览器的桌面版或 Android 版,在地址栏输入 vivaldi://game 即可启动。

另外如果你想了解 Vivaldi 浏览器,可以看看少数派之前的文章。

CS Online

《反恐精英》,又叫 Counter-Strike 或者 CS,可以说是好一票人的回忆。和当初在电脑课上大家用 U 盘拷贝分享然后开启局域网「警匪大战」的经历不同,现在我们可以直接在浏览器中游玩 CS 了。

在 主页 中,你可以看到不同的房间、地图、设置和地区服务器过滤等等设置,俨然一个 CS「私服在线版」;事实上左侧提供的商店、会员、等设置也说明它的确有这种性质,不过这并不影响我们随时打开以游客身份来一把。

值得一提的是,第一次加载游戏要等待一段时间,之后速度才会快很多。游戏画面接近 CS 1.5 版本,玩过几场下来发现对网速(或者更准确地说「网络环境」)有一定要求,毕竟服务器全都设在海外,延迟、卡顿必不可少,因为高 ping 被踢也是常事。

所以还是建议有网络环境的朋友尝试了。

红色警戒 2

说起这个大家应该也都不陌生吧。

对部分老玩家来说,「On my way」「Sir. Yes, sir.」都是早已能够脱口而出的台词了。年轻时总会想如果能快速玩上一把《红警 2》那该多美啊——现在你就有这个 机会 了。

和上面的 CS 不同,网页版《红色警戒 2》采用了充满童年记忆的登录界面,看到这个飞艇我就已经开始微笑了。注册账户后登录可以看到许多对局,我们可以创建自己的对局或者加入他人的对局。另外创建对局后还可以设置密码等朋友来一起玩,以往在客户端版本中能够看到的地图、超级武器等选项也一应俱全。

由于网络缘故,我在公司的网络下每次都会卡在游戏加载页面(你看真的是「摸鱼」在写),好在一个捡垃圾群里的各位帮我试玩并截取了以下图片;据他们反馈说会有一点点延迟,具体表现为拖动会有点卡。

感谢来自 nzg 以及暴躁的 kg 代打的游戏截图

但胜在能玩,欢迎大家分享游玩体验。

游戏数量非常丰富的 Crazy Games

Crazy Games 可以算得上是国外的 4399 了?

这个网站里的游戏种类非常丰富,画面和质量大多也属于中上乘,至少比记忆中的 4399 小游戏好很多(不是看不起 4399,毕竟我以前也是玩着它们过来的)。打开网页后能看见非常多的游戏界面,鼠标放上去还能大概预览游戏的介绍视频。

Crazy Games 首页

点进游戏页面,你能够看见游戏主页有一个 PLAY 按钮即可开始加载,加载的时候会显示游戏的操控玩法;游戏页面左边和右边还有下面都是其他游戏的推荐。值得一提的是,很多游戏我们都可以在 Chrome 的地址栏选择「安装」,游戏底部的 Menu 也有安装按钮,遇到喜欢的不妨这样「马克」一下日后备用。

Crazy Games 是全英文界面,可能会对大家的英语阅读水平有一点点要求,好在游戏本身不会有太复杂的单词,而且相信玩过许多游戏的大家能够很轻松地意会游戏目标;游戏加载速度有一点慢,我不知道是我电脑性能问题还是网络问题;游戏内容方面,在这里玩到类·极品飞车和 CS 这种游戏,其中 CS 也是多人在线,手感可不是《枪火英雄》能比的。

Human Benchmark

Human Benchmark 是用来测试人类反应力的,许多职业选手也会用来测试自己的反应力,下面就是 Astralis 战队的几位 CSGO 职业哥玩这个游戏的视频。

通常来说,越年轻反应越快,毕竟十六岁小青年;但也有一些老大哥状态非常好,有时候还能提前预判一手。

进入网站后,首页就是最经典的反应力测试,下面还有其他几项挑战可以切换。

首页的游戏规则是,页面会突然从红色变为绿色,你需要用你最快的反应速度点下鼠标。如果你点太早会出现 Too soon!的提示,如果变绿后点击,页面上会显示出从页面变成绿色到你点击的时间差。

你在页面下方可以看见所有参与测试的人的平均数值,看看你在哪个区间里面。

其他的游戏比如:Sequence Memory 和 Aim Trainer 分别是两款其他类型的游戏。

Sequence Memory 是让你根据之前九宫格高亮的顺序,重新点击一遍,考验你的记忆力的。但是注意等所有的色块亮完以后,再去点击,否则会判定你失败;

Aim Trainer 则是许多 FPS 玩家喜爱的,游戏会在页面随机出现一个靶子,你只需要移动鼠标并点击它即可。这个也是用来测试反应力的,在很多视频、直播网站上,你或许都见过许多 FPS 职业选手平时用这类游戏来进行训练了。

DOS / Flash 游戏保存计划

DOS 游戏保存计划 主要保存那些在DOS操作系统下运行的游戏,在没有 Windows 操作系统的时候,许多游戏如早期 RPG《仙剑》《金庸》,都是在经典的DOS下运行的。

Flash 在 2020年12月31日 被 Adobe 停止服务后,许多游戏和 Flash 制作的动画也逐渐消失了,而 Flash 保存计划 则为我们留下了一些关于 Flash 动画回忆。

这两个网站都收录了许多老游戏,非常复古,适合怀旧。

其中 DOS 游戏网站大约有 1898 款这类型的游戏,从游戏列表和右边的滚动条看出其数量庞大。

体验了一把童年的回忆《仙剑奇侠传》,游戏界面一出来就觉得「爷青回」了。可是我没敢多玩,摸鱼摸鱼,偷偷摸一摸是最好的,一直玩反而没那个刺激感了。

「Flash 保存计划」则可以当成一个个动画片来看,需要操作的不多,观赏性高于游玩性。

经典的火柴人战斗 Flash

摸鱼虽好,可莫贪图太多,毕竟生活还是要继续的。在工作和学习的工程中适当的放松可以缓解压力放空大脑,反而会提升工作效率。

最后,如果你觉得不过瘾,可以在评论区分享你的「摸鱼」小游戏;如果你在寻找其他的放松方式,还可以看一下少数派往期的「摸鱼指南」。

用强化学习玩转Chrome暗藏的小恐龙跳一跳,这儿有一份教程

夏乙 郭一璞 发自 凹非寺

量子位 出品 | 公众号 QbitAI

什么!未连接到互联网!!

明明是联网状态,为什么我想访问的页面

无!法!打!开!

淡定。

作为一个Google Chrome浏览器的用户,当你看到上面那个页面时,不要沮丧。换个角度一想,墙内还能有更多的Play时间哦~

你有没有注意到画面里那个小恐龙?

当你遇到打不开网页的时候,只需要再点击一下这个页面(手机),或者按下空格(电脑),随着小恐龙轻轻一跳——

一个新世界开启了。

这个“恐龙跳一跳”其实是藏在Chrome浏览器里好多年的一个彩蛋。小恐龙是一只霸王龙(T-Rex)。

2013年Chrome开始用这个小恐龙的图像代替令人烦恼的404页面。2014年秋天,这只恐龙被正式改造成一个横版小游戏。以彩蛋的方式隐藏在新版Chrome浏览器里。

呐,如果你还不知道这个彩蛋,可以抓紧试一试。比方说——

访问一个不翻墙就看不了的网页

或者直接输入:chrome://dino

或者访问:/d/file/gt/2023-09/dvmsvc4e2wx (需翻墙)

后来,这个小游戏也成了不少AI练手的对象。

比如最近就有人在YouTube上贴了一段视频,展示了他如何用神经网络+遗传算法,让一个AI系秀于浏览器之中。

我们把精华的部分截取了一下,就是下面这段视频。

动图版:

速度已经快到飞起

总而言之,一句话,这个AI能轻松玩到2万多分……

你能玩到几分?大概率是玩不到这个成绩的吧。毕竟在chromedino页面上,人类玩家的历史最高分是18842。

不过,上传这段视频的作者,并没有详细公布他用的方法,当然也没有给出一个开源的地址。不过不要紧,也有别人公开分享了更多细节。

例如,GitHub上就有一个开源的代码“IAMDinosaur”,同样也是利用神经网络+遗传算法,来搞定恐龙跳一跳。

地址在此:https://github/ivanseidel/IAMDinosaur

美中不足,上面这个项目也没有配上太详尽的解读。然而好消息是,最近有个国外的小哥Ravi Munde,列了一份非常详尽的教程。

这个教程用的方法是强化学习中的Q-learning,比较适合入门练手,而且对硬件的要求不高。

量子位搬运这份教程如下。

Q-learning了解/复习一下

对动物来说,强化学习的能力是与生俱来的。拿儿童学步来举例,如果小朋友努力的迈出第一步,就会获得父母的鼓励——可能是鼓掌叫好,也可能是一块糖;但如果小朋友坚决不肯学习走路,那父母就不会给它糖吃了。强化学习就是依照这类激励行为而设置的。

而在这个游戏中,对我们的AI小恐龙来说,强化学习需要让他在无监督的情况下,先认识到做出不同动作的结果,并且以获得高分为最高激励。

一个典型的强化学习闭环

Ravi Munde用Q-learning模拟了一个特殊函数,这个函数驱动AI在不同状况下做出正确的选择。

Q-learning是强化学习的一种无模型实现,根据Q值对每个状态进行判断此时如果采取行动,能获得怎样的奖励。一个样本Q表让我们了解数据的结构。在恐龙跑酷游戏中,状态是当前的游戏截图,能采取的行动是跳或不跳[0,1]

一个样本Q表

Ravi Munde决定用深度神经网络来决定小恐龙何时起跳,而且要在最简单的强化学习实现基础上,引入不同参数来辅助它。

缺乏已标记的数据让强化学习非常不稳定。为了获得适用于这个游戏的数据,Munde小哥决定,先让小恐龙自己瞎跳几千次,把每个动作的反馈记下来,然后从数据中随机挑选一些来训练模型。

但之后,Munde小哥发现,他训练了一个倔强的模型——模型坚定的认为,跳,一定比不跳好。所以,为了让模型在训练时能在跳与不跳之间多尝试一下,他引入了一个函数ɛ来决定行动的随机性,然后再逐渐减小它的值来削减随机性,最终让模型去选择最有可能获得奖励的行动。

赞誉分布(Credit Assignment)问题可能会让模型陷入混乱——目前获得的奖励究竟来自于过去的哪个行为呢?在恐龙跑酷游戏中,小恐龙跳到半空中后无法再次跳跃,但模型可能会在恐龙处于半空中时发出跳跃指令,这种情况就让恐龙非常容易砸到仙人掌上。

在这种情况下,“砸到仙人掌上”这个负反馈实际上是此前上一次做出跳跃决定的结果,而不是刚刚恐龙在半空中时做出的跳跃结果所导致的。

在面临这种问题的情况下,可以引入贴现因子(Discount Factor)γ来决定模型做出动作时看得多远。γ间接解决了赞誉分布问题,在这个游戏中,当γ=0.99时,模型认识到在无障碍时随便跳会导致真的遇到障碍时自己正在半空中,无法继续跳跃。

除了这两个参数之外,后面就几乎不需要任何参数了。

#game parametersGAMMA = 0.99 # decay rate of past observations original 0.99OBSERVATION = 50000. # timesteps to observe before trainingEXPLORE = 100000 # frames over which to anneal epsilonFINAL_EPSILON = 0.0001 # final value of epsilonINITIAL_EPSILON = 0.1 # starting value of epsilonREPLAY_MEMORY = 50000 # number of previous transitions to rememberBATCH = 32 # size of minibatchFRAME_PER_ACTION = 1你需要准备的是

Python 3.6

Selenium

OpenCV

PIL

Chromium driver for Selenium

Keras

略微解释一下这几个工具。

构建这个AI模型,需要用Python编程。而游戏是用JavaScript写成的。所以,得借助一些工具才能更好地沟通。

Selenium是一种流行的浏览器自动化工具,用于向浏览器发送操作指令,以及获取各种游戏参数。

接口的事情搞定了,还得想办法获得游戏截屏。用Selenium也行,但是速度很慢,截屏和处理一次大约得1秒钟。

用PIL和OpenCV能够更好地完成截屏和图像预处理,可以达到5fps的帧率。你可能觉得还是慢,但已经足够对付这个游戏了。

游戏模块

下面这个模块,实现了Python和浏览器(使用Selenium)的沟通。

'''* Game class: Selenium interfacing between the python and browser* __init__(): Launch the broswer window using the attributes in chrome_options* get_crashed() : return true if the agent as crashed on an obstacles. Gets javascript variable from game decribing the state* get_playing(): true if game in progress, false is crashed or paused* restart() : sends a signal to browser-javascript to restart the game* press_up(): sends a single to press up get to the browser* get_score(): gets current game score from javascript variables.* pause(): pause the game* resume(): resume a paused game if not crashed* end(): close the browser and end the game'''class Game: def __init__(self,custom_config=True): chrome_options = Options() chrome_options.add_argument(\"disable-infobars\") self._driver = webdriver.Chrome(executable_path = chrome_driver_path,chrome_options=chrome_options) self._driver.set_window_position(x=-10,y=0) self._driver.set_window_size(200, 300) self._driver.get(os.path.abspath(game_url)) #modifying game before training if custom_config: self._driver.execute_script(\"Runnernfig.ACCELERATION=0\") def get_crashed(self): return self._driver.execute_script(\"return Runnerstance_.crashed\") def get_playing(self): return self._driver.execute_script(\"return Runnerstance_.playing\") def restart(self): self._driver.execute_script(\"Runnerstance_.restart()\") time.sleep(0.25)# no actions are possible # for 0.25 sec after game starts, # skip learning at this time and make the model wait def press_up(self): self._driver.find_element_by_tag_name(\"body\").send_keys(Keys.ARROW_UP) def get_score(self): score_array = self._driver.execute_script(\"return Runnerstance_.distanceMeter.digits\") score = ''.join(score_array) # the javascript object is of type array with score in the formate[1,0,0] which is 100. return int(score) def pause(self): return self._driver.execute_script(\"return Runnerstance_.stop()\") def resume(self): return self._driver.execute_script(\"return Runnerstance_.play()\") def end(self): self._driver.close()恐龙智能体模块

这个模块在游戏模块的帮助下,用于控制小恐龙的动作。

class DinoAgent: def __init__(self,game): #takes game as input for taking actions self._game = game; self.jump(); #to start the game, we need to jump once time.sleep(.5) # no action can be performed for the first time when game starts def is_running(self): return self._game.get_playing() def is_crashed(self): return self._game.get_crashed() def jump(self): self._game_up() def duck(self): self._game_down()游戏状态模块

神经网络直接使用这个模块,来执行操作并获取新的状态。

'''get_state(): accepts an array of actions, performs the action on the agentreturns : new state, reward and if the game ended.'''class Game_sate: def __init__(self,agent,game): self._agent = agent self._game = game def get_state(self,actions): score = self._game.get_score() reward = 0.1*score/10 # dynamic reward calculation is_over = False #game over if actions[1] == 1: #else do nothing self._agent.jump() reward = 0.1*score/11 image = grab_screen() if self._agent.is_crashed(): self._game.restart() reward = -11/score is_over = True return image, reward, is_over #return the Experience tuple预处理

游戏修改

原始的游戏相对复杂,比如游戏速度会逐渐加快,障碍物会改变,还会出现云朵、星星、地面纹理等。一次同时学习这么多东西会消耗大量时间,甚至在训练过程中引入不必要的噪音。

为此作者修改了游戏的源代码、简化局面,去除了一些视觉元素(云、历史最佳成绩等),还有让恐龙的奔跑速度保持不变。

原图

修改后

图像处理

原始截图的分辨率为1200×300,包含三个通道。作者计划使用4个连续的屏幕截图作为模型的单一输入,也就是1200×300×3×4。

问题是,这个小哥只有一个i7的CPU可用,所以他的电脑没办法在处理这个尺寸输入的同时玩游戏。所以,还得继续用OpenCV的库调正截图大小、裁剪等。最终输入图像大小为40×20像素,单通道,并用Canny突出显示边缘。

def grab_screen(_driver = None): #bbox = region of interest on the entire screen screen = np.array(ImageGrab.grab(bbox=(40,180,440,400))) image = process_img(screen)#processing image as required return imagedef process_img(image): #game is already in grey scale canvas, canny to get only edges and reduce unwanted objects(clouds) # resale image dimensions image = cv2.resize(image, (0,0), fx = 0.15, fy = 0.10) #crop out the dino agent from the frame image = image[2:38,10:50] #img[y:y+h, x:x+w] image = cv2.Canny(image, threshold1 = 100, threshold2 = 200) #apply the canny edge detection return image

然后,堆叠4张图创建单个输入,也就是:40×20×4。请注意,这里小恐龙也裁减掉了,因为整个学习过程,只需要知道障碍物和与边缘的距离即可。

模型架构

现在输入有了,用模型输出来玩游戏的方法也有了,只差模型架构。

小哥选择把3个卷积层压平,连接到一个512神经元的全连接层(dense layer)上。池化层直接被砍掉了,这个东西在图像分类问题上很有用,但是玩Dino的时候神经网络只需要知道障碍物的位置,池化层就起不了什么作用了。

多层网络架构

这个模型的输出,形状和可能的操作数量一样。模型会预测各种操作的Q值,也叫discounted future reward,然后我们选数值最高的那个。

下面这段代码,就能召唤一个用TensorFlow后端的Keras来搭建的模型:

#model hyper parametersLEARNING_RATE = 1e-4img_rows , img_cols = 40,20img_channels = 4 #We stack 4 framesACTIONS = 2def buildmodel(): print(\"Now we build the model\") model = Sequential() model.add(Conv2D(32, (8, 8), strides=(4, 4), padding='same',input_shape=(img_cols,img_rows,img_channels))) #20*40*4 model.add(Activation('relu')) model.add(Conv2D(64, (4, 4), strides=(2, 2), padding='same')) model.add(Activation('relu')) model.add(Conv2D(64, (3, 3), strides=(1, 1), padding='same')) model.add(Activation('relu')) model.add(Flatten()) model.add(Dense(512)) model.add(Activation('relu')) model.add(Dense(ACTIONS)) adam = Adam(lr=LEARNING_RATE) modelpile(loss='mse',optimizer=adam) print(\"We finish building the model\") return model开始训练

接下来,就是见证奇迹的时刻~~

也就是用一段代码来训练模型,这段代码的任务是:

从无操作开始,得到初始状态initial state(s_t)

观察玩游戏的过程,代码中的OBSERVATION表示步数

预测一个操作的效果

在Replay Memory中存储经验

训练阶段,从Replay Memory里随机选择一组,用它来训练模型

如果game over了,就重开一局

更详细的,可以看这段自带注释的代码:

'''Parameters:* model => Keras Model to be trained* game_state => Game State module with access to game environment and dino* observe => flag to indicate wherther the model is to be trained(weight updates), else just play'''def trainNetwork(model,game_state): # store the previous observations in replay memory D = deque() #load from file system # get the first state by doing nothing do_nothing = np.zeros(ACTIONS) do_nothing[0] =1 #0 => do nothing, #1=> jump x_t, r_0, terminal = game_state.get_state(do_nothing) # get next step after performing the action s_t = np.stack((x_t, x_t, x_t, x_t), axis=2).reshape(1,20,40,4) # stack 4 images to create placeholder input reshaped 1*20*40*4 OBSERVE = OBSERVATION epsilon = INITIAL_EPSILON t = 0 while (True): #endless running loss = 0 Q_sa = 0 action_index = 0 r_t = 0 #reward at t a_t = np.zeros([ACTIONS]) # action at t #choose an action epsilon greedy if random.random() <= epsilon: #randomly explore an action print(\"----------Random Action----------\") action_index = random.randrange(ACTIONS) a_t[action_index] = 1 else: # predict the output q = model.predict(s_t) #input a stack of 4 images, get the prediction max_Q = np.argmax(q) # chosing index with maximum q value action_index = max_Q a_t[action_index] = 1 # o=> do nothing, 1=> jump #We reduced the epsilon (exploration parameter) gradually if epsilon > FINAL_EPSILON and t > OBSERVE: epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / EXPLORE #run the selected action and observed next state and reward x_t1, r_t, terminal = game_state.get_state(a_t) last_time = time.time() x_t1 = x_t1.reshape(1, x_t1.shape[0], x_t1.shape[1], 1) #1x20x40x1 s_t1 = npend(x_t1, s_t[:, :, :, :3], axis=3) # append the new image to input stack and remove the first one # store the transition in D Dend((s_t, action_index, r_t, s_t1, terminal)) D.popleft() if len(D) > REPLAY_MEMORY #only train if done observing; sample a minibatch to train on trainBatch(random.sample(D, BATCH)) if t > OBSERVE s_t = s_t1 t = t + 1 print(\"TIMESTEP\", t, \"/ EPSILON\", epsilon, \"/ ACTION\", action_index, \"/ REWARD\", r_t,\"/ Q_MAX \" , np.max(Q_sa), \"/ Loss \", loss)

将这个模型用到从Replay Memory里随机选择的一批上:

def trainBatch(minibatch):for i in range(0, len(minibatch)): loss = 0 inputs = np.zeros((BATCH, s_t.shape[1], s_t.shape[2], s_t.shape[3])) #32, 20, 40, 4 targets = np.zeros((inputs.shape[0], ACTIONS)) #32, 2 state_t = minibatch[i][0] # 4D stack of images action_t = minibatch[i][1] #This is action index reward_t = minibatch[i][2] #reward at state_t due to action_t state_t1 = minibatch[i][3] #next state terminal = minibatch[i][4] #wheather the agent died or survided due the action inputs[i:i + 1] = state_t targets[i] = model.predict(state_t) # predicted q values Q_sa = model.predict(state_t1) #predict q values for next step if terminal: targets[i, action_t] = reward_t # if terminated, only equals reward else: targets[i, action_t] = reward_t + GAMMA * np.max(Q_sa) loss += model.train_on_batch(inputs, targets)主体方法

调用下面的方法,就能启动上面的训练流程:

#argument: observe, only plays if true, else trainsdef playGame(observe=False): game = Game() dino = DinoAgent(game) game_state = Game_sate(dino,game) model = buildmodel() trainNetwork(model,game_state)结果

这个模型,小哥用一周的时间训练了200万帧,其中前100万帧用来调整游戏参数修补bug,后100万帧真正用来训练。

现在,这个模型的最好成绩是265分。从下面的得分和损失变化图里,能看出模型的loss在后100万帧逐渐稳定,比较低,但是会随时间波动。

游戏得分

后100帧的损失(loss)

目前的局限

虽然这个模型后来表现还算可以了,但比人类还是差了一大截。

当然,别忘了这个小哥比较穷,他只有一个i7的CPU。

他认为,模型学得还不够快,得分还不够高,要怪这样几个因素:一是因为用CPU来学习,它总是掉帧;二是供这个AI来玩耍的图像实在是太小了,只有40×20,在当前的模型架构下就可能导致了特征的损失,还拖慢了学习速度。

如果改用GPU,说不定……

相关链接

用GPU究竟会不会改善,你们可以拿这份代码来试试:

https://github/ravi72munde/Chrome-Dino-Reinforcement-Learning

原文地址:

https://medium/acing-ai/how-i-build-an-ai-to-play-dino-run-e37f37bdf153

One More Thing

其实嘛,让AI搞定小恐龙这件事,本质上跟让AI搞定Flappy Bird是一样的。如果你想深入研究一下这件事,这里再推荐两篇。

机器学习玩转Flappy Bird全书:六大“流派”从原理到代码

使用神经网络+遗传算法玩转Flappy Bird | 教程

就酱~

— 完 —

诚挚招聘

量子位正在招募编辑/记者,工作地点在北京中关村。期待有才气、有热情的同学加入我们!相关细节,请在量子位公众号(QbitAI)对话界面,回复“招聘”两个字。

量子位 QbitAI · 头条号签约作者

վ'ᴗ' ի 追踪AI技术和产品新动态

更多攻略
游戏推荐
更多+