TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

将棋でディープラーニングする その17(強化学習の実装)

前回の日記に書いたように方策ネットワークを使って自己対戦できるようになったので、AlphaGoの手法(RL policy network)で強化学習の実装を行った。

教師ありで十分に訓練できていないので、今の時点で強化学習を行っても効果はでないと思われるが、実装方法の確認を目的にとりあえず実装を先に行った。

AlphaGoの論文に書かれている強化学習の手法は以前の日記に書いた通り、REINFORCE algorithmが使われている。

以下の式で表される勾配で方策ネットワークのパラメータを更新する。
\displaystyle
\Delta \rho = \frac{\alpha}{n} \sum_{i=1}^n \sum_{t=1}^{T^i} \frac{\partial \log p_\rho (a_t^i \mid s_t^i)}{\partial \rho} (z_t^i - v(s_t^i))

v(s_t^i)は、状態価値関数の値で、局面ごとに値が異なるため、Chainerで実装するには、サンプルごとの異なる値を勾配に掛ける必要があり、コアな処理に手を入れないとと実装できそうにない。(TensorFlowの方が低レベルの計算ができるので向いているかもしれない。)
※追記:softmax_cross_entropyに手を入れることで実装できました。その19の記事を参照。

ただし、v(s_t^i)は、バリューネットワークができる前は、常に0で学習するため、v(s_t^i)を使用しない場合について実装した。

勾配計算の実装

上記の式は対数尤度(交差エントロピー誤差)に勝敗による報酬z_t^iを掛けたものであるため、学習率に勝敗の報酬を掛けて、損失をsoftmax_cross_entropyとして実装した。

方策ネットワークによる対局

自己対戦部分は、方策ネットワークにより指し手を確率に応じて選択し、勝敗の判定にUSIエンジンを補助的に使用した。
ほぼ勝敗がはっきりした局面で指し手を進めても学習に悪影響があるため、USIエンジンが探索で求めた評価値で勝敗がはっきりした時点(勝率0.85以上)で、その時点の評価値から算出した勝率を報酬の値とした。
評価値から勝率の予測は、elmoなどで採用されている以下の式で計算した。
\displaystyle
\frac{1}{1 + \exp(\frac{-a}{600})}
これを勝率0.5の報酬が0になるようにして平行移動とスケーリングして使用した。

方策ネットワークで指し手を確率で選ぶ際には、AlphaGoの論文に書かれているように、下記の式で表されるBolzmann分布を使用した。
\displaystyle
\frac{\exp(Q_t(a)/\tau)}{\sum_{b=1}^n \exp(Q_t(b)/\tau)}
\tauは、温度(softmax tempature)と呼ばれる正定数で、温度が高い場合すべての行動が同程度に起こり、低い場合は選択確率の差がより大きくなる。
適切な値が分かっていないので、ひとまずAlpahGoの論文と同じ値0.67を使用した。

Chainerでは、softmax_cross_entropyの入力は、出力層の活性化関数に入力する値を入力とするので、入力はlogスケールの確率になっている。
そのため、通常はsoftmaxを使用する代わりに、上記式のQ_t(a)にその値を入力して計算する。

自己対戦の実装

方策ネットワークは、GPUを使用して同時計算が可能であるため、対局はミニバッチ単位で行い、各対局を同時に1手ずつ進める。
ミニバッチは、半分を先手番と半分を後手番とする。

USIエンジンで局面を評価して終局判定を行い、終局した対局をミニバッチから取り除く。
ミニバッチの全対局が終了したら、対局ごとをバッチの単位として、その対局のすべての局面を入力、選択した手を教師データとして、損失をsoftmax_cross_entropyとして誤差逆伝播を行いパラメータを更新する。
その際、学習率は定数に勝敗の報酬を掛けた値を使用する。
負けた対局の場合は、学習率は負の値になるので、損失が増える方向に学習される。

対局相手は、以前に更新したパラメータからランダムに選んだパラメータを使用する。

自己対戦の実装はこちらのAlphaGoのクローンの実装を大いに参考にした。

実行結果

以上を実装して、以前に教師ありで学習したパラメータを初期値として、強化学習を実行した。

方策ネットワークによる対局は並列で行えるが、USIエンジンによる評価値の算出はシーケンシャルな処理になるため、1手0.1秒に設定しても、非常に時間がかかる。
16ゲームをミニバッチとした場合、1イテレーション(16ゲームの対局)に1分半もかかった。

初期値から40イテレーションを学習してみたが、勝率が上がる傾向は見られなかった。
f:id:TadaoYamaoka:20170520193049p:plain

なお、学習率の定数は小さい値(0.001)にしていないと値が発散してうまく学習できなかった。

初期値の教師ありで学習したパラメータの質が良くないので、これ以上は学習を試すのは保留する。

次回はバリューネットワークの実装を試す予定。

GitHubにソースを公開しました。
github.com