MCTS的一些简单理解/扩展及其实现细节

主要上学期写NoGo的时候这个算法基本没写对

这学期写Hex的时候,大概弄明白了之后就想简单记录一下

(主要面向PKU的两门屑课的屑作业,说的就是你!计概!AI基础!

主要是写给我的女孩看的,希望她能看懂,能会写,不要比别人差


简单理解

mcts算法基于很简单的蒙特卡洛算法,即随机撒点,利用概率期望收敛的原理来求近似精确解

然后还要借助一个公式:UCB公式

\[UCB\_Value(u, a) = \frac {Q(u, a)}{N(u, a)} + c * \sqrt{\frac{ln\ N(u)}{N(u, a)}} \]

其中\(Q(u, a)\)\(u\)状态下走\(a\)这一步的所有后续迭代中,多次模拟的总收益,\(N(u, a)\)\(u\)状态下走\(a\)这一步的总次数,\(N(u)\)为走到\(u\)状态的总次数

这个c是一个常数,表示exploration即探索指数

MCTS的算法是基于搜索树展开的,一个状态,下一步棋,就得到它的几个子状态,也就是子节点

我们以当前棋盘状态为根节点开始展开,在一定时间内往后探索许多步,然后根据搜索结果确定当前状态的下一步下在哪里

可以看到跟"走一步看五步"的思路有些像


我们每次迭代,也称之为tree policy,是在树上找一个还没被扩展过的叶子进行扩展

接下来,在这个叶子所处的棋局状态下进行随机落子直到终局,这一步,称之为default policy

在随机落子之后,我们会得到一个结局,也就是哪一方赢了,然后我们顺着这个叶子不停地往父亲跳(也就是回到自己的上一步状态),往上更新

如果根据当前状态落子方和最后胜负方进行更新收益,打个比方,比如当前是下了一步白棋,最后白棋赢了,那么这一步收益加一,反之不加。(但是不管有没有收益,次数都要加一)

这就是back propagate 反向传播


那么我们还有很大的疑问,回到tree policy处,你说的轻巧,那怎么找这个扩展的叶子呢

简单思考,我们需要从最初状态(树根)往下走

1'如果当前的节点所有儿子都被访问过(当前状态的所有子状态都被访问过),那么我们就会选一个儿子走下去,然后继续这个过程

2'不然我们就可以随机选一个没访问过的子状态,它就是我们要的叶子啦

那1'中这个儿子怎么选呢?

这个时候就要请出UCB公式啦

好像这个算法又被称作UCT,即UCB for Tree

这个算法的灵魂就在利用这个公式进行扩展的儿子的选取

当我们面临选择儿子的时候,我们选这个UCB值最大的,准没啥大问题!

它可以帮助我们的蒙特卡洛过程尽快收敛


于是我们的框架就搭建完啦

利用UCB公式选叶子(Tree Policy) -> 随机落子直到终局(Default Policy) -> 把随机落子的结果向上反向传播更新节点权值(Back Propagate)

我自己写的样例代码放ubuntu paste上


还有一些实现的小细节

1' UCB公式里常数c怎么选取?

可以看到,c越大我们的搜索树会越宽,c越小我们的搜索树会越深

按理说,c大一点会很好,可以有更多选择,能选出更好的点

但是我们的算力是有限的,尤其是通常只有一秒供计算(例如botzone上)

那此时,我们应该尽可能搜的深,确保选出相对好的点

于是我们选择较小的c效果会很不错

2' 搜索结束后我们怎么做出决策得到下一步该落在哪里

不是直接利用UCB公式!(我因为这里写错吃过不少亏)

而是把探索指数设为0,也就是只考虑胜率

这是容易理解的,因为UCB值最高只意味着我们最想探索它,而不是它一定最好

3' 一些卡常加速技巧

为了加快速度,建议用指针写搜索树代替map存状态,因为一个节点存的信息多,会比较大,hash/比较起来会很慢

棋盘不要存在结点内,因为复制很慢

可以每次搜索都把根节点处的棋盘复制一遍,然后跟着搜索的过程一步步带下去



一些扩展

我的女孩暂时还用不到

但简单更一下

我翻阅论文学到的扩展主要是三个方面下功夫


1' 在default policy上

(1) 在simulation中加入一些专家策略来加快蒙特卡洛方法的收敛

但一定要注意随机性,不然会影响效果

专家策略可以是一些简单的落子策略

在Hex中,我用的方法是加入bridge-fix

(把两个横纵坐标各差1的且中间没有对方棋子的一对棋子,叫一个桥,可以看到无论对方在中间哪一个位置落子,我方在另一个位置落子是依然能把这两个棋子连起来的)

(在模拟中,当一方破坏了另一方的桥时,另一方立即随机选一个被破坏的桥补上)

(这个策略的效果非常好,在最终测试中,我仅凭在MCTS上加入这一个策略,获得了rk5)

关于专家策略,还可以对棋盘利用预学习好的估值函数,得到下一步的所有情况估值,用softmax正则化一下(随机性),依概率选择下一步

这里的估值函数可以利用较简单的线性算法,也可以使用神经网络训练

(2) 在simulation中加入对整体输赢情况的判断

即利用神经网络/专家策略,在simulation前简单判断当前叶子的输赢概率

将这个概率以一个权重混合simulation的结果,作为本次simulation的reward向上更新

参考AlphaGo就是在default policy中同时加入了使用神经网络的(1)和(2)


2' 在tree policy里,选择儿子节点时

普通mcts是纯利用UCB公式选择儿子节点

我们考虑提升UCB公式的效果

一种方式是

利用预准备好的一些规则初始化UCB公式里的Q和N

比如说:使其与父亲的父亲(即上一步)一致,先手时给它赋一个不为0的初值....

感觉有点偏人类智慧了

另一种方式是

我们考虑在UCB公式时增大数据量

这个时候就可以加入RAVE值(Rapid Value Estimation)

也就是考虑在棋局开局时,我们的一些落子之间,先后顺序意义不会很大

于是我们在back propagate时,把当前点的所有子节点中和当前步相同的动作,都用来更新当前点的rave值

rave值如何计算可以类似UCB公式

然后我们利用一个逐渐衰减的值来平衡rave和ucb值

比如利用这个函数的值\(\beta = \sqrt{\frac{K}{3N(s)+ K}}\),其中K是一个常数,N(s)是棋盘当前已经走了多少步

虽然我两次使用RAVE效果都很差,不知道是不是我不会调超参数

加入rave值的完整版本在这里(虽然效果不太好,但我写都写了,就把它贴上了)


3' 在最终作决策时

加入一些人类智慧/神经网络对落子价值进行估值(有点像default policy里的)

在最终决策时按比例与模拟时得到的胜率权值求和,再计算最大值来决定下一次落子的位置

好像本次比赛有人加入了二距离估值啥的,不是很懂,应该是有用的


最后,我用来比赛的版本在这里(只加入了fix-bridge策略),仅供参考!

posted @ 2023-03-13 16:30  deaf  阅读(761)  评论(1编辑  收藏  举报