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