-
-
Notifications
You must be signed in to change notification settings - Fork 143
定跡の作成
やねうら王で用いる定跡のフォーマットは、次の形式です。
[定跡フォーマット]
sfen sfen文字列
この局面での指し手1 相手の応手1 この指し手を指したときの評価値 そのときの探索深さ その指し手が選択された回数
この局面での指し手2 相手の応手2 この指し手を指したときの評価値 そのときの探索深さ その指し手が選択された回数
…
sfen xxxx..
相手の応手がないときは"none"と書いておく。
例)
sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1
7g7f 3c3d 0 32 2
sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/2P6/PP1PPPPPP/1B5R1/LNSGKGSNL w - 2
3c3d 2g2f 0 32 1
あと、先頭にDBのフォーマットのバージョン識別用の文字列として次の文字列を入れておく。
#YANEURAOU-DB2016 1.00
🖋 備考
- やねうら標準定跡のように評価値なしの定跡DBにおいては出現頻度の高い順で並んでいることが保証されている。
- やねうら大定跡のように評価値つきの定跡DBにおいては手番側から見て評価値の良い順に並んでいることは保証されている。
1),2)から、1つ目に書かれている指し手がベストの指し手と言えます。
指し手の出現頻度(「その指し手が選択された回数」)のところは、やねうら大定跡のように評価値つきの定跡の場合、意味をなさないので、エンジンのバージョンを100倍したもの(V3.21なら321)を記入してあります。これにより、「古いエンジンで思考させた指し手は採用しない」などの処理を行うことが出来るようになります。
⚠ ファイル形式はASCIIとする。UTF-8のBOM付きでも読み込めるはずなのだが保証はしない。
定跡生成系のコマンドはmakebookというコマンドです。
makebookコマンドは、MAKE_BOOK_CMD,EVAL_LEARN をdefineしてコンパイルしたときにのみ有効となります。
- やねうら王では、NNUEのLEARN版などで使えます。
- ふかうら王では、標準でMAKE_BOOK_CMDは有効になっているので、makebookコマンドが使えます。
拡張子が.sfenというファイルは、
startpos moves 7g7f ...
のようにUSIプロトコルの局面コマンド("position")の形式で1局の棋譜が1行に収められている棋譜ファイルであるとします。("position"という文字列は省略されていると考えてください。)
例えば、5局分の棋譜であれば、5行からなるテキストファイルです。(改行があるまでを1行とする)
拡張子が.dbというファイルは、やねうら王で用いる定跡ファイルです。
DBとは言うものの、単なるテキスト形式のファイルなので、テキストエディタで開いて編集することができます。
このファイルを思考エンジンの実行ファイルがあるのと同じフォルダに"book"というフォルダを作って配置します。
makebook from_sfen book.sfen standard_book.db moves 16
- | 意味 |
---|---|
book.sfen | 読み込ませたい棋譜ファイル |
standard_book.db | 書き出したい定跡ファイル名 |
moves 16 | 16手目まで |
棋譜を読み込ませてそれを定跡DBに変換する。
各局、"moves"のあとに指定した手数の局面までを出力する。
makebook from_sfen bw black_book.sfen white_book.sfen standard_book.db moves 16
"from_sfen"の直後に"bw"(black and whiteの意味)を指定した場合、sfenファイルとして2つのファイルが指定できます。
- 1つ目のsfenファイルは、そのsfenファイルから先手の局面だけを抽出して使う。
- 2つ目のsfenファイルは、そのsfenファイルから後手の局面だけを抽出して使う。
また、このとき、sfenファイル名として"no_file"を指定すると、先手用 or 後手用のsfenファイルは無しであるというのを指定できます。
例)
makebook from_sfen bw no_file white_book.sfen standard_book.db moves 16
例)
makebook think 2016.sfen yaneura_book.db moves 16 depth 32
2016.sfenという棋譜を読みこみ、その16手目までをdepth 32(思考する時の深さ)で思考させて、そのときの上位3手を定跡ファイルに書き出します。
💡 nodes 100000000 のように、探索node数を指定することもできる。
⚠ やねうら王のLEARN版 など、この機能に対応している思考エンジンが必要となります。
- 定跡ファイル上に、すでにそれ以上深い深さで探索した結果がある場合は、その局面については探索しません。
- 同じ深さの場合、登録されている手の数がMultiPVで指定されている手の数以上登録されている場合もその局面については探索しません。
🖋 Hash、Threads、MultiPVの設定に従い思考されるので、事前にこれらのパラメーターを適切な値に設定しておくこと。
- '.'が1局面終わった合図。'S'がセーブ(ファイルに保存)したという合図。一定間隔ごとに自動的に保存されます。
- 中断するときはこの'S'マークが出たのを確認してから終了させると良いでしょう。
- 保存中に終了させるとファイルが破損する可能性があります。
- 前回から新たな指し手が追加されていないときは's'(小文字)を表示して、ファイルには書き出しません。
- Contemptの値は無視されます。(0だとみなされる)
- IgnoreBookPlyオプションは無視されます(falseとみなされます)
💡 book_save_interval 【秒数】 というオプションでbookの保存間隔を指定できます。(デフォルト15分)
例)
Hashメモリ4096MB、スレッド数8(それぞれのスレッドが1局面ずつ担当して検討)、MultiPV 5(それぞれの局面で候補手5手書き出し)、
棋譜2016.sfenの16手目までを深さ32で探索して定跡ファイルuser_book1(すでに存在すれば該当局面のみを上書き更新)を生成。
Hash 4096
Threads 8
MultiPV 5
makebook think 2016.sfen user_book1.db moves 16 depth 32
makebook think 2016.sfen yaneura_book.db startmoves 5 moves 16 depth 32
startmovesを指定すると、思考対象局面をそこからにします。(省略時は1が指定されているものとみなされます。)
上の例だと、(初期局面を1手目として)5手目から16手目の局面を思考対象とします。
makebook think 2016.sfen yaneura_book.db startmoves 5 moves 16 depth 32 cluster 1 3
makebook think 2016.sfen yaneura_book.db startmoves 5 moves 16 depth 32 cluster 2 3
makebook think 2016.sfen yaneura_book.db startmoves 5 moves 16 depth 32 cluster 3 3
上の例は、PCを複数台で定跡を並列生成するときに用います。
"cluster 【X】【Y】"の形で指定して、Y台あるうちのX台目ということを意味します。
上の例は、3台あって、1台目、2台目、3台目のPCで上のように入力すると思考対象局面を分散させています。もちろん3台以外の場合であってても同様にすれば並列生成できます。
makebook think bw black_2016.sfen white_2016.sfen yaneura_book.db startmoves 5 moves 16 depth 32 cluster 3 3
"from_sfen"のときと同様に、直後に"bw"を指定した場合、sfenファイルとして2つのファイルが指定できます。
- 1つ目の.sfenファイルは、その.sfenファイルから先手の局面だけを抽出して使う。
- 2つ目の.sfenファイルは、その.sfenファイルから後手の局面だけを抽出して使う。
makebook merge yaneura_book1.db yaneura_book2.db yaneura_book3.db
定跡のマージ機能。上の例は、yaneura_book1.dbとyaneura_book2.dbとをマージ(合体)させてyaneura_book3.dbを作成します。
- 1つの局面に対して、深く探索されたほうの結果を優先。
- 1つの局面に対して、2つの定跡ファイルがどちらも同じ深さで探索されている場合は、候補手(MultiPV)が多いほうを採用。
makebook thinkコマンドで自動生成した定跡をマージするのに用います。
makebook sort book_src.db book_sorted.db
局面の文字列(sfen文字列)でソートします。
上例では、book_src.dbを読み込み、並び替えられた、book_sorted.dbを出力しています。
対局時に、定跡をメモリに丸読みしたくないときには(BookOnTheFlyオプションがオンの時)は、sfen文字列順に並んでいないといけません。(sfen文字列順でソートされていないとバイナリサーチが出来ないため。)
そのため、sortされていない定跡ファイルを読み込んで並べ替えて出力する機能が必要となります。それがこのmakebook sortコマンドです。
💡 このコマンドを用いてsortしなくとも、mergeコマンドなど、定跡DBファイルの書き出し時にはsortをしてから書き出します。なので、普段はこのコマンドを使う必要はありません。手で定跡を編集した場合などに用います。
⚠ このコマンドは、使っていないので廃止しました。
makebook extract_sfen_from_db
定跡の変換コマンドです。定跡DBから局面をsfen形式で抽出します。
book/read_book.db
を読み込んで、そのなかの局面図のみのsfenファイル
book/write_sfen.txt
を書き出します。
// こんなコマンド使わずとも、grep等でsfenで始まる行を抽出すればいいだけなのだが…。
ここで書き出された局面をスーパーテラショック定跡の生成の時に棋譜として与えれば、この棋譜の局面をある程度優先して掘ってくれるので特定の定跡を狙い撃ちするような定跡を作る時に役立ちます。
⚠ このコマンドは、使っていないので廃止しました。
makebook extract_sfen_from_sfen
USIのPositionコマンドで指定できる形で書かれた棋譜ファイルなどを読み込み、その各局面をsfen形式で書き出すコマンド
book/read_sfen.txt
を読み込んで、そのなかの局面図のみのsfenファイル
book/write_sfen.txt
を書き出す。
Positionコマンドなので"startpos moves xxx.."とか"sfen xxx moves yyy.."のような形式の棋譜が指定できる。
従来手法(テラショック化コマンド)だと千日手が複雑に絡むと正確な手順が得られなかった。
これを改善する新しいアルゴリズムをやねうらおが開発したのが2023年5月。このコマンドのことをペタショック化コマンドと呼ぶ。通常のmin-maxアルゴリズムでは組み合わせ爆発を起こしてどうしようもなくなるような複雑に合流する定跡に対して、超高速で正確な千日手に関して正確にmin-maxした感じの定跡が得られる。
また、このコマンドを用いることを前提に、定跡を自動生成する新たな手法も開発した。この手法をペタショック定跡手法と呼ぶ。
makebook peta_shock book1.db peta_shock.db
book/book1.db
を読み込んで
book/peta_shock.db
を書き出す。
前者はmakebook thinkなどで生成された定跡。
💡 depth = MAX_PLY で min-maxしたときと同じ結果が O(N * MAX_PLY)で返ってくる。グラフが合流してようが循環してようが、組み合わせ爆発しない。
💡 このコマンドによって、テラショック化コマンドは、完全にオワコンになった。テラショック化コマンドは、そのうち丸ごと削除します。😅
💡 今回のペタショックコマンドは、300万局面をペタショック化(定跡ツリーでmin-max探索した時と同じ評価値に変更)するのに4分(ノートパソコンで実測)なので、数千万局面に対する定跡のペタショック化であっても十分実用に耐える速度である。
draw_value(千日手時のスコア)は、エンジンオプションのDrawValueBlackで設定できる。
例
DrawValueBlack -100
makebook peta_shock book1.db peta_shock.db
エンジンオプションのFlippedBookがtrueの時は、先手番の局面しか書き出さない。(デフォルト)
※ 後手番の局面はそれをflipした局面が書き出されているので、実際に使う時にFlippedBookをtrueにしておけば、後手番は、反転された先手の局面にヒットするはずだから。
ペタショック化、豆知識。
- 使用メモリを少なくするためにUSI_Hash 0を指定する。
- 評価関数ファイルはいらないので、SkipLoadingEval trueにする
コマンド例
USI_Hash 0
SkipLoadingEval true
DrawValueBlack 0
DrawValueWhite 0
makebook peta_shock book.db user_book1.db
quit
⚠ 入力する定跡DBは、sfen文字列でsortされていること。されてないなら、makebook sortコマンドを使ってsortしてからこのコマンドを適用すること。
💡 高速化のための豆知識
またNOEはNum Of Entriesの意味で、DB上のSfen局面の数。 これが事前に定跡ファイルの2行目で指定されていれば、固定サイズのメモリ確保で済むので処理が端折れる。
- shrinkモードについて
⇨ 最善手と同じ評価値の指し手以外を削除して、定跡ファイルを小さくするモードです。
makebook peta_shock book.db user_book1.db shrink
999 : 抜け出せないループ
+1000 : 先手は千日手を打開する権利を持っていない。
+2000 : 後手は千日手を打開する権利を持っていない。
例
3999 : 先手も後手も千日手を打開する権利を持っていない千日手ループ
1010 : 先手は千日手を打開できず、後手は千日手を打開して10手後に末端の局面に到達することができる。
やねうら王形式の定跡DBファイルに対して、次に思考対象とすると良い局面(定跡DB上にはない局面)を提案する機能。
ただし、定跡DBに登録されている指し手を辿っていける局面に限る。
使い方
makebook peta_shock_next book.db peta_next_sfens.txt 2000
book.db : やねうら王形式の定跡DBファイル。フォルダはbook/ 固定。ゆえに、この場合、book/book.db のファイルが操作対象。
peta_next_sfens.txt : 書き出すファイル。1行に一つ局面が書き出される。sfen形式。フォルダはbook/ 固定。この場合、book/peta_next_sfens.txt に書き出される。
2000 : 2000局面書き出す。指定した数に足りないこともある。また、PV(最善応手列)付近に評価値が-99999になっている指し手があると、その指し手で進めた局面も書き出す。この時、指定いた数である2000を少しオーバーすることがある。
基本的に、PVを掘っていくために用いる。PVのleaf node(末端局面)からのbestmoveを1つ目の局面として書き出して、そのbestmoveが定跡上にないとした時のPVのleaf nodeのbestmoveを2つ目の局面として書き出して、…と以下これを繰り返してPV周辺をまとめて書き出す。
しかしこの方法だと、一部の枝しか掘れていないことがある。
具体的には、
・評価値 10
・評価値 5
の枝があるとして、前者をまずは掘る。そうすると局面は分岐して(1手ごとに2倍ぐらいに分岐していく)、そのどこかは評価値5よりは大きいから、またそこを掘る。
そんな感じにして繰り返していくと評価値5の枝がいつまでも掘れない。たぶん永遠に掘れない。
これは枝の選出方法に問題があると言わざるを得なくて、本来はMCTS風に、掘った時の期待値の高そうなところを選出すべきなのであろう。
しかしそうすると、MCTSを実装しないといけなくなる。ループや合流を含むので、このMCTSの実装、わりと大仕事である。
そこで、末端局面の指し手の評価値にガウスノイズを加算する機能を追加した。これにより、うまい具合に列挙できる。
makebook peta_shock_next book.db sfens.txt 1000 eval_noise 20
⇨ 1000局面を書き出す。20はleaf nodeの指し手の評価値に加える乱数の大きさ。
この場合、評価値に、平均 = 0 , 標準偏差 = 20 ガウスノイズを加算する。
(これを加えることで序盤の指し手を開拓しやすくなる)
あと、次に掘る局面までの手順を取得して、その手順の周辺を掘りたいことがある。このための機能も追加した。from_startposオプションである。
makebook peta_shock_next book.db sfens.txt 1000 from_startpos
// ⇨ startpos moves ... の形式で局面を出力する。
⚠ 入力する定跡DBは、sfen文字列でsortされていること。されてないなら、makebook sortコマンドを使ってsortしてからこのコマンドを適用すること。
定跡を掘っていく時に、次に掘るべき局面をどうやって決定するかと言うと、ペタショックNext(コマンド名 makebook peta_shock_next)では、定跡上のPVのleaf moveで進めた局面を選出していた。
1つ選出して、そのあと、そのleaf moveが無くなったと仮定して親に評価値を伝播しなおして、またroot(初期局面)からのPV leaf moveを調べて…を繰り返していた。
これは評価関数がある程度信頼できるならうまく機能するのだが、root付近で掘れていない枝が大量に残ることとなった。
そこで、leaf moveにガウスノイズを加えると言うのが改善のアイデアだった。これはうまく機能して、root付近で掘れていない枝はある程度潰すことができた。
しかし、PV leafを掘るというのがあまりいいアイデアではなくて、対策すべきは、相手がどの指し手をやってきてもちゃんと勝負の形になる(信頼できる)leafに辿り着けることである。
そのようなleafとは、つまり、プレイヤーが先手であるとしたら、先手 ⇨ 定跡DBの最善手を選ぶ , 後手 ⇨ 定跡DBのいずれかの指し手を選ぶ というのを繰り返して到達できるleaf nodeの集合であって、それらの局面が信頼できることが大切なのである。
定跡の平均分岐がa手だとすると、30手目でのnodeはa ^ 30あるのだが、先手の最善手が1手のみ、後手の平均分岐がa手だとすると、a ^ 15で済む。これは、a ^ 30の平方根ぐらいで、要するに、その局面ぐらいはちゃんと調べておきましょうということである。
定跡局面が1億だとして、その平方根とは1万局面である。(わりと少ない) これらを選出して優先して展開すべきではないかと思った。
ただし、展開するとしても、優先順位はつけるべきで、後手にとって評価値の悪い順にするだとか、遷移確率の高い順にするだとか、何らか工夫は必要である。 ⇨ 相手がすべての指し手(≒ランダム)なので、PVのevalより良くなるはず。だから、昇順にして悪いほうから調べていくべき。
本コマンドでは、評価値の高い順に指定された数の局面だけ列挙するようになっている。
例えば、以下のように1000を指定した場合、先手がDBの最善手を選んだ時(後手はDBの任意の指し手)の末端の局面が500個、後手がDBの最善手を選んだ時(先手はDBの任意の指し手)の末端の局面が500である。
コマンド例
makebook peta_shock_next2 user_book1_20240119080417.db peta_next.sfens 1000 eval_limit 400 from_startpos
上のコマンド例の400は、(ペタショック化した時の)評価値の絶対値が400以内の指し手のみを辿ると言う意味である。
以前、テラショックコマンドでこれを行う専用コマンドがあったのだが、削除した。
なぜなら、例えば、次に掘る局面を makebook peta_shock_next from_startposで求めて、makebook thinkでその局面について思考させる、という動作を繰り返すbatファイルを書くだけで自動定跡掘りができるからである。
定跡生成に関して参考になりそうな記事
定跡フォーマットについて
定跡生成コマンドについて
スーパーテラショック定跡について