TadaoYamaokaの日記

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

将棋AIの実験ノート:方策の分布を学習 その2

以前に、指し手を学習するより、方策の分布を学習した方が、方策の精度が上がるということを書いた。
しかし、現在、dlshogiの強化学習で生成している教師局面フォーマット(hcpe)では、方策の分布を記録していない。

そこで、方策の分布を出力できるように、教師データのフォーマットを見直した。

フォーマット

今までは、局面単位に出力して、不要な局面(合法手が1手しかない局面)は、出力していなかった。
また、対局の境目が明確にわからないようになっていた。
そのため、自己対局の結果を棋譜として確認することが難しかった。
固定長フォーマットで、シャッフル可能で、データサイズを小さくするということを要件してしていたためである。

今回見直すにあたり、方策の分布を記録するため可変長になるため、棋譜にも変換できるフォーマットにすることにした。
開始局面から、指し手と方策の分布をシーケンシャルに記録していく形にする。

AobaZeroは、CSAフォーマットにコメントで方策の分布を記録しているが、冗長でファイルサイズが膨らむため、バイナリフォーマットとした。

バイナリ棋譜フォーマット(hcpe3)
HuffmanCodedPosAndEval3 = np.dtype([
    ('hcp', dtypeHcp), # 開始局面
    ('moveNum', np.uint16), # 手数
    ('result', np.uint8), # 結果(xxxxxx11:勝敗、xxxxx1xx:千日手、xxxx1xxx:入玉宣言、xxx1xxxx:最大手数)
    ('opponent', np.uint8), # 対戦相手(0:自己対局、1:先手usi、2:後手usi)
    ])
MoveInfo = np.dtype([
    ('selectedMove16', dtypeMove16), # 指し手
    ('eval', dtypeEval), # 評価値
    ('candidateNum', np.uint16), # 候補手の数
    ])
MoveVisits = np.dtype([
    ('move16', dtypeMove16), # 候補手
    ('visitNum', np.uint16), # 訪問回数
    ])

対局ごとにHuffmanCodedPosAndEval3を1つ出力し、1手ごとに1つのMoveInfoと、合法手の数分のMoveVisitsを出力する。

ノイズの影響の除去

AlphaZeroでは、自己対局時にルートノードにディリクレ分布のノイズを加えて、最善手だけではなく、新しい手も試すようにしている。
dlshogiでも、ルートノードにノイズを加えている。
(ただし、ディリクレ分布ではなく、ランダムに手の確率を1にした分布と方策の平均を使用している。)

ノイズにより選んだ手は、ほとんどの場合悪い手であるため、ノイズを含んだ分布を学習するのは方策の精度に悪影響がでる可能性がある。
そこで、ノイズがない場合に選択した手とノイズを加えたことにより選択した手が異なる場合に、その手の訪問回数から減算することにした。
これは、KataGoからヒントを得たアイディアである。
参考:[1902.10565] Accelerating Self-Play Learning in Go 3.2

AobaZeroとの相互運用

バイナリ棋譜フォーマット(hcpe3)から、AobaZeroの形式にも変換できるスクリプトを作成した。
DeepLearningShogi/hcpe3_to_csa.py at feature/hcpe3 · TadaoYamaoka/DeepLearningShogi · GitHub

また、AobaZeroの棋譜から、バイナリ棋譜フォーマット(hcpe3)に変換できるスクリプトを作成した。
DeepLearningShogi/aoba_to_hcpe3.py at feature/hcpe3 · TadaoYamaoka/DeepLearningShogi · GitHub

まとめ

指し手を学習するより、方策の分布を学習する方が精度が上がる見込みがある。
そのため、自己対局で方策の分布を出力できるようにした。

dlshogiの自己対局で新フォーマットのデータを生成して、方策の分布の有無による比較を別途行う予定。