NN キャッシュ(推論キャッシュ)は、方策・価値ネットワークの推論結果を局面キーで保存し、同じ局面を再び評価するときにニューラルネットワークの再実行を避けるためのキャッシュである。
AlphaZero 型のモンテカルロ木探索(MCTS)では、葉ノードを展開するときに policy と value をニューラルネットワークで求める。GPU 推論は強力だが、探索中に何度も呼ぶと重い。そのため、同一局面に対する policy/value を保存しておくと、トランスポジションや再訪問時の無駄な推論を減らせる。
典型的には、次のような情報を保存する。
注意点として、NN キャッシュは置換表と似ているが目的が違う。置換表は探索結果、深さ、境界値、最善手などを保存して探索を再利用する表である。一方、NN キャッシュはニューラルネットワークの推論結果を保存する。探索結果そのものではなく、葉評価の入力を節約するための仕組みである。
dlshogi の selfplay/self_play.cpp には、自己対局生成用の明示的な NN キャッシュがある。
struct CachedNNRequest {
float value_win;
std::vector<float> nnrate;
};
typedef LruCache<uint64_t, CachedNNRequest> NNCache;
typedef LruCacheLock<uint64_t, CachedNNRequest> NNCacheLock;
nnrate は合法手ごとの policy prior、value_win は value 出力に相当する。既定の nn_cache_size は 8388608 で、コマンドラインオプションから変更できる。
探索中に pos->getKey() が NN キャッシュに存在すれば、子ノードを展開したあと、キャッシュ済みの nnrate と value_win を使う。存在しなければ QueuingNode で局面をバッチ推論キューへ積み、EvalNode でニューラルネットワークを実行したあと、得られた policy/value を CachedNNRequest として nn_cache.Insert(...) する。
selfplay/LruCache.h では LRU 方式のキャッシュと LruCacheLock が用意されている。LruCacheLock は参照中の要素を pin し、並列探索中に参照しているキャッシュ要素が途中で破棄されにくいようにするための仕組みである。
NN キャッシュはバッチ推論と相性が良い。
ただし、キャッシュヒットが多すぎる設計が常に良いとは限らない。巨大なキャッシュはメモリを消費し、探索スレッド間の同期やキャッシュロックのコストも持つ。実装では、キャッシュサイズ、バッチサイズ、探索スレッド数、GPU の推論速度をまとめて調整する必要がある。
NN キャッシュのキーは、ニューラルネットワークの入力が依存する情報を正しく表していなければならない。
将棋では、盤上の駒、持ち駒、手番はもちろん重要である。さらに、実装によっては次のような情報も結果に影響しうる。
例えば Lc0 には CacheHistoryLength という設定があり、ニューラルネットワークが履歴を入力に使う場合、キャッシュキーにも必要な履歴を含めないと、同一盤面だが異なる履歴の評価を誤って再利用しうる、という注意点が示されている。将棋でも、局面キーだけで十分かどうかは、そのエンジンの入力特徴とルール処理に依存する。
NN キャッシュは高速化のための実用的な仕組みだが、探索の意味を壊さない範囲で使う必要がある。
selfplay/self_play.cppselfplay/LruCache.h