如何使用自对弈强化学习训练一个五子棋机器人Alpha Gobang Zero
前言
2016年3月,Alpha Go 与围棋世界冠军、职业九段棋手李世石进行围棋人机大战,以4比1的总比分获胜,在当时引起了轩然大波。2017年10月,谷歌公布了新版五子棋程序 AlphaGo Zero。与击败李世石的 Alpha Go 不同的是,AlphaGo Zero 在训练过程中没有使用任何人类棋谱,一切从零开始。训练了 72 小时后,它就以100比0的成绩完胜前辈 Alpha Go Lee。虽然 AlphaGo Zero 看起来十分强大,但是实现起来并不是很困难。下面从 AlphaGo Zero 的基本原理出发,设计并训练一个五子棋机器人 Alpha Gobang Zero.
策略-价值网络
与Alpha Go Lee不同的是,AlphaGo Zero 将分离的策略网络和价值网络组合在一个策略-价值网络中。下面将从策略-价值网络的输入与输出以及内部结构等方面对其进行介绍。
输入与输出
由于围棋棋盘的尺寸为 19×19,所以策略-价值网络接受 19×19×17 的输入 stst,这个输入代表了棋盘的状态。如下图所示,stst 由当前玩家过去的8个落子位置特征平面、对手过去的8个落子位置特征平面和 1 个代表当前玩家颜色的特征平面组成。假设当前玩家使用黑棋,那么在当前玩家的每一落子位置特征平面中,玩家棋子所在位置的值为 1,其他位置的值为 0,对手的落子位置特征平面同理。对于最后一个颜色特征平面,由于当前玩家使用黑棋,所以特征平面的值为全 1。

输入策略-价值网络的 ss 经过内部的层层的处理之后,得到移动概率向量 p∈R362p∈R362和当前玩家胜利的概率 vv 。我们将 19×19 的棋盘展平为 361 维的棋盘,那么 p 的前 361 维的每一个元素 pipi 代表在361维棋盘的第 ii 维的落子概率,最后一维代表停一手的概率。我们将在蒙特卡洛树小节中介绍这两个输出的使用方法,下面来看看 AlphaGo Zero 策略-价值网络的内部结构。
模型结构
如下图所示,策略-价值网络由 1 个 Convolutional block、19 或 39 个 Residual Block、1 个 Policy Head 和 1 个 Value Head 组成,其中 Policy Head 输出 pp ,而 Value Head 输出 vv 。

Convolutional block
策略-价值网络的第一块是 Convolitional block,它由 1 个卷积层、1 个批归一化层和 1 个 ReLU 函数组成。由于输入 stst 的维度为 19×19×17,所以卷积层包含 256 个滤波器组,每个组包含 17 个 3×3 大小的滤波器。在卷积过程中,滤波器的步长为 1,同时为了保持输入的宽高不变,需要置 padding 为 1。经过卷积模块、批归一化模块和 ReLU 处理后,Convolutional block 的输出为 19×19×256 的特征图像。

Residual block
为了提升网络的特征提取能力并防止出现梯度消失问题,在卷积层下面堆叠着 19 个或 39 个 Residual block,如下图所示,每个 Residual block 由 2 个组成类似于 Convolutional block 的子模块构成,唯一不同的就是在第二个子模块的非线性激活之前加上了跳连接,使输入与批归一化模块的输出相加再输入 ReLU 函数,最终输出 19×19×256 的特征图像。

Policy head
从最后一个残差块输出的特征图像作为 Policy head 的输入,经过 Policy head 内部的卷积层、批归一化层和全连接层的处理之后,得到维度为 19×19+1=362 的移动概率向量 pp 。实际上为了计算误差的方便,全连接层后会有一个 log_softmax,得到对数概率 logplogp。

Value head
最后一个残差块的输出还会输入 Value head 中,与 Policy head 不同的是,Value head 里面有两个全连接层:第一个全连接层将输入映射为 256 维的向量,第二个全连接层再将 256 维的向量变为标量,最后经过 tanhtanh 函数将这个标量压缩到 [−1,1][−1,1] 区间,得到 vv。

Alpha Gobang Zero 的网络结构
Alpha Gobang Zero 的策略-价值网络延续了 AlphaGo Zero 的架构,由于算力的限制,对 AlphaGo Zero 的神经网络作出以下修改:
- 使用了 9×9 的棋盘,输入 stst 只保留当前玩家和对手过去3步的落子记录,去掉了代表当前玩家的颜色特征平面(主要是训练过程中发现加了这一层误差降不下来),所以 stst 的维度为 9×9×6;
- Convolutional layer 的卷积层的输出维度减少128维;
- Residual layer 只有 4 个;
- pp 的维度是 9×9=81 维,因为五子棋没有停一手的操作;
- Value head 的第一个全连接层将输入向量映射到 128 维,而不是 256 维。
蒙特卡洛树搜索
在 19×19 的棋盘上,要穷举出接下来的所有走法是不太现实的一件事,所以 AlphaGo 系列都使用了蒙特卡洛树搜索(MCTS)算法。如下图所示,AlphaGo Zero 的 MCTS 包含四个步骤,分别是:选择、拓展与评估、反向传播和演绎。下面来详细介绍 MCTS 的各过程。

选择
蒙特卡洛树的每一个节点代表一种棋盘状态 sisi(下面使用状态来命名节点),树上的每一个父节点 ss 与其所有子节点的边上都存着一些变量:
- P(s,a)P(s,a) 代表从父节点 ss 进行动作 aa 后到达子节点 scsc 的先验概率;
- N(s,a)N(s,a) 代表对子节点 scsc 的访问次数;
- Q(s,a)Q(s,a) 代表子节点 scsc 上的累计平均奖赏;
- U(s,a)=cpuctP(s,a)ΣbN(s,b)−−−−−−−−√/(1+N(s,a))U(s,a)=cpuctP(s,a)ΣbN(s,b)/(1+N(s,a)),代表在子节点 scsc 上应用上限置信区间算法(UCT)得到的值,其中 cpuctcpuct 为探索常数,它的值越大,就越有可能探索未被访问或者访问次数较少的子节点;
假设棋盘上当前落子数为 tt,棋盘状态表示为 stst,那么蒙特卡洛树的根节点就对应着这个 stst。又假设我们打算对当前局面进行 nitersniters 次蒙特卡洛树搜索,那么每一次搜索都会从根节点出发,根据 a∗t=argmaxat{Q(st,at)+U(st,at)}at∗=argmaxat{Q(st,at)+U(st,at)} 进行动作 a∗tat∗ (对应一维棋盘上的一个落点)到达子节点 st+1st+1,接着重复上述步骤直至遇到叶节点 sLsL 或者游戏结束为止。
拓展与评估
当我们在选择过程中遇到叶节点 sLsL(这个节点对应的游戏还未结束)时,先前介绍的神经网络就可以派上用场了。我们将叶节点对应的棋盘状态输入策略-价值网络,神经网络对棋局进行评估后得到移动概率向量 pp 和当前玩家获胜的概率 vv。需要指出的是,这里的当前玩家可能不是根节点对应的那个玩家,因为每进行一次选择动作,就会切换一次当前玩家。
移动概率向量 pp 将用来拓展叶节点 sLsL,pp 中的每一个元素分别 sLsL 的一个子节点的先验概率 P(s,a)P(s,a),同时我们需要将所有子节点的访问次数初始化为 0。
反向传播
在拓展与评估步骤中我们得到了叶节点对应的玩家的获胜概率 vv,所谓的反向传播,就是指将这个 vv 传播到从根节点到叶节点这一路的所有节点上(不包含叶节点),我们可以使用递归做到这一点。由于这些节点的当前玩家一直在切换,所以将 −v−v 传入递归函数。至此我们完成了一次搜索。
演绎
当我们完成 nitersniters 次搜索后,根节点的每个子节点都被访问过若干次了。接下来就根据根节点的各个子节点的访问次数 N(s,a)N(s,a),计算选择动作 aa 的概率:
其中 ττ 为温度常数。我们最后根据每个节点的 ππ 来随机选择一种动作 a∗a∗ 并在棋盘上执行。从公式可以看出,温度常数越小,就越有可能选择 ππ 最大的那种动作,即越趋近于仅利用,而温度常数越大,越趋近于仅探索。
训练 Alpha Gobang Zero
前面已经介绍了AlphaGo Zero的工作原理,下面来看看在不依赖于任何人类棋谱的情况下,如何从零训练一只能有正常人水平的 Alpha Gobang Zero。
自对弈
Alpha Gobang Zero 总共自对弈了 4400 局,我们使用自对弈来生成用于训练的数据,其中每一局的过程都是相同的:
- 清空棋盘,初始化三个空列表:
pi_list
、z_list
、feature_planes_list
,分别用存储在一局中每个动作对应的 ππ,这一局的赢家对每一个动作的当前玩家的奖赏值,及这一局中的每个棋盘状态 stst; - 将当前的棋盘状态 stst 添加到
feature_planes_list
中,并根据 stst 执行 500 次蒙特卡洛树搜索,得到 a∗a∗ 和 ππ,注意这里的 ππ 是一个向量,维数 9×9=81,代表所有动作的移动概率。将 ππ 添加到pi_list
中; - 使用 a∗a∗ 更新棋盘并判断游戏是否结束,如果还未结束回到步骤2,如果结束进行骤4;
- 根据最后的赢家计算出
z_list
中的每一个元素,计算规则为:赢家与每一个动作的当玩家相同则为 1,不同为 -1,平局为 0。由于五子棋具有旋转不变性和镜像对称性,所以将做了旋转变换和水平镜像变换的各个(feature_planes_list, pi_list,z_list)
添加到 self-play 数据集中,其中feature_planes_list
中的各feature_planes
在训练过程中将作为策略-价值网络的输入,pi_list
和z_list
的各元素将作为标签; - 结束一局自对弈。
需要指出的是,根据论文中的说法,自对弈前 30 步的温度常数 τ=1τ=1,后面的温度常数 τ→0τ→0。同时为了增加探索,在拓展步骤中需要给策略-价值网络的输出 pp 添加狄利克雷噪声,使得 P(s,a)=(1−ε)pa+εηaP(s,a)=(1−ε)pa+εηa,其中 ηa∼Dir(0.03)ηa∼Dir(0.03), ε=0.25 ε=0.25。
在 AlphaGo Zero 的 MCTS 中,为了提高搜索速度而使用了多线程。考虑到做这个项目只是出于学习目的,所以并没有将多线程添加到代码逻辑中。
训练
训练方法
当 self-play 数据集的长度超过 start_train_size
时,就可以正式开始训练了。训练步骤为:
- 从数据集中随机抽出大小为
batch_size
的样本集; - 将样本集含有的
feature_planes_list
作为一个mini_batch
输入策略-价值网络,输出维度为(batch_size, 81)
的批量 logplogp 和维度为(batch_size, 1)
的批量 vv; - 根据损失函数 l=(z−v)2−πTlogp+c∥θ∥l=(z−v)2−πTlogp+c‖θ‖ 更新神经网络的参数,其中 cc 是控制 L2 权重正则化水平的参数;
- 结束一次训练。
每当我们完成一次策略-价值网络的训练之后,就可以接着进行一局自对弈以产生新的数据,然后再进行一次训练,就这样一直循环下去。随着训练次数的增加,学习率会逐渐减小,具体变化如下表所示:
训练次数 | 学习率 |
---|---|
0-1500 | 10−210−2 |
1500-2500 | 10−310−3 |
>2500 | 10−410−4 |
有一点需要指出:Alpha Gobang Zero 的自对弈数据始终由最新的神经网络产生,而在 AlphaGo Zero 中自对弈数据是由历史最佳的策略-价值网络产生的。只使用最新模型产生训练数据的原因主要有两点:
- 像 AlphaGo Zero 那样频繁评估以决定历史最优模型的操作是很耗时的;
- Deep Mind 后来在 Nature上 发表了一篇论文《Mastering Chess and Shogi by Self-Play with a General Reinforcement Learning Algorithm》,里面的 Alpha Zero 也只使用最新模型来产生数据。
虽然不使用历史最优模型来产生数据,但是根据 AlphaGo Zero 的评估思想,我们会定期让当前模型和历史最优模型进行 PK,如果当前模型的胜率超过 55%,就将历史最优模型更新为当前模型。
训练结果
最终的损失曲线如下图所示:

可以看到即使训练到了后期,损失曲线还是在振荡,有一点原因可能是:受到现实条件的约束,训练的时候并没有连续自对弈 4400 局下来,而是每天训练上 1000 局左右就停掉,好让电脑可以休息上一段时间,结果在接着训练的时候忘了调整学习率了,直至第3300局才意识到这一点。
虽然损失曲线一直在振荡,不过在和 Alpha Gobang Zero 对弈的过程中可以明显感受到它的水平在一直提高,自对弈到 3000 局的时候就已经有正常人的水平了。下面来看些 Alpha Gobang Zero 自对弈的棋谱:



可以看到训练了 800 次之后,AI 还是有些进步的,知道开局的时候该往中间走了,而且在对方快连成五颗的情况下也知道要去堵他了。但如第二张棋谱所示,在白方4、6、8已经连成3颗时,黑方还不会去堵它。而且在已经连成4颗棋子的情况下,白方居然没有绝杀黑方,而是下了 12 这个棋。对比第一张图就可以得知:此时的AI还只会下前几手,后面表现的越来越差,几乎是在乱走,所以没有绝杀对方。
可以看到训练到4400局之后,AI 已经掌握了开局和攻守的诀窍。一开始双方就挨得很近,试图阻止对方连成3颗。在黑方的5、7、9连成3颗时,白方也及时堵住。在白方8、14、10连成3颗时,黑方暂时没有理他,而是在左下角下了15,与自己的1、3、13连成了4颗,企图先下手为强。在白方将其堵住之后,黑方才在右上角下了17将其堵住。在此之后双方交替出现连续3颗的情况,但是都被对手及时堵住了,说明AI已经学会了堵3颗的技巧。最后几步双方都杀的很急,不过感觉白方确实要弱一些,最终黑方取胜。
最后给出一张自己被 AI(白方)击败的游戏截图:

写在最后
Alpha Gobang Zero 的实现原理和过程已经介绍完毕,具体代码请移步 github,以上~
转 https://www.cnblogs.com/zhiyiYo/p/14683450.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2021-07-08 使用gitlab自带的ci/cd实现.net core应用程序的部署
2021-07-08 详解C++中的多态和虚函数