fv.binなしとの対決

floodgateのbona6_no_fvとの対戦なのですが、ひよこカルロ将棋neoとzattai-bonanzaばかりが当たり(37回)、レーティングが近いはずのbona6_no_fvとは全く当たらなかったので(2回!!)、zattai-bonanzaを撤去しました。

マッチングアルゴリズムの問題なのでしょうか?私にはよくわかりません。

そうしますとbona6_no_fvとそこそこ当たるようになりまして、以下がその結果です。

8-0-1のようで、圧倒的にひよこカルロ将棋neoのほうが勝率が高いとわかりました。ひよこカルロ将棋neoがバグっているわけではなさそうなので安心しました。

しかしひよこカルロ将棋neoの持ち時間の使い方が適当すぎて悲しいものがあるので、今日はその調整をしたいと思います。うまくすればR30ぐらい上がるかも知れません。

SetEventは早いのか遅いのか

コメント欄でイベントを使ったほうが安定して早いのではないかという指摘を頂戴しております。

NW
WaitForSingleObjectからの復帰のほうが、安定して早いほうに100カノッサ

私はSleep(0)からの復帰のほうが絶対早いと信じているのでここは私も100カノッサを賭けて勝負です!!

#include "stdafx.h"
#include <windows.h>

#include "timer.h"
using namespace hiyoko_shogi;

#define EVENT_NAME TEXT("Event Object Test")

volatile bool quit = false;
DWORD WINAPI DoThread(DWORD ThreadCount)
{
  HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME);

  int i = 0;
  while(true)
  {
    /* 所有権獲得まで待機 */
    WaitForSingleObject(hEvent, INFINITE);

    ++i;

    if (quit)
      break;
  }
  printf("times = %d\n",i);
	
  CloseHandle(hEvent);
  return 0;
}

void* work = NULL;
DWORD WINAPI DoThread2(DWORD ThreadCount)
{
  int i = 0;
  while(true)
  {
    /* 所有権獲得まで待機 */
    if (work == NULL)
    {
      Sleep(0);
      continue;
    }
    ++i;
    work = NULL;

    if (quit)
      break;
  }
  printf("times = %d\n",i);
  return 0;
}


/* メイン関数 */
int main_(int argc, char **argv)
{
  HANDLE hThread;

#ifdef USE_EVENT
  HANDLE hEvent;
  hEvent = CreateEvent(NULL, FALSE, TRUE, EVENT_NAME);
	ResetEvent(hEvent);
  hThread = CreateThread(
		NULL, 0, (LPTHREAD_START_ROUTINE)DoThread,
		/*(LPVOID)i*/ 0, 0, NULL);
#else
	hThread = CreateThread(
		NULL, 0, (LPTHREAD_START_ROUTINE)DoThread2,
		/*(LPVOID)i*/ 0, 0, NULL);
#endif

  // スレッド起動待ち
  Sleep(1000);

  Timer time;

  time.reset();
  int c = 0;

#ifdef USE_EVENT
  // イベントを用いる場合
  for(;;)
  {
    SetEvent(hEvent);
    if ((c++ & 0xfff) == 0xfff
      && time.elapsed() >= 1000)
      break;
  }
  quit = true;
  SetEvent(hEvent);

  /* 後処理 */
  CloseHandle(hEvent);
  CloseHandle(hThread);
#else
  // イベントを用いない場合
  for(;;)
  {
    work = (void*)1;
    if ((c++ & 0xfff) == 0xfff
      && time.elapsed() >= 1000)
      break;
  }
  quit = true;
  work = (void*)1;
#endif

  // 結果が表示されるのを待つ。
  Sleep(5000);

  return(0);
}

(c++ & 0xfff) == 0xfff のくだりは、timerの取得に要する時間を希釈したかったので一定回数ごとにしかelapsedを呼び出さないようにするためのhackです。

■ 実験環境

Windows7 x64にて測定
・コアは2つ(Corei5)

さて、結果はと言いますと…

SetEventを用いた場合 → 100万〜210万回

そこそこいいスコアですね。1回当たり1us(1マイクロ秒)以下です。
しかしよく考えると、メインスレッド側で連続してSetEventした場合、worker側でWaitForSingleObjectするタイミングですでにシグナル状態になっていて、待機状態にならずそのまま続行している可能性はありそうです。そういう意味では本当はもう少し悪いスコアなのかも知れません。

まあ、そのへんはあとあと考えるとしまして、Sleepを用いたほうも計測してみます。

Sleepを用いた場合 → 1000万〜1200万回!!

圧倒的です。一桁違います。

ですが、上で書きましたように、メインスレッドでwork=(void*)1;し続けているのでworkerがSleep(0)せずにぐるぐる回っているだけかも知れません。そりゃそうですね。

そこで、メインスレッド側のループでSleep(0)を入れることにしました。いくらSleep(0)が速いとは言え、戻ってくるまでにサブスレッド側はSleep(0)での待機状態になっているでしょうからね。

この場合を計測しますと…600万回!!

これでもまだ圧倒的です。Sleep(0) 一回で足りない可能性もあるので、Sleep(0)を2つ書いてみることにしました。

この場合を計測しますと..400万回!!

裏を返せば、メインスレッドのみに着目すればSleep(0)は(他のプロセスがほとんど動いていないなら)秒間800万回は実行出来るとも解釈できるかも知れません。すなわち、1回の実行が100ns(ナノ秒)程度。

気になったので念のためSleep(0)を1秒間に何回できるのかを同環境にて計測してみましたところ…1300万回程度でした。

つまり、WaitForSingleObjectで待つ1/5ぐらいの時間でSleep(0)から復帰できるということですね。


以上のような結果が出ました。
100カノッサはひよこがありがたく頂戴いたしました。

ひよこカルロ将棋neo v1.19公開しました。

思考時間を使いすぎることがあったので別スレッドでタイマーを回してあまりの場合には思考を打ち切るようにしました。

思考時間を何故使いすぎていたかと言いますと、ponderで思考していて置換表にある程度の情報があると、置換表にヒットしているうちはiterationがすこぶる早く回り、あるところで置換表にヒットしなくなるので突然1回のiterationで思考時間を消費しまくるという現象です。

ひよこカルロ将棋neoは、思考時間は「前回までのiterationでこれくらい時間を使っているので、次のiterationはこれくらいだよね、だったら時間をオーバーしそうだからやめとくね」というような予測をもとに思考の打ち切りをするのですが、前回までのiteration時間がアテにならないときは、このへんがうまくいかず、無駄に思考時間を使いすぎていました。

そこで別スレッドでタイマーを監視し、我慢の限界を超えたらストップさせるという、そういう仕組を導入しました。

あと、思考時間1秒のときも0.8秒まではしっかり使うようにしました。3秒なら2.8秒までは思考します。2.8秒を超えそうになったらタイマーを監視しているスレッドが停止させるという仕組みです。これにより、X.8秒ギリギリまで使うことが出来るようになったので持ち時間の消費(サーバー計測による)が極力抑えられるはずです。

1.12と自己対戦させてみましたところ、1.19のほうが8割ほど勝ち越すようでした。まあ、例によって同ソフト対決なのでわずかでも探索量の多いほうが圧倒的に勝ち越すということなのかも知れません。

一応floodgateに新しいバージョンを投入しておきます。R30ぐらい上がってくれると嬉しいのですが…。

bona6_no_fv_with_bookについて

bona6_no_fvはfloodgateの2週間レーティングで現在R1851です。おそらくこのへんで安定するものと思います。ひよこカルロ将棋neoともっと当たれば、もう少bona6_no_fvのレーティングが下がるはずなのですが、当たらないものは仕方ありません。

それで、次はbona6_no_fv_with_bookという定跡つきのfv.binなしのBonanza6がfloodgateに登場しました。どこのどなたかは知りませんが、これまた面白い実験だと思います。

駒得のみの評価関数の場合、定跡ありと定跡なしとでどれくらいレーティングが変わるのか興味深いです。

ひよこカルロ将棋neoもよく「定跡を入れたらどう?」と言われております。

定跡を編集するプログラムを書いたりするのは結構の手間なのです。また、定跡を編集する手間もあります。BonanzaぐらいRが高ければ、プロの棋譜のうち評価値が0から大きく
離れない部分までを定跡として自動登録するようなことは可能でしょうけども、いまのひよこカルロ将棋neoにそれが出来るはずもなく、見送っております。

ひよこカルロ将棋neoが強くなったら、そのような方法で定跡DBをプロの棋譜から自動的に作成しようと思っております。

あとは、このあと評価関数を改善していく上で、定跡なしでどういう序盤を指すのかを見て、その評価関数で序盤がきちんと指せるのか知っておきたいという思いもあります。

まあ、そんなわけでして、ひよこカルロ将棋neoが定跡を搭載するはもう少し先になりそうです。

それはともかく、bona6_no_fv_with_bookでどれくらいのレーティングがつくのか楽しみです。bona6_no_fv_with_bookがR2000を超えるようでしたら、ひよこカルロ将棋neoも定跡の搭載について考えねばなりませんね。

週刊ひよこ将棋 創刊しました!!

週刊ひよこ将棋 創刊しました。
週刊ひよこ将棋は将棋ソフトです。雑誌などではありません。念のため。

例によってこのブログの一番上のところから実行ファイルがダウンロードできます。

週刊ひよこ将棋は毎週新しい評価関数の形を試してみるという実験プロジェクトです。
なお、動作にはBonanza6のfv.binが必要ですので週刊ひよこ将棋と同じフォルダに入れてください。

ゆくゆくは自力学習させますが、まずはいろんな評価関数の形でどれくらい強くなるのかをてっとり早く知りたいので今回はfv.binの一部を利用する形となっております。

前回floodgateに投入していたzattai-bonanzaから、KKPの評価値を少し手心を加えまして、そして、npsをひよこカルロ将棋neoの20%ダウン程度で済むように高速化しましたのでR2200程度になっているのではないかと思います。

floodgateに投入していきなりgps_normalにボコられましたので、やっぱりそんなに強くないのかも知れませんが。

週刊ひよこ将棋の計画

コンピューター将棋ソフトの評価関数は近年、「npsを落としてでも正確に」という方向性で進化しています。GPS将棋などにはその傾向が顕著に見られます。

ひよこ将棋シリーズもゆくゆくはそういう複雑で精緻な評価関数に向けて進化していかなければならないわけですが、その前に単純な評価関数でどれくらいの強さになるのかを調べることによって、そのあとの足がかりとします。

そこで、まずBonanza6の評価関数より軽い評価関数について考えてみなければなりません。

週刊ひよこ将棋 創刊号 → Bonanza-KKP
週刊ひよこ将棋 第二号 → Bonanza-KKP + KPPの重要な部分のみ
という予定で考えています。

第二号ではKPPの一部のみを差分計算しやすい形で取り出して使います。

Bonanza4以降のKPPの計算は1000回程度のテーブル参照が必要になります。
Bonanza6では差分評価が導入され、玉を移動させないときは100回程度のテーブル参照で済むようになっています。玉を移動させるときは差分計算せずにそのまま計算するようになっています。

玉を動かす指し手は全ての指し手の1/8ぐらいだと思うので、平均的には100回*7/8 + 1000回 * 1/8 = 212.5回程度のテーブル参照をしている計算になります。

これをこの1/5程度に減らしたKPPの評価関数を私は考えたので、まずはこれを試してみます。1/5程度の計算量で計算できるなら、同nps比較でR100ぐらい下がっても、npsの高さで元のBonanzaを上回るかも知れません。

R100以上下がるようなら…他の評価関数の形を試すことにします。

ただ、1/5程度の計算量で計算できると言いましても、最初から差分計算して極限までチューンするコードを書くのは、これで全然強くなかったときにはまったくの無駄なので、まずは差分計算は一切せずにストレートに計算するコードを書きます。

ストレートに計算するのでBonanza6の(差分計算ありの)評価関数よりさらに時間がかかります。

また、この評価関数はfv.binの一部のみを使用するので、その部分のみを棋譜から学習しなおせば、さらに強くなると思われます。

そのような伸びしろはあるものの、まずはこのストレートに計算したバージョンでR2300程度になってもらいませんと、そこまでする価値がないという判断をせざるを得ません。

ともかく、週刊ひよこ将棋 第二号はそんな予定でいます。

週刊ひよこ将棋シリーズではいろんな評価関数の形を試してみたいと思っています。