qleap/Opening_dataset_by_NAGISA_V4
Opening dataset by NAGISA_V4 平手から 15手目までを方策ネット b15c256 で広げ、NNUE エンジン attic-gensfen が NAGISA_V4 (HalfKA-2304) を読んで depth 9・MultiPV 5 で採点した序盤局面集。 manaka-teacher が MPK1 ストリームに落とし、manaka-pack が同一局面を 1 行に 畳み込んで、この parquet を書いた。学習側がそのまま読む形である。 自己対局のコーパスではない。 対局は 1 局も指していない。木を広げて採点しただけで、 局面同士に前後関係は無い。勝敗を持つデータが要るなら qleap/Knowledge_distilled_dataset_by_NAGISA_V4 のほう。あちらと同じスキーマ・同じ変換規約なので、混ぜて読める。 行数: 19,896,088 (畳み込み前のレコード数: 19,898,611) シャード数: 4 (data/teacher-00000.parquet …) 合計: 1,137… See the full description on the dataset page: https://huggingface.co/datasets/qleap/Opening_dataset_by_NAGISA_V4.
Opening dataset by NAGISA_V4
平手から 15手目までを方策ネット b15c256 で広げ、NNUE エンジン attic-gensfen が NAGISA_V4 (HalfKA-2304) を読んで depth 9・MultiPV 5 で採点した序盤局面集。 manaka-teacher が MPK1 ストリームに落とし、manaka-pack が同一局面を 1 行に 畳み込んで、この parquet を書いた。学習側がそのまま読む形である。
自己対局のコーパスではない。 対局は 1 局も指していない。木を広げて採点しただけで、 局面同士に前後関係は無い。勝敗を持つデータが要るなら qleap/Knowledge_distilled_dataset_by_NAGISA_V4 のほう。あちらと同じスキーマ・同じ変換規約なので、混ぜて読める。
- 行数: 19,896,088 (畳み込み前のレコード数: 19,898,611)
- シャード数: 4 (
data/teacher-00000.parquet…) - 合計: 1,137,896,896 B
先頭 3 つが 5,000,000 行、最後が残りの 4,896,088 行。zstd 圧縮、row group は 8192 行。
from datasets import load_dataset
ds = load_dataset("qleap/Opening_dataset_by_NAGISA_V4", split="train")生成のしかた
設定を知る手立てはこの節しかない。 parquet のフッタが述べているのは変換側が 適用した規約であって、探索を回した条件ではない。
局面を選んだ側
出発点は平手 1 局面のみ。そこから 1 手ずつ 15手目まで、次を繰り返して広げた。
- 提案 — 方策ネット
b15c256(ONNX) が top-p 0.90、親 1 つにつき最大 16 手 - 採点 —
attic-gensfenが--depth 16 --multipv 1 --eval-tolerance 0 - 親にするかどうか — その局面の評価値を先手視点に直したものが、平手の 評価値 54cp から ±50 センチポーン以内なら次の手数の親にする
2 と 3 の評価値はこのデータに入っていない。 ここに並んでいるラベルは、 局面集合を据え置いたまま depth 9 / MultiPV 5 で付け直したものである。したがって:
- ここの
cpで「互角なら採用」をやり直しても、木を作ったときと同じ集合にはならない。 - 木の形(どの局面が存在するか)には depth 16 の採点が効いているが、その採点はこのデータに無い。
局面の存在理由とラベルが別の探索から来ている、というのはこのデータの性質なので、 選抜の再現が要る用途には向かない。ラベルとして使うぶんには問題ない。
採点を通った候補は 3 で落ちたものも含めて全て入っている。 基準にした評価値が 無いので、いまある cp で絞り直しても当時と同じ集合にはならない。新しい基準に よる別の選抜として扱うこと:
import numpy as np, pyarrow.parquet as pq, pyarrow.compute as pc
t = pq.read_table("data/teacher-00000.parquet", columns=["ply", "candidates"])
c = t["candidates"].combine_chunks()
off = np.asarray(c.offsets)
cp = c.flatten().field("cp").to_numpy(zero_copy_only=False).astype(np.int64)
best = np.maximum.reduceat(cp, off[:-1]) # 最善手の cp。候補は cp 順ではない
ply = t["ply"].to_numpy().astype(np.int64)
pov = np.where(ply % 2 == 1, best, -best) # 先手視点に直す
balanced = np.abs(pov - 54) <= 50 # 全 4 シャードで 9,866,539 行木の形そのものには 3 が効いている。 採用された局面だけを次の手数の親にしたので、 「互角から外れた局面の、さらに先」はこの中に無い。
採点した側
全段を一律 depth 9 で付けている。 手数による探索深さの差は無い。
`FV_SCALE` が centipawn を centipawn たらしめている。 NNUE が積み上げた内部の値は この数で割られて初めて評価値になる。同じネットをエンジン既定の 16 で読めば、ここに 並んでいる cp はすべて 1.75 倍の値になっていた。他所のデータと混ぜるなら、まずこの数が 一致しているかを見ること。二つの尺度をまたいで混ぜた教師データは、どちらのものでもない 数字で学習することになる。
MultiPV 1 と MultiPV 5 では同じ局面でも root value が変わる。 5 本ぶんの探索が 1 本目の枝刈りを緩めるためで、非決定性ではない (depth 9 / MultiPV 1 を 2 回走らせると バイト単位で一致することは確認してある)。同じ 1500 局面 (15手目) を depth 9 で測った実測:
1500 局面中 1478 局面で値が動き、平均 |差| は 36.5cp、最大 346cp だった。同じ 1500 局面を depth 16 で測ると平均 |差| 30.2cp、最大 241cp で、浅いほうが MultiPV の影響を強く 受ける。他所の MultiPV 1 のデータと直接比べないこと。
変換で落ちた行は無い
manaka-teacher は 19,898,611 行を読んで 19,898,611 レコードを書いた。拒否 0 行 (move_not_candidate 0、malformed_record 0)。
自己対局のコーパスが 3% ほど落とすのは、わざと混ぜたランダム手が MultiPV の外にあって 「指した手に対応する評価値が無い」ためである。ここは指した手そのものが無いので、 その拒否は起こりようがない。
スキーマ
合法手の一覧も合法手マスクも入っていない。要るなら packed から作り直す。
勝敗の列は無い
このデータは局面と評価値である。対局していないので勝敗は存在しない。0 を入れれば 引き分けと読まれ、NaN は (1 − ratio)·z + ratio·q のような混ぜ方を突き抜けてしまう。 列が無ければ「述べられていない」と読める。参照実装の学習側は None / Option::None を 返し、価値の教師信号は value_q 一本になる。
評価値と勝敗を混ぜる lambda を既定で持つ学習設定にこのデータを渡すなら、 明示的に評価値のみ (lambda = 1) にすること。
packed の中身
96 バイト、manaka_core::pack。どちらのエンジンの入力層でもない。Manaka の accumulator も DLManaka の入力面も、読み込み時にここから組み立てる。
バイト
0..81 盤面。1 マス 1 バイト、マス番号順
81..88 先手の持ち駒。駒種ごとに 1 バイト
88..95 後手の持ち駒。同じ順
95 手番。0 = 先手 / 1 = 後手マス 0 が 9a、マス 80 が 1i。square = (9 − file) × 9 + (rank − 1)。空マスは 0xFF。 持ち駒は歩・香・桂・銀・金・角・飛の順に 1 バイトずつ。
`packed` の手番は `ply` の偶奇と必ず一致する (19,896,088 行すべてで確認)。奇数手数が 先手番、偶数手数が後手番である。
候補手は手であってラベルではない
mv は move16 — chisaki の手の表現そのまま。
bit 15 │ 14 │ 13 ──────── 7 │ 6 ──────── 0
✗ │ 成 │ from または 81+駒種 │ toto が bit 0–6、移動元が bit 7–13、成りが bit 14。打つ手は移動元の欄に 81 + 持ち駒の 番号が入るので、打った駒種は手のビットだけから読める。方策ラベルも動かした駒も ビット演算だけで出るので、将棋のライブラリは要らない。
`candidates` は `cp` 順ではない。`mv` の昇順である。 全 79,569,273 個の隣接ペアが 昇順で、candidates[0] が最善手なのは 19,896,088 行中 4,616,729 行しかない。 最善手が要るなら `cp` の最大を取ること。先頭を最善手と決め打ちにした読み方は 4 分の 3 の行で別の手を拾う。
prob は行内で合計 1 になる (実測 0.9999997〜1.0000002、float32 の丸めのぶん)。
候補手の数は MultiPV の幅であって、数えれば分かる。 実測:
5 に満たないのは合法手が MultiPV の幅より少ない局面、すなわち王手を受けている局面 である。6 は畳み込みで、同じ盤面を指した二つの探索が別の手を挙げ、和集合が両方を 残したもの。長さ 5 を決め打ちにした読み方は落ちる。
これは方策ネットの分布ではない。 木を広げるときに使った b15c256 の確率はこの データに入っていない。ここにあるのは NNUE の探索が付けた評価値から作った分布である。
畳み込み
同一局面は 1 行。 鍵は packed の 96 バイトだけで、(packed, ply) ではない。 13手目で現れた盤面と 15手目で現れた同じ盤面は、探索から見れば同じ 1 局面である。
value_q— 畳み込んだレコードの平均candidates—mvで突き合わせた和集合。cpはその手を挙げたレコードの平均ply— 最小weight— レコード数。1 はその局面が 1 回だけ現れたということ
畳み込みで消えたのは 2,523 レコード、全体の 0.0127%。
最大が 2 である。自己対局のコーパスでは平手が 20 万回以上畳み込まれるが、ここでは 木が同じ盤面を二度作らないので、転置で 2 回出会うのが上限だった。畳み込み後の packed は 19,896,088 行すべてで一意である (重複 0 件)。
packed に手数は入らないので、13手目の盤面と 15手目の同じ盤面は同じ行に畳まれる。 2,523 組がこれにあたる。
prob は畳み込みの後に一度だけ計算する。平均した cp に対する温度 100 センチ ポーンの softmax である。
手数の内訳 (畳み込みで ply は最小を取る):
1手目から 15手目まで揃っているが、量は 15手目に集中している (全体の 57%)。 手数が奇数の段は全て先手番、偶数の段は全て後手番なので、片方の段だけ使うと手番が 偏る。
詰みは候補手にだけ、しかも全て負で入っている
詰みは ±(32000 − 詰みまでの手数) で表される。`value_q` に ±1 は 1 行も無い (実測範囲 -0.8351〜0.9999987)。root は 1 局面も詰んでいない。
`cand` の側には 3,917 個入っている (3,917 行に 1 個ずつ、全体の 0.02%)。値は -31998 が 3,850 個、-31994 が 67 個で、全て負である。手数の内訳は 11手目 9 / 12手目 6 / 13手目 323 / 14手目 267 / 15手目 3,312。
つまり root は詰んでいないが、候補手の下位に「指すと詰まされる手」が入っている 局面がある。符号が負なのはそのため (手番側視点)。
出所は王手を受けた局面である。 角交換から角を打ち込んで成る筋が 15手以内に成立 するので、馬に王手をかけられた局面がこの中にある。合法手が数手しか無く、そのうち玉が 逃げる手が持ち駒の角で詰む、という形。方策ネットが詰みを探した結果ではない。
この 3,917 手の `prob` は全て 0 である。 温度 100 の softmax が exp((-31998 − top)/100) を float32 の 0 に落とすためで、方策としては候補から外れる。 詰みが最善手の側にある行 (one-hot に切り替わる行) は 0 行だった。
`cp` を回帰の目標にするなら、この 3,917 個は -32000 付近の外れ値として効く。 value_q だけ見て「詰みは入っていない」と判断すると取り逃がす。
key-value メタデータ
シャードのフッタが、変換側の適用した規約を述べている。読む前に確認すること。
詰みの閾値は ±(mate_scale − mate_slack)、value_q は eval_coef を係数とする tanh である。1512.173 は dlshogi の 756.0865 を倍にした値。あちらは [0, 1] の sigmoid 用に較正されていて、同じ勝率曲線を tanh で [-1, 1] に通すと係数が倍になる。
value_q が tanh(最善手の cp / 1512.173) と一致することは、畳み込みが起きていない 19,893,565 行すべてで確認した (最大差 9.9e-08、float32 の丸めのぶん)。
`mate_scale` は V3 のシャードでは 30000 である。二つは互換ではない。片方の尺度の 詰みは、もう片方では単に大きな評価値になる。他所のシャードと畳み込む前にこのキーを 見ること。
行の並び
全体を 1 回シャッフルしてある。畳み込みのあと、コーパス全体に対して固定 seed の Fisher–Yates を 1 回かける (manaka-pack の --buckets 1 経路)。決定的なので同じ ストリームからは同じシャードが出るが、順序としての意味は無い。
手数の並びは残っていない。 ply のラグ 1 自己相関は 0.000237、行番号との相関は 0.000340 だった。
train/val の分け方
行をランダムに切ってよい。 盤面は一意で、ラベルは局面ごとに決まる探索の評価値 なので、切り方に条件は無い。対局単位で切る必要は無い — 対局が無いので、そのための `game_id` のような列も無い。
使いどころ
序盤の局面の広さを稼ぐためのもの。 自己対局のコーパスは開始局面が少ないと同じ 手順を何度も踏むので、それを散らす目的で作った。
SPRT の自己対局の開始局面には使わないこと。 採点を通った候補を全部含んでいるので、 互角から大きく外れた局面が入っている。開始局面が要るなら上の balanced で絞ってから 使うこと。ただしその balanced は木を作ったときの選抜とは別物である (上記)。
出所とライセンス
平手から自前の方策ネットと評価関数だけで生成したもので、外部の局面集は使っていない。
