nnue-pytorchのメモ

速報的意味合いが強いので完成度が低いまま公開しつつ更新する。

 

WCSC34優勝のたぬきチームのnnue-pytorchの動作テストである。

 

github.com

 

チームのアピール文。

https://www.apply.computer-shogi.org/wcsc34/appeal/Hey,_you_wanna_be_a_CSA_member/appeal.pdf

 

python環境はWindows11のpython 3.11で試す。

近頃dockerやWSLなのだろうが、私はWindowsネイティブメインでまだやっている。

 

よくわからないので、まずは最新ブランチを採用

 

仮想環境も使わずライブラリをいれる。

pip install python-chess==0.31.4 pytorch-lightning torch matplotlib

 

pytorch-lightningは新しすぎるらしく1.9.0に入れなおす。

他にtensorboardXも要るらしいのでpipで入れておく。

 

教師データはこちらを使う

 

教師データをシャッフルして9:1に分けて学習開始。

GPU負荷がはいりlossが順調に減っているので学習されてそうである。

 

とりあえずここまで

実のところ以前も触っているので難なく動くが,python-chessをまだ要求しているなど荒削りの部分は全く改善していないようだ。

pytorch-lightningを含め新しいライブラリに対応させておくようなことも今後を考えると必須だろう。

あまり確認していないがstockfish界隈はその辺どうなっているんだろうか。

 

一晩して、学習はできたようだ。

可視化ツールなども動作していることを確認。

実際のエンジンで使えるかどうかということで試す。

 

nnue-pytorchは1024x2-8-96という実験treeを持ってきたことが判明。

エンジン本体は以下のtreeが1024x2-8-64なので、これの64を96にしてビルドしたものを用いる。

github.com

 

で、一応弱いがそこそこ将棋になっている棋譜が完成した。

プログラムとして一通り動作していることを確認したことになる。

 

ちなみに、たぬきチームの野田さんからは学習データとして自身がHugging Faceに公開しているHao depth9を勧められた。

これから試す人は是非使って頂きたい。

 

huggingface.co

 

ということで一旦完結。

第34回世界コンピュータ将棋選手権

会場は川崎市の産業振興会館。

別件で来たこともあるが,選手権としては実はまだ3度目の来館。

初参加の2018年と2019年に次いでの3度目である。

 

全てコロナ禍のせいであるが,実際のところ電竜戦の運営をしているものからすると

リモートの方がずっと効率的で参加者も運営も楽である。

昨年もリモート参加であったが,会場が閑散としていたため今年度は現地参加が義務付けられている。

 

前日入りして一次予選会場の様子を見ながら挨拶に回る。

当然随分ぶりなのでちょっと顔と名前の認識が怪しい人もいたりするがそういうのはお互い様。近況報告とともに技術的な立ち話を楽しむ。

女流四段の竹部先生にお茶を入れてもらう(ノルマひとつ達成である)

 

二次予選の朝。

会場入りしてから設定を始めるのは高リスクなので早朝に起きてホテルで設定を始める。

随分久しぶりにUbuntuインスタンスを設定するとパッケージのコンフリクトがあった。原因がよくわからずとにかく過去に使っていたパッケージバージョンを指定しながらコマンドラインから入れてみた。

GPUドライバにも不具合があるのかGPUが認識されていなかったので,ドライバを入れなおしてリブートしたのは1回戦の開始15分前である。

なお,この時点でホテルを出ていないのでホテルWiFiでのリモート対局が確定する。

会場徒歩圏のホテルでこの状況はまったく酷いものである。

 

初戦,対局開始直後に気づく。

使用メモリの設定が桁違いに少ない。普段のPCの設定でビルドしてしまったようだ。

これはマシンパワーを生かせない感じ。

 

対局スケジュールでどこか30分程度空くところがあれば移動しようと考えるがどうやら無理っぽい。開始早々の千日手でもない限り今日は一日ホテルリモートとなりそう。

 

設定変更しようとしたが何故か二回戦が前倒しでスタートしたためそのままの設定で二回戦も開始。

完全リモートの方がずっとマシと言えるくらいのしっちゃかめっちゃか状態。

Slackを見ると公式アナウンスは前倒しすると書かれていた。これはもうホテルから動けないな。

会場に土産を置いてあるのだが,対局相手に配ることもできなくなったのでもうだれか適当に食ってくれという感じだ(笑)

 

三回戦も前倒しで始まってしまった。

早朝から何も食べてないが,このホテルが外出時に電源が落ちるタイプだと困るので動けない。結局三回戦終了後に移動した。

 

ーーー

追記5/7:

と、ここまで書いて実際に移動したのだが全く更新することが出来ずに大会を終えた。

結果的に三連勝後に三連敗を喫するなどして、結局二次予選敗退であった。

 

詳細は更新するかもしれないし別に立てるかもしれない。

とにかく準備ミスの範疇に入るものが多発で上位争いをする資格がないとしか言えない状況であったので、予選落ちは妥当と考えている。

もちろん組み合わせの妙として決勝入りの可能性もゼロではなかった気もする。

言い訳は色々あるが技術的な未公開ネタは出す機会があると思うので保留する。

 

ほぼ予想範囲内であるが移動はデメリットが大きすぎるので上位争いを目指すのであれば、やはりひとりで参加するのは危険である。

もしかすると普段使いの27インチモニタと広い机があったら、ホテルから会場への移動がなかったら、朝の設定でトラブルがなかったかもしれないという話である。

 

最低限の役割として不戦敗をせずに切られ役を全うできたことで満足することにする。

現地参加で有意義であったことは電竜戦含め多くの若手を喚起しモチベーションアップに繋げた点であろうか。

オンラインのみでの知り合いも多く一通り挨拶ができたかどうかはやや自信がない。

 

予選落ちが確定してからはゆっくり眠れるようになった。

それにしても要所で人為的ミスをしてしまうとストレスと疲労感がすごい。

二番絞りの二番煎じ

扱い遅れましたが、DeepMindがまた話題の新作です。

本当に素晴らしいですね。

www.itmedia.co.jp

 

一切の探索なしでチェスのグランドマスターレベルだそうです。

どこかで聞いたような話ですね。

二番絞り@将棋倶楽部24の戦型分析 - 48's diary

二番絞り@将棋倶楽部24の戦型分析・続編 - 48's diary

 

一年前に私が将棋で到達したところです。

多くの対戦プレイヤーには感謝申し上げます。

実際チェスよりも将棋の方が駒の数、盤面サイズ、駒の再利用と複雑性が高いことからコンピュータで扱う場合も高度であるとされています。

 

また、Google DeepMindの方からプログラムの方が出ています。

GitHub - google-deepmind/searchless_chess: Grandmaster-Level Chess Without Search

どちらかというと学習後のモデルの方を見せていただきたかったですね。アルゴリズム的にはそれほど複雑ではないのですが、とても計算量が真似できないレベルですので

 

で、二番煎じまでは言い過ぎに思っていたのですがこの手法、本当に二番絞りそっくりです。

二番絞りは2020年にKristallweizenの学習データの流用と言うことで始めたプロジェクトで命名由来もそのままです。KristallweizenはNNUE型評価関数で多くの局面評価で教師データを生成します。8億くらいまでは覚えています。

本件のDeepMindも同じようにNNUE型評価関数が導入されたStockfishで多くの局面評価を行い、そのデータを深層学習モデルに学習させたものです。まったく同じプロセスですね。ただ、局面数が1000億の桁だそうです。

敢えて差異を探すと二番絞りはonehotベクトルで学習させたのに対してDeepMindは合法手全ての局面評価値を算出した分布学習であった点ですね。計算量も保存するデータ量も桁違いです。

 

二番絞りの方はその後リソース不足に悩みながらもAobaZeroのデータ流用や独自の強化学習に入ります。

計算機リソースがあればKristallweizenのデータのみでも同じレベルに届いたかもしれませんね。暇と金が余っていれば試してみたいですが計算機リソースをそこに突っ込む気はしないですね。

Googleほんとうにすごい。私の初期の手法のみでも到達可能と検証いただいて感謝申し上げます。

 

Kristallweizen覚書

2018年5月5日世界コンピュータ将棋選手権で初参加優勝

第28回世界コンピュータ将棋選手権

2019年5月5日世界コンピュータ将棋選手権で準優勝

第29回世界コンピュータ将棋選手権

後でみるとこの2年が選手権参加数のピークでした。

 

同5月9日GitHubで公開。

GitHub - Tama4649/Kristallweizen: 第29回世界コンピュータ将棋選手権 準優勝のKristallweizenです。

 

直後からニコ生で利用されるが表記名が安定しません。

ニコ生の評価値の話 - 48's diary

 

2019年7月13日にベラルーシミンスクの「銀冠」と言う名の将棋倶楽部からのYouTube中継が藤田麻衣子さん(元女流棋士の方です)のツイートで拡散されておりました。

公開から2か月程度でこの使われ方です。

 

www.youtube.com

 

このタイミングで中村真梨花女流のツイートです。

ニコ生の方で聞き手をされた後のようですが、もしかしたらこの時期に既に将棋AIに関して詳しい情報が入っていたのかもしれません。プロ棋戦の関係者になかなか名前を憶えていただけなかった中珍しいケースです。しかたなく妥協で呼称も「白ビール」を容認するようになりました。

 

その後、ドワンゴ社主催の新棋戦ということで叡王戦が開催されたのですが、見届け人として応募して見届けて来ました。評価値表示のソフト制作者として特別待遇頂いて楽しい一日でした。当日突発で出演することになりましたが動画はまだ残っていると思います。

叡王戦予選の見届け人をやってきました。 - 48's diary

 

この後、コロナ禍でほとんど情報交換がなかったのですが、随分普及していたようです。というのも将棋界は不思議とPCがネットに繋がっておらずバイナリだけUSBメモリで回覧されることが多かったようです。

IT産業に居られる方は驚くと思いますが、未だに32bitバイナリは無いかとかAVX2非対応機はどうしたらいいのかと言う質問が飛びます。

 

2021年秋になって日本将棋連盟からKristallweizenを公式戦配信の評価値・読み筋に使う件の打診がありました。オンラインでの打診でしたのでその後正式な書面でもあるものかと思っていたのですが、そういうのは特になく日本将棋連盟としてもプレスリリースもなく現在に至ります。

連盟モバイルの勝率表示の件 - 48's diary

ドワンゴはじめ所謂報道側のAI利用は多くあったのですが連盟が使うと言うのは歴史的転換点と思いますが、私の知らないところでプロ棋士間でもそういう意識もなく日常的なツールとなるほどに普及していたようです。この辺はコロナ禍の影響ですがリアルタイムの情報を得ていないのを残念に思います。

前世代に当たるelmoや技巧を利用している棋士は若手の一部と後に聞きました。

つまり、2019年から2021年の間にほとんどのプロ棋士が日常的にAIを使うようになったわけです。

 

その後、2022年から毎日新聞社朝日新聞社YouTube中継にKristallweizenを使用することになります。

将棋のYoutube配信 - 48's diary

第80期名人戦第5局@倉敷 - 48's diary

第81期名人戦第3局@高槻 - 48's diary

2019年から個々の記者は使っていたそうなのですが利用許可を取って公に使用するという考えがずっとなかったそうです。ある記者に聞くと自分でインストールしたものでもなくそもそも出所が不明だったそうです。そりゃ普段使いしていても公に出来ませんね。ソフト名も知らなかったそうです。連盟の利用がきっかけですね。

ちなみに投入前後でライブ接続数は桁違いに増えたそうです。無音でほぼ静止画の将棋対局を考えれば納得です。

そういえば先日朝日新聞社YouTubeチャンネルの登録者数が10万人を超えて銀の盾を頂いていました。記者や周辺スタッフの方が力を入れておられますが紙面とは違うメディアとして独立できそうな勢いですね。

 

それから印象的な出来事です。

2022年のヨーロッパ将棋選手権でのひとこまです。

欧州でもKristallweizenが将棋のキーワードとして多くのプレイヤーに広く認識されています。

詳しくは知らないのですがこの年はコロナ禍明け的な意味もあるのか世界選手権も併催されて大イベントとなったようです。

 

欧州の方が名前を認識してくれているのが面白いですね。

Chess Programming Wikiにページがあるくらいです。もしかしてチェス界隈の方が名前が通るのでしょうか。

Kristallweizen - Chessprogramming wiki

 

そういえば、その後の開発はどうなったかという話ですが、2020年から電竜戦絡みもあり二番絞り開発に重点を置いており、世界選手権で複数エントリーを禁じているためお蔵入りとなっています。昨年か一昨年の中継動画にもありましたが歴代最高勝率を誇ったままだそうです。

幾つかの公にしていないアイデアはあるので、そのうち更新するかもしれません。また思想的なものを引き継いでほぼ後継的な活動をして下さるチームも複数ありますので有難い限りです。

 

Go言語で将棋(2024春)

Pythonのイベントに呼ばれていた頃はメインで弄っていたのはGo言語でした。

非常にシンプルで並列性能が出るのが魅力ですね。Googleらしい言語です。

言語の習作として将棋のエンジンをフルスクラッチで作り始めたのが世界選手権優勝した2018年の末です。

Go言語雑感 - 48's diary

構想としては当時非同期プログラムが書きにくかったPythonに代わればプログラム自体が見通しよく発展的改造をするのにも便利だなという動機と、もう一つは完全にフルスクラッチでゲーム系の一通りのアルゴリズムをコピペせずに実装する練習という意味でもあります。

前者としては2019年以降の白ビールのMulti PonderシステムがGo言語に完全に置き換わっており非同期で複数のエンジンを操ります。類似の例としてはGPS将棋が大規模クラスタとして有名ですがこちらは1秒刻みの同期並列ですので全く仕組みが違います。(非同期で多数のクライアントと通信するので所謂今風のサーバの仕組みです)

後者の方はいろいろと遊べますので並列化にしても古典的アルゴリズムをわざわざ実装した実験などがあり下記のTSEC3ではYBWCを実装しその性能を計測しています。

電竜戦TSEC3予選の結果 - 48's diary

ちなみにこのYBWCモデルでfloodgateレートが約2500、YSS1000Kにやや劣る程度でした。

スクラッチ勢の行方(その2) - 48's diary

 

ほかにもGo言語のバージョンアップなので仕様変更があった場合のテストコードとして利用しています。下記の件では書式が変わっていますが性能的にはあまり影響なかった例ですね。

Go 1.22のrange over int - 48's diary

 

最近、追加で思い付きの改造をしましたが思いの外影響がなく、相変わらずYSS1000Kに勝ち越すのは難しそうです。

 

 

昨年と比べシングルスレッドで2倍近い探索速度が出るようになりましたが探索深さは一つ変わるか変わらないかくらいで、結果棋力への影響は微差のようです。(YBWC外してます)

1fileマッチ関係で確認したことはC言語で類似実装をすれば更に2倍程度速いことでやはりビット演算や小さいループ・分岐を多く使うプログラムは高級言語向きではないようです。

 

ほかにも独自実装のエンジンを複数持っておくのは電竜戦運営側のテスト対局でも多く役に立ちます。

Electron将棋のデバッグにもお役に立てたようです。

USIプロトコルにおけるScoreの扱い · Issue #786 · sunfish-shogi/electron-shogi · GitHub

#色々弄ってますがすぐ忘れてしまうので自分用のメモです。

---

追記:

YSS1000kはStockfishを参考にされているらしいのでフルスクラッチで勝ちに行くのはさすがに難問だったかもしれません。

---

追記4月22日:

その後,一部バグ発見で修正後シングルスレッドでfloodgateレート約2500を確認。

YSS1000Kには届かず。

第2回マイナビニュース杯電竜戦ハードウェア統一戦の戦型分類

第2回マイナビニュース杯電竜戦ハードウェア統一戦が無事終了しました。

第2回マイナビニュース杯電竜戦統一ハードウェア統一戦

 

優勝はBURNING BRIDGESとなりました。2020年末の電竜戦TSEC以来の優勝となります。

全ての棋譜は公開されており、またまとめてダウンロード可能となっています。

 

せっかくですので簡単に集計してみました。

 

柿木将棋にお任せですが以下のようになりした。

 

棋譜数    :   250
先手勝ち   :   129  宣言勝ちを含む
後手勝ち   :   101  宣言勝ちを含む
先手宣言勝ち :     8
後手宣言勝ち :     8
千日手    :    19
持将棋    :     1
中断     :     0
先手勝率   : 0.561  129勝101敗
後手勝率   : 0.439
平均手数   : 175.724  千日手を含む
平均手数   : 181.918  千日手を除く

 

先手勝率56%とおとなしい感じですが、これはハードウェア差がないことと時間差をつけた点が効いたものと思われます。平均手数は180手前後、これは私が初参加した2018年の世界選手権からあまり変わらない感じでしょうか。ちなみに2017年から2018年で結構伸びたと言われています。

また、宣言勝ちおよび千日手は人間の将棋に比べて異常に多いのが最近のコンピュータ界隈の傾向です。今後はこの傾向が強くなるのでしょうか。気になるところです。

 

次に戦型分類です。

 

                  戦型         棋譜数  割合(%)  先手勝率 先手勝ち 後手勝ち
──────────────────────────────────────
  1:                  相掛かり:    87  34.8%    0.529        45      40
  2:            角換わりその他:    62  24.8%    0.611        33      21
  3:          角換わり腰掛け銀:    36  14.4%    0.700        21       9
  4:              後手四間飛車:    14   5.6%    0.769        10       3
  5:              その他の戦型:    13   5.2%    0.308         4       9
  6:              先手三間飛車:    13   5.2%    0.273         3       8
  7:                      矢倉:    10   4.0%    0.333         3       6
  8:            横歩取りその他:     8   3.2%    1.000         8       0
  9:                      雁木:     4   1.6%    0.000         0       4
 10:                相振り飛車:     1   0.4%    1.000         1       0
 11:              後手三間飛車:     1   0.4%    1.000         1       0
 12:      先手角交換型振り飛車:     1   0.4%    0.000         0       1
──────────────────────────────────────
計                                 250   100%    0.561       129     101

 

昨年は振り飛車勢が居たため振り飛車率は高かったのですが今年は相掛かり・角換わりで74%と居飛車率が高かったようです。

振り飛車では後手四間飛車・先手三間飛車が多いとなっておりこれも今後注目されるところでしょうか。

ちなみに二番絞りは後手で自然に四間飛車をすることがあるので少し親近感があります。

 

個々の棋譜に関しては時間をかけてゆっくり眺めていくとします。

 

Python 3.13までに学んでおきたい疑似乱数の話

ヘビーなPythonユーザなら把握していると思うが,次バージョンでGIL解消のオプションが入る。現時点ではリリース時に複数のバイナリが出てくるのか実行時オプションになるのか実際に出てみないことには分からない。一番可能性が高いのは現状アルファ版と同じって期待だろう。

 

peps.python.org

 

で,当然並行処理関係でPythonを避けていた問題が一気に解消することになる。Web系はもちろん,機械学習分野においても苦心の策が無用になることも多くあるだろう。

その辺は横においておいて,今日は疑似乱数の話をメモしておく。

乱数と疑似乱数の差が分からない人は一旦他で学んできた方がいい。

 

簡単に言うと乱数は文字通りランダムな数なので予測も再現も不可能なものである。しかしながら計算機で扱う乱数はほぼ全て疑似乱数であり,何らかの計算で得られる数値でしかない。そのためアルゴリズムと初期値が分かれば100%再現可能である。これが本件の背景。

 

GILと何が関係あるかと言えば並行処理である。疑似乱数は生成アルゴリズムに基づき数列を作るがその数列を取り出す順序が異なれば再現しない。これは並行処理で複数のスレッド(プロセスでも軽量スレッドでも)から呼び出される疑似乱数では問題となる。

では,どうして対応するかと言えば例えばGoogleが管理している並列計算ライブラリのJAXがどうしているか見てみよう。

GitHub - google/jax: Composable transformations of Python+NumPy programs: differentiate, vectorize, JIT to GPU/TPU, and more

pmapを使うサンプルコードを見ると

keys = random.split(random.PRNGKey(0), 8)
mats = pmap(lambda key: random.normal(key, (5000, 6000)))(keys)

このようなコードがあり,乱数のキーを生成し並行処理するプロセス毎にキーを渡している。これにより乱数の種(seed)が各プロセス毎に固定される。

 

では,JAX以外でどうすればいいか。

Python標準のrandomならrandomクラスを継承した乱数生成器を並行処理するスレッド数用意すればいい。

numpyならGeneratorをスレッド数用意すればいい。

Random Generator — NumPy v1.26 Manual

ということになる。

親で生成して渡してもいいし,seed値だけ渡して子スレッドで生成してもseedが再現できれば再現性は問題ないだろう。

もちろんnumpyを使うのがお勧めである。

 

もちろん再現性気にしない勢はお好きにどうぞ。

未対応な高級ライブラリ勢は3.13リリース前後ですぐに対応するものと思う。