はじめに
将棋AI「技巧(ぎこう)」を自分の手で訓練して強くしてみる開発日記、第4回です。
今回は学習の3段目、指し手の「実現確率」の学習(--learn-probability) をやります。
技巧の本家リポジトリはこちら(オープンソース・GPL)。
前回までのあらすじ
-
第1回:技巧をソースからビルドして起動。評価関数が空っぽで
score cp 0(評価値ずっと0)の"知識ゼロ"状態を確認。
-
第2回:floodgateから棋譜を約12.8万局集めて
kifu_db.txtを作成(途中、後手勝ちが消えるバグをEDAで発見して修正)。進行度を学習。
-
第3回:評価関数を13時間かけて学習。直後に初手から投了するという心臓に悪い事件があったが、原因はこちらのテスト方法のミスで、評価関数は無事。無事
score cp 0を卒業。
そして今回。例によって、またメモリ周りでやらかします。
相変わらず Claude(Claude Code)と一緒に 進めています。
今回のゴール
- 「実現確率」とは何か・なぜ必要かを理解する
-
--learn-probabilityで実現確率モデルを学習する - 学習の様子を可視化して、ちゃんと学習できているか確認する
「実現確率」って何? なぜ要る?
技巧は、αβ探索という方法で「この手を指したら相手はこう来て…」と先を読みます。このとき、読む手の順番がめちゃくちゃ大事です。良さそうな手を先に試せば、「もうこれより良い手はなさそう」と早めに見切って無駄な枝を切れる(=枝刈り)ので、同じ時間でより深く読めます。
そこで使うのが実現確率モデル。「この局面で、この手が実際に指される確率はどれくらいか?」を、手の特徴(駒の種類・移動先・駒を取るか・取ったときの損得(SEE)・過去によく指された手か…など)から予測する、軽量なモデルです。
評価関数が「局面の良し悪しを測る目」だとしたら、実現確率は「どの手から読むか決める勘」みたいなものですね。
やらかし:メモリ49GBを使い果たして強制終了
さっそく学習を回しました。
./bin/release --learn-probability
ところが1時間半ほど回したところで、プロセスが忽然と消えました。ログを確認すると、終了コードが 137。これは「OSに強制終了された(SIGKILL)」のサインです。dmesg(カーネルのログ)を見ると、犯人がはっきり書いてありました。
Out of memory: Killed process 3724769 (release)
total-vm:54367164kB, anon-rss:49284544kB ...
メモリ49GBを使い果たして、OOM Killer(メモリ不足時にプロセスを殺すOSの機能)に処刑されていました。 うちのマシンの全メモリです。
原因は、実現確率の学習の作り。教師にする局面ごとに、その局面の全合法手ぶんの特徴ベクトルをメモリに溜め込むため、
3万局 × 1局あたり多数の局面 × 1局面あたり約100手ぶんの特徴 ≒ 50GB
と、デフォルトの「3万局」設定がうちのメモリには大きすぎたのでした。技巧はもともと潤沢なメモリのマシンを想定して作られているんですね。
対処はシンプルに、教師の局数を 3万 → 1万 に削減(ソースの kNumTeacherData を変更)。これでメモリ使用量は約16GBに収まりました。
// 3万局だとメモリ49GBを使い切るので1万局に削減
constexpr int kNumTeacherData = 10000;
教訓その2:デフォルト設定は自分のマシンに合うとは限らない。 第2回のデータ確認といい、毎回どこかでメモリかデータに殴られています。
1万局で再挑戦+可視化
仕切り直して回したら、今度は約34分で完走しました。256回のイテレーションで、テスト用の棋譜に対する「上位1手が実際に指された手とどれだけ一致するか」を見てみます。
◆ 実現確率モデルの一致率(テスト棋譜・上位1手が実際の手と一致した割合)
iter 1 |████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 20.9%
iter 32 |██████████████████████████░░░░░░░░ 35.6%
iter 64 |██████████████████████████████░░░░ 38.4%
iter 128 |███████████████████████████████░░░ 39.0%
iter 256 |████████████████████████████████░░ 39.2%
◆ 損失(Loss) の低下
iter 1 |██████████████████████████████ 2.17M
iter 32 |████████ 1.32M
iter 64 |██ 1.09M
iter 256 | 1.02M
一致率は 20.9% → 39.2% まで上がって頭打ち。最初は全合法手から当てずっぽう(約100手から1つなので数%〜)に近かったのが、約4割は「一番指されやすい手」を当てられるようになりました。Lossも最初の1回で激減して、あとは微調整という典型的な学習カーブです。
軽い分析:評価関数の一致率(49%)と何が違う?
第3回の評価関数学習では一致率が約49%でした。今回の実現確率モデルは約39%。「評価関数より低いじゃん」と思いますが、役割が違います。
| 実現確率モデル | 評価関数(+探索) | |
|---|---|---|
| 速さ | 超高速(特徴を見るだけ) | 遅い(実際に先を読む) |
| 役割 | 読む手の順番決め | 最終的な手の決定 |
| 一致率 | 約39% | 約49% |
実現確率は「全部の手を一瞬でそれっぽい順に並べる」のが仕事なので、一手一手をじっくり読む評価関数+探索より一致率が低いのは当然。むしろ一瞬の判断で4割当たるなら、探索の枝刈りを効かせる"先頭ふるい"としては十分優秀です。野球で言うと、評価関数が「じっくり考える4番打者」、実現確率は「とりあえず塁に出る俊足の1番打者」みたいな分担ですね。
今回のまとめ
Gikou
├─ プログラム本体 ✓
├─ USI通信 ✓
├─ 棋譜DB(kifu_db.txt) ✓
├─ 進行度(progress.bin) ✓
├─ 評価関数(params.bin) ✓
├─ 実現確率(probability.bin) ✓ ← 今回完成!
└─ 定跡(book.bin) ✗ ← 次回
- 実現確率モデルは「探索で読む手の順番を決める勘」。一致率 20.9%→39.2% で学習完了
- 3万局だとメモリ49GBを食い潰してOOM死 → 1万局に減らして解決
- 残るは定跡(book)だけ
次にやること
いよいよ最後のピース、定跡データベースの作成(--create-book) です。これで技巧が"フル装備"になり、いざ実戦へ…という流れなのですが、
実はこの後、学習した技巧をWindowsの将棋GUIに載せて対局させたら、複雑な局面で突然クラッシュするという事件が起きます。そして犯人は、なんと今回学習したこの実現確率モデルでした。……という話は、また次回に。やらかしの在庫が尽きません。