This commit is contained in:
noobpwnftw
2020-09-08 09:35:53 +08:00
parent e5f05fa2b9
commit d21424c8d3
15 changed files with 61 additions and 238 deletions

View File

@@ -56,7 +56,6 @@ SRCS = benchmark.cpp bitbase.cpp bitboard.cpp endgame.cpp evaluate.cpp main.cpp
nnue/features/enpassant.cpp \
nnue/nnue_test_command.cpp \
extra/sfen_packer.cpp \
learn/gensfen2019.cpp \
learn/learner.cpp \
learn/gensfen.cpp \
learn/convert.cpp \
@@ -908,7 +907,7 @@ learn: config-sanity
EXTRALDFLAGS=' $(BLASLDFLAGS) -fopenmp ' \
all
profile-learn: net config-sanity objclean profileclean
profile-learn: config-sanity objclean profileclean
@echo ""
@echo "Step 1/4. Building instrumented executable ..."
$(MAKE) ARCH=$(ARCH) COMP=$(COMP) $(profile_make) \

View File

@@ -32,13 +32,6 @@
#include "thread.h"
#include "uci.h"
#ifdef EVAL_LEARN
namespace Learner
{
extern bool use_raw_nnue_eval;
}
#endif
namespace Eval {
bool useNNUE;
@@ -947,27 +940,32 @@ make_v:
/// evaluation of the position from the point of view of the side to move.
Value Eval::evaluate(const Position& pos) {
#ifdef EVAL_LEARN
if (Learner::use_raw_nnue_eval) {
return NNUE::evaluate(pos);
if (Options["Training"]) {
Value v = NNUE::evaluate(pos);
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
return v;
} else {
bool classical = !Eval::useNNUE
|| abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count());
Value v = classical ? Evaluation<NO_TRACE>(pos).value()
: NNUE::evaluate(pos) * 5 / 4 + Tempo;
if (classical && Eval::useNNUE && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count()))
v = NNUE::evaluate(pos) * 5 / 4 + Tempo;
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
return v;
}
#endif
bool classical = !Eval::useNNUE
|| abs(eg_value(pos.psq_score())) * 16 > NNUEThreshold1 * (16 + pos.rule50_count());
Value v = classical ? Evaluation<NO_TRACE>(pos).value()
: NNUE::evaluate(pos) * 5 / 4 + Tempo;
if (classical && Eval::useNNUE && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count()))
v = NNUE::evaluate(pos) * 5 / 4 + Tempo;
// Damp down the evaluation linearly when shuffling
v = v * (100 - pos.rule50_count()) / 100;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(v, VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
return v;
}
/// trace() is like evaluate(), but instead of returning a value, it returns

View File

@@ -11,10 +11,6 @@
#include "../uci.h"
#include "../syzygy/tbprobe.h"
#if defined(USE_BOOK)
#include "../extra/book/book.h"
#endif
#include <chrono>
#include <random>
#include <regex>
@@ -54,11 +50,7 @@ namespace Learner
static bool detect_draw_by_consecutive_low_score = false;
static bool detect_draw_by_insufficient_mating_material = false;
// Use raw NNUE eval value in the Eval::evaluate().
// If hybrid eval is enabled, training data
// generation and training don't work well.
// https://discordapp.com/channels/435943710472011776/733545871911813221/748524079761326192
static bool use_raw_nnue_eval = true;
static std::vector<std::string> bookStart;
// Helper class for exporting Sfen
struct SfenWriter
@@ -313,13 +305,6 @@ namespace Learner
int ply,
int& random_move_c);
Value evaluate_leaf(
Position& pos,
std::vector<StateInfo, AlignedAllocator<StateInfo>>& states,
int ply,
int depth,
vector<Move>& pv);
// Min and max depths for search during gensfen
int search_depth_min;
int search_depth_max;
@@ -674,69 +659,6 @@ namespace Learner
return random_move_flag;
}
Value MultiThinkGenSfen::evaluate_leaf(
Position& pos,
std::vector<StateInfo, AlignedAllocator<StateInfo>>& states,
int ply,
int depth,
vector<Move>& pv)
{
auto rootColor = pos.side_to_move();
for (auto m : pv)
{
#if 1
// There should be no illegal move. This is as a debugging precaution.
if (!pos.pseudo_legal(m) || !pos.legal(m))
{
cout << "Error! : " << pos.fen() << m << endl;
}
#endif
pos.do_move(m, states[ply++]);
// Because the difference calculation of evaluate() cannot be
// performed unless each node evaluate() is called!
// If the depth is 8 or more, it seems
// faster not to calculate this difference.
#if defined(EVAL_NNUE)
if (depth < 8)
{
Eval::NNUE::update_eval(pos);
}
#endif // defined(EVAL_NNUE)
}
// Reach leaf
Value v;
if (pos.checkers())
{
// Sometime a king is checked. An example is a case that a checkmate is
// found in the search. If Eval::evaluate() is called whne a king is
// checked, classic eval crashes by an assertion. To avoid crashes, return
// VALUE_NONE and let the caller assign a value to the position.
v = VALUE_NONE;
}
else
{
v = Eval::evaluate(pos);
// evaluate() returns the evaluation value on the turn side, so
// If it's a turn different from root_color, you must invert v and return it.
if (rootColor != pos.side_to_move())
{
v = -v;
}
}
// Rewind the pv moves.
for (auto it = pv.rbegin(); it != pv.rend(); ++it)
{
pos.undo_move(*it);
}
return v;
}
// thread_id = 0..Threads.size()-1
void MultiThinkGenSfen::thread_worker(size_t thread_id)
{
@@ -760,12 +682,7 @@ namespace Learner
auto th = Threads[thread_id];
auto& pos = th->rootPos;
pos.set(StartFEN, false, &si, th);
#if defined(USE_BOOK)
// Refer to the members of BookMoveSelector defined in the search section.
auto& book = ::book;
#endif
pos.set(bookStart[prng.rand(bookStart.size())], false, &si, th);
// Vector for holding the sfens in the current simulated game.
PSVector a_psv;
@@ -800,35 +717,6 @@ namespace Learner
flush_psv(result.value());
break;
}
#if defined(USE_BOOK)
if ((next_move = book.probe(pos)) != MOVE_NONE)
{
// Hit the constant track.
// The move was stored in next_move.
// Do not use the fixed phase for learning.
sfens.clear();
if (random_move_minply != -1)
{
// Random move is performed with a certain
// probability even in the constant phase.
goto RANDOM_MOVE;
}
else
{
// When -1 is specified as random_move_minply,
// it points according to the standard until
// it goes out of the standard.
// Prepare an innumerable number of situations
// that have left the constant as
// ConsiderationBookMoveCount true using a huge constant
// Used for purposes such as performing
// a random move 5 times from there.
goto DO_MOVE;
}
}
#endif
{
auto [search_value, search_pv] = search(pos, depth, 1, nodes);
@@ -916,18 +804,7 @@ namespace Learner
// Get the value of evaluate() as seen from the
// root color on the leaf node of the PV line.
// I don't know the goodness and badness of using the
// return value of search() as it is.
// TODO: Consider using search value instead of evaluate_leaf.
// Maybe give it as an option.
// Use PV moves to reach the leaf node and use the value
// that evaluated() is called on that leaf node.
const auto leaf_value = evaluate_leaf(pos, states, ply, depth, search_pv);
// If for some reason the leaf node couldn't yield an eval
// we fallback to search value.
psv.score = leaf_value == VALUE_NONE ? search_value : leaf_value;
psv.score = search_value;
psv.gamePly = ply;
@@ -948,9 +825,6 @@ namespace Learner
// Update the next move according to best search result.
next_move = search_pv[0];
}
RANDOM_MOVE:;
auto random_move = choose_random_move(pos, random_move_flag, ply, actual_random_move_count);
if (random_move.has_value())
{
@@ -962,13 +836,7 @@ namespace Learner
{
break;
}
// Clear the sfens that were written before the random move.
// (???) why?
a_psv.clear();
}
DO_MOVE:;
pos.do_move(next_move, states[ply]);
// Call node evaluate() for each difference calculation.
@@ -1095,18 +963,10 @@ namespace Learner
is >> detect_draw_by_consecutive_low_score;
else if (token == "detect_draw_by_insufficient_mating_material")
is >> detect_draw_by_insufficient_mating_material;
else if (token == "use_raw_nnue_eval")
is >> use_raw_nnue_eval;
else
cout << "Error! : Illegal token " << token << endl;
}
#if defined(USE_GLOBAL_OPTIONS)
// Save it for later restore.
auto oldGlobalOptions = GlobalOptions;
GlobalOptions.use_eval_hash = use_eval_hash;
#endif
// If search depth2 is not set, leave it the same as search depth.
if (search_depth_max == INT_MIN)
search_depth_max = search_depth_min;
@@ -1130,15 +990,26 @@ namespace Learner
output_file_name = output_file_name + "_" + to_hex(r.rand<uint64_t>()) + to_hex(r.rand<uint64_t>());
}
bookStart.clear();
{
std::string line;
std::ifstream myfile ("3moves_v2.epd");
if (myfile.is_open())
{
while (getline(myfile,line))
{
bookStart.push_back(line);
}
myfile.close();
}
}
std::cout << "gensfen : " << endl
<< " search_depth_min = " << search_depth_min << " to " << search_depth_max << endl
<< " nodes = " << nodes << endl
<< " loop_max = " << loop_max << endl
<< " eval_limit = " << eval_limit << endl
<< " thread_num (set by USI setoption) = " << thread_num << endl
#if defined(USE_BOOK)
<< " book_moves (set by USI setoption) = " << Options["BookMoves"] << endl
#endif
<< " thread_num = " << thread_num << endl
<< " bookStart = " << bookStart.size() << endl
<< " random_move_minply = " << random_move_minply << endl
<< " random_move_maxply = " << random_move_maxply << endl
<< " random_move_count = " << random_move_count << endl
@@ -1188,11 +1059,6 @@ namespace Learner
std::cout << "gensfen finished." << endl;
#if defined(USE_GLOBAL_OPTIONS)
// Restore Global Options.
GlobalOptions = oldGlobalOptions;
#endif
}
}
#endif

View File

@@ -1 +0,0 @@
// just a place holder

View File

@@ -98,12 +98,6 @@ namespace Learner
// data directly. In those cases, we set false to this variable.
static bool convert_teacher_signal_to_winning_probability = true;
// Use raw NNUE eval value in the Eval::evaluate(). If hybrid eval is enabled, training data
// generation and training don't work well.
// https://discordapp.com/channels/435943710472011776/733545871911813221/748524079761326192
// This CANNOT be static since it's used elsewhere.
bool use_raw_nnue_eval = true;
// Using WDL with win rate model instead of sigmoid
static bool use_wdl = false;
@@ -1616,15 +1610,6 @@ namespace Learner
uint64_t eta1_epoch = 0; // eta2 is not applied by default
uint64_t eta2_epoch = 0; // eta3 is not applied by default
#if defined(USE_GLOBAL_OPTIONS)
// Save it for later restore.
auto oldGlobalOptions = GlobalOptions;
// If you hit the eval hash, you can not calculate rmse etc. so turn it off.
GlobalOptions.use_eval_hash = false;
// If you hit the replacement table, pruning may occur at the previous evaluation value, so turn it off.
GlobalOptions.use_hash_probe = false;
#endif
// --- Function that only shuffles the teacher aspect
// normal shuffle
@@ -1796,7 +1781,6 @@ namespace Learner
else if (option == "dest_score_min_value") is >> dest_score_min_value;
else if (option == "dest_score_max_value") is >> dest_score_max_value;
else if (option == "convert_teacher_signal_to_winning_probability") is >> convert_teacher_signal_to_winning_probability;
else if (option == "use_raw_nnue_eval") is >> use_raw_nnue_eval;
// Otherwise, it's a filename.
else
@@ -2076,18 +2060,9 @@ namespace Learner
// Save once at the end.
learn_think.save(true);
#if defined(USE_GLOBAL_OPTIONS)
// Restore Global Options.
GlobalOptions = oldGlobalOptions;
#endif
}
} // namespace Learner
#if defined(GENSFEN2019)
#include "gensfen2019.cpp"
#endif
#endif // EVAL_LEARN

View File

@@ -23,7 +23,7 @@ namespace Eval {
}
if (perspective == BLACK) {
epSquare = rotate180(epSquare);
epSquare = flip_rank(epSquare);
}
auto file = file_of(epSquare);

View File

@@ -23,9 +23,9 @@
namespace Eval::NNUE::Features {
// Orient a square according to perspective (rotates by 180 for black)
// Orient a square according to perspective (flip rank for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63));
return Square(int(s) ^ (bool(perspective) * SQ_A8));
}
// Find the index of the feature quantity from the king position and PieceSquare

View File

@@ -11,9 +11,9 @@ namespace NNUE {
namespace Features {
// Orient a square according to perspective (rotates by 180 for black)
// Orient a square according to perspective (flip rank for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63));
return Square(int(s) ^ (bool(perspective) * SQ_A8));
}
// Find the index of the feature quantity from the ball position and PieceSquare

View File

@@ -11,9 +11,9 @@ namespace NNUE {
namespace Features {
// Orient a square according to perspective (rotates by 180 for black)
// Orient a square according to perspective (flip rank for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63));
return Square(int(s) ^ (bool(perspective) * SQ_A8));
}
// Index of a feature for a given king position.

View File

@@ -11,9 +11,9 @@ namespace NNUE {
namespace Features {
// Orient a square according to perspective (rotates by 180 for black)
// Orient a square according to perspective (flip rank for black)
inline Square orient(Color perspective, Square s) {
return Square(int(s) ^ (bool(perspective) * 63));
return Square(int(s) ^ (bool(perspective) * SQ_A8));
}
// Find the index of the feature quantity from the king position and PieceSquare

View File

@@ -69,7 +69,7 @@
namespace Eval::NNUE {
// Version of the evaluation file
constexpr std::uint32_t kVersion = 0x7AF32F16u;
constexpr std::uint32_t kVersion = 0x7AF32F17u;
// Constant used in evaluation value calculation
constexpr int FV_SCALE = 16;

View File

@@ -68,8 +68,6 @@ namespace {
return Value(223 * (d - improving));
}
bool training;
// Reductions lookup table, initialized at startup
int Reductions[MAX_MOVES]; // [depth or moveNumber]
@@ -195,8 +193,6 @@ void Search::init() {
for (int i = 1; i < MAX_MOVES; ++i)
Reductions[i] = int((22.0 + std::log(Threads.size())) * std::log(i));
training = Options["Training"];
}
@@ -1011,7 +1007,7 @@ moves_loop: // When in check, search starts from here
// Step 12. Pruning at shallow depth (~200 Elo)
if ( !rootNode
&& !(training && PvNode)
&& !(Options["Training"] && PvNode)
&& pos.non_pawn_material(us)
&& bestValue > VALUE_TB_LOSS_IN_MAX_PLY)
{
@@ -2070,17 +2066,6 @@ namespace Learner
rootMoves.push_back(Search::RootMove(m));
assert(!rootMoves.empty());
//#if defined(USE_GLOBAL_OPTIONS)
// Since the generation of the substitution table for each search thread should be managed,
// Increase the generation of the substitution table for this thread because it is a new search.
//TT.new_search(th->thread_id());
// ª If you call new_search here, it may be a loss because you can't use the previous search result.
// Do not do this here, but caller should do TT.new_search(th->thread_id()) for each station ...
// ¨Because we want to avoid reaching the same final diagram, use the substitution table commonly for all threads when generating teachers.
//#endif
}
}

View File

@@ -115,7 +115,9 @@ void TranspositionTable::clear() {
/// TTEntry t2 if its replace value is greater than that of t2.
TTEntry* TranspositionTable::probe(const Key key, bool& found) const {
if (Options["Training"]) {
return found = false, first_entry(0);
}
TTEntry* const tte = first_entry(key);
const uint16_t key16 = (uint16_t)key; // Use the low 16 bits as key inside the cluster

View File

@@ -82,7 +82,7 @@ void init(OptionsMap& o) {
o["Use NNUE"] << Option(true, on_use_NNUE);
// The default must follow the format nn-[SHA256 first 12 digits].nnue
// for the build process (profile-build and fishtest) to work.
o["EvalFile"] << Option("nn-82215d0fd0df.nnue", on_eval_file);
o["EvalFile"] << Option("nn.bin", on_eval_file);
#ifdef EVAL_NNUE
// When the evaluation function is loaded at the ucinewgame timing, it is necessary to convert the new evaluation function.
// I want to hit the test eval convert command, but there is no new evaluation function