TadaoYamaokaの日記

山岡忠夫Homeで公開しているプログラムの開発ネタを中心に書いていきます。

リバーシ(オセロ)で深層強化学習 その4(並列実行)

前回DQNリバーシ(オセロ)の強化学習を試して、ランダムより強くなることを確認した。
しかし、シングルステッドでシングルゲームを繰り返しているため1万イテレーションの実行に約14時間かかった。

方策勾配法のアルゴリズムであるA2Cでは、環境を並列実行して効率を高めるという工夫がされている。
AlphaStarでも並列対戦が実装されている。

私が作成しているdlshogiでもゲームを並列で実行することで、GPUの使用効率を高めている。

DQNでも、同様にゲームを並列実行することで、効率を高めることができる。
Pythonでは、GILの制約によりマルチスレッドにしても効率がそれほど上がらない問題があるので、シングルスレッドで複数ゲームを並列で実行して、方策の計算を複数ゲームをまとめてミニバッチで処理することでGPUによる並列化を行い高速化を行うことにする。

環境の並列実行

OpenAIのbaselinesでは、環境を並列実行のためにVecEnvクラスが実装されている。
そこで、VecEnvクラスを参考にして環境の並列処理を実装した

baselinesではマルチプロセスで環境を並列化しているが、リバーシの1ステップの処理は、creversiではSIMDを使って高速化しているため、マルチプロセスのオーバヘッドの方が大きくなる。
そこで、シングルステッドで繰り返しで処理することにした。

DQNの並列処理の実装

学習の間隔

前回のシングルスレッド、シングルゲームでの実装では、エピソード終了の区切りで学習を行っていたが、環境を並列実行するとエピソードの終了のタイミングが環境ごと異なる。
そのため、エピソードの終了ではなく一定のステップごとに学習するようにする。
ただし、ステップ数の間隔は、前回の16エピソード終了ごととだいたい同じになるように調整した。

1ステップの間に、ミニバッチサイズ分のゲームが1ステップ進行するため、

OPTIMIZE_PER_STEPS = (60 * 16 + BATCH_SIZE - 1) // BATCH_SIZE

という式で、1ゲーム60手として16エピソードごとになるようにした。

ミニバッチサイズは256とした。

方策オフについて

前回はエピソード区切りで学習していたため、1ゲームの間に方策が変わることはない。
上記のように一定のステップ間隔で学習した場合、ゲームの途中で方策が変わることになる。

方策勾配法などの方策オンの学習では、ゲームの途中で方策を変えることはできない。
一方、DQNで使用されているQ学習は、方策オフ型であり、挙動方策と推定方策を分けることができる。
よって、ゲームの途中で方策を変更しても問題がない。

学習結果

DQNを並列実行して、1万イテレーション学習した結果は以下のようになった。

損失

損失のグラフは以下のようになった。
f:id:TadaoYamaoka:20191208213014p:plain

3000イテレーションくらいまでは、前回のシングルゲームと比べて、損失が上昇している。
この違いは、並列実行することで、ミニバッチサイズ分のゲームが同じ方策を使って経験データを蓄積するため、序盤のランダムに近い方策によって生成されたデータをより多く学習するためと思われる。
3000イテレーション以降は、比較的安定して学習できている。

強さ

ランダムと1000局対局した結果は、833勝152敗15分となった。
信頼区間95%の勝率は、86.7~82.2%となった。

前回のシングルゲームの64.9~58.8%と比べて、高くなっている。
学習間隔の計算で小数点の繰り上げを行っているので、生成した局面は多くなっているのでそのままは比較できないが、実行時間に対して効率よく学習できている。

学習時間

並列実行することで、前回は1万イテレーションで、約14時間かかったが、約1時間30分に短縮された。

まとめ

環境を並列実行することで、DQNの学習を効率化できた。
マルチスレッドやマルチプロセスにしないでも、シングルスレッドで環境を並列化することでGPUを効率的に利用できる。

次回は、強化学習の他のアルゴリズムを試していきたい。

ソース

DQNの並列実行のソースはこちら。
creversi_gym/dqn_parallel.py at master · TadaoYamaoka/creversi_gym · GitHub