五子棋AI边缘试探
开头声明:
关于机器学习方面的代码是完全复制自:AlphaZero实战:从零学下五子棋(附代码)-知乎,我就是用qt写了一个简易的GUI,然后花了60+块大洋租了一台阿里云的服务器,跑了3K轮的19*19棋盘下的人工智能。
初来乍到,仔细研究了MIT许可证的限制具体应如何执行,但如有侵权还望指正。
起因
作为一个大一的新生老油条,这学期选了唯一的一个院级选修课,作为这门课的大作业之一,想做点高端的,又没什么技术,所以只能去“抄”了。而做出一个类似于AlphaZero一样的人工智能是我自从其打败柯洁以来的想法,一直都没有下决心,所以这次就捎带脚了。
经过
代码找到了Github-AlphaZero_Gomoku,原版提供了一个简单的CUI方便人机对战,在两天苦读人工智能相关代码无果(我太菜了)之后,只能去研究一下封装类的接口,然后写个GUI(按照要求)包装一下。
GUI
本着“学就学点有用的”的大原则,我查看了一下几个比较经典的GUI库,觉得PyQt还是很有潜力的,毕竟这几年来出了好几个版本,依旧在更新,网上反应也不错,不想tk一样好多坑,就选了PyQt。
随便找了个教程,随便粘过来一个空白窗口的代码,F5,一次通过,窗口映入眼帘,不错。教程地址:PyQt5教程
PyQt的整体需要自己敲代码的部分就是继承了一个QT的类,在这个类里面定义各种函数,就相当于监听各种事件,当然也可以定义一些正常的函数用来自己用。
在__init__
的过程中主要用来声明各种类里面的需要用到的变量,然后调用真正的initUI
函数进行窗口的渲染。
由于五子棋的棋盘,我决定使用画布进行绘制,所以在initUI
中声明了窗口的大小之后,标题内容之后,使用self.show()
然后会调用paintEvent
函数。由于之后需要频繁的画线,所以就直接把画棋盘声明为一个单独的函数。
1 | qp = QPainter() |
创建画笔 设置画笔(边框) 设置笔刷(填充) 画一个方框
当然还有画圆,画线,画点的方法,不再赘述。
后来一个可以点击的棋盘就这样诞生了。
人工智能模块调用
经过几个小时的阅读,原项目中的架构大概是这样的:
MCTSPlayer中有着标准的接口,便于Game进行调用大致有几个
set_player_ind
设置玩家编号reset_player
重置玩家状态get_action
取你下在哪里
这下我就遇到了一个严重的问题,因为我需要在这个函数运行结束之后返回人下在哪里了,然而GUi又是事件驱动的,只有点击之后方法才会被调用。经过一番百度之后,我进行了下面几个尝试:
关于调用问题的解决
协程——yield/send 以及 async/await (失败)
这两个方式看似非常美好,但是遗憾的是,get_action
的调用是在Game中,而且我并不能在get_action
中调用一个函数等待一个还没运行的函数的执行。在大量的尝试都宣告失败之后,放弃这个方法。
线程切换——greenlet (成功?)
1 | from greenlet import greenlet |
在这个简单的例子中我们可以看到 test1
执行了一般就通过gr2.switch()
去执行test2
,当test2
中执行gr1.switch()
还会从之前gr2.switch()
离开的地方继续运行。这样我就有了一个大胆的想法。
GUI画完之后调用AI,AI请求落子的时候调回来GUI处理用户操作,再调回AI返回落子信息。
这是一个不完美的解决方案,因为在线程从GUI切换到AI的时候,GUI就会处于一种无法响应的状态。只要不在不能落子的时候点击棋盘是看不出来的。
多线程+线程间通信 (未尝试)
这个也是我当时的想法,但是由于时间比较紧张就没有考虑,以后处理吧。
模型训练
光有枪没有蛋肯定是不行的。在原来的代码里,作者十分良心的写了5种价值网络的写法,在这里我研究了一下TF的表示无法理解之后,就直接采用了原版使用的Theano and Lasagne
作为基础的实现版本。
因为训练基于AlphaZero的理论,所以不需要外界提供数据。这就是所谓的:“两个萌新通过随机落子日夜切磋,两周之后终成大师。”的故事吧。当然我没有Google一样强大的TPU进行计算,两周肯定远远不够,再加上我开始的时候就剩下一周了,emmmmm
经过5天的训练,现在训练到3K轮了,基本已经直到连5个棋可以赢的地步,但是还不知道如何去下套,去劫杀。但是也懂得好多套路。现在的损失刚刚讲到二点几,可能还是需要自己训练一下吧。回望500轮那个智障的AI,连怎么赢都不知道,现在已经这样了,还是挺有意思的。
最终结果:
结果
写这个文章的目的就是在正式开始写实验报告的时候有点13数,顺便记录一下自己的工作。就是这样。Githu仓库