前回はモンテカルロ木探索について適当に紹介しました。今回は学習編です。学習といっても難しいことはしません。単にAI同士で対局したデータを集める⇒ネットワークに学習させる⇒以下ループ、するだけです。ただAI同士の対局では同じ手ばっかり指し手も意味ないので、一定確率で次善手やランダムな手を指すようにしています。
対局データは、(盤面,指し手,最終的な結果,評価値)を何万局といった単位で生成したものになります。
最終的な結果は勝敗の2値ではなく石数の差をシグモイド関数で0から1に変換したものになります。
損失関数は、
- 方策ネットワークは実際の指し手(64次元ベクトル)と予測した指し手(64次元ベクトル)の交差エントロピー誤差(※実際の指し手はone hotベクトルなので、実質log1個だけ)
- 価値ネットワークは最終的な結果(1次元ベクトル)と予測した評価値(1次元ベクトル)の二値交差エントロピー誤差
この二つの誤差を単純に足して、モーメンタム付きのSGDで最適化します。
ただし、途中までこの方法でやっていましたが、方策ネットワークは前世代の指し手を模倣するだけで、対局結果は考慮していません。
そこで方策ネットワークの誤差にActor critic法を使います。前世代の対局データがActor、前世代の価値ネットワークがcriticになります。
Actor Critic版誤差
評価値が悪かったのに勝ったら大きくなり、評価値が良かったのに負けたら小さくなります。これにより良い手はよりよく学ぶようになり、悪い手は反面教師にできるようになります。0.5を足しているのは山岡さんによると前世代のAIが考えた指し手なのだからある程度信頼するべきだからだそうです。本家のActor criticでは0.5を足したりしませんが、足さないで試しましてみると結果はよくなかったです。
価値ネットワークの誤差も改良します。ブートストラップという方法を使います。これは自分のネットワークを自分自身で改良するという方法で、まあ数式にすると、
です。
bceは二値交差エントロピーです。
前の世代の価値ネットワークも参考にするということですね。何でこうしてるかは忘れました。
実装、面倒なのでlossの計算だけ。データセットを用意したりオプティマイザーを用意するなどは普通の深層学習と変わりません。
dataが盤面、policyが指して(one hotベクトル)、valueが結果、evalが評価値です。
output1が方策ネットワークの出力、output2が価値ネットワークの出力です。
output1,output2 = model(data) //sumを使っているが実際には1個以外全部0 loss_p = torch.sum(- policy * F.log_softmax(output1,dim=1),1) z = value - eval + 0.5 loss_p = (loss_p * z).mean() loss_p += (F.softmax(output1, dim=1) * F.log_softmax(output1, dim=1)).sum(dim=1).mean() loss_v = loss_bce(output2,value) loss_e = loss_bce(output2,eval) loss = loss_p + loss_v * 0.7 + loss_e * 0.3
学習結果
世代ごとの説明、19世代でactor critic、25世代で完全読みを実装してます。
自己対局 | epoch | |
---|---|---|
第一世代 | 1万局のランダム対戦 | 82 |
第二世代 | 1万局の2手読みαβ探索 | 100 |
第三世代 | 1万局の2手読みαβ探索 | 100 |
第四世代 | 1万局の2手読みαβ探索 | 100 |
第五世代 | 1万局の2手読みαβ探索 | 100 |
第六世代 | 1万局の100playoutMCTS | 100 |
第七世代 | 1万局の100playoutMCTS | 100 |
第八世代 | 6000局の100playoutbat8MCTS + 4000局の100playoutMCTS | 100 |
第九世代 | 1万局の100playoutbat8MCTS | 100 |
第十世代 | 1万局の100playoutMCTS | 20 |
第11世代 | 1万局の100playoutMCTS | 20 |
第12世代 | 1万局の100playoutMCTS | 20 |
第13世代 | 1万局の100playoutMCTS | 20 |
第14世代 | 1万局の100playoutMCTS | 20 |
第15世代 | 1万局の300playoutMCTS | 20 |
第16世代 | 4万局の100playoutMCTS | 20 |
第17世代 | 6万局の100playoutMCTS | 20 |
第18世代 | 8万局の100playoutMCTS | 20 |
第19世代 | 32000局の100playoutMCTS | 60 |
第20世代 | 40000局の500playoutMCTS | 60 |
第21世代 | 40000局の500playoutMCTS+前世代 | 60 |
第22世代 | 40000局の500playoutMCTS+前世代+前々世代 | 60 |
第23世代 | 40000局の500playoutMCTS+前世代+前々世代 | 60 |
第24世代 | 80000局の800playoutMCTS+前世代+前々世代 | 80 |
第25世代 | 134000局の1000playoutMCTSp10 | 70 |
第26世代 | 82000局の1000playoutMCTSp10+前世代 | 60 |
第27世代 | 120000局の1000playoutMCTSp10+前世代 +前々世代 | 60 |
最初はランダム探索で、その後方策ネットワークだけでαβ探索により教師データを作成していました。モンテカルロ木探索を実装してからはプレイアウトを増やしたり対局数をどんどん増やしていって27世代までやりました。
勝率比較
個人的にやってたのですごいわかりづらいですが、genは世代、haltはプレイアウト数、batは未評価ノードをvirtul lossにして、蓄積する回数、pは完全読みの手数です。
勝ち | 負け | 引き分け | ||
---|---|---|---|---|
第10世代 | ||||
gen10/halt100/ bat8/epoch20 | gen1/halt100/bat8 | 98 | 2 | 0 |
gen2/halt100/bat8 | 99 | 1 | 0 | |
gen3/halt100/bat8 | 95 | 5 | 0 | |
gen4/halt100/bat8 | 91 | 9 | 0 | |
gen5/halt100/bat8 | 89 | 10 | 1 | |
gen6/halt100/bat8 | 81 | 16 | 5 | |
gen7/halt100/bat8 | 80 | 18 | 2 | |
gen8/halt100/bat8 | 82 | 15 | 3 | |
gen9/halt100/bat8 | 82 | 17 | 1 | |
第20世代 | ||||
gen20/halt100/bat8 | gen19/halt100/bat8 | 175 | 121 | 4 |
gen18/halt100/bat9 | 184 | 109 | 7 | |
gen17/halt100/bat10 | 184 | 104 | 12 | |
gen16/halt100/bat11 | 223 | 70 | 7 | |
第24世代 | ||||
gen24/halt100/bat8/p4 | gen23/halt100/bat8/p4 | 159 | 131 | 10 |
gen22/halt100/bat8/p4 | 173 | 117 | 10 | |
gen21/halt100/bat8/p4 | 191 | 101 | 8 | |
第27世代 | ||||
gen27/halt500/bat8/p8 | gen24/halt500/bat8/p8 | 170 | 120 | 10 |
gen25/halt500/bat8/p8 | 150 | 142 | 8 | |
gen26/halt500/bat8/p8 | 143 | 148 | 9 |
自己対局ばっかりで実際どれほどの強さか分かりにくいですが、私はオセロのこと全然分からないので実際によく分からないです。ただしその辺の無料オセロゲームは相手にならないです。
またEgaroucidのレベル5くらいに(こちらの方が30倍くらい読む時間長いですが^^)勝てます。
学習で分かったこと
10世代の結果を見た通り1世代どころか9世代にすら勝率ばくあがりしています。これはエポックを100から20に下げたからです。どうして下げるとよくなったかというと、学習が進むとある点で価値ネットワークの精度はあがるけど、方策ネットワークの精度は下がっていくようになります。lossを足し合わせているので、そういうことが起きてしまうのですね。おそらくそれが原因でlossは減ったのに弱くなるといったことが起こったのだと思います。学習しまくればいいってもんでもないんですね。
私の実装ではvirtual lossはあまり意味ないようです。virtual lossはgpuの並列計算能力を生かすために、未評価のノードの評価を保留して、とりあえず評価値に0を代入しておき、未評価ノードがたまったらいっきに評価する手法です。gpuの計算効率はあがるのですが、探索精度は下がります。Deepmindや山岡さんが実装しているAIはネットワークが非常に大きいので計算時間のほとんどがGPUです。それに対して私がやっているAIはネットワークがそこまで大きくないのに対して、探索部分はPythonで並列処理なしで実行しているため、CPU部分の計算時間の割合が結構大きいです。そのためGPUの計算時間削減が探索精度の低下に見合っていません。
おわり