mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-26 12:06:22 +08:00
test
This commit is contained in:
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// just a place holder
|
||||
@@ -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
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Eval {
|
||||
}
|
||||
|
||||
if (perspective == BLACK) {
|
||||
epSquare = rotate180(epSquare);
|
||||
epSquare = flip_rank(epSquare);
|
||||
}
|
||||
|
||||
auto file = file_of(epSquare);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user