在本篇(piān)博文中,你(nǐ)将会了解并实现AlphaZero。AlphaZero是一(yī)个令人大开眼界且超乎(hū)寻常的(de)强化学习算法,它以绝对的优势战胜(shèng)了多(duō)名(míng)围棋以及国际象棋冠军。本文(wén)将(jiāng)会带你使用AlphaZero来解决一(yī)个益智小游戏(xì)(Dots and Boxes)并将其部署(shǔ)成一个(gè)纯Javascript构建(jiàn)的Web应用。
AlphaZero最(zuì)关键也是(shì)最令(lìng)人诧异(yì)的(de)一点,就是(shì)其能够在不依赖于(yú)外部先验知识的情(qíng)况下在棋盘类游戏中获得(dé)超越人类的(de)表现(xiàn)。AlphaZero通过自我博(bó)弈汲取(qǔ)经验知识来不断精通游戏。
我们会借助于Github上由Surag Nair开发的一个“简化后的、高度(dù)灵活的、经过注释(shì)的且易于理解的”Python版AlphaZero来进行该项目。
你大可以先去这里玩一(yī)玩这个游戏(xì)。而(ér)Web应用以及具体(tǐ)的Javascript实现代码可以在这里获取得到。这份代码(mǎ)是从该(gāi)Python实现中移(yí)植过来的。
https://carlos-aguayo.github.io/alphazero/
有关AlphaZero的原理,你可以阅读这篇由Silver,David等人撰写的论文:“Mastering the game of Go without human knowledge” nature 550.7676 (2017): 354–359.
Dots and Boxes小游戏(xì)
Dots and Boxes是一个常见的儿童益(yì)智游(yóu)戏(xì),不过其具有令人讶异的复杂度。
该(gāi)游戏中,两名(míng)玩家轮(lún)流在(zài)两个(gè)相邻点之间放置一条(tiáo)水平(píng)或垂直线。如(rú)果某个 1×1 的(de)小正(zhèng)方形的(de) 4 条边都被连(lián)上了,那么补齐这个小方块的一方就获得 1 分(fèn),得(dé)分的玩家被奖(jiǎng)励多走(zǒu)一步,再(zài)连一(yī)条线。当棋(qí)盘(pán)已满时(shí),游戏(xì)结束,并(bìng)且(qiě)得分最(zuì)高的玩家获胜。
(译(yì)者注:这个游戏(xì)相(xiàng)当有意思,建议先去玩(wán)玩(wán)看,点这(zhè)里。能不能战(zhàn)胜AlphaZero就看你(nǐ)了!)
人工智能与棋盘游戏
机(jī)器(qì)是否能够产生智(zhì)能(néng),我们(men)已经为此思(sī)考了(le)很久很(hěn)久。那(nà)么,该如何验证(zhèng)机(jī)器具有智能呢?一个常(cháng)用方法就是玩棋(qí)盘(pán)游(yóu)戏,比如国际(jì)象棋(qí),看看(kàn)其是否(fǒu)具有(yǒu)超人的(de)能力,甚至击败(bài)世界冠军。
1957年,Herbert Simon预言计算机系(xì)统能够在十年内击败国际(jì)象棋冠军(jun1)。虽说实际上花的(de)时间长了点(diǎn),但是在1997年5月(yuè),计算(suàn)机击败了当时的国际象棋冠军——Garry Kasparov。
(译者注:战胜Kasparov的机器被命名(míng)为DeepBlue,意为“深蓝”)
尽管这一(yī)里程碑事件意(yì)义非凡,但人们仍可以(yǐ)争论这一计算机系统是(shì)否“智能”。
这(zhè)一类计算(suàn)机(jī)系统由以下三个(gè)组件构(gòu)成:
1. 人为定义(yì)的评价函(hán)数;
2. 博(bó)弈(yì)树搜(sōu)索算法;
3. 极为强悍的硬件设备(bèi)。
评价函数会将棋盘盘面作(zuò)为输入并输出该盘(pán)面的“价值”。高价值(zhí)表示当(dāng)前玩家处(chù)于非常有利的位置。例如,在国际象(xiàng)棋(qí)棋盘上,玩(wán)家即将进行“将死(sǐ)”时就会对应一个非常高的值。
博弈树搜索算法(fǎ)(比如 Minimax)在所有可能的走棋中进行搜索(suǒ),寻找那些(xiē)能够确保(bǎo)得到高价值棋盘(pán)盘面(miàn)的路径。对于那(nà)些已经明知不可能有(yǒu)效的(de)路径(jìng)可以直接(jiē)放弃搜索,从而使算(suàn)法变(biàn)得更有效率。这就是(shì) Alpha-beta剪枝 的作用。
最后,搭配上异常强悍的硬件,你就将拥有一台(tái)能够打(dǎ)败国际(jì)象棋世(shì)界冠军(jun1)的机器。
问题在哪儿?经验丰富的(de)棋(qí)手(shǒu)人(rén)为地精(jīng)心调制(zhì)这些评价(jià)函数。这些(xiē)计算机系统还依赖于(yú)一本本记录着最佳(jiā)走棋的开局棋谱。游戏中局(jú),还(hái)会用到通过(guò)研究大师(shī)们的博弈而精心(xīn)构造的评价函数(shù)。这些函数还会经由象棋大师们进一步的(de)优化调整。
例如,我们完全就(jiù)可以为 Dots and Boxes 构造一个评价(jià)函数。一个(gè)合理而(ér)直接的选择就是做一个得分的(de)比较。得分的(de)正向差值越大,游戏(xì)盘面就对我们(men)越有利。大多数情况下,这是可行的。然而,在 Dots and Boxes 中,就像许多其他棋盘类(lèi)游戏(xì)一样,最佳的走法可能(néng)需要牺牲短期利益(yì)来换取长期利益。在 Dots and Boxes 游(yóu)戏(xì)中,有时最好不要急于得分并获得额外(wài)先手,相(xiàng)反,要迫使对(duì)手走某一步棋。因此,我(wǒ)们必(bì)须考虑大量复杂场景(jǐng)并精心(xīn)调制评价函数(shù)!
击败Kasparov的(de)评价函数需要识(shí)别多达8000个盘面特征(zhēng)!而且其中(zhōng)绝大多数都是(shì)手动描述并调整的!
所以,倒也不是贬低(dī)这(zhè)个击败国际象棋世(shì)界冠军重要里程碑的意思,只是,需(xū)要顶级玩家来(lái)定(dìng)义(yì)这些(xiē)计算机的行为并手动(dòng)调整如此多的变(biàn)量(liàng)实在是有(yǒu)够折腾人的。
AlphaZero是什么?为何它如此(cǐ)令(lìng)人心(xīn)潮澎(péng)湃(pài)?
AlphaZero是(shì)首个能够(gòu)在国际象棋(qí)、围棋等游戏中达(dá)到超越人(rén)类水平、击败世(shì)界冠军(jun1)的(de)计算机系统(tǒng),且(qiě)它仅依赖于游戏规则,无需任(rèn)何人类先验知识。
仅凭给定的游戏规则,AlphaZero即可进行自我博(bó)弈。逐步习得游戏策略与技巧(qiǎo),很快即(jí)可获得超(chāo)人的表现。
像DeepBlue这样的系统会需要国际象(xiàng)棋专家的协助(zhù),而AlphaZero却是凭借自我博弈来变强大(dà)的。不单单是(shì)在国际象棋(qí)上,哪(nǎ)怕是围(wéi)棋,AlphaZero同样表现出超越人类的强大统治(zhì)力。考虑到围棋相较(jiào)于其他棋盘(pán)游戏(xì)更大的博弈空间等因素,对计算机来说,围棋是个极为复杂的游戏。
人类从几千年来数百万(wàn)次的博弈中方才积累了诸如围棋(qí)和(hé)国际象棋等游戏(xì)的技艺,而AlphaZero,一个(gè)仅使用游戏(xì)规则信(xìn)息的算法,却能够在几(jǐ)天时间内重新寻获(huò)这些知识并发现新(xīn)的游戏(xì)策略。
甚至还有一(yī)部(bù)关于它的纪录片。
(译者注(zhù):这部(bù)纪录(lù)片很值得一看,无法访问YouTube的(de)同学可(kě)以在B站观看,链接在此。不过需要注明的是,本纪录片中(zhōng)实际(jì)上使用(yòng)的是AlphaGo算(suàn)法(fǎ),而非AlphaZero,准确来说,AlphaZero是AlphaGo的(de)进(jìn)阶版本,全名为AlphaGo Zero。纪(jì)录片中与李世石博弈的AlphaGo在跟AlphaGo Zero 博弈时,0-100全负,并且(qiě),AlphaGo Zero在训练(liàn)中未使用任(rèn)何手工(gōng)设计(jì)的特征或者围棋领(lǐng)域的专业知识,仅(jǐn)仅以(yǐ)历(lì)史(shǐ)棋面作为(wéi)输入,其训练数据全部来自于自(zì)我博弈。可谓恐怖如斯!)
AlphaZero是怎么做到仅凭(píng)自我博(bó)弈就(jiù)习(xí)得技艺的呢?
回想一下,像DeepBlue那(nà)样依赖于人为定(dìng)义(yì)的“评价(jià)函数”的系统(tǒng)会把棋(qí)盘的盘面(miàn)状态作为输入,再输出该(gāi)状(zhuàng)态的“价(jià)值”。
如(rú)今(jīn),对于(yú)深度学习模型来说,输(shū)入(rù)一张照片然后识(shí)别出照片(piàn)里是猫还是狗简直简单到爆了。那么有个想法(fǎ)就(jiù)是,把棋盘盘面作为一个(gè)深(shēn)度学习模型的输入并(bìng)且训练(liàn)它,让它预测这样的盘面(miàn)布置是会输还(hái)是会赢。
但(dàn)是(shì),要训练一个机(jī)器学习模型,就需要数据,海量的数据。从哪儿能得到(dào)那么多(duō)棋局博(bó)弈(yì)的数据呢(ne)?很简单,我们就让电脑自己跟自己下着玩(wán)儿,生成一堆棋(qí)局(jú),然(rán)后再把它们做成(chéng)一个数据集用来训(xùn)练。
AlphaZero的训练(liàn)算法
这个算法简单明了:
1. 让计算机自我博弈数(shù)局,记录每一步走(zǒu)棋。一旦胜负已分,就给之(zhī)前的每一步走棋(qí)打上标签——棋(qí)面最终是“赢”或是“输”。如此一来,我们就(jiù)获得了一个可以用于神经网络(Neural Network,NN)训(xùn)练(liàn)的数据集(jí),让该网络学会判断给定棋面是(shì)“赢面”还是“输面”;
2. 复制(zhì)这个(gè)神经(jīng)网络。用上一步得(dé)到的数据集训练该克隆网络;
3. 让克隆(lóng)网络与原始神(shén)经网络互相博弈;
4. 上一步中获胜的网络留下,败者弃之;
5. 重复(fù)第1步。
呼哈,就像是魔法似的,经过多轮迭代后,你就将获(huò)得一个(gè)世界级模型。这个模(mó)型在短短4小时内便超(chāo)越了最(zuì)强大的计算(suàn)机象棋程序。
AlphaZero的组件
AlphaZero由两部分构成。我(wǒ)们已经提及(jí)了第一部分,就是神经网络。第二部分则是“蒙特卡洛(luò)树搜索(Monte Carlo Tree Search)”,或者简称MCTS。
1. 神经网络(NN)。以棋面作为(wéi)输入,输出(chū)该棋面的(de)“价值”,外加所有(yǒu)可能(néng)走法的(de)概率分布。
2. 蒙特卡洛(luò)树搜索(MCTS)。理想情况下,使用神经网络就(jiù)足(zú)以选择下(xià)一步走法了。不过,我们仍(réng)然(rán)希望(wàng)考虑尽(jìn)可能多的棋面,并(bìng)确保我们的的确确(què)选择了最好的走法(fǎ)。MTCS和Minimax一(yī)样,是一(yī)种可以帮助(zhù)我们寻找可(kě)能(néng)棋面的(de)算法(fǎ)。与Minimax不同(tóng)的是,MTCS能(néng)够(gòu)帮助(zhù)我们更加高效地搜寻博弈树。
让我们深入细(xì)节,看一(yī)看下一步走棋究竟(jìng)是如何得到的(de)
我们不妨先看看AlphaZero在决定下(xià)一步走棋(竞(jìng)技模式(shì))时具体干了什么,然后再(zài)去探究它的训练过(guò)程,这样可以帮助我(wǒ)们更容易地理解AlphaZero。
神经网络在(zài)分类(lèi)这件事儿上表(biǎo)现(xiàn)得(dé)异(yì)常(cháng)出色,例如(rú)区分猫(māo)跟狗。所以这里的(de)想法(fǎ)很简单直接,神经网络能学会区(qū)分棋局输赢的类别吗?更具(jù)体(tǐ)地(dì)来说,就(jiù)是让神经网(wǎng)络预测一个(gè)表(biǎo)示棋(qí)局输(shū)赢概率的数值。此(cǐ)外,它(tā)还将输(shū)出所有(yǒu)可(kě)能(néng)走(zǒu)法的概(gài)率(lǜ)分布,来表示(shì)我们下一步应该如何决策。
神经网络将博弈状态作为输入并输(shū)出一(yī)个(gè)输赢概率(lǜ)数值以及(jí)所有可(kě)能走(zǒu)法的概率分布。对(duì)于Dots and boxes这个小游戏来说,游戏状(zhuàng)态由(yóu)三个元素(sù)表示(shì):首先,某一条线是(shì)否已被占(zhàn)用,这(zhè)可以(yǐ)用一个含有0与(yǔ)1的数(shù)组来表示,如果玩家已经画了(le)某条线,则置其为1,否则为0;第二(èr),当前的走法是(shì)否(fǒu)是(shì)空过;第三,双方玩家的得分(fèn)。我们可(kě)以用这三个元(yuán)素来(lái)表示所(suǒ)有需(xū)要的信息,用其计算当前盘(pán)面(miàn)的(de)价值并预测下一步的(de)走法。
让我们分析一下下图中的博(bó)弈情形,该轮轮到蓝色玩(wán)家(jiā)走。蓝色方有(yǒu)两个选(xuǎn)择,按(àn)照图中(zhōng)上面的(de)走法来画线(xiàn)就会输(shū),按照下面的走法就会赢。
(译(yì)者注(zhù):左(zuǒ)下角是每根线的编号。如果你刚刚已经在网页上跟AlphaZero玩过这个(gè)游(yóu)戏了,那(nà)么相信(xìn)这张图(tú)是很容易理解的。上方第一(yī)种走法只(zhī)顾眼(yǎn)前短期利益,最终葬(zàng)送(sòng)好局。)
如果蓝色(sè)方走23再走21,那么红色(sè)方(fāng)必(bì)赢。然而,如果蓝色方走23后再(zài)走9,那蓝色方就赢了(le)。要是AlphaZero在蓝色(sè)方,它怎么知道哪一种(zhǒng)走法能够赢下来(lái)呢?
你可以用这个(gè)在线notebook复现我们即将(jiāng)呈(chéng)现的(de)效果(guǒ)。
将棋面送入神经网络(luò),我们就能得到(dào)下一步(bù)走(zǒu)在不同(tóng)位置(zhì)的概(gài)率:
move_probability[0]: 9.060527501880689e-12 move_probability[1]: 3.9901679182996475e-10 move_probability[2]: 3.0028431828490586e-15 move_probability[3]: 7.959351400188552e-09 move_probability[4]: 5.271672681717021e-11 move_probability[5]: 4.101417122592821e-12 move_probability[6]: 1.2123925357696643e-16 move_probability[7]: 6.445387395019553e-23 move_probability[8]: 2.8522254313207743e-22 move_probability[9]: 0.0002768792328424752 move_probability[10]: 1.179791128073232e-13 move_probability[11]: 5.543385303737047e-13 move_probability[12]: 3.2618200407341646e-07 move_probability[13]: 4.302984970292259e-14 move_probability[14]: 2.7477634988877216e-16 move_probability[15]: 1.3767548163795204e-14 move_probability[16]: 8.998188305575638e-11 move_probability[17]: 7.494002147723222e-07 move_probability[18]: 8.540691764924446e-11 move_probability[19]: 9.55116696843561e-09 move_probability[20]: 4.6348909953086714e-12 move_probability[21]: 0.46076449751853943 move_probability[22]: 2.179317506813483e-20 move_probability[23]: 0.5389575362205505 move_probability[24]: 5.8165523789057046e-15 |
同时,我们(men)还(hái)能(néng)得到当前棋局的(de)赢面有多大:
-0.99761635 |
你可以在这(zhè)里查阅与这些(xiē)输出相关的代码。
这些(xiē)输出值有一些很有意思的地方,我们来细(xì)品一下:
-
在所有可能画线的位置,23号(hào)、21号以及9号的概率值最大。如果神经网络(luò)选择(zé)在(zài)23号以及(jí)21号位置(zhì)处画(huà)线,那么它就能够得到1分。另外,23号才是能够赢下来的走法,而(ér)相应地,从网络输出的概率来看,23号(hào)位置的概率(0.53)恰好比21号的(0.46)稍微高一点儿。
-
神经网络(luò)也会给不能够画线的位置输(shū)出一个概率值(zhí)。虽然如此,但是代码上还是(shì)要进行限制,以(yǐ)确(què)保计算机不会在不合规则的(de)位(wèi)置(zhì)画线(xiàn)。
-
棋(qí)面的输赢概率(lǜ)为-0.99。这(zhè)意味着AlphaZero认为它已(yǐ)经输掉游戏了。这(zhè)个概率值的范围是-1(输)到(dào)1(赢)。这个值本应该很接近于(yú)1(赢)而不是-1(输)的,毕竟我们知道目前这个(gè)局面赢面很大。也(yě)许我们应该(gāi)多训练几轮来让(ràng)AlphaZero准确预估棋面的输赢概率(lǜ)。
我(wǒ)们很容易利用神经网络的输(shū)出来决定下(xià)一步的走(zǒu)法。
在棋盘游戏中(现实(shí)生(shēng)活中也是(shì)),玩家在(zài)决定下一步怎么走(zǒu)的时候往往会(huì)“多想几步”。AlphaZero也一样。我们用(yòng)神经网(wǎng)络来选择最佳的下一(yī)步走法后,其余(yú)低(dī)概率的位置就被忽(hū)略(luè)掉了。像Minimax这一类传(chuán)统的AI博弈树搜索算法效率都很低,因为这些算法在做(zuò)出最终选择(zé)前(qián)需要穷尽每一种(zhǒng)走法。即使是(shì)带(dài)有较少分支因子的游(yóu)戏(xì)也会使其博弈搜索空间变(biàn)得像是脱缰(jiāng)的野马似的难以驾驭(yù)。分支因(yīn)子就(jiù)是(shì)所有可能的走法的数量(liàng)。这个数量会(huì)随着游戏的进(jìn)行不断变化。因此(cǐ),你可以试着计算一(yī)个平均分支因子数,国际象棋的平均(jun1)分(fèn)支(zhī)因子(zǐ)是35,而(ér)围(wéi)棋则是250。
这(zhè)意味着,在国际象棋中(zhōng),仅走两步(bù)就有1,225(35²)种可能的棋面,而在围棋中(zhōng),这个数字会变(biàn)成62,500(250²)。在Dots and Boxes游戏中,对于一个3×3大小(xiǎo)的棋盘,初始的分支因子数是(shì)24,随(suí)着棋盘不断被填(tián)充(chōng),这个数字会(huì)不断(duàn)减少(除非空过)。所(suǒ)以,在行(háng)至中局,分支因子变为15的时(shí)候,仅走3步就会有(yǒu)多达2730(15*14*13)种可能的棋面。
现在,时(shí)代变了,神经网络将(jiāng)指(zhǐ)导(dǎo)并告诉我们哪些博弈路径值得(dé)探索(suǒ),从而避(bì)免被许多(duō)无用的搜(sōu)索路(lù)径所淹没。现在神经网(wǎng)络告(gào)诉我们(men)23号和21号都是非常值得一探(tàn)究竟的(de)走法。
接着(zhe),蒙特(tè)卡(kǎ)洛树(shù)搜索算(suàn)法(fǎ)就将(jiāng)登(dēng)场啦!
蒙特卡洛(luò)树搜索(MCTS)
神经网络为我们指示了下一(yī)步可能的(de)走法。蒙特卡洛树(shù)搜索算(suàn)法将帮助我们遍历这些节点来最终选择下一步(bù)的走法。
去这个(gè)链接看看论文中有(yǒu)关蒙(méng)特卡洛树(shù)搜索的图形化描述。
使用MCTS的具(jù)体(tǐ)做法(fǎ)是这(zhè)样的(de),给定一个(gè)棋面,MCTS共进行N次模(mó)拟。N是模型的(de)超参数。N次模(mó)拟结束后(hòu),下一步的走(zǒu)法将是这N次模(mó)拟中所经次数最多的一步。你可以由这里的(de)代码(mǎ)一窥究竟:
# https://github.com/suragnair/alpha-zero-general/blob/5156c7fd1d2f3e5fefe732a4b2e0ffc5b272f819/MCTS.py#L37-L48 for i in range(self.args.numMCTSSims): # self.args.numMCTSSims, the number of MCTS simulations to compute self.search(canonicalBoard) # "search" is a MCTS simulations s = self.game.stringRepresentation(canonicalBoard) # Count how many times we have visited each node counts = [self.Nsa[(s, a)] if (s, a) in self.Nsa else 0 for a in range(self.game.getActionSize())] if temp == 0: # Pick the node that was visited the most bestAs = np.array(np.argwhere(counts == np.max(counts))).flatten() bestA = np.random.choice(bestAs) probs = [0] * len(counts) probs[bestA] = 1 return probs |
进行N次(cì)MCTS模拟
一次MCTS模拟从当前的棋(qí)盘(pán)状态出发,沿着博弈树中具(jù)有最大“置信(xìn)区间上界(jiè)(UCB)”值(后文会(huì)给出定义)的节点不断向(xiàng)下(xià)追溯,直到遇到之(zhī)前(qián)从未见过的棋盘状(zhuàng)态,也叫(jiào)做“叶子(zǐ)”状态。这就是(shì)原论(lùn)文中Part A所谓(wèi)的“选(xuǎn)择(Select)”。
置信区间上界是什(shí)么呢?用数(shù)学形式来说就是 Q(s, a) + U(s, a)。其(qí)中(zhōng) s 是状(zhuàng)态(tài),a 是(shì)走法。Q(s, a) 是我们希望(wàng)由走(zǒu)法“a”构成状态“s”能够获得(dé)的(de)期望值,与Q-Learning中的期望值(zhí)一致。记住了,在这(zhè)种情(qíng)况下,该(gāi)值的范(fàn)围是-1(输(shū))到1(赢)。U(s, a) ∝ P(s, a) / (1 + N(s, a))。这意味着U正比(bǐ)于(yú)P和N。其中,P(s, a) 是元(yuán)组(zǔ) (s, a) 的先验概率值,这个值是(shì)从神经网(wǎng)络那里得到的,而 N(s, a) 是已经访问过状态 s 与对应的走法 a 的次数。
# Upper Confidence Bound ucb = Qsa[(s,a)] + Ps[s,a] * sqrt(Ns[s]) / (1 + Nsa[(s,a)] |
UCB的要点在于,其起初更倾向于具有较(jiào)高先验概率(lǜ)(P)和(hé)较(jiào)低访问(wèn)次数(N)的走法,但渐渐地会倾向于具(jù)有较高动作价值(Q)的走法(fǎ)。
你(nǐ)不妨看看(kàn)这里的代码好好理解(jiě)一下。
# https://github.com/suragnair/alpha-zero-general/blob/5156c7fd1d2f3e5fefe732a4b2e0ffc5b272f819/MCTS.py#L105-L121 cur_best = -float('inf') best_act = -1 # pick the action with the highest upper confidence bound for a in range(self.game.getActionSize()): if valids[a]: if (s, a) in self.Qsa: u = self.Qsa[(s, a)] + self.args.cpuct * self.Ps[s][a] * math.sqrt(self.Ns[s]) / ( 1 + self.Nsa[(s, a)]) else: u = self.args.cpuct * self.Ps[s][a] * math.sqrt(self.Ns[s] + EPS) # Q = 0 ? if u > cur_best: cur_best = u best_act = a a = best_act next_s, next_player = self.game.getNextState(canonicalBoard, 1, a) next_s = self.game.getCanonicalForm(next_s, next_player) # Recursively visit the node v = self.search(next_s) |
Part A——选择具有最高置信(xìn)区间上界值的(de)走法
一旦找到一(yī)个叶子状态(tài),就(jiù)把这个棋面状态送入神(shén)经网络。这是(shì)论文中称作的(de)Part B,“扩展与评估”。且看代码。
# leaf node self.Ps[s], v = self.nnet.predict(canonicalBoard) valids = self.game.getValidMoves(canonicalBoard, 1) self.Ps[s] = self.Ps[s] * valids # masking invalid moves sum_Ps_s = np.sum(self.Ps[s]) self.Ps[s] /= sum_Ps_s # renormalize self.Vs[s] = valids self.Ns[s] = 0 |
Part B——扩展与评(píng)估(gū)
最(zuì)后,我(wǒ)们(men)将传回(huí)神经网络返回的值。这就是(shì)论文(wén)所说的Part C——“备份”。您可(kě)以在(zài)此处看到(dào)相关代码。
v = self.search(next_s) if (s, a) in self.Qsa: self.Qsa[(s, a)] = (self.Nsa[(s, a)] * self.Qsa[(s, a)] + v) / (self.Nsa[(s, a)] + 1) self.Nsa[(s, a)] += 1 else: self.Qsa[(s, a)] = v self.Nsa[(s, a)] = 1 self.Ns[s] += 1 return -v |
Part C——备份(fèn)
决定(dìng)下一步如何走
让我们来看看AlphaZero面对上文提及的(de)棋面时(shí)会决定(dìng)如何走。
AlphaZero会进(jìn)行50次(cì)蒙特卡(kǎ)洛树搜(sōu)索模拟。
你可以用这个(gè)在线(xiàn)notebook复(fù)现下面展(zhǎn)示的结果(guǒ)。
下(xià)面(miàn)展示(shì)的(de)就(jiù)是每(měi)次(cì)迭代(dài)的路径:
Simulation #1 -> Expand root node Simulation #2 -> 23 Simulation #3 -> 21 Simulation #4 -> 9 Simulation #5 -> 17 Simulation #6 -> 12 Simulation #7 -> 19 Simulation #8 -> 3 Simulation #9 -> 18 Simulation #10 -> 23,24 Simulation #11 -> 21,24 Simulation #12 -> 23,24,21 Simulation #13 -> 21,24,23,24 Simulation #14 -> 23,24,9 Simulation #15 -> 23,24,17 Simulation #16 -> 21,24,9 Simulation #17 -> 23,24,12 Simulation #18 -> 23,24,18 Simulation #19 -> 21,24,17 Simulation #20 -> 23,24,21,24,9 Simulation #21 -> 21,24,19 Simulation #22 -> 23,24,3 Simulation #23 -> 21,24,18 Simulation #24 -> 23,24,19 Simulation #25 -> 21,24,23,24,17 Simulation #26 -> 23,24,21,24,18 Simulation #27 -> 23,24,21,24,3 Simulation #28 -> 21,24,3 Simulation #29 -> 23,24,21,24,19 Simulation #30 -> 21,24,12 Simulation #31 -> 23,24,21,24,9,24 Simulation #32 -> 21,24,23,24,12 Simulation #33 -> 23,24,21,24,9,24,18 Simulation #34 -> 21,24,23,24,9,24,17 Simulation #35 -> 23,24,21,24,9,24,12 Simulation #36 -> 23,24,21,24,9,24,3 Simulation #37 -> 21,24,23,24,9,24,19 Simulation #38 -> 23,24,21,24,9,24,18,17 Simulation #39 -> 21,24,23,24,9,24,18,17,24 Simulation #40 -> 23,24,21,24,9,24,18,17,24,19 Simulation #41 -> 21,24,23,24,9,24,18,17,24,19,24 Simulation #42 -> 23,24,9,21 Simulation #43 -> 23,24,9,18 Simulation #44 -> 23,24,9,17 Simulation #45 -> 23,24,9,19 Simulation #46 -> 23,24,9,12 Simulation #47 -> 23,24,9,21,24 Simulation #48 -> 23,24,9,3 Simulation #49 -> 23,24,9,21,24,18 Simulation #50 -> 23,24,9,21,24,17 |
上面显示的结果(guǒ)的意思(sī)是(shì):在(zài)第一次模拟中,由于算法(fǎ)之(zhī)前并未见过这(zhè)个棋(qí)面,因(yīn)此输(shū)入的棋面实际上(shàng)是一(yī)个“叶(yè)子”状态节点,需要先“扩(kuò)展”这个节点。所谓扩展就是把棋面送到神经网络(luò)里对每个位(wèi)置(zhì)进行概率评估。
Simulation #1 -> Expand root node |
在第二次模拟中,因(yīn)为(wéi)上步我们(men)已经扩展了(le)根节点,因此它(tā)不再是一(yī)个“叶子”节点了,就此,我们可以对(duì)具(jù)有最高置信区间上界值(zhí)的节点进行(háng)搜索:
# https://github.com/suragnair/alpha-zero-general/blob/5156c7fd1d2f3e5fefe732a4b2e0ffc5b272f819/MCTS.py#L105-L121 cur_best = -float('inf') best_act = -1 # pick the action with the highest upper confidence bound for a in range(self.game.getActionSize()): if valids[a]: if (s, a) in self.Qsa: u = self.Qsa[(s, a)] + self.args.cpuct * self.Ps[s][a] * math.sqrt(self.Ns[s]) / ( 1 + self.Nsa[(s, a)]) else: u = self.args.cpuct * self.Ps[s][a] * math.sqrt(self.Ns[s] + EPS) # Q = 0 ? if u > cur_best: cur_best = u best_act = a a = best_act next_s, next_player = self.game.getNextState(canonicalBoard, 1, a) next_s = self.game.getCanonicalForm(next_s, next_player) # Recursively visit the node v = self.search(next_s) |
具有最大置信区间上界值的(de)是23号位置。搜索算法(fǎ)深入(rù)在23号位置画线的状(zhuàng)态,由于这个状(zhuàng)态在之前搜索算法(fǎ)也没见过,因此这也(yě)是个(gè)“叶子”节点(diǎn)状态,搜索算法会“扩展”这个(gè)状态。就这样(yàng),第二(èr)次模拟也完成啦。
Simulation #2 -> 23 |
这个时候,还记得(dé)上面(miàn)神经网络输出的输赢概率吗,神(shén)经网络认为在(zài)23号位置画线必输无(wú)疑。神经网络可以进行更多轮的(de)训练来确保这的确(què)是个很差的(de)走法。不(bú)过目(mù)前来说,这(zhè)就(jiù)足够了,我们后面会认识到这一点的。
接下(xià)来的模(mó)拟中,会(huì)依(yī)次搜索剩下(xià)的走(zǒu)法中具有最(zuì)大置信区间(jiān)上界的状(zhuàng)态(tài),不过只有下面给出的这些。因为在访问(wèn)完以下这些走(zǒu)法之后(hòu),搜索算法会发现(xiàn)剩下的状态都具有很(hěn)低的概率(lǜ),也就(jiù)是说其置(zhì)信区间上界都很低,也就不必搜(sōu)索了(le)。
Simulation #3 -> 21 Simulation #4 -> 9 Simulation #5 -> 17 Simulation #6 -> 12 Simulation #7 -> 19 Simulation #8 -> 3 Simulation #9 -> 18 |
在(zài)之后的模拟中(zhōng),一个令人兴奋的模式逐渐揭开面纱。记住(zhù),能够赢下来的走法序列是23,24(对应空过),9。
(译者注:填上23号之后,由于(yú)补全了一个正方形,因此对方空(kōng)过。这里给(gěi)出的(de)序列是(shì)两(liǎng)方的走法序(xù)列。)
Simulation #10 -> 23,24 Simulation #11 -> 21,24 Simulation #12 -> 23,24,21 Simulation #13 -> 21,24,23,24 Simulation #14 -> 23,24,9 Simulation #15 -> 23,24,17 Simulation #16 -> 21,24,9 Simulation #17 -> 23,24,12 Simulation #18 -> 23,24,18 Simulation #19 -> 21,24,17 Simulation #20 -> 23,24,21,24,9 Simulation #21 -> 21,24,19 Simulation #22 -> 23,24,3 Simulation #23 -> 21,24,18 Simulation #24 -> 23,24,19 |
在第10至第24次模拟中(zhōng),很明显,MCTS已(yǐ)经把注意力放(fàng)在了21号节点与23号节点上(shàng)。这说得通,因为(wéi)这两种走法都能让(ràng)我方(fāng)得(dé)1分。
Simulation #33 -> 23,24,21,24,9,24,18 Simulation #34 -> 21,24,23,24,9,24,17 Simulation #35 -> 23,24,21,24,9,24,12 Simulation #36 -> 23,24,21,24,9,24,3 Simulation #37 -> 21,24,23,24,9,24,19 Simulation #38 -> 23,24,21,24,9,24,18,17 Simulation #39 -> 21,24,23,24,9,24,18,17,24 Simulation #40 -> 23,24,21,24,9,24,18,17,24,19 Simulation #41 -> 21,24,23,24,9,24,18,17,24,19,24 |
在第33至第(dì)41次模拟中,搜索算(suàn)法深入探(tàn)究了那些导致败(bài)局的走(zǒu)法。这里要注(zhù)意到一件(jiàn)有意思的事情。尽管追究得很(hěn)深,但是搜索算法并(bìng)没(méi)有抵达游戏终局,后面还有可以走的步骤。
Simulation #42 -> 23,24,9,21 Simulation #43 -> 23,24,9,18 Simulation #44 -> 23,24,9,17 Simulation #45 -> 23,24,9,19 Simulation #46 -> 23,24,9,12 Simulation #47 -> 23,24,9,21,24 Simulation #48 -> 23,24,9,3 Simulation #49 -> 23,24,9,21,24,18 Simulation #50 -> 23,24,9,21,24,17 |
接着,在第42次至(zhì)第50次模拟中,通(tōng)过神经网(wǎng)络的场外援助,搜索算法意识到了23,24,21或者21,24,23都不是(shì)好的(de)走(zǒu)法,这下子,它全然投入(rù)到能够获胜的走法序列:23,24,9。
在50次模(mó)拟后,是(shì)时候(hòu)做(zuò)出决定了。MCTS选择了其访问次(cì)数最(zuì)多的位置。下(xià)面列出了(le)每个(gè)走法(fǎ)的访问次数(只统计路径中的第一个位置):
counts[3] = 1 counts[9] = 1 counts[12] = 1 counts[17] = 1 counts[18] = 1 counts[19] = 1 counts[21] = 15 counts[23] = 28 |
3,9,12,17,18以及19号位置只在最初10次模(mó)拟中访(fǎng)问(wèn)了1次。接(jiē)着MCTS专(zhuān)注于21和23号位置(zhì),且在最后9次(cì)模(mó)拟中都先走23号。因为23号位置被访问(wèn)次数最多(duō),达到了28次之多,因此MCTS最终(zhōng)返回23作为(wéi)下(xià)一步的走(zǒu)法。
关键(jiàn)点(diǎn)是什么(me)?
-
通过每一次模拟,MCTS依靠神经网络, 使用累计价值(Q)、神经(jīng)网(wǎng)络(luò)给出的走法(fǎ)先(xiān)验(yàn)概率(P)以及访问对(duì)应节点的频率这些数字的组(zǔ)合,沿着最有希(xī)望获胜的路径(换句话说,也就是具有最(zuì)高(gāo)置信区(qū)间上界的路(lù)径)进(jìn)行探索。
-
在每一次(cì)模拟中(zhōng),MCTS会尽可能向(xiàng)纵深进行探索直(zhí)至遇(yù)到它从(cóng)未见过的盘面状(zhuàng)态,在这种情况下,它会通过神经网络来评估(gū)该盘面状态的优劣。
如果我们将上述方法与使用(yòng)带有Alpha-Beta剪枝以及一个评价函数的Minimax之类的传(chuán)统方法进(jìn)行比较(jiào),我们可以发现以下几点:
-
在Minimax中,博(bó)弈树的搜索深度是由(yóu)算法设计者自行设(shè)定的。它(tā)无论如何都(dōu)会(huì)搜(sōu)索(suǒ)到那(nà)个深度(dù),然后用那个可爱的评价函数进行盘面评估。要是没有(yǒu)Alpha-Beta剪枝(zhī)的话,它就得访问给定深度(dù)下所有(yǒu)可能的盘面节点,极(jí)为低效。在上面的情形(xíng)中,如(rú)果还可以走(zǒu)8步,要搜(sōu)索的深度为3,就意(yì)味着(zhe)总(zǒng)共需要评估336个(gè)盘面状态。使用MCTS,仅仅用(yòng)了50次(cì)模拟,也就是(shì)50次评估,而且在搜索中还尽可能(néng)搜索得足够深。
-
Alpha-Beta剪枝能够帮助我们将336这个(gè)数字减少。然(rán)而,却(què)并(bìng)不能(néng)帮我们找到一条优良的博弈路径。
-
我们(men)是一(yī)直在用神经网络来对盘面进行(háng)评(píng)估(gū)的,而不是某个认为定义的评价函数。
-
很(hěn)有意思的(de)是,在(zài)起初(chū)几步中,神经网络并没有(yǒu)做出正确的盘面评(píng)估。然而,随着(zhe)在博弈树中搜索(suǒ)的深度提升,它自动修正了它的输(shū)出,而且搜索并未抵达游戏终(zhōng)局。
-
最后,要注意到AlphaZero的优雅(yǎ)与(yǔ)简洁。而在Alpha-Beta剪枝中,你(nǐ)的不断跟踪(zōng)alpha和beta参数来知悉(xī)哪些(xiē)路(lù)径(jìng)被砍掉了,你(nǐ)还得用(yòng)一个(gè)人为(wéi)定义(yì)的评价函数,更不要说这(zhè)个函数(shù)又笨重又丑陋(lòu)。MCTS与NN让所有(yǒu)这一切都变得(dé)异常优雅与(yǔ)简洁。你甚至可以在Javascript中把这一切都搞定!
训练神经网(wǎng)络
至此,我(wǒ)们还缺最后一个关键部分。究竟该如何训练这个神经网络呢?
不(bú)要害怕(pà),嘻嘻,贼简单。我们之前提(tí)到的步骤是:
1. 让计算机在“训(xùn)练模式”下自我博弈数局,记录每一步(bù)走棋。一旦胜负已分,就给之前的每一(yī)步(bù)走棋(qí)打上标签(qiān)——棋面最终是“赢”或是“输(shū)”。如此一(yī)来,我们就获得了(le)一个可以用(yòng)于神(shén)经网络(Neural Network,NN)训(xùn)练(liàn)的数据(jù)集,让该网络学会判断给定棋面是“赢面”还是“输面”;
2. 复制神经网络。用上一步(bù)得(dé)到的数(shù)据集训练(liàn)该(gāi)克隆网(wǎng)络;
3. 让克(kè)隆网络与(yǔ)原(yuán)始神经网(wǎng)络(luò)互(hù)相博弈;
4. 上一步中获胜的网络留下,败者(zhě)弃之;
5. 重复第1步。
什么叫在“训练(liàn)模式”下(xià)进行博弈呢?这个区别非常细微。当(dāng)在(zài)“竞技(jì)模(mó)式”下博弈时,我们会选择访问次(cì)数最多的走法(fǎ)。而在“训(xùn)练模式(shì)”下,在游(yóu)戏刚(gāng)开始的一定步数(shù)内,我(wǒ)们会将(jiāng)不同走法的访问次(cì)数变成概率分布,以此鼓励(lì)网络对不同的走法进行探索。举个(gè)例子,假设有3中可能的走法(fǎ),对应的访问次(cì)数(shù)分(fèn)别(bié)是[2, 2, 4]。那(nà)么在竞(jìng)技模式中(zhōng),由于第三种走法(fǎ)的访问次数最多,所以我们就选择第三种走法。但是在训(xùn)练模式中,我们会将[2, 2, 4]变成一(yī)个概率分(fèn)布,因(yīn)为2+2+4=8,因此概率(lǜ)分布就是[2/8, 2/8, 4/8] 或(huò)者说是 [0.25, 0.25, 0.5]。换句话说(shuō),我们在50%的情况下(xià)会选(xuǎn)择第(dì)三种走(zǒu)法,而第一(yī)以及第二种走法有25%的概(gài)率被选中。
接着(zhe),我们用一个简单的(de)井字棋来(lái)描(miáo)述一(yī)下数据集的构建。
在上面这副图片中,1号玩(wán)家(jiā)执X获胜。
我们可(kě)以将盘面中未落子(zǐ)的地方记(jì)作0,1号玩家(jiā)打X的位置记(jì)作1,2号玩家打圈的地方记(jì)作-1。
那么,上(shàng)图中(zhōng)的棋盘各个盘面就可以变成下边这样:
0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 0 0 0 0 0 -> 0 0 0 -> -1 0 0 -> -1 1 0 -> -1 1 0 -> -1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 -1 0 1 |
或者(zhě),我们(men)将盘面降为一维(wéi)表示(shì),就(jiù)是这样:
[0, 0, 0, 0, 0, 0, 0, 0, 0] [1, 0, 0, 0, 0, 0, 0, 0, 0] [1, 0, 0,-1, 0, 0, 0, 0, 0] [1, 0, 0,-1, 1, 0, 0, 0, 0] [1, 0, 0,-1, 1, 0,-1, 0, 0] [1, 0, 0,-1, 1, 0,-1, 0, 1] |
然后我们(men)要(yào)做两件(jiàn)事情。第一(yī)件事,找到所有属于1号玩家轮(lún)次的棋盘盘面。我们只会给神经网络(luò)喂入1号玩(wán)家的相关盘面数据。在(zài)井字棋中,很(hěn)容易就能挑选出(chū)来。而2号玩家轮(lún)次的那些(xiē)盘面(miàn),直接将其数(shù)据取(qǔ)相反数,使其变为1号玩家视角下(xià)的盘面状态。
也就是,将(jiāng):
[0, 0, 0, 0, 0, 0, 0, 0, 0] # Player 1 turn [1, 0, 0, 0, 0, 0, 0, 0, 0] # Player 2 turn [1, 0, 0,-1, 0, 0, 0, 0, 0] # Player 1 turn [1, 0, 0,-1, 1, 0, 0, 0, 0] # Player 2 turn [1, 0, 0,-1, 1, 0,-1, 0, 0] # Player 1 turn [1, 0, 0,-1, 1, 0,-1, 0, 1] # Player 2 turn |
变为:
[ 0, 0, 0, 0, 0, 0, 0, 0, 0] # Player 1 turn [-1, 0, 0, 0, 0, 0, 0, 0, 0] # Player 1 turn [ 1, 0, 0,-1, 0, 0, 0, 0, 0] # Player 1 turn [-1, 0, 0, 1,-1, 0, 0, 0, 0] # Player 1 turn [ 1, 0, 0,-1, 1, 0,-1, 0, 0] # Player 1 turn [-1, 0, 0, 1,-1, 0, 1, 0,-1] # Player 1 turn |
第二件事,我们对获(huò)得的(de)每一条数(shù)据向后增加1位数据,“1”表示(shì)1号玩家赢(yíng),“-1”表示1号(hào)玩家输。如此一来(lái),数据就(jiù)变成了(le):
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] # Winning board [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] # Losing board [ 1, 0, 0,-1, 0, 0, 0, 0, 0, 1] # Winning board [-1, 0, 0, 1,-1, 0, 0, 0, 0, 0] # Losing board [ 1, 0, 0,-1, 1, 0,-1, 0, 0, 1] # Winning board [-1, 0, 0, 1,-1, 0, 1, 0,-1, 0] # Losing board |
嗯(èn),这个(gè)数据集现在有模有(yǒu)样了呢!你(nǐ)看,这样一来(lái),我们(men)就获得了一批用于(yú)训练神经网络的数据,并让神经网络(luò)学习(xí)判断盘面的输(shū)赢。
哦(ò),我们(men)好像漏掉了(le)概(gài)率。那些不同走法的概率分布怎么得到呢?记住了,在训练模式下,我们每(měi)一步也还是会进行MCTS模拟的。正如我们会记录下(xià)不同(tóng)的(de)盘(pán)面,我们也会将对应的概率进行记录。
然后我(wǒ)们就会复制(zhì)(或者(zhě)说(shuō)是克隆)神经网络,并用新获得的数据训练(liàn)这个克隆(lóng)网(wǎng)络,我们所期望的,是(shì)用上(shàng)新获得(dé)的数据后,这个克隆(lóng)网络(luò)可(kě)以变得更强一点(diǎn)儿。通过与原(yuán)始网络进行对抗,我们可以验证(zhèng)其是(shì)否真的变强了。如果克隆网络的胜率超过55%,我们就(jiù)把原始(shǐ)网络丢掉(diào),这个克隆网络便可取而(ér)代之。
这个过程会一直重复下去,神经网络也在(zài)这(zhè)个过程(chéng)中不断变强。
你可以在(zài)这儿看到论文中的相(xiàng)关(guān)图表。
数据集(jí)中如何包含(hán)更多(duō)更具代表性的数(shù)据呢?相较于神经网络输出的原(yuán)始走法概率分布,数据集会倾向于根据MCTS生成的(de)概(gài)率来选择更具借鉴性的走法。通(tōng)过(guò)让MCTS搜索足够多较深(shēn)的博弈路径,神经(jīng)网络可以获(huò)取更优质(zhì)的数据并更加高(gāo)效地学习。
试试(shì)看(kàn)用这个Colab Notebook训练(liàn)一(yī)个Dots and Boxes模型(xíng)吧。
将其部署(shǔ)至一个(gè)Web应用
几(jǐ)个月前,我(wǒ)发了(le)一篇博文,带你大致过了一(yī)遍使用(yòng)TensorFlow.js将(jiāng)Keras或者TensorFlow模型部署至(zhì)Javascript的过程。这里我们要做(zuò)的事(shì)情大差不差。我们会把用Keras训练得到(dào)的(de)模型转换成能够被(bèi)TensorFlow.js调用的模型(xíng)。
这个Notebook展(zhǎn)示(shì)了如何将(jiāng)一个预训练的Dots and Boxes博弈模型(xíng)转换(huàn)为一个(gè)TensorFlow.js模型。
一旦转(zhuǎn)换完成,这个模型就(jiù)能够很轻(qīng)松地使用Javascript进行调(diào)用(yòng)。不妨看看这里。
结论
在(zài)本篇博文中(zhōng),你(nǐ)认识了AlphaZero,一个(gè)能(néng)够在双方零和博弈的棋盘(pán)游戏(xì)中战胜世界冠军的强化(huà)学习算(suàn)法。
你也了解了它是如何使用蒙特卡(kǎ)洛树搜索算法(fǎ)以及神经网络来找到(dào)最佳的下一步走法,以及如何(hé)训(xùn)练(liàn)这样一(yī)个神(shén)经网络(luò)。