车栗子 发自 凹非寺

量子位 出品 | 公众号 QbitAI

要了解机器学习、了解神经网路,一种非常有趣的方式,就是找个小游戏,自己实现一下。

游戏里的每一位AI玩家 (智能体) ,都是一个小小的神经网路。

△ 训练前

一开始,它们什么都不懂,刚开局就GG。但有了进化演算法 (Evolution) ,AI可以在一代一代更迭中,掌握强大的游戏技能。

△ 成效显著

比如,在这个赛车游戏里,萌新车手跑出去不久便撞上了马路牙子。不过,用不了几代进化,就有AI车手可以毫发无损地跑完一周。

进化的赛车手

AI赛车手如何快速炼成?

首先,要有一个庞大的车队。

赛车AI的开发者,是一位叫做Johan Eliasson的程序猿。他的训练计划里,有650辆车同时训练。

第二,AI开车需要一些空间信息。

这5条黄线代表5个距离,指示离马路牙子还有多远;

除此之外,AI还需要知道自己当前的速率,以及方向

这样算来,神经网路一共会接受7个输入

第三,就是让神经网路不断进化。

输入七个值,处理一下,输出两个值

一个代表方向盘,按照数值大小,分为左转右转方向不变三种操作;

一个代表油门/刹车,按照数值大小,分成加速减速速率不变三种操作。

最开始,谁不知道游戏规则,输出值很随机。

△ 第一代,死得快

650辆车冲出去没多久,路边就出现了大批车辆的尸体。

但重要的是,依然有硕果仅存的汽车,不止通过了第一次的右转考验,也机智地发现下一处弯道应该猛烈左转

虽然,它并没有成功掉头,但依然是下一代的希望:进化演算法就是要在每一代里,选出最优秀的一只或者几只智能体,繁育出色的后代。

(这里,程序猿没有找到一种很好的方法,让系统自动选择出优质的个体,于是就手动马克了,反正也没有训练很多代。)

所以,第二代的650辆车,全是这一只智能体的子嗣,各自有些轻微的变异:神经网路的权重发生小小的变化。

变异是为了保持车队的多样性,期待从中生产出更加优秀的个体,繁育下一代。

△ 第二代,马克三只

由于继承了优良传统,第二代汽车大部分都完成了第一次右转,还有一小部分掉头成功。

这一次,把冲到最前面的3辆车马克一下,它们差一点就能达成两次连续掉头的S形操作。

既然,第三代的父母有3位,那么繁育过程中除了变异之外,还涉及杂交:就是把不同的神经网路揉到一起:

第三代的表现更加精进,有的智能体完成了多次连续转弯,胜利在望。

第四代,便有智能体跑完一周,值得纪念:

程序猿说:这效果比想像的好多了,之前我还有点怀疑,现在只能说机器学习好厉害。

第四代车队中的佼佼者,已经不太撞到路肩,只是速度还很慢,就像一大波僵尸。

车队的主人表示,后面的主要任务就是训练速度。毕竟,这是赛车。

到了第十代,速度已经有了明显的提升,不过依然不算快:

△ 第十代

不知训练了多少代,现在把每一代的优质选手放在一起跑。你看,好快:

△ 这才是赛车

成果斐然之余,程序猿还请大家注意一个重点:

AI不知道自己在开车,不知道有跑道。它只接收七个输入的数值,不了解它们代表什么意思,也不知道自己输出的每一个决策,产生了怎样的效果。

就算这样,它还是学会了怎样在游戏世界里更好地生存。

不想开车?还有其他游戏

虽然,赛车游戏的视频,没有讲到程序猿用了怎样简单的神经网路。不过,可以通过另一个简单的游戏,来感受一下:

Flappy Bird

这个HTML 5实现, 也是进化演算法和神经网路共同的结晶。而且开源了。

它的作者ssusnic说,神经网路部分,用的是突触神经网路 (Synaptic Neural Network) 库。

没有650辆车那么多,种群里只有10只小鸟。每一只,都是一个三层的神经网路:

输入层:两个神经元

隐藏层:六个神经元输出层:一个神经元

输入两个值,代表小鸟当前距离下一个障碍物的相对位置,分成水平距离 (x) 和竖直距离 (y) 。

经过隐藏层的处理,输出一个0-1之间的数值,决定下一步要不要扇翅膀:

如果大于0.5,就扇翅膀,反之就不操作。

△ 第一代

第一代的小鸟,都是随机神经网路 (Random Neural Networks) ,集体见光死。

全部阵亡之后,要选出四只最优质的小鸟去繁殖。问题来了,肉眼看去相差无几,要选哪几只?

并不需要手动选择,而是用一个适应度函数 (Fitness Function) :

适应度 = 小鸟存活的最远距离 - 小鸟到下一个障碍物的距离

△ 这里,讨论水平距离

适应度分数排名前四的小鸟,按下面的规则繁殖10个后代:

· 前两名,杂交出一个后代

· 4只中随机选两只,杂交出三个后代· 4只中随机选两只,分别直接复制,生成两个 (和上代一样的) 后代· 给每个后代加入一些变异

就这样,来到第11代,已经有小鸟飞出很远且毫发无伤:

第23代,完全碾压,可以换个游戏了:

吃豆豆

当然,不是所有游戏都像小鸟扇翅膀这样简单。

吃豆人 (Pacman) 就是个更复杂的栗子。比如,前面要躲避的障碍物都是静止的,Pacman的敌人是移动的,穷追不舍。

重点是,为了抓到Pacman,四个敌人还有各自不同的运动规则:

红色鬼,直接瞄准Pacman的位置进发;

粉色鬼,瞄准Pacman前方的第四格;蓝色鬼,利用Pacman和红色鬼的位置来搞伏击;橙色鬼,原本和红色鬼一样,但当它和Pacman的距离近到8格以内,就会朝一个角落退缩,那是它出发的地方。

并且,Pacman吃到无敌大豆豆的时候,敌人还会从追击模式转成逃跑模式。

复杂的游戏,自然也需要更加精密的演算法,来帮智能体进化。

名叫Code Bullet的程序猿,就用了一种叫做NEAT的神经进化演算法。

这种方法很特别,神经网路在迭代过程中,不止权重会变化,网路结构也会变复杂

而网路结构越复杂,就可以支持越复杂的行为,让Pacman在险恶的世界里存活。

虽有强大的演算法,训练还是要由浅入深

先不加无敌大豆豆,也不加敌人,给AI一个简单的世界。

这样它就能吃光所有豆豆?不存在的。

第一代最优秀的智能体,也只会右转不会左转,还把自己困在一处,永远走不出去。

第二代,有的选手已经学会了左转,但依然会困住。

……

第十八代,眼看快要吃光豆豆,AI还是困死了自己。

在平静中训练20代之后,把敌人放出来,AI又要从手忙脚乱开始重新适应:

不过好在,智能体学以很快便学会了躲避敌人:

又过了40代,是时候加入无敌大豆豆了。

这一层防护令AI如有神助,就像在经历了40代卧薪尝胆之后,开启了复仇模式,疯狂追击。

终于,有智能体在第七十三代通关成功:

视频的画外音里,程序猿的声音都激昂了起来,这场战役的胜利他期待已久。

除了发表获奖感言,谢过各路亲友的支持,他也没忘在GitHub上分享代码 (传送门见文底) 。

动手吧,少年

同学,你也去搭个神经网路打游戏吧,呆萌的游戏也能打出热血的气质。

以后,神经网路可能就是你女朋友了。

(友情提示:虽然赛车游戏没有开源代码,但OpenAI Gym里面有赛车场,也可以去试试。)

赛车游戏视频传送门:

youtube.com/watch?

OpenAI Gym赛车场:

Gym: A toolkit for developing and comparing reinforcement learning algorithms

Flappy Bird代码传送门:

ssusnic/Machine-Learning-Flappy-Bird

Pacman代码传送门:

Code-Bullet/PacNeat (NEAT演算法)Code-Bullet/PacmanGame (搭建游戏环境)

量子位 · QbitAI

?? ? 追踪AI技术和产品新动态

戳右上角「+关注」获取最新资讯↗↗

如果喜欢,请分享or点赞吧~比心?


推荐阅读:
相关文章