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