Created
June 26, 2017 08:45
-
-
Save ohga/40ed7da65be259238dc6b610515f3802 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/source/Makefile b/source/Makefile | |
index 82f12fc..60ff9bf 100644 | |
--- a/source/Makefile | |
+++ b/source/Makefile | |
@@ -97,6 +97,7 @@ SOURCES = shogi.cpp \ | |
usi.cpp \ | |
thread.cpp \ | |
tt.cpp \ | |
+ progress.cpp \ | |
extra/book/apery_book.cpp \ | |
extra/book/book.cpp \ | |
extra/bitop.cpp \ | |
diff --git a/source/engine/2017-early-engine/2017-early-search.cpp b/source/engine/2017-early-engine/2017-early-search.cpp | |
index d46ee43..80ab594 100644 | |
--- a/source/engine/2017-early-engine/2017-early-search.cpp | |
+++ b/source/engine/2017-early-engine/2017-early-search.cpp | |
@@ -170,6 +170,8 @@ namespace YaneuraOu2017Early | |
// depth(残り探索深さ)に応じたfutility margin。 | |
Value futility_margin(Depth d) { return Value( PARAM_FUTILITY_MARGIN_ALPHA * d / ONE_PLY); } | |
+ Value futility_margin2(Depth d, double p) { return Value((int)(50 + 150 * p) * (d / ONE_PLY)); } | |
+ | |
// 残り探索depthが少なくて、王手がかかっていなくて、王手にもならないような指し手を | |
// 枝刈りしてしまうためのmoveCountベースのfutilityで用いるテーブル | |
@@ -1083,6 +1085,8 @@ namespace YaneuraOu2017Early | |
// 1手詰めがなかったのでこの時点でもsave()したほうがいいような気がしなくもない。 | |
} | |
+ const double progress = Prog::evaluateProgress(pos); | |
+ | |
// ----------------------- | |
// Step 5. Evaluate the position statically | |
// ----------------------- | |
@@ -1193,7 +1197,7 @@ namespace YaneuraOu2017Early | |
if ( !RootNode | |
&& depth < PARAM_FUTILITY_RETURN_DEPTH * ONE_PLY | |
- && eval - futility_margin(depth) >= beta | |
+ && eval - futility_margin2(depth, progress) >= beta | |
&& eval < VALUE_KNOWN_WIN) // 詰み絡み等だとmate distance pruningで枝刈りされるはずで、ここでは枝刈りしない。 | |
return eval; | |
// 次のようにするより、単にevalを返したほうが良いらしい。 | |
diff --git a/source/position.cpp b/source/position.cpp | |
index b9978d6..6ff44bf 100644 | |
--- a/source/position.cpp | |
+++ b/source/position.cpp | |
@@ -274,6 +274,7 @@ void Position::set(std::string sfen) | |
#if !defined(EVAL_NO_USE) | |
st->materialValue = Eval::material(*this); | |
Eval::compute_eval(*this); | |
+ Prog::computeProgress(*this); | |
#endif | |
// --- effect | |
@@ -1014,6 +1015,7 @@ void Position::do_move_impl(Move m, StateInfo& new_st, bool givesCheck) | |
// 上と同じ意味。 | |
st->sum.p[0][0] = VALUE_NOT_EVALUATED; | |
#endif | |
+ st->progress.setNoProgress(); | |
// 直前の指し手を保存するならばここで行なう。 | |
diff --git a/source/position.h b/source/position.h | |
index 485b4ef..600d4ef 100644 | |
--- a/source/position.h | |
+++ b/source/position.h | |
@@ -6,6 +6,8 @@ | |
#include "evaluate.h" | |
#include "extra/key128.h" | |
#include "extra/long_effect.h" | |
+#include "progress.h" | |
+ | |
struct Thread; | |
// -------------------- | |
@@ -143,6 +145,7 @@ struct StateInfo | |
// 2)は、このnodeのEvalSum sum(これはdo_move_null()でコピーされている)から | |
// 計算出来るから問題ない。 | |
StateInfo* previous; | |
+ Prog::ProgressSum progress; | |
}; | |
diff --git a/source/progress.cpp b/source/progress.cpp | |
new file mode 100644 | |
index 0000000..4caac02 | |
--- /dev/null | |
+++ b/source/progress.cpp | |
@@ -0,0 +1,676 @@ | |
+/* | |
+読み太(yomita), a USI shogi (Japanese chess) playing engine derived from | |
+Stockfish 7 & YaneuraOu mid 2016 V3.57 | |
+Copyright (C) 2004-2008 Tord Romstad (Glaurung author) | |
+Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author) | |
+Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author) | |
+Copyright (C) 2015-2016 Motohiro Isozaki(YaneuraOu author) | |
+Copyright (C) 2016 Ryuzo Tukamoto | |
+ | |
+This program is free software: you can redistribute it and/or modify | |
+it under the terms of the GNU General Public License as published by | |
+the Free Software Foundation, either version 3 of the License, or | |
+(at your option) any later version. | |
+ | |
+This program is distributed in the hope that it will be useful, | |
+but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+GNU General Public License for more details. | |
+ | |
+You should have received a copy of the GNU General Public License | |
+along with this program. If not, see <http://www.gnu.org/licenses/>. | |
+*/ | |
+ | |
+#include "misc.h" | |
+#include "shogi.h" | |
+ | |
+#include <fstream> | |
+#include <iomanip> | |
+#include <iostream> | |
+#include <sstream> | |
+ | |
+#include "evaluate.h" | |
+#include "position.h" | |
+#include "progress.h" | |
+ | |
+namespace Prog | |
+{ | |
+ | |
+ValueProg PROGRESS[SQ_NB][Eval::fe_end]; | |
+ | |
+// 進行度ファイルの読み込み | |
+void load() | |
+{ | |
+ std::string p = path_combine((std::string)Options["EvalDir"], PROGRESS_BIN); | |
+ std::ifstream ifs(p, std::ios::binary); | |
+ | |
+ if (!ifs) { | |
+ std::cout << "info string can't open " << p << "." << std::endl; | |
+ return; | |
+ } | |
+ | |
+ ifs.read(reinterpret_cast<char *>(PROGRESS), sizeof(PROGRESS)); | |
+ std::cout << "info string open success " << p << "." << std::endl; | |
+} | |
+ | |
+// 進行度を全計算 | |
+double computeProgress(const Position &pos) | |
+{ | |
+ auto sq_bk0 = pos.king_square(BLACK); | |
+ auto sq_wk1 = Inv(pos.king_square(WHITE)); | |
+ | |
+ auto list_fb = pos.eval_list()->piece_list_fb(); | |
+ auto list_fw = pos.eval_list()->piece_list_fw(); | |
+ | |
+ int64_t bkp = 0, wkp = 0; | |
+ | |
+ for (int i = 0; i < PIECE_NO_KING; i++) { | |
+ bkp += PROGRESS[sq_bk0][list_fb[i]]; | |
+ wkp += PROGRESS[sq_wk1][list_fw[i]]; | |
+ } | |
+ | |
+ pos.state()->progress.set(bkp, wkp); | |
+ | |
+ return pos.state()->progress.rate(); | |
+} | |
+ | |
+// 進行度を差分計算 | |
+double calcProgressDiff(const Position &pos) | |
+{ | |
+ auto now = pos.state(); | |
+ | |
+ if (!now->progress.isNone()) | |
+ return now->progress.rate(); | |
+ | |
+ auto prev = now->previous; | |
+ | |
+ if (prev->progress.isNone()) | |
+ return computeProgress(pos); | |
+ | |
+ auto sq_bk0 = pos.king_square(BLACK); | |
+ auto sq_wk1 = Inv(pos.king_square(WHITE)); | |
+ auto list_fb = pos.eval_list()->piece_list_fb(); | |
+ auto list_fw = pos.eval_list()->piece_list_fw(); | |
+ | |
+ auto &dp = now->dirtyPiece; | |
+ auto prog = prev->progress; | |
+ auto dirty = dp.pieceNo[0]; | |
+ int k = dp.dirty_num; | |
+ | |
+ if (dirty >= PIECE_NO_KING) { | |
+ if (dirty == PIECE_NO_BKING) { | |
+ prog.bkp = 0; | |
+ | |
+ for (int i = 0; i < PIECE_NO_KING; i++) | |
+ prog.bkp += PROGRESS[sq_bk0][list_fb[i]]; | |
+ | |
+ if (k == 2) { | |
+ prog.wkp -= PROGRESS[sq_wk1][dp.changed_piece[1].old_piece.fw]; | |
+ prog.wkp += PROGRESS[sq_wk1][dp.changed_piece[1].new_piece.fw]; | |
+ } | |
+ } else { | |
+ prog.wkp = 0; | |
+ | |
+ for (int i = 0; i < PIECE_NO_KING; i++) | |
+ prog.wkp += PROGRESS[sq_wk1][list_fw[i]]; | |
+ | |
+ if (k == 2) { | |
+ // prog.bkp -= PROGRESS[sq_bk0][dp.pre_piece[1].fb]; | |
+ // prog.bkp += PROGRESS[sq_bk0][dp.now_piece[1].fb]; | |
+ prog.bkp -= PROGRESS[sq_bk0][dp.changed_piece[1].old_piece.fb]; | |
+ prog.bkp += PROGRESS[sq_bk0][dp.changed_piece[1].new_piece.fb]; | |
+ } | |
+ } | |
+ } else { | |
+#define ADD_BWKP(W0, W1, W2, W3) \ | |
+ prog.bkp -= PROGRESS[sq_bk0][W0]; \ | |
+ prog.wkp -= PROGRESS[sq_wk1][W1]; \ | |
+ prog.bkp += PROGRESS[sq_bk0][W2]; \ | |
+ prog.wkp += PROGRESS[sq_wk1][W3]; | |
+ | |
+ if (k == 1) { | |
+ ADD_BWKP(dp.changed_piece[0].old_piece.fb, dp.changed_piece[0].old_piece.fw, | |
+ dp.changed_piece[0].new_piece.fb, dp.changed_piece[0].new_piece.fw); | |
+ } else if (k == 2) { | |
+ ADD_BWKP(dp.changed_piece[0].old_piece.fb, dp.changed_piece[0].old_piece.fw, | |
+ dp.changed_piece[0].new_piece.fb, dp.changed_piece[0].new_piece.fw); | |
+ ADD_BWKP(dp.changed_piece[1].old_piece.fb, dp.changed_piece[1].old_piece.fw, | |
+ dp.changed_piece[1].new_piece.fb, dp.changed_piece[1].new_piece.fw); | |
+ } | |
+ } | |
+ | |
+ now->progress = prog; | |
+ | |
+ return now->progress.rate(); | |
+} | |
+ | |
+double evaluateProgress(const Position &pos) | |
+{ | |
+ double s = calcProgressDiff(pos); | |
+#if 0 | |
+ double ss = computeProgress(b); | |
+ | |
+ if (s != ss) | |
+ std::cout << b.key(); | |
+#endif | |
+ return s; | |
+} | |
+ | |
+} // namespace Prog | |
+ | |
+#if 0 | |
+#ifdef LEARN | |
+namespace Learn | |
+{ | |
+ | |
+#if 0 | |
+ // 楽な実装だが重い | |
+ Move csaToMove(std::string csa, Board& b) | |
+ { | |
+ for (auto m : MoveList<LEGAL_ALL>(b)) | |
+ if (csa == toCSA(m)) | |
+ return m; | |
+ | |
+ return MOVE_NONE; | |
+ } | |
+#endif | |
+ | |
+ // なので直接変換する | |
+ Move csaToMove(std::string csa, Board& b) | |
+ { | |
+ File f; | |
+ Rank r; | |
+ Square from; | |
+ Move ret; | |
+ | |
+ if (csa[0] != '0') | |
+ { | |
+ f = File('9' - csa[0]); | |
+ r = Rank(csa[1] - '1'); | |
+ from = sqOf(f, r); | |
+ } | |
+ else | |
+ from = SQ_MAX; | |
+ | |
+ f = File('9' - csa[2]); | |
+ r = Rank(csa[3] - '1'); | |
+ Square to = sqOf(f, r); | |
+ | |
+ // 持ち駒を打つとき、USIにこの文字列を送信する。 | |
+ static const std::string piece_to_csa[] = { "", "KA", "HI", "FU", "KY", "KE", "GI", "KI", "OU", "UM", "RY", "TO", "NY", "NK", "NG" }; | |
+ PieceType pt; | |
+ | |
+ for (pt = BISHOP; pt < 15; pt++) | |
+ if (piece_to_csa[pt] == csa.substr(4)) | |
+ break; | |
+ | |
+ assert(pt < 15); | |
+ | |
+ if (from == SQ_MAX) | |
+ ret = makeDrop(toPiece(pt, b.turn()), to); | |
+ else | |
+ ret = pt == typeOf(b.piece(from)) ? makeMove<LEGAL>(from, to, toPiece(pt, b.turn()), b) | |
+ : makeMovePromote<LEGAL>(from, to, b.piece(from), b); | |
+ | |
+ return ret; | |
+ } | |
+ | |
+#define eta 0.1 | |
+ struct Weight | |
+ { | |
+ double w, g, g2; | |
+ | |
+ void addGrad(double delta) { g += delta; } | |
+ | |
+ // 勾配値を重みに反映させる | |
+ bool update() | |
+ { | |
+ if (g == 0) | |
+ return false; | |
+ | |
+ g2 += g * g; | |
+ double e = std::sqrt(g2); | |
+ | |
+ if (e != 0) | |
+ w = w - g * eta / e; | |
+ | |
+ g = 0; | |
+ | |
+ return true; | |
+ } | |
+ }; | |
+ | |
+ Weight prog_w[SQ_MAX][Eval::fe_end]; | |
+ | |
+ // 勾配配列の初期化 | |
+ void initGrad() | |
+ { | |
+ memset(prog_w, 0, sizeof(prog_w)); | |
+#if 1 | |
+ for (auto k : Squares) | |
+ for (auto p1 = Eval::BONA_PIECE_ZERO; p1 < Eval::fe_end; ++p1) | |
+ prog_w[k][p1].w = Prog::PROGRESS[k][p1]; | |
+#endif | |
+ } | |
+ | |
+ // 現在の局面で出現している特徴すべてに対して、勾配値を勾配配列に加算する。 | |
+ void addGrad(Board& b, double delta_grad) | |
+ { | |
+ auto f = delta_grad; | |
+ auto list_fb = b.evalList()->pieceListFb(); | |
+ auto list_fw = b.evalList()->pieceListFb(); | |
+ auto sq_bk0 = b.kingSquare(BLACK); | |
+ auto sq_wk1 = inverse(b.kingSquare(WHITE)); | |
+ | |
+ for (int i = 0; i < PIECE_NO_NB; ++i) | |
+ { | |
+ prog_w[sq_bk0][list_fb[i]].addGrad(f); | |
+ prog_w[sq_wk1][list_fw[i]].addGrad(f); | |
+ } | |
+ } | |
+ | |
+ // 教師の数も少なく、現在のニューラルネットワークの出力値を求める時間も少ないので、mini batchは必要ない。 | |
+ void updateWeights() | |
+ { | |
+ for (auto sq : Squares) | |
+ for (int i = 0; i < Eval::fe_end; ++i) | |
+ { | |
+ auto& w = prog_w[sq][i]; | |
+ | |
+ if (w.update()) | |
+ Prog::PROGRESS[sq][i] = (Prog::ValueProg)w.w; | |
+ } | |
+ } | |
+ | |
+ double dsigmoid(double x); | |
+ | |
+ // 勾配を計算する関数 | |
+ double calcGrad(double teacher, double now) | |
+ { | |
+ return now - teacher; | |
+ } | |
+ | |
+ // 誤差を計算する関数(rmseの計算用) | |
+ double calcError(double teacher, double now) | |
+ { | |
+ double diff = now - teacher; | |
+ return diff * diff; | |
+ } | |
+ | |
+ struct Game | |
+ { | |
+ short ply; | |
+ Move move[512]; | |
+ }; | |
+ | |
+ typedef std::vector<Game> GameVector; | |
+ | |
+ // 進行度学習をマルチスレッドで行うクラス | |
+ struct ProgressLearner : public MultiThink | |
+ { | |
+ ProgressLearner() {} | |
+ virtual void work(size_t thread_id); | |
+ | |
+ std::vector<GameVector> games; | |
+ GameVector errors; | |
+ | |
+ // updateWeightsしている途中であることを表すフラグ | |
+ std::atomic_bool updating; | |
+ | |
+ // 親クラスにもあるがこちらでも定義する。 | |
+ uint64_t max_loop; | |
+ }; | |
+ | |
+ void ProgressLearner::work(size_t thread_id) | |
+ { | |
+ const int MAX_PLY = 256; | |
+ StateInfo st[MAX_PLY + 64]; | |
+ auto th = Threads[thread_id]; | |
+ auto& b = th->root_board; | |
+ const bool is_main = th == Threads.main(); | |
+ th->add_grading = true; | |
+ | |
+#ifdef _DEBUG | |
+ const int interval = 5000; | |
+#else | |
+ const int interval = 10000000; | |
+#endif | |
+ | |
+ for (int loop = 0; loop < max_loop;) | |
+ { | |
+ for (auto game : games[thread_id]) | |
+ { | |
+ // 平手初期局面セット | |
+ b.init(USI::START_POS, th); | |
+ | |
+ // 再生 | |
+ for (int i = 0; i < game.ply - 1; i++) | |
+ { | |
+ Move m = game.move[i]; | |
+ b.doMove(m, st[i]); | |
+ | |
+ // 教師の進行度 | |
+ auto t = (double)(i + 1) / (double)game.ply; | |
+ | |
+ // NNの出力 | |
+ auto p = Prog::evaluateProgress(b); | |
+ | |
+ // 勾配ベクトル | |
+ auto delta = calcGrad(t, p); | |
+ | |
+ addGrad(b, delta); | |
+ | |
+ if (is_main) | |
+ { | |
+#if 0 | |
+ SYNC_COUT << b << "progress = " << p * 100.0 << "%\n" | |
+ << "teacher = " << t * 100.0 << "%" << SYNC_ENDL; | |
+#endif | |
+#if 1 | |
+ static int j = 0; | |
+ if (++j % interval == 0) | |
+ { | |
+ SYNC_COUT << "now = " << std::setw(5) << std::setprecision(2) << p * 100 << "%" | |
+ << " teacher = " << std::setw(5) << std::setprecision(2) << t * 100 << "%" << SYNC_ENDL; | |
+ } | |
+#endif | |
+ } | |
+ } | |
+ } | |
+ | |
+ // メインスレッドだけがWeightを変更する。 | |
+ if (is_main) | |
+ { | |
+ int j = 0; | |
+ double sum_error = 0; | |
+ | |
+ for (auto game : errors) | |
+ { | |
+ // 平手初期局面セット | |
+ b.init(USI::START_POS, th); | |
+ | |
+ // 誤差の計算 | |
+ for (int i = 0; i < game.ply - 1; i++) | |
+ { | |
+ Move m = game.move[i]; | |
+ b.doMove(m, st[i]); | |
+ auto t = (double)(i + 1) / (double)game.ply; | |
+ auto p = Prog::evaluateProgress(b); | |
+ sum_error += calcError(p, t); | |
+ j++; | |
+ } | |
+ } | |
+ | |
+ auto rmse = std::sqrt(sum_error / j); | |
+ SYNC_COUT << std::endl << std::setprecision(8) << "rmse = " << rmse << SYNC_ENDL; | |
+ | |
+ updating = true; | |
+ | |
+ for (auto t : Threads.slaves) | |
+ t->cond.notify_one(); | |
+ | |
+ // 他のスレッドがすべてaddGradし終えるのを待つ。 | |
+ for (auto t : Threads.slaves) | |
+ { | |
+ std::unique_lock<Mutex> lk(t->update_mutex); | |
+ t->cond.wait(lk, [&] { return !t->add_grading; }); | |
+ } | |
+ | |
+ updateWeights(); | |
+ updating = false; | |
+ | |
+ for (auto t : Threads.slaves) | |
+ t->cond.notify_one(); | |
+ | |
+ if (++loop % 100 == 0) | |
+ { | |
+ Prog::save(std::to_string(loop / 1000)); | |
+ SYNC_COUT << localTime() << SYNC_ENDL; | |
+ } | |
+ } | |
+ | |
+ // その他のスレッドは待ってもらう。 | |
+ else | |
+ { | |
+ // updateWeights中にaddGradしないためのwait機構。 | |
+ th->add_grading = false; | |
+ | |
+ // update中なので、addGradする前にメインスレッドがupdateを終えるのを待つ。 | |
+ std::unique_lock<Mutex> lk(th->update_mutex); | |
+ | |
+ th->cond.wait(lk, [&] { return (bool)updating; }); | |
+ | |
+ // join待ちしているメインスレッドに通知。 | |
+ th->cond.notify_one(); | |
+ | |
+ // updateスレッドが終わるのを知らせてくれるまで待つ。 | |
+ th->cond.wait(lk, [&] { return !updating; }); | |
+ | |
+ th->add_grading = true; | |
+ | |
+ ++loop; | |
+ } | |
+ } | |
+ | |
+ // 最後に一回保存! | |
+ if (is_main) | |
+ Prog::save("end"); | |
+ } | |
+ | |
+ // 進行度学習 | |
+ void learnProgress(Board& b, std::istringstream& is) | |
+ { | |
+ std::string token, | |
+ file_name = "records_2800.txt", | |
+ dir = ""; | |
+ | |
+ // 最初は10000くらいで。 | |
+ uint64_t loop_max = 100000; | |
+ | |
+ while (true) | |
+ { | |
+ token.clear(); | |
+ is >> token; | |
+ | |
+ if (token == "") | |
+ break; | |
+ | |
+ if (token == "loop") | |
+ is >> loop_max; | |
+ else if (token == "file") | |
+ is >> file_name; | |
+ else if (token == "dir") | |
+ is >> dir; | |
+ } | |
+ | |
+ // 勾配配列の初期化 | |
+ Prog::load(); | |
+ initGrad(); | |
+ USI::isReady(); | |
+ | |
+ // file_nameは技巧形式の棋譜 | |
+ std::vector<std::string> kifus; | |
+ readAllLines(path(dir, file_name), kifus); | |
+ | |
+ // 手数 | |
+ int max_ply; | |
+ StateInfo st[512]; | |
+ int kifu_num = 0; | |
+ int thread_size = USI::Options["Threads"]; | |
+ const int kifu_size = kifus.size() / 2; | |
+ const int kifu_per_thread = static_cast<int>(std::ceil((double)kifu_size / (double)thread_size)); | |
+ | |
+ ProgressLearner pl; | |
+ pl.max_loop = loop_max; | |
+ | |
+ for (int i = 0; i < thread_size; i++) | |
+ pl.games.push_back(GameVector()); | |
+ | |
+ // どうせ学習棋譜は少ないのであらかじめすべてメモリに読み込んでおく。 | |
+ for (auto it = kifus.begin(); it < kifus.end(); ++it) | |
+ { | |
+ // (技巧の形式) | |
+ // 1行目 : <棋譜番号> <対局開始日> <先手名> <後手名> <勝敗(0:引き分け, 1 : 先手勝ち, 2 : 後手勝ち)> <手数> <棋戦> <戦型> | |
+ // 2行目 : <CSA形式の指し手(1手6文字)を一行に並べたもの> | |
+ std::istringstream iss(*it++); | |
+ | |
+ // ゲームの手数を取得(plyが出てくるまで読み飛ばす) | |
+ for (int i = 0; i < 6; i++) | |
+ iss >> std::skipws >> token; | |
+ | |
+ Game g; | |
+ | |
+ g.ply = max_ply = atoi(token.c_str()); | |
+ | |
+ iss.clear(std::stringstream::goodbit); | |
+ iss.str(*it); | |
+ | |
+ // 平手初期局面セット | |
+ b.init(USI::START_POS, Threads.main()); | |
+ | |
+ // ゲームの進行 | |
+ for (int i = 0; i < max_ply - 1; i++) | |
+ { | |
+ token.clear(); | |
+ iss >> std::skipws >> token; | |
+ Move m = csaToMove(token, b); | |
+ g.move[i] = m; | |
+ b.doMove(m, st[i]); | |
+ } | |
+ | |
+ if (kifu_num++ <= kifu_size - 100) | |
+ pl.games[kifu_num / kifu_per_thread].push_back(g); | |
+ else | |
+ pl.errors.push_back(g); | |
+ } | |
+ | |
+ pl.setLoopMax(loop_max); | |
+ pl.think(); | |
+ } | |
+ | |
+ extern void initLearn(Board& b); | |
+ Score ab(Board& b, Score alpha, Score beta, Depth depth); | |
+ | |
+ // 現在の評価関数で残り深さ1で読んだ時の評価値と、それ以上の残り深さで読んだときの評価値の誤差を調べる。 | |
+ // futility marginを決定するのに使えるはず | |
+ void analyzeFutility(std::istringstream& is) | |
+ { | |
+ std::string token, | |
+ | |
+#ifdef _DEBUG | |
+ file_name = "records23.txt", | |
+#else | |
+ file_name = "records_2800.txt", | |
+#endif | |
+ dir = ""; | |
+ | |
+ while (true) | |
+ { | |
+ token.clear(); | |
+ is >> token; | |
+ | |
+ if (token == "") | |
+ break; | |
+ | |
+ if (token == "file") | |
+ is >> file_name; | |
+ else if (token == "dir") | |
+ is >> dir; | |
+ } | |
+ | |
+ USI::isReady(); | |
+ | |
+ // file_nameは技巧形式の棋譜 | |
+ std::vector<std::string> kifus; | |
+ readAllLines(path(dir, file_name), kifus); | |
+ std::ofstream ofs("futility.tsv"); | |
+ ofs << "progress\tdiff\n"; | |
+ | |
+ // 手数 | |
+ int max_ply; | |
+ StateInfo st[512]; | |
+ USI::isReady(); | |
+ auto& b = Threads.main()->root_board; | |
+ int num = 0; | |
+ std::vector<std::pair<double, int>> futility; | |
+ | |
+ for (auto it = kifus.begin(); it < kifus.end(); ++it) | |
+ { | |
+ // (技巧の形式) | |
+ // 1行目 : <棋譜番号> <対局開始日> <先手名> <後手名> <勝敗(0:引き分け, 1 : 先手勝ち, 2 : 後手勝ち)> <手数> <棋戦> <戦型> | |
+ // 2行目 : <CSA形式の指し手(1手6文字)を一行に並べたもの> | |
+ std::istringstream iss(*it++); | |
+ | |
+ // ゲームの手数を取得(plyが出てくるまで読み飛ばす) | |
+ for (int i = 0; i < 6; i++) | |
+ iss >> std::skipws >> token; | |
+ | |
+ max_ply = atoi(token.c_str()); | |
+ | |
+ iss.clear(std::stringstream::goodbit); | |
+ iss.str(*it); | |
+ | |
+ // 平手初期局面セット | |
+ b.init(USI::START_POS, Threads.main()); | |
+ TT.clear(); | |
+ | |
+ // ゲームの進行 | |
+ for (int i = 1; i < max_ply; i++) | |
+ { | |
+ token.clear(); | |
+ iss >> std::skipws >> token; | |
+ Move m = csaToMove(token, b); | |
+ b.doMove(m, st[i]); | |
+ | |
+ if (!b.inCheck()) | |
+ { | |
+ // 1手読み。evaluateだと駒取りが残っている状態なのでmarginが大きく出る。 | |
+ Score static_eval = Eval::evaluate(b); // ab(b, -SCORE_INFINITE, SCORE_INFINITE, ONE_PLY); | |
+ Score deep_eval = ab(b, -SCORE_INFINITE, SCORE_INFINITE, 1 * ONE_PLY); | |
+ | |
+ // 進行度 | |
+ double progress_rate = (double)i / (double)max_ply; | |
+ | |
+ | |
+ int diff = abs(static_eval - deep_eval); | |
+#if 0 | |
+ std::cout << b; | |
+ | |
+ std::cout << "p = " << progress_rate | |
+ << " diff = " << diff | |
+ << " static_eval = " << static_eval | |
+ << " deep_eval = "<< deep_eval << std::endl; | |
+#endif | |
+ if (diff < Score(3000)) | |
+ futility.push_back(std::make_pair(progress_rate, diff)); | |
+ } | |
+ } | |
+ | |
+ if (num++ > 1000) | |
+ break; | |
+ } | |
+ | |
+ double stats[1001] = { 0.0 }; | |
+ int count[1001] = { 0 }; | |
+ | |
+ for (auto f : futility) | |
+ { | |
+ int id = int(f.first * 100.0); | |
+ assert(id >= 0 && id < 100); | |
+ stats[id] += f.second; | |
+ count[id]++; | |
+ } | |
+ | |
+ for (int i = 0; i < 100; i++) | |
+ { | |
+ if (count[i]) | |
+ stats[i] /= (double)count[i]; | |
+ | |
+ ofs << (double)i / (double)100.0 << "\t" << stats[i] << std::endl; | |
+ } | |
+ } | |
+} // namespace Learn | |
+ | |
+#endif // LEARN | |
+ | |
+#endif | |
diff --git a/source/progress.h b/source/progress.h | |
new file mode 100644 | |
index 0000000..e9c91a6 | |
--- /dev/null | |
+++ b/source/progress.h | |
@@ -0,0 +1,87 @@ | |
+/* | |
+読み太(yomita), a USI shogi (Japanese chess) playing engine derived from | |
+Stockfish 7 & YaneuraOu mid 2016 V3.57 | |
+Copyright (C) 2004-2008 Tord Romstad (Glaurung author) | |
+Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad (Stockfish author) | |
+Copyright (C) 2015-2016 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad (Stockfish author) | |
+Copyright (C) 2015-2016 Motohiro Isozaki(YaneuraOu author) | |
+Copyright (C) 2016 Ryuzo Tukamoto | |
+ | |
+This program is free software: you can redistribute it and/or modify | |
+it under the terms of the GNU General Public License as published by | |
+the Free Software Foundation, either version 3 of the License, or | |
+(at your option) any later version. | |
+ | |
+This program is distributed in the hope that it will be useful, | |
+but WITHOUT ANY WARRANTY; without even the implied warranty of | |
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
+GNU General Public License for more details. | |
+ | |
+You should have received a copy of the GNU General Public License | |
+along with this program. If not, see <http://www.gnu.org/licenses/>. | |
+*/ | |
+ | |
+#pragma once | |
+ | |
+#include "shogi.h" | |
+ | |
+#include "evaluate.h" | |
+ | |
+#define PROGRESS_BIN "progress.bin" | |
+#define SAVE_PROGRESS_DIR "save" | |
+ | |
+namespace Prog | |
+{ | |
+ | |
+// 進行度 | |
+// KP値により、局面の進行度を取得する。 | |
+// 進行度は32bitで表され、教師棋譜から開始局面を進行度0(0%)、終了局面を進行度1(=100%)として学習を行う。 | |
+typedef int32_t ValueProg; | |
+ | |
+extern ValueProg PROGRESS[SQ_NB][Eval::fe_end]; | |
+ | |
+// 進行度ファイルの読み込み | |
+void load(); | |
+ | |
+// 進行度を全計算 | |
+double computeProgress(const Position &pos); | |
+ | |
+// 進行度を差分計算 | |
+double calcProgressDiff(const Position &pos); | |
+ | |
+// ↑二つのラッパー | |
+double evaluateProgress(const Position &pos); | |
+ | |
+struct ProgressSum { | |
+ ProgressSum(){}; | |
+ | |
+ void set(int64_t b, int64_t w) | |
+ { | |
+ bkp = b; | |
+ wkp = w; | |
+ } | |
+ | |
+ // 進行度を計算する。 | |
+ // 2^20は適当に決めた。 | |
+ double rate() const | |
+ { | |
+ return double(bkp + wkp) / double(1 << 20); | |
+ } | |
+ | |
+ // まだ進行度の計算を済ませていないかどうかを返す。 | |
+ bool isNone() const | |
+ { | |
+ return bkp == INT64_MAX; | |
+ } | |
+ | |
+ // まだ進行度の計算を済ませていないことを示す値を入れておく。 | |
+ void setNoProgress() | |
+ { | |
+ bkp = INT64_MAX; | |
+ } | |
+ | |
+ int64_t bkp, wkp; | |
+}; | |
+ | |
+} // namespace Prog | |
+ | |
diff --git a/source/usi.cpp b/source/usi.cpp | |
index 3efac41..ba4d2f2 100644 | |
--- a/source/usi.cpp | |
+++ b/source/usi.cpp | |
@@ -98,10 +98,13 @@ namespace Search { | |
break; | |
pos.do_move(m, state[ply++]); | |
+ Prog::evaluateProgress(pos); | |
} | |
- while(ply > 0) | |
+ while(ply > 0){ | |
pos.undo_move(pv[--ply]); | |
+ Prog::evaluateProgress(pos); | |
+ } | |
} | |
} | |
@@ -281,9 +284,12 @@ namespace USI | |
pos_->do_move(m, si[ply]); | |
++ply; | |
+ Prog::evaluateProgress(*pos_); | |
} | |
- while (ply > 0) | |
+ while (ply > 0){ | |
pos_->undo_move(moves[--ply]); | |
+ Prog::evaluateProgress(*pos_); | |
+ } | |
}; | |
#if !defined (USE_TT_PV) | |
@@ -479,6 +485,7 @@ void is_ready() | |
{ | |
// 評価関数の読み込み | |
Eval::load_eval(); | |
+ Prog::load(); | |
// チェックサムの計算と保存(その後のメモリ破損のチェックのため) | |
eval_sum = Eval::calc_check_sum(); | |
@@ -560,8 +567,10 @@ void position_cmd(Position& pos, istringstream& is) | |
SetupStates->push(StateInfo()); | |
if (m == MOVE_NULL) // do_move に MOVE_NULL を与えると死ぬので | |
pos.do_null_move(SetupStates->top()); | |
- else | |
+ else { | |
pos.do_move(m, SetupStates->top()); | |
+ Prog::evaluateProgress(pos); | |
+ } | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment