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