Skip to content

Commit

Permalink
- ふかうら王 PolicyBook導入
Browse files Browse the repository at this point in the history
  • Loading branch information
yaneurao committed Dec 4, 2024
1 parent 0c7115b commit 9ba6152
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 1 deletion.
1 change: 1 addition & 0 deletions source/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ SOURCES = \
memory.cpp \
book/book.cpp \
book/apery_book.cpp \
book/policybook.cpp \
extra/bitop.cpp \
extra/long_effect.cpp \
extra/sfen_packer.cpp \
Expand Down
2 changes: 2 additions & 0 deletions source/YaneuraOu.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@
<ClInclude Include="bitboard.h" />
<ClInclude Include="book\apery_book.h" />
<ClInclude Include="book\book.h" />
<ClInclude Include="book\policybook.h" />
<ClInclude Include="config.h" />
<ClInclude Include="engine\dlshogi-engine\dlshogi_min.h" />
<ClInclude Include="engine\dlshogi-engine\dlshogi_types.h" />
Expand Down Expand Up @@ -688,6 +689,7 @@
<ClCompile Include="book\makebook.cpp" />
<ClCompile Include="book\makebook2015.cpp" />
<ClCompile Include="book\makebook2023.cpp" />
<ClCompile Include="book\policybook.cpp" />
<ClCompile Include="engine\dlshogi-engine\dlshogi_searcher.cpp" />
<ClCompile Include="engine\dlshogi-engine\PrintInfo.cpp" />
<ClCompile Include="engine\dlshogi-engine\PvMateSearch.cpp" />
Expand Down
6 changes: 6 additions & 0 deletions source/YaneuraOu.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@
<ClInclude Include="memory.h">
<Filter>リソース ファイル</Filter>
</ClInclude>
<ClInclude Include="book\policybook.h">
<Filter>リソース ファイル\book</Filter>
</ClInclude>
<ClInclude Include="numa.h">
<Filter>リソース ファイル</Filter>
</ClInclude>
Expand Down Expand Up @@ -594,6 +597,9 @@
<ClCompile Include="engine.cpp">
<Filter>リソース ファイル</Filter>
</ClCompile>
<ClCompile Include="book\policybook.cpp">
<Filter>リソース ファイル\book</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="..\script\readme.txt">
Expand Down
173 changes: 173 additions & 0 deletions source/book/policybook.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include "../config.h"

#if defined(USE_POLICY_BOOK)

#include <numeric> // accumurate

#include "policybook.h"
#include "../misc.h"
#include "../position.h"
#include "../thread.h"
#include "../usi.h"

#define POLICY_BOOK_FILE_NAME "eval/policy_book.db"
#define POLICY_BOOK_BIN_NAME "eval/policy_book.db.bin"

// ----------------------
// Policy Bookの読み込み
// ----------------------
void PolicyBook::read_book()
{
SystemIO::TextReader reader;
/* 2度目のisreadyに対しては読み込まない措置 */
if (book_body.size() == 0)
{
// binary化されたPolicyBookがあるなら、それを読み込む。
SystemIO::BinaryReader bin_reader;
if (bin_reader.Open(POLICY_BOOK_BIN_NAME).is_ok())
{
// ファイルサイズ
size_t bin_size = bin_reader.GetSize();

// PolicyBookEntryのentry数
size_t num_of_records = bin_size / sizeof(PolicyBookEntry);

sync_cout << "info string read " << POLICY_BOOK_BIN_NAME
<< " , " << num_of_records << " records." << sync_endl;

book_body.resize(num_of_records);
if (bin_reader.Read(book_body.data(), bin_size).is_ok())
{
sync_cout << "info string read done." << sync_endl;
}
else {
sync_cout << "info string Error! : read error." << sync_endl;
book_body.clear();
}
}
else if (reader.Open(POLICY_BOOK_FILE_NAME).is_ok())
{
// ないなら、いったん読み込んで、binary化する。

sync_cout << "info string read " << POLICY_BOOK_FILE_NAME << sync_endl;

u64 counter = 0;
std::string sfen, moves_str;
Position pos;
StateInfo si;
std::vector<int> ratios;
reader.ReadLine(sfen);
if (sfen != "#YANEURAOU-POLICY-DB2024 1.00")
{
sync_cout << "info string Error! policy book header" << sync_endl;
Tools::exit();
}
while (true)
{
if (reader.ReadLine(sfen).is_not_ok())
break;
pos.set(sfen, &si, Threads.main());

reader.ReadLine(moves_str);
auto moves = StringExtension::split(moves_str);
// 7g7f 123 のように指し手と出現頻度が書いてあるので正規化する。
ratios.clear();
PolicyBookEntry pbe;
for (size_t i = 0, j = 0; i < POLICY_BOOK_NUM; ++i)
{
if (i * 2 + 1 < moves.size())
{
Move16 move16 = USI::to_move16(moves[i * 2 + 0]);

// 不成の指し手があるなら、それを無視する。
// (これを計算に入れてしまうと、Policyの合計が100%にならなくなる)
if (!pos.pseudo_legal_s<false>(pos.to_move(move16)))
continue;

pbe.move_ratio[j].move16 = move16;
ratios.push_back(StringExtension::to_int(moves[i * 2 + 1], 1));
}
else {
// 残りをmove::none()にしておく。
pbe.move_ratio[j].move16 = Move16::none();
pbe.move_ratio[j].ratio = 0;
}
++j;
}

// vlを足して (1 << 16) -1になるように正規化
int total = std::accumulate(ratios.begin(), ratios.end(), 0);
// 不成の指し手を除外した結果、0になることはある。これは正規化できない意味がないentry。
if (total == 0)
continue;

for (size_t i = 0; i < ratios.size(); ++i)
pbe.move_ratio[i].ratio = u16(u64((1 << 16) - 1) * ratios[i] / total);

pbe.key = pos.hash_key();
book_body.push_back(pbe);

if (++counter % 100000 == 0)
sync_cout << "info string read " << counter << sync_endl;
}
// keyでsortしておかないと、二分探索ができなくて困る。
std::sort(book_body.begin(), book_body.end(), [](PolicyBookEntry& x, PolicyBookEntry& y)
{ return x.key < y.key; });

// keyが重複しないことを確認する。(これが成り立っていないと二分探索したあと局面が一意に定まらない。)
for (size_t i = 0; i < book_body.size() - 1; ++i)
if (book_body[i].key == book_body[i + 1].key)
{
sync_cout << "info string warning! PolicyBookEntry.key is duplicated." << sync_endl;
break;
}

sync_cout << "info string read " << counter << "..done." << sync_endl;

SystemIO::BinaryWriter bin_writer;
if (bin_writer.Open(POLICY_BOOK_BIN_NAME).is_ok())
{
size_t write_size = book_body.size() * sizeof(PolicyBookEntry);
sync_cout << "info string write " << POLICY_BOOK_BIN_NAME << " , write size = " << write_size << " bytes." << sync_endl;

bin_writer.Write(book_body.data(), write_size);
sync_cout << "info string ..done!" << sync_endl;
}

/*
// テストコード
pos.set_hirate(&si,Threads.main());
auto key = pos.hash_key();
auto ptr = probe_policy_book(key);
if (ptr != nullptr)
for(int i=0; i < POLICY_BOOK_NUM; ++i)
{
auto& mr = ptr->move_ratio[i];
sync_cout << "move = " << mr.move16 << " , ratio = " << mr.ratio << sync_endl;
}
*/

}
}
}

// Policy Bookのなかに指定されたkeyの局面があるか。
// 見つからなければnullptrが返る。
PolicyBookEntry* PolicyBook::probe_policy_book(HASH_KEY key)
{
PolicyBookEntry pbe;
pbe.key = key;

// std::lower_boundを使って検索
auto it = std::lower_bound(book_body.begin(), book_body.end(), pbe,
[](const PolicyBookEntry& a, const PolicyBookEntry& b) {
return a.key < b.key;
});

// 見つかった場合にキーが一致するか確認

// 同じキーを持つentryが複数ないことが保証されている。(読み込み時にwarningがでる)
return (it != book_body.end() && it->key == key) ? &*it : nullptr;
}

#endif
46 changes: 46 additions & 0 deletions source/book/policybook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#ifndef POLICYBOOK_H_INCLUDED
#define POLICYBOOK_H_INCLUDED

#include "../config.h"

#if defined(USE_POLICY_BOOK)

#include <vector>
#include "../types.h"
#include "../extra/key128.h"

static_assert(HASH_KEY_BITS == 128 , "HASH_KEY_BITS must be 128");

// ============================================================
// Policy Book
// ============================================================
struct MoveRatio
{
Move16 move16;
u16 ratio;// 2^16 - 1 を100%とする割合の表現
};
constexpr int POLICY_BOOK_NUM = 4;
struct PolicyBookEntry
{
HASH_KEY key;
MoveRatio move_ratio[POLICY_BOOK_NUM];
// 128bit(=16bytes) + 16bytes = 32bytes
};

class PolicyBook
{
public:
// PolicyBookを読み込む。
void read_book();

// Policy Bookのなかに指定されたkeyの局面があるか。
// 見つからなければnullptrが返る。
PolicyBookEntry* probe_policy_book(HASH_KEY key);

private:
// PolicyBook本体
std::vector<PolicyBookEntry> book_body;
};
#endif

#endif
40 changes: 39 additions & 1 deletion source/engine/dlshogi-engine/UctSearch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,11 @@ namespace dlshogi
make_input_features(*pos, current_policy_value_batch_index, packed_features1, packed_features2);

// 現在のNodeと手番を保存しておく。
policy_value_batch[current_policy_value_batch_index] = { node, pos->side_to_move() /* , pos->key() */ , value_win};
policy_value_batch[current_policy_value_batch_index] = { node, pos->side_to_move() ,
#if defined(USE_POLICY_BOOK)
pos->hash_key() ,
#endif
value_win};

#ifdef MAKE_BOOK
policy_value_book_key[current_policy_value_batch_index] = Book::bookKey(*pos);
Expand Down Expand Up @@ -925,6 +929,11 @@ namespace dlshogi
const ChildNumType child_num = node->child_num;
ChildNode * uct_child = node->child.get();

#if defined(USE_POLICY_BOOK)
HASH_KEY key = policy_value_batch[i].key;
auto* policy_book_entry = grp->get_dlsearcher()->policy_book.probe_policy_book(key);
#endif

// 合法手それぞれに対する遷移確率
std::vector<float> legal_move_probabilities;
// いまからemplace_backしていく回数がchild_numであることはわかっているので事前に要素を確保しておく。
Expand Down Expand Up @@ -961,9 +970,38 @@ namespace dlshogi
// Boltzmann distribution
softmax_temperature_with_normalize(legal_move_probabilities);

#if !defined(USE_POLICY_BOOK)
for (ChildNumType j = 0; j < child_num; j++) {
uct_child[j].nnrate = legal_move_probabilities[j];
}
#else
if (policy_book_entry == nullptr)
{
for (ChildNumType j = 0; j < child_num; j++) {
uct_child[j].nnrate = legal_move_probabilities[j];
}
} else {
// Policy Bookに従う。

// 元のPolicyの按分率
float original_policy_ratio = 0.2f;

for (ChildNumType j = 0; j < child_num; j++) {
uct_child[j].nnrate = legal_move_probabilities[j];
for (size_t k = 0 ; k < POLICY_BOOK_NUM; ++k)
{
if (policy_book_entry->move_ratio[k].move16 == uct_child[j].move.to_move16())
{
uct_child[j].nnrate = original_policy_ratio * uct_child[j].nnrate
+ (1.0f - original_policy_ratio) * policy_book_entry->move_ratio[k].ratio / ((1 << 16) - 1);
// PolicyBookEntryのratioは合計が(1 << 16) - 1になるように正規化されている。

break;
}
}
}
}
#endif

// valueの値はここに返すことになっている。
*policy_value_batch[i].value_win = *value;
Expand Down
4 changes: 4 additions & 0 deletions source/engine/dlshogi-engine/UctSearch.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ namespace dlshogi
Node* node; // どのNodeに対するEvalNode()なのか。
Color color; // その時の手番

#if defined(USE_POLICY_BOOK)
HASH_KEY key; // この局面のhash key
#endif

// 通常の探索では、このポインターはNodeVisitor::value_win を指している。
float* value_win; // leaf nodeでのvalue_winの値(これを辿ってきたNodeに対して符号を反転させながら伝播させていく)
};
Expand Down
5 changes: 5 additions & 0 deletions source/engine/dlshogi-engine/dlshogi_searcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ namespace dlshogi
// leaf nodeでの詰み探索用のMateSolverの初期化
for (auto& uct_searcher : thread_id_to_uct_searcher)
uct_searcher->InitMateSearcher(search_options);

#if defined(USE_POLICY_BOOK)
policy_book.read_book();
#endif

}

// 全スレッドでの探索開始
Expand Down
6 changes: 6 additions & 0 deletions source/engine/dlshogi-engine/dlshogi_searcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "../../position.h"
#include "../../book/book.h"
#include "../../book/policybook.h"
#include "../../mate/mate.h"
#include "dlshogi_types.h"
#include "PvMateSearch.h"
Expand Down Expand Up @@ -381,6 +382,11 @@ namespace dlshogi
// root局面でdf-pnが詰みを見つけているときは、これがMove::none()以外になる。
Move rootMateMove;

#if defined(USE_POLICY_BOOK)
// PolicyBook本体
PolicyBook policy_book;
#endif

private:

// Root Node(探索開始局面)を展開する。
Expand Down
Loading

0 comments on commit 9ba6152

Please sign in to comment.