やねうら王classicの開発目標がApery(WCSC25)であった。
これは評価関数が3駒関係(手番なし)という点で、ほぼ同一だとみなせるからである。
そこでやねうら王classicとAperyとを対戦させたのだが、Apery(WCSC25)は切れ負け設定だと時間の使い方がとても下手で10分切れ負けだと序盤で時間を使いきってしまう。(おそらく選手権の持ち時間に合わせて思考時間が調整されているため) これではフェアな比較とは言いがたい。
そこで持ち時間なし、1手5秒で120局ほど対戦させてみた。63-7-50 勝率0.56。(+R40程度) とりあえず勝ち越すようなのでとりあえず目標は達成である。
しかし、AperyTwig(大樹の枝)は、Apery(WCSC25)に7割ぐらい勝ち越すらしいので、R147ほど高い。それからするとやねうら王classicとAperyTwigとの差はR100程度離れている計算になる。
何の差なのかと思って考えてみたところ、一つ目は評価関数に入っている手番である。これでR50前後変わる気がする。あとは評価関数の学習の質である。やねうら王classicの評価関数バイナリ、時間がなかったのでオンライン学習で、適当にまわして、適当に停止させた。(適当すぎ)
本来なら自己対戦をしてテストなどすべきだが、これを学習させていたときPCが足りてなくて自己対戦で勝率を見るどころではなかったので本当、適当だった。このへんでR50前後、損をしている気がする。
あとはsingular extensionである。やねうら王classic、何故かこれを入れると弱くなる。理由はよくわからない。バグっているとしか思えない。singular extensionで弱くなるケースなんて経験したことがないし、聞いたこともない。「自己対戦ではsingular extensionは勝率が上がりすぎるので信用してはならない」と言われてるぐらいなのに、全く強くならない。どういうことなんだ、こんにゃろー。
とりあえず、singular extensionでR50ぐらい損をしている気がする。
あと、nps、低すぎる。私の予想より20%ぐらい遅い。何かやらかしてしまっている可能性大。LONG EFFECT LIBRARYでの長い利きの更新は確かに遅い(プロファイラで見たら15%ぐらい消費)のだけど、まあ利きは今後評価関数に使う予定なのでこれは良しとする。でも、この15%がなくともやはり予想より20%ほど遅いのだ。どうなってるのかよくわからん…。やねうら王2015、ここまで遅くなかったのに…。この部分でR30ぐらい損をしている。
そんなわけで、ストレートに計算すると、R50 + R50 + R50 + R30 = R180ほどトータルで損をしている。(気がする)
ともかく、AperyTwigにはまだ追いつけそうにない。誰かデバッグきぼんぬ。
AperyTwigの評価関数バイナリも読み込めるようにしようと考えていたが、5月の選手権が終わったらまた新しい評価関数バイナリが公開される気もするので、それが公開されてから考えればいいかとも思っている。
singular extensionの件、marginの係数を8から50に変えたら、結構勝ち越していました(1手1秒)。もし測定間違いでしたらすいません。
おおおお。マジですか…。色々試してみます。ありがとうございます!
– Value rBeta = ttValue – 8 * depth / ONE_PLY;
+ Value rBeta = ttValue – 50 * depth / ONE_PLY;
で1手 0.5秒~1.5秒(ランダム)で
335-61-351(74.2% R-8.1)
負け越し…。singular判定など、他の条件どこか間違えてる可能性が出てきました…。
約750局で負け越しですか…大変失礼しました。
昨日は30番勝負で「20勝8敗2引き分け」「19勝7敗4引き分け」などたまたま良さそうな結果が続いたのですが、そもそも30局では少なすぎるのですね…
追加で色々試した結果を、参考までに以下に書きました。今のところ解決策等は見えていません。
———-
classic2.36(singularあり) vs classic2.36(singularなし)
(classic-tceではなくclassicを使用)
【条件1】こちらは「10」で固定で
// singular延長をするnodeであるか。
bool singularExtensionNode = !RootNode
&& depth >= 10 * ONE_PLY // Stockfish , Apreyは、8 * ONE_PLY
&& (以下略)
【条件2】param1を可変で
// このmargin値は評価関数の性質に合わせて調整されるべき。
Value rBeta = ttValue – param1 * depth / ONE_PLY;
■30番勝負(将棋所、1手1秒、1スレッド、ponderなし)
param1= 0 → 15-0-15 singular率:64%
param1= 8 → 15-1-14 singular率:38%
param1= 10 → 18-3-9 singular率:35%
param1= 20 → 12-4-14 singular率:22%
param1= 45 → 19-4-7 singular率:11%
param1= 50 → 20-2-8 singular率:14%
param1= 50 → 11-0-19 singular率:10%
param1= 75 → 16-3-11 singular率: 8%
param1=100 → 14-1-15 singular率: 7%
■100番勝負(将棋所、1手5秒、1スレッド、ponderなし)
param1= 30 → 45-3-52 singular率:15%
param1= 50 → 48-7-45 singular率: 9%
———-
・「depth >= 10 * ONE_PLY」なので、もっと思考時間を長くしないと効果が出ないのでしょうか…?
1手1秒や1手5秒の場合、singularExtensionNodeの値がtrueになるのは、全体の0.3%~0.4%程度でした。
・上記結果では、singular率が10%程度になるのはparam1が50程度のときのように見えます。
なお、singular率は1局の中でも変化し、終盤になると上昇するようです。
・singularとは別の話ですが、improvingの値がtrueになるのは70%~75%程度のようです。
2手前の局面から評価値が上昇しているのは25%~30%程度で、
「(ss )->staticEval == VALUE_NONE」が35%~40%、
「(ss – 2)->staticEval == VALUE_NONE」が15%~20%ほどあるようです。
(通常探索でMOVES_LOOP:まで到達した中での確率。MOVES_LOOP:まで到達するのは全体の45%~50%程度。)
improvingについてはおそらく想定内の結果かと思っています。
いま気づいたのですが、MOVES_LOOPの直前で評価関数の差分計算が出来るようにevaluate()を呼び出すようにしてあるのですが、ここで、staticEvalにも値を突っ込んだほうがいいですね。(最新版ではそうなっています)
if (pos.state()->sumKKP == VALUE_NONE)
ss->staticEval = evaluate(pos);
そうしないと、(ss-2)->staticEval == VALUE_NONEになってしまい、improvingの判定に影響が出るような…。
あと、singularのマージンなのですが、VALUE_KNOWN_WIN、これいくらに設定するかという問題がありまして、優等局面、このスコアにしていると相手番から見ると -VALUE_KNOWN_WIN で、ここからmarginを引くとVALUE_MATED_IN_MAX_PLYより小さくなったりして詰みのスコアとみなされかねないという。なので、VALUE_KNOWN_WINは、普通、VALUE_MATE_IN_MAX_PLY – 1000とかにしとかないといけないのですが、50 * depthをsingularのmarginとすると容易にこの現象が起きるような…。なかなか盲点です。
> そうしないと、(ss-2)->staticEval == VALUE_NONEになってしまい、improvingの判定に影響が出るような…。
この動作がStockfishの意図している動作のような気もしますね。要するに王手がかかっているときは、評価関数を呼び出さず、ss->staticEval = VALUE_NONEにしておいて、王手がかかっている局面であったことを示すフラグ代わりに使い、improvingの値はこの内容を踏まえていると。
うーん。だとしたら、evaluate()呼び出したときにstaticEvalに代入するのは間違いなんですよね…。でもそれだとNULL MOVEのときにstaticEvalから値を取り出せなくて少しそんな気も…。うーん..
(1)ss->staticEvalの件ですが、通常探索で王手がかかっているときはすぐにMOVES_LOOPに飛んで
NULL MOVEのところは通らないので、NULL MOVEで呼ばれた側がss->staticEvalから値を取り出せない心配はしなくてもよいのではないでしょうか?
ただ、VALUE_NONEに「王手がかかっている」というあまり直観的ではない意味を持たせてしまっているのも分かりずらいので
例えばssに王手フラグを追加するような方法はダメなのでしょうか...?
(2)VALUE_KNOWN_WINの件と似たような話で(たいして似ていませんが)、
評価値が未計算か否かを「if (pos.state()->sumKKP == VALUE_NONE)」で判定されていますが、
sumKKPは(FV_SCALE * FV_SCALE_KKP)で割る前の値ですので、
たまたま32002(=VALUE_NONE)と一致する可能性もゼロではないと思います。
何かもっとありえない値にしておく方が無難かと思いますが、いかがでしょうか?
(100万回に1回ほど差分計算ではなく全計算が走るだけですので、棋力への影響は皆無だと思いますが)
> NULL MOVEで呼ばれた側がss->staticEvalから値を取り出せない心配は
うおー、、そうでした..
> ただ、VALUE_NONEに「王手がかかっている」というあまり直観的ではない意味を持たせてしまっているのも分かりずらいので
> 例えばssに王手フラグを追加するような方法はダメなのでしょうか...?
別途フラグが要るのが(Stockfish的には)イケてないという判断なのでしょうね。
> sumKKPは(FV_SCALE * FV_SCALE_KKP)で割る前の値ですので、
> たまたま32002(=VALUE_NONE)と一致する可能性もゼロではないと思います。
うおー、すごい!よく気づきましたね…。
私はまったく気づいてませんでした(^^;
修正しときます(`・ω・´)ゞ