TadaoYamaokaの開発日記

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

nnue-pytorchを試す その2

前回、nnue-pytorchをWindowsLinuxで動作確認した。

今回は、前回説明したqsearchで動く局面を除外した場合としない場合で精度の比較を行った。

qsearch除外の実装

Stockfishのnnue-pytorchの--smart-fen-skippingは、学習の実行時に指し手が駒をとる手と王手の局面を除くようになっているが、epochごとに繰り返し実行されるため学習時に実行するのは効率が悪い。
そこで、事前にすべての訓練データに対してqsearchを実行して、qsearchのPVが1以上の局面を除く処理を実装した。
(実行時に除くにはqsearchをマルチスレッドで動くようにするのが面倒だったという理由もある。)

filter_psvというツールを実装した。
nnue-pytorch/filter_psv at develop · TadaoYamaoka/nnue-pytorch · GitHub

シングルスレッドで実装しているが、ファイル単位でマルチプロセスで実行することで効率を上げることができる。
Linuxでは、xargsを使うと便利である。
コマンド例:

ls *.evalfix.psv.bin | xargs -n 1 -P 128 -i sh -c '/work/nnue-pytorch/filter_psv/filter_psv {} $(basename {} .psv.bin).filtered.psv.bin'

シャッフルの実装

nnue-pytorchは、訓練データをシャッフルする処理が実装されていない。
PyTorchのデータローダの機能で1epochごとにシャッフルを行うのが一般的だが、nnue-pytorchはバッチデータの作成を独自に実装しているため、標準のシャッフル処理が実行できない。

また、epochの概念がファイルのデータを使い切る単位ではなく、100000000局面を1epochとしている。
訓練データの局面は、epochとは関係なく順番に読み込み、使い切ったら初めから読み直す。

シャッフルは、学習実行前に、別のツールで行うことを想定している。

事前にシャッフルを行うのが手間がかかるのと、ファイルを使い切るごとに再シャッフルを行う方がよいため、nnue-pytorchにシャッフル処理を実装した。
訓練データの局面数分のインデックスの配列を作成し、シャッフルを行い、シャッフルしたインデックスの順にデータを使うようにする。
データを使い切ったら再シャッフルする。

ファイルのランダムアクセスが必要になるため、システムコールを減らすため事前に全て読み込むようにした。
実行環境のメモリが十分にないと実行できないが、全データを読み込めるメモリが十分にある環境で実行する想定である。
(最近のOSは、ページファイルをキャッシュするので、ファイルをランダムアクセスしてもそれほど性能劣化しないとは思うが。)

使用データ

訓練データには、最新のdlshogiの20ブロックのモデルの自己対局で生成した4.5億局面を、テストデータには、floodgateのR3500以上の棋譜からサンプリングした856,923局面(重複なし)を使用した。

qsearchのフィルタ処理

訓練データにqsearchのフィルタ処理を実行した結果、約3億局面(65%)が残った。

実行結果

100epoch訓練した結果、訓練損失と評価損失は、以下の通りになった。
青がqsearchのフィルタ処理なし、赤がqsearchのフィルタ処理ありである。

train_loss


val_loss


訓練損失(train_loss)の考察

訓練損失は、qsearchのフィルタ処理ありの方が、明確に低くなっている。
qsearchのフィルタ処理ありの方が、学習が進みやすいことがわかった。

評価損失(val_loss)の考察

しかし、floodgateの棋譜に対する評価損失は、どちらも上昇しており、訓練データに過剰適合している。

qsearchのフィルタ処理ありの方が、損失が高くなっており、訓練損失とは逆の結果になった。
テストデータにfloodgateの棋譜を使った理由は、訓練データを生成した方法とは異なるデータで評価した方がより強さと相関が見られると考えたためである。
しかし、NNUEの学習では、訓練データとは性質が異なるデータでは評価ができないようだ。

dlshogiの学習では、floodgateの棋譜との一致率と強さに相関があることを確かめているので、同じ方法をとったが、NNUEの評価には使えないようである。

強さを確認するには、実際に対局して確かめる必要がありそうである。

強さの確認

実際に対局を行い強さを確認した。

1手3秒、8スレッドで、互角局面集を使用して計測した。
基準ソフトとして、1手100ms、2スレッドの水匠5をリーグに追加している。

結果は以下の通りとなった。

   # PLAYER         :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)     W    D     L  D(%)
   1 suisho5-2th    :   324.8   20.4  2228.0    2370    94     100  2228    0   142     0
   2 qsearch-8th    :  -118.8   13.2   828.5    2375    35     100   825    7  1543     0
   3 normal-8th     :  -206.0   14.8   505.5    2379    21     ---   502    7  1870     0

qsearch-8thがqsearchのフィルタあり、normal-8thがqsearchのフィルタなしである。

qsearchのフィルタありの方が、R+87.2だけ強くなることが確認できた。
なお、4.5億局面を100epoch学習しただけでは、水匠5とは相当レーティングの差がある。

まとめ

nnue-pytorchにqsearchで動く局面を除いて学習を行う処理を実装した。
また、ファイルを使い切るごとにシャッフルする処理を実装した。

qsearchのフィルタのありとなしで比較した結果、qsearchのフィルタありで学習した方が強くなることが確認できた。

また、floodagateの棋譜を使って評価損失を計測すると、学習が進むほど損失が上がる傾向が見られた。
NNUEの学習では、訓練データと異なる方法で生成した棋譜はテストデータに使えないことがわかった。

次は、dlshogiモデルの知識蒸留を試したい。