TadaoYamaokaの開発日記

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

cshogiにWebアプリを追加

開発しているPythonの高速な将棋ライブラリcshogiに、ブラウザでUSIエンジン同士の対局が行える機能を追加した。
f:id:TadaoYamaoka:20210710233101p:plain

python -m cshogi.web.app --engine1 E:\game\shogi\gikou2_win\gikou.exe --engine2 E:\game\shogi\apery_wcsc28\bin\apery_wcsc28_bmi2.exe --byoyomi 1000 --port 8000

のように実行して、ブラウザから「http://localhost:8000/」にアクセスして、USIエンジン同士の対局を観戦することができる。

これだけだと、将棋所のような便利なネイティブアプリがあるのでわざわざこの機能を使う必要はないが、Google Colab上で実行することができる。

Google Colabで実行

USIエンジン同士の対局
# cshogiインストール
!pip install cshogi

# Lesserkaiビルド
!wget http://shogidokoro.starfree.jp/download/LesserkaiSrc.zip
!unzip LesserkaiSrc.zip
%cd LesserkaiSrc/Lesserkai
!make
%cd -

# 対局実行
import cshogi.web.app
cshogi.web.app.colab(engine1='/content/LesserkaiSrc/Lesserkai/Lesserkai', engine2='/content/LesserkaiSrc/Lesserkai/Lesserkai', byoyomi=1000)

のようにして実行できる。
f:id:TadaoYamaoka:20210710233717p:plain

人間との対局

engine1もしくは、engine2に「human」を指定すると、人間との対局もできる。

cshogi.web.app.colab(engine1='human', engine2='/content/LesserkaiSrc/Lesserkai/Lesserkai', byoyomi=1000)

まとめ

GPUをもっていない方にも、Google Colab上でディープラーニングの将棋AIの学習から、自分で学習したモデルとの対局を体験してもらいたくてWebアプリを開発した。

バックエンドは、Colabに標準でインストールされているFlaskを使用して、フロントエンドはBootstrapとjQueryを使用した。
デザインはシンプルで、ほぼ最低限の機能しか実装していないので、徐々に使いやすくしていきたい。
バグなど教えてもらえると助かります。

Windows TerminalでMSYS2を起動する設定

備忘録
settings.jsonのprofilesのリストに以下の設定を追加する。

    {
      "closeOnExit": true,
      "colorScheme": "Campbell",
      "commandline": "cmd.exe /c \"set CHERE_INVOKING=1&& set MSYSTEM=MINGW64&& C:\\msys64\\usr\\bin\\bash.exe --login\"",
      "fontFace": "Consolas",
      "fontSize": 10,
      "guid": "{xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}",
      "historySize": 9001,
      "icon": "C:\\msys64\\mingw64.ico",
      "name": "MSYS2 MinGW 64-bit",
      "snapOnInput": true,
      "startingDirectory": "c:\\msys64\\home\\%USERNAME%"
    },

guidは、PowerShellで、以下の実行して取得する。

[guid]::NewGuid()

将棋AI実験ノート:ブロック数とフィルタ数とSE有無の比較

同一データを学習した際に、ResNetのブロック数とフィルタ数とSE(Squeeze-and-Excitation)有無により、精度、強さがどう変わるかを比較した。

比較対象

  • ResNet 10ブロック、192フィルタ
  • ResNet 15ブロック、224フィルタ
  • ResNet 20ブロック、256フィルタ
  • 上の3パターンそれぞれについて、SE有無

教師データ

  • floodgate R3800以上 2019~2021/5 (mateで打ち切り)
  • 水匠3改で、100万ノード固定。初期局面から24手までにランダム16回(評価値最善手から200以内)
  • BURNING BRIDGES(TSEC電竜)で、100万ノード固定。初期局面から24手までにランダム16回(評価値最善手から200以内)
  • dlshogi with GCTで、3000プレイアウト

合計約3億局面

なお、やねうら王でhcpe3を生成できるように改造した。※生成が終わっても終了しないバグあり(原因不明。誰か直してくれると助かります)。

学習

  • 5エポック
  • 4096バッチ
  • 学習率 0.04から1エポックごとに半分
  • 平均化、評価値補正あり
  • 2エポックからSWA開始
  • 初期値から学習
  • 方策の分布を学習(dlshogi with GCTの棋譜のみ)

精度比較

テストデータに、floodgateのレート3500以上の対局の棋譜からサンプリングした856,923局面を使用した。

SWAなしモデル
  • SEなし
モデル 方策損失 価値損失 方策正解率 価値正解率 方策エントロピー
10ブロック 1.6379119 0.51276293 0.4730206 0.72314205 1.51876836
15ブロック 1.61451681 0.54952968 0.47836673 0.70189649 1.51121722
20ブロック 1.60430686 0.5144527 0.4825369 0.71952424 1.41729831
  • SEあり
モデル 方策損失 価値損失 方策正解率 価値正解率 方策エントロピー
10ブロック 1.64183599 0.51881258 0.47337911 0.71709992 1.48604457
15ブロック 1.62213967 0.5027243 0.47863532 0.73173113 1.43584003
20ブロック 1.60740022 0.50509531 0.48131072 0.72798019 1.43475938

ブロック数が大きいほど精度が高くなり、SE有無ではSEありの方が精度が上がる。
(SEなし15ブロックの価値は、10ブロックよりも価値の損失が上がっているが、その分方策の損失が低くなっている。
 方策と価値は交互に学習が進むため、エポックごとにどちらの損失が下がるかが入れ替わることがある。)

SWAありモデル
  • SEなし
モデル 方策損失 価値損失 方策正解率 価値正解率 方策エントロピー
10ブロック 1.57109508 0.49425657 0.48709244 0.7399652 1.51186802
15ブロック 1.54070064 0.48825147 0.49433273 0.74414004 1.47669404
20ブロック 1.5281703 0.48567744 0.49743904 0.74624089 1.46332859
  • SEあり
モデル 方策損失 価値損失 方策正解率 価値正解率 方策エントロピー
10ブロック 1.57355652 0.49556847 0.48687874 0.73878457 1.4777351
15ブロック 1.5464818 0.48941931 0.49345689 0.74358067 1.43782014
20ブロック 1.53439988 0.488463 0.49672319 0.74488159 1.41932988

ブロック数が大きいほど精度が高い傾向は、SWAなしモデルと同じである。
SWAなしと比較して、SWAありの方が損失が大きく下がっており、精度が高くなっている。
意外なことに、SE有無では、SEなしの方が精度が高い。
SEとSWAは相性が悪いようだ。

強さ比較

SWAあり、SEなしのモデルについて、強さを比較した。
dlshogiのエンジン設定は2GPU、3スレッド。

持ち時間1分、1手1秒加算
  • ResNet10ブロック vs GCT電竜(model-0000167) vs 水匠3改(8スレッド)
# PLAYER          :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)     W    D     L  D(%)
1 susho3kai8th    :     5.0    6.3  2474.5    4847    51      90  2341  267  2239     6
2 model-0000167   :    -2.1    6.4  2404.5    4853    50      55  2225  359  2269     7
3 resnet10        :    -2.8    6.3  2396.0    4850    49     ---  2209  374  2267     8
  • ResNet10ブロック vs ResNet15ブロック vs 水匠3改(8スレッド)
# PLAYER          :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)     W    D     L  D(%)
1 resnet10        :    12.5    6.3  2642.0    5020    53      63  2498  288  2234     6
2 resnet15        :    10.7    6.5  2624.0    5022    52     100  2479  290  2253     6
3 susho3kai8th    :   -23.2    6.3  2264.0    5018    45     ---  2111  306  2601     6
  • ResNet10ブロック vs ResNet20ブロック vs 水匠3改(8スレッド)
# PLAYER          :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)     W    D     L  D(%)
1 resnet10        :    20.2    7.1  2093.0    3857    54     100  1953  280  1624     7
2 resnet20        :    -9.0    7.2  1855.0    3857    48      63  1699  312  1846     8
3 susho3kai8th    :   -11.2    7.3  1835.0    3852    48     ---  1691  288  1873     7

10ブロックの強さは、GCT電竜とほぼ同じになっている。
方策の分布を学習した場合でも強くできることが確認できた。
15ブロックと20ブロックの方が精度が高いが、10ブロックの方が強いという結果になった。

持ち時間5分、1手2秒加算
  • ResNet10ブロック vs GCT電竜(model-0000167) vs 水匠3改(8スレッド)
# PLAYER         :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
1 susho3kai8th   :    19.7   13.8   524.5     968    54      97  484   81  403     8
2 resnet10       :    -3.0   14.1   478.5     969    49      86  428  101  440    10
3 model-0000167  :   -16.7   14.2   452.0     973    46     ---  409   86  478     9
  • ResNet10ブロック vs ResNet15ブロック vs 水匠3改(8スレッド)
# PLAYER          :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
1 resnet15        :    11.3   13.5   515.0     984    52      86  476   78  430     8
2 susho3kai8th    :    -1.9   14.1   485.5     979    50      72  443   85  451     9
3 resnet10        :    -9.4   14.0   471.5     981    48     ---  428   87  466     9
  • ResNet10ブロック vs ResNet20ブロック vs 水匠3改(8スレッド)
# PLAYER          :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
1 resnet20        :     8.5   13.8   515.5     995    52      84  464  103  428    10
2 resnet10        :    -3.6   13.5   488.5     992    49      55  436  105  451    11
3 susho3kai8th    :    -4.9   13.0   484.0     989    49     ---  442   84  463     8

持ち時間が長い場合は、10ブロックより15ブロック、20ブロックが強いという結果になった。
持ち時間が長い場合は、ブロック数が大きい方が有利と言えそうだ。
この持ち時間では15ブロックが一番良さそうである。
さらに持ち時間が長くなると20ブロックの方がよくなる可能性がある。

また、10ブロックのモデルがGCT電竜よりも強くなっている。
持ち時間が長い場合は、方策の分布を学習した方が良さそうである。

まとめ

同じ教師データを使用して学習した場合、モデルのブロック数が大きいほど精度が上がることがわかった。
SWAなしの場合、SEありの方が精度が高くなる。
しかし、意外なことに、SWAありだと、SEがない方が精度が高くなった。
SEとSWAは相性が悪いようだ。

強さの比較では、持ち時間が短い場合は、10ブロックで指し手のみを学習するのが強そうである。
しかし、持ち時間が長くなると、大きいブロック数で、方策の分布を学習した方が強くなりそうであることがわかった。
GCTが電竜戦で優勝して、世界コンピュータ選手権では振るわなかったことを裏付ける結果となった。

dlshogiをPyPIに登録

今までdlshogiの学習を行うために、python環境を作成して、boostをインストールして、C++からPythonモジュール(cppshogi)をビルドしてという手順が必要だったが、pipコマンドでインストールできるようにPyPIに登録した。

pip install dlshogi

アップデートの場合は、

pip install dlshogi -U

で、導入できるようにした。

学習用スクリプトも複数(train_hcpe.py~train_hcpe3.py)あったが、train.pyに統一した。

python -m dlshogi.train

で学習を実行できる。

これで、Google Colabでの学習も容易になる。


utilsにあったスクリプトは、dlshogi.utilsパッケージに移動した。

python -m dlshogi.utils.csa_to_hcpe3

のようにして、実行できる。

注意事項

新しいtrain.pyは、今までと保存形式が変わっている。互換性のために以前の形式も読めるようにしている。
詳細は、この記事を参照。

dlshogiの教師データのフォーマットには、hcpeフォーマット(指し手のみ、局面単位)と、hcpe3フォーマット(方策の分布、対局単位)の2つがあるが、自動判別してどちらも読めるようにしている。
複数ファイルを入力できるので、混在もできる。
hcpe3を指し手のみで学習するには、「--temperature 0」を指定する。
指し手のみの場合、方策が決定論的にならないように、エントロピー正則化項の係数「--beta 0.001」を指定するとよい。

「--use_average」と「--use_evalfix」と「--use_swa」はデフォルトオフだが指定した方がよい。

GPUにTensorCoreがある場合は、「--use_amp」を指定した方がよい。

その他

dlshogiのソースを修正しなくても、ユーザ定義のモデルファイルを学習できるように、「--user_network」オプションを追加した。
「--user_network package0.package1.ClassName」
のようにして、クラス名をパッケージ付きで指定する。

まとめ

学習環境の構築でハードルがあったが、簡単に構築できるようにした。
これでdlshogiの学習を簡単に試せるようになったと思う。


dlshogi.cppshogiと、cshogiと組み合わせて、Pythonのみでdlshogiのモデルを使って大会参加とかもできるので、ディープラーニングと将棋AIに興味あるけどC++は分からないという方も試してみてください。

将棋AIの進捗 その57(SWAの修正)

dlshogiの学習では、SWA(Stochastic Weight Averaging)を導入している。

今までは、1世代学習するごとに、平均化した重みを出力して、次の世代ではその重みを使用して学習していた。
しかし、SWAは通常複数エポックに渡って平均化してから、最後に平均化した重みを出力を行う。
また、Leela Chess Zeroでは、複数世代にわたって重みを平均化しているようなので、dlshogiもそのように変更した。

実装方法の変更

dlshogiにSWAを実装したときは、まだPyTorchに正式にSWAが組み込まれていなかったため、contribのソースをコピーして使用していたが、このソースはSWAモデルの保存/読み込みに対応していなかったので、PyTorch 1.7以降で使えるようになったAveragedModelを使用するように変更した。

forwardの複数引数対応

AveragedModelは、モデルのforwardの引数が一つの場合しか想定されていない。
dlshogiでは、2つの引数を使用するため、そのままでは利用できなかった。

そこで、AveragedModelのインスタンスのforwardメソッドを一時的に書き換える処理を行った。

以下のように2つの引数を1つの辞書で受け取って、それを展開して元のメソッドに渡すようにした。

        forward_ = swa_model.forward
        swa_model.forward = lambda x : forward_(**x)
        update_bn(hcpe_loader(train_data, args.batchsize), swa_model)
        del swa_model.forward

データローダでは、2つの入力を辞書で渡すようにした。

def hcpe_loader(data, batchsize):
    for x1, x2, t1, t2, value in Hcpe3DataLoader(data, batchsize, device):
        yield { 'x1':x1, 'x2':x2 }

精度比較

今までの1世代ごとに平均化する方法と、複数世代に渡って平均化する方法で精度を比較した。

訓練データには、dlshogiの強化学習で生成した3世代分のデータ(各世代1億局面)を使用した。
1世代目はSWAなしで学習し、2世代目からSWAありで学習した。
今までの方法では、2世代目平均化した重みを出力して、それを読み込んで3世代目を学習した。
新しい方法では、2世代目と3世代目で続けて重みを平均化した。
4回測定し、テスト損失の平均で評価した。
テストデータには、floodgateのレート3500以上の対局の棋譜からサンプリングした856,923局面を使用した。

テスト方策損失 テスト価値損失
今までの方法 1.782588468 0.522283693
新しい方法 1.780505845 0.521579308
SWAなし 1.84344225 0.545063128

新しい方法では、今までの方法より、方策、価値ともに損失が低下しており精度が向上している。
今までの方法でも、SWAなしよりは精度が高い。

使用方法

新しい方法は、dlshogiのtrain_hcpe3.pyに実装している。
複数世代でSWAの重みを引き継げるように、モデルと状態の保存形式を、PyTorchのcheckpoint形式に変更した。
今まではChainerからの互換性を引きずっていたので、モデルと状態は別々のファイルになっていた。

--checkpointと--resume

「--checkpoint」で、保存ファイル名を指定する。
PyTorchのチュートリアルによると拡張子に*.pthをつけるようだ。
読み込みは「--resume」に「--checkpoint」で保存したファイル名を指定する。

--initmodelと--stateの廃止

今まで使用していた「--initmodel(-m)」は廃止する(互換性のため残している)。
互換性のため「--resume」で今までの「--state」で保存した状態も読めるようにしている。
「--state」は廃止した。

--model

「--model」で、SWAの重みを反映したモデルを出力する。
このファイルは、これまでのモデルファイルと互換性がある。

その際、Batch Normalizationの再計算を行う。
ここが時間がかかるので、途中でSWAを反映したモデル出力が必要なければ、「--checkpoint」だけで学習は継続できる。

なお、Batch Normalizationの再計算は全訓練データの順伝播を行うため時間がかかるが、使用する訓練データの量が精度にかなり影響するため、減らさない方がよい。


今回の変更は、train_hcpe3.pyにしか反映していない(train_hcpe3.pyは、train_hcpe.pyと同等の学習もできる)。
train_hcpe.pyは互換性のために変更していない。

まとめ

複数世代に渡って重みを平均化できるように、SWAの実装を変更した。
新しい方法の方が精度が上がることが確かめられた。

将棋AI実験ノート:自己対局時のノードの再利用

以前に、dlshogiの自己対局でノードの再利用を行うと、テスト損失が上昇する(過学習する)という問題が起きたことを書いた。

方策の分布を学習するようにしたため、問題も起きにくくなっている可能性があるので、ノードの再利用(ルートノードはクリア)を行うオプションを追加した(--reuseオプション)。

比較

再利用ありとなしで1.5億局面生成して、精度を比較してみた。
学習済みモデルに対して、方策の分布を学習するようにして追加学習を行った。
8回測定を行い平均をとった。
テストデータに、floodgateのレート3500以上の対局の棋譜からサンプリングした856,923局面を使用した。

再利用 テスト方策損失 テスト価値損失 テスト方策エントロピー
なし 1.61784394 0.466380099 1.305249458
あり 1.60676804 0.463773155 1.315900149

考察

再利用ありの方が、方策、価値ともにテスト損失が下がっており、精度が上がっている。
以前に起きたようなテスト方策エントロピーの低下も起きていない。

強さの比較

持ち時間1分1秒加算、2GPUで対局して強さを比較した結果は、以下の通り。

   # PLAYER           :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
   1 reuse            :    13.9   10.5   913.0    1725    53      61  821  184  720    11
   2 noreuse          :    11.3   10.4   903.5    1725    52     100  814  179  732    10
   3 susho3kai18th    :   -25.1   10.3   770.5    1724    45     ---  693  155  876     9

有意差はなかった。

生成速度

GPUあたり4スレッド、8GPU

再利用 nodes/sec
なし 276.1
あり 153.9

GPUあたりのスレッド数が同一だと、生成速度は55.7%になった。
ゲーム木が深くなるため、ノード選択の回数が増えるためと考えられる。
再利用する場合は、GPUあたりのスレッド数を増やす必要がある(その分ハードの制約が厳しくなる)。

まとめ

自己対局時にノードを再利用することで、テスト局面に対する精度が上がることが確かめられた。
ただし、精度の向上が強さには反映されることは測定できていない(持ち時間をかえる、強化学習を世代を増やすなど条件を変えてみる必要がある)。

ノードを再利用する局面の生成速度が落ちるため、質をとるか量をとるかのトレードオフになる(どっちが良いかは確認できていない)。

将棋AI実験ノート:方策の学習に温度パラメータを導入

以前にdlshogiで方策の分布を学習できるようにしたが、方策の分布を学習したモデルで対局すると、指し手のみを学習したモデルよりも弱くなるという問題が起きている。
温度パラメータの調整である程度強くできたが、指し手のみを学習したモデルには及んでいない。
分布を学習することで、探索する手が広がるため、探索の深さが浅くなることが原因と考えている。

この特性は、強化学習を行う際には、新しい手を探索しやすくなるため有効に働く。
モデルの方策と価値のfloodgateの棋譜に対する精度も高くなることが分かっている。
世界コンピュータ選手権向けのモデルでは、方策の分布を学習して強化学習したモデルで生成した指し手を、別のモデルで学習するという手法をとった。

温度パラメータで調整

指し手のみを学習する際、方策を記録したhcpe3フォーマットから、指し手のみを記録したhcpeフォーマットに変換を行っていたが、これを温度パラメータで調整できるようにした。
学習する方策\piを、ルートの子ノードaの訪問回数をN(a)、温度\tauとして、
\displaystyle
\boldsymbol{\pi} = N(a)^{1/\tau}
として、学習する。

温度が0の場合は、指し手のみを学習する、温度が1の場合は、方策の分布を学習する。
温度が1より低い場合は、より訪問回数が多い手の確率が高くなり、温度が1より大きい場合はより確率が均一になる。

温度パラメータを変えて学習

温度パラメータを変えて学習した際の、テスト損失、テストエントロピーを比較した。
学習データにはdlshogiの強化学習で生成した4千万局面を使用し、既存モデルに追加学習した。
8回測定を行い平均をとった。
テストデータに、floodgateのレート3500以上の対局の棋譜からサンプリングした856,923局面を使用した。

温度 テスト方策損失 テスト価値損失 テストエントロピー
0 1.817141809 0.52847061 1.350876433
0.3 1.770466749 0.527390403 1.354992663
0.5 1.739721565 0.52895042 1.393257098
0.8 1.704725165 0.523376935 1.489995551
0.9 1.690805215 0.523304745 1.555975463
1 1.681393201 0.522688101 1.592738526
1.1 1.680972238 0.523270845 1.639763045

f:id:TadaoYamaoka:20210514131844p:plainf:id:TadaoYamaoka:20210514131848p:plain

考察

温度が1の場合に価値の損失が一番低くなる。
また、温度が上がるほどエントロピーが上昇する(確率が均一に近づく)。
上で述べた性質が実験からも確認できた。

強さの比較

温度1と温度0.9で学習したモデルを使用して強さを比較した。
探索パラメータは、温度1で学習したモデル用に調整している。

持ち時間1分、1秒加算

   # PLAYER                 :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
   1 gct_075_opt20_tmp09    :     6.6   13.0   592.0    1152    51      65  527  130  495    11
   2 gct_075_opt20_tmp1     :     2.0   12.9   582.5    1155    50      83  519  127  509    11
   3 susho3kai18th          :    -8.6   12.7   554.5    1151    48     ---  501  107  543     9

※gct_075_opt20_tmp09:温度0.9で学習したモデル、gct_075_opt20_tmp1:温度1で学習したモデル

有意差なしという結果になった。
ただし、モデルに合わせて探索パラメータも調整が必要になるので、差が出なかった可能性がある。

まとめ

方策を学習するか、指し手のみを学習するかを温度パラメータで調整できるようにした。
これによって、hcpe3フォーマットからhcpeへの変換が不要になる。
train_hcpe3で、指し手のみの学習もできるようになった。

温度パラメータによって、精度、エントロピーに違いがでることが確認できた。
温度が1の場合は精度が高く、エントロピーが高くなる。温度が0に近づくにつれ精度とエントロピーが下がる。
エントロピーが下がると、探索の幅より深さを優先することになるので、対局では有利に働く場合がある(持ち時間にも依存すると思われる)。