勾配降下党青年局

万国のグラーディエントよ、降下せよ!

半年前に自分で作った機械学習によるオセロAIの復習をする4

前回モンテカルロ木探索について適当に紹介しました。今回は学習編です。学習といっても難しいことはしません。単にAI同士で対局したデータを集める⇒ネットワークに学習させる⇒以下ループ、するだけです。ただAI同士の対局では同じ手ばっかり指し手も意味ないので、一定確率で次善手やランダムな手を指すようにしています。
対局データは、(盤面,指し手,最終的な結果,評価値)を何万局といった単位で生成したものになります。
最終的な結果は勝敗の2値ではなく石数の差をシグモイド関数で0から1に変換したものになります。
損失関数は、

  1. 方策ネットワークは実際の指し手(64次元ベクトル)と予測した指し手(64次元ベクトル)の交差エントロピー誤差(※実際の指し手はone hotベクトルなので、実質log1個だけ)
  2. 価値ネットワークは最終的な結果(1次元ベクトル)と予測した評価値(1次元ベクトル)の二値交差エントロピー誤差

この二つの誤差を単純に足して、モーメンタム付きのSGDで最適化します。
ただし、途中までこの方法でやっていましたが、方策ネットワークは前世代の指し手を模倣するだけで、対局結果は考慮していません。
そこで方策ネットワークの誤差にActor critic法を使います。前世代の対局データがActor、前世代の価値ネットワークがcriticになります。
Actor Critic版誤差=(1.の誤差)×(最終的な結果-評価値+0.5)
評価値が悪かったのに勝ったら大きくなり、評価値が良かったのに負けたら小さくなります。これにより良い手はよりよく学ぶようになり、悪い手は反面教師にできるようになります。0.5を足しているのは山岡さんによると前世代のAIが考えた指し手なのだからある程度信頼するべきだからだそうです。本家のActor criticでは0.5を足したりしませんが、足さないで試しましてみると結果はよくなかったです。
価値ネットワークの誤差も改良します。ブートストラップという方法を使います。これは自分のネットワークを自分自身で改良するという方法で、まあ数式にすると、
0.7×bce(実際の結果,出力)+0.3×bce(評価値,出力)です。
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の計算時間削減が探索精度の低下に見合っていません。

おわり