
- 这次选择了游戏“蛇”。
- Go网络的库已创建。
- 找到了学习的原理,取决于记忆的“深度”。
- 为开发人员之间的游戏编写服务器。
游戏的精髓
也许许多人还记得游戏“ Snake”,这是诺基亚手机上的标准应用程序。 其本质是这样的:一条蛇在田野中移动,如果找不到食物,蛇会减少,如果发现食物,蛇会增加。 如果蛇撞上障碍物,它就会死亡。
我对规则进行了一些更改:如果蛇崩溃,它不会死,而只是停下来,继续减少。 此外,蛇可分为两半。 如果蛇体内只剩下一个细胞,而十步之内都找不到食物,那么她就会死去变成食物。
我们将训练控制蛇的机器人。 如果蛇分裂,则机器人将控制另一条蛇,而蛇又可以分裂。
以网络生物学家Mikhail Tsarkov的蛇实验为基础。
神经网络
作为任务的一部分,用Go语言编写了神经网络库。 在研究神经网络的工作时,我使用了
foo52ru视频日记和Tariq Rashid的书-创建神经网络。
CreateLayer(L []int)
函数
CreateLayer(L []int)
创建具有所需层数和层大小的神经网络。 除最后一层外,在每个层上都添加了位移神经元。 我们将数据馈送到第一层,然后从最后一层获得结果。
一个例子:
CreateLayer([]int{9, 57, 3, 1})
在这里,我们创建了具有九个输入的神经网络。 两个隐藏层分别为57和3个神经元和一个神经元以获得结果。 位移神经元通过加号自动添加到我们设置的神经元中。
该库使您可以:
- 将数据提交到网络输入。
- 通过访问最后一层获得结果。
- 提出正确的答案,并通过调整关系的权重进行培训。
初始键权重由接近零的随机值给出。 为了激活,我们使用了物流功能。
机器人训练
该机器人在入口处收到一个9x9的正方形场,中间是蛇的头。 因此,我们的神经网络将有81个输入。 馈送到输入的单元格顺序无关紧要。 在培训期间,网络会“自行解决”自身所在的位置。
为了指示障碍物和其他蛇,我使用了从-1到0(不包括在内)的值。 空单元格的值为0.01,食品为0.99。
在神经网络的输出处,使用了5个神经元进行动作:
- 沿X轴向左移动;
- 在右边
- 沿y轴;
- 下来
- 一分为二。
机器人的运动由神经元决定,神经元的输出值最大。
步骤0。随机化器
首先,创建了一个机器人随机程序。 所以我称一个随机行走的机器人。 有必要验证神经网络的有效性。 通过适当的训练,神经网络应该可以轻松击败它。
第1步。没有记忆的学习
每次移动后,我们都会调整输出神经元的键值权重,该值表示最高值。 我们不接触其他输出神经元。
给出了以下值进行训练:
- 找到食物:0.99
- 朝任何方向移动:0.5
- 失去身体细胞而找不到食物(为此提供了10个动作):0.2
- 站立不动(撞到障碍物或卡住):0.1
- 站立不动,拥有一个身体细胞:0.01
经过这样的训练后,这些机器人很快就开始击败随机发生器,我设定了任务:创建可以击败随机机器人的机器人。
A / B测试
为了完成此任务,创建了一个程序,根据神经网络的配置将蛇分为两部分。 在野外,生产了每种配置的20条蛇。
由一个机器人控制的所有蛇都具有相同的神经网络。 在他管理中的蛇越多,他们越常面对不同的任务,训练的速度就越快。 例如,如果一条蛇学会了避免死锁或在死胡同时分裂成两半,则该机器人的所有蛇会自动获得这些技能。
通过更改神经网络的配置,可以获得良好的结果,但这还不够。 为了进一步改进算法,我决定使用内存进行多次移动。
第2步。学习记忆
对于每个机器人,我为8个动作创建了一个内存。 机器人建议的字段状态和移动记录在内存中。 之后,我对移动之前的所有八个州的权重进行了调整。 为此,我使用了一个校正因子,与行进深度无关。 因此,每一步都导致权重的调整不是一次而是八次。
不出所料,记忆机器人开始迅速击败未经记忆训练的机器人。
步骤3.根据存储深度减小校正系数
接下来,我尝试根据存储深度减小校正因子。 对于最后一步,建立了用于调整权重的最大系数。 在此之前的过程中,整个内存中的校正因子都会减小,依此类推。

校正系数的线性降低取决于内存的深度,这导致新的机器人开始击败使用单个系数的机器人。
接下来,我尝试使用校正因子的对数减少。 该系数降低了一半,具体取决于每次移动的存储深度。 因此,“很久以前”做出的动作对学习的影响要比“新”动作对学习的影响小得多。
校正系数对数降低的机器人开始击败具有线性关系的机器人。
机器人服务器
事实证明,提高“泵浦”机器人的水平可能是无限的。 我决定创建一个服务器,开发人员可以在此服务器上相互竞争(无论使用哪种编程语言),从而为Snakes编写有效的算法。
协议书
要进行授权,您需要将GET请求发送到“游戏”目录并指定用户名,例如:
.../game/?user=masterdak
您需要指定站点的地址以及服务器部署所在的端口,而不是“ ...”。
接下来,服务器将以JSON格式发出指示会话的响应:
{"answer":"Hellow, masterdak!","session":"f4f559d1d2ed97e0616023fb4a84f984"}
之后,您可以在野外请求地图和蛇的坐标,并在请求中添加会话:
.../game/?user=masterdak&session=f4f559d1d2ed97e0616023fb4a84f984
服务器将显示以下内容:
{ "answer": "Sent game data.", "data": { "area": [ ["... ..."] ], "snakes": [ { "num": 0, "body": [ { "x": 19, "y": 24 }, { "x": 19, "y": 24 }, { "x": 19, "y": 24 } ], "energe": 4, "dead": false } ] } }
区域字段将使用以下值指示比赛场地的状态:
0
接下来是一个由蛇控制的数组。
蛇的
身体位于
身体阵列中。 如您所见,开始时蛇的整个身体(包括头部-第一个单元格)在相同的位置“ x”:19,“ y”:24。这是由于在游戏开始时,蛇从洞中出来,这是由场地上的一个单元定义的。 此外,身体和头部的坐标将不同。
以下结构(Go中的示例)定义了所有服务器响应选项:
type respData struct { Answer string Session string Data struct { Area [][]int Snakes []struct { Num int Body []Cell Energe int Dead bool } } } type Cell struct { X int Y int }
接下来,您需要通过将
移动添加到GET请求中来发送蛇执行的
移动 ,例如:
...&move=u
u-表示命令;
d-下;
l-左侧;
r-在右边;
-减半
多条蛇(例如,七条蛇)的命令如下所示:
...&move=ud/urld
一个角色-一个团队。 答案应包含对您所控制的所有蛇的命令。 否则,某些蛇可能不会收到命令,并将继续执行旧的动作。
该字段以150 ms的间隔更新。 如果60秒钟内未收到命令,则服务器将关闭连接。
参考文献
为了避免影响效果,对于那些有兴趣的人,请给我发消息。 作为响应,我将发送服务器的IP地址。 或者,您可以使用程序的源代码部署服务器。
我既不是编程专家,也不是神经网络专家。 因此,我会犯错误。 我按原样传播代码。 如果经验丰富的开发人员会展示出所犯的错误,我将感到非常高兴。
- 神经网络库以及游戏“井字游戏”
- Snake Master-服务器
- 蛇大师-机器人
- 蛇世界2
UPD临时上传
服务器IP地址 。 现在,那里只启动了一个漫游器随机化程序(SnakeBot0)。 我希望服务器崩溃不会那么快。