●NineDayFever 局面評価の極北を目指しています。 対戦中に現れた局面を調べ、機械学習結果の欠陥を探して修正しているのは去年と同じです。プログラムはほぼbonanza 6.0のままですが、一部手が入っています。 局面評価に関し、今年の版でアップデートされた点は以下の通りです。詳細は後述します。 1. 評価値が誤っていそうな局面をログから抽出するようにしたことで、棋譜には現れないが思考中に現れる誤った評価値がついた局面を解析できるようになりました。 2. 未知の局面への対応力増加を期待して、学習するパラメータを増やしました。 3. 評価値の計算で手番を考慮するようにしました。 以上3点の変更(下記のプログラム変更5., 6.は含まない)で、去年出場した版と比較して、レーティング向上は200弱、オリジナルのbonanza6.0とのレーティング差は380程度となっています(持ち時間2分リーグ戦での評価結果)。 プログラムに関しては、以下の点でオリジナルのボナンザに手が入っています。 4. 上記3.で述べた手番考慮eval処理を実装しています。 5. 上記3. により、一手パスの影響を精度良く見積もれるようになったので、null move pruning 処理を効率化しています。一手パス直後のスコアで beta cut が起こらなさそうだとわかったら、null move pruningを打ち切っています。 6. 去年の版で入っていたちょっとだけ高速化に加え、stockfish を参考にして探索部分を一部アップデートしました。一部の処理は簡単にbonanza6.0に入れることができてそれなりの効果があるようです。 ●詳細 1. ログ解析   去年出場した版では、自己対戦の棋譜に現れた局面を解析対象にしていました。これはそれなりに効果があったのですが、持ち時間が多い対局で、解析に使った局面からかけ離れた局面が思考中に現れてくると、bonanza 6.0に戻ってしまいます。  思考中に現れたおかしな評価値がついている局面すべてを拾いだすときりがないのですが、ログに出力されている手順中に現れた局面を調べるようにしたところ、頭打ちぎみだったレーティングが向上しました。 2. 学習パラメータ増加  bonanza6.0 の機械学習では、学習に使った教師データに現れない駒の配置に対しては 0 が入るため、学習が不十分な局面に入ると、評価の基準が駒得・駒損ぐらいしかなくなってしまいます。しかし、角や桂馬の頭に歩を打つなど、駒がどこにあっても考慮の対象となるパターンはあるはずです。 この問題に対処するため、今年のNDFでは、機械学習時に、kppパラメータをkpp絶対、kpp相対、pp絶対、pp相対のパラメータに分割しています。 kpp(k, p0, p1) = kpp絶対(左右反転(k,p0,p1))+kpp相対(平行移動(k,p0,p1))+pp絶対(左右反転(p0,p1))+pp相対(平行移動(p0,p1))  左右反転、平行移動は、引数の駒の種類、配置に対して、あるid を返す関数です。左右反転は盤面を左右反転して同一になる配置であれば同じidが返ります。平行移動は左右反転に加えて平行移動して同一になる配置であれば同じidを返します。  kpp絶対は、bonanza6.0のkppと同じ意味になってます。絶対と言ってますが、左右反転は考慮されていて、左右反転して同一になる配置には同じパラメータを使います。kpp相対は、左右反転に加え、平行移動で同一になるかどうかを考慮します。ppは王以外の2駒の配置関係で、絶対、相対はkppと同様に、平行移動なし、ありに対応します(実際は、pp絶対、pp相対は、先後反転して同一になる配置も同一パラメータを使い、和をとるときに符号を反転しているのですが、細かくなるのでここでは簡略化してます)。  このように分割したパラメータに対し、機械学習を実行します。得られた4つのパラメータの和を取って従来のkppテーブルに入れることで、探索部分には一切手を入れずにこの機械学習結果を使えます。  一見、kpp絶対が他のパラメータによる影響を吸収してしまい、何も変わらないという結果になるように思えますが、2乗の正則化項(各パラメータの2乗の和を取ったものに一定の係数をかけた値を最適化の目的関数に足す)を使うことで、共通して現れるパラメータの絶対値をできるだけ大きくして、kpp絶対の絶対値を小さくするような解が得られます。  たとえば、教師データにおいて、王がどこにいても、角の頭に歩を打つようなパターンが良い評価になっていたとします。2乗の正則化項を使って機械学習すると、pp相対の値を高くして、その分kpp絶対などの値を小さくするような解が得られることになります。角の頭に歩を打つ配置は、教師データの中に現れていない位置であっても、pp相対を通じて高い評価値がついていることになります。結果的に、学習に現れなかったパターンに対する評価値の精度向上がある程度期待できます。 3. 手番考慮評価関数  bonanza 6.0 では駒の配置だけから評価値を計算しており、手番は考慮していません。中盤になって駒がぶつかるようになれば、静止探索によって手番を握っているかどうかの差が出てくるのですが、序盤での一手パスのような手や、先後同形に近い局面の評価が難しい(完全に先後同形だと評価値は必ず0になるため)という問題がありました。  bonanza6.0に手番を考慮した評価関数を実装するためには、以下のような制約条件を満たす必要があります。  ・先手の評価値に -1 をかけると後手の評価値になるという性質を満たすこと  ・計算があまり重くないこと(一手前の評価値から差分演算で評価値を求めるなどの高速化手法がそのまま使えることが望ましい)  ・機械学習でパラメータが決定できること  今年の NDF では、以下のようにして、これらの条件を満たしながら手番を考慮したevaluate()を実装しています。  局面X の評価値を、Sboard(X)とSturn(X)という二つの関数によって表現します。Sboard(X)は従来の評価値を返す関数、Sturn(X)は手番を持っている側へのボーナスで、先手後手反転しても値が変わらないという性質を持っています。評価値は、次に指すのが自分なら、Sboard(X)+Sturn(X)、相手が指すならSboard(X)-Sturn(X)となります。この方法で、「先手のスコアに -1 をかけると後手のスコアになる」という前提を保持して手番を考慮する評価関数を作ることができます。このやり方だと、一手パスのペナルティは、Sturn(X) *2になります(スコアが Sboard(X)+Sturn(X)から Sboard(X)-Sturn(X) に変わるため)。     Sturn(X) の実装方法はいろいろありそうですが、NDFでは、Sboard(X)と同様、3駒関係であらわしています。kpp/kkp テーブルの一要素の中に2つのパラメータが格納されていて、それぞれの和をとっていくことになります。このような構成を採用した理由は、Sturn に大きく影響する特徴量が何なのかよくわからなかったことと、高速化のための差分計算手法がそのまま適用できるという利点があったためです。evaluate.c の変更量は数百行程度です。  計算時間については、単純に実装すると、nps が 10%ほど低下します。あまり影響がないレベルではありますが、SSE命令を使うと10%高速化できるので、速度的なペナルティは気にする必要はなさそうです(正確に言うと、同様に従来の evaluate に SSE命令を適用すると 10%高速化されたので、結局10%損しているのですが)。  学習に関しては、(Sturn は先後対称であるため)計算の際に符号に気をつける必要はありますが、結局多数のパラメータの線形和で表現されているという性質は同じなので、従来の機械学習をそのまま拡張することで対応可能です。ただし、パラメータ数が倍に増えているため、教師データも倍必要ということになり、使うのがプロ棋士の棋譜だけだと良い学習結果を得られるかどうかは不明です。NDF方式では学習に使う局面数が増えているので、パラメータ数の増加に対応することができているようです。