]> git.sesse.net Git - stockfish/commitdiff
Normalize evaluation
authorJoost VandeVondele <Joost.VandeVondele@gmail.com>
Mon, 31 Oct 2022 19:36:43 +0000 (20:36 +0100)
committerJoost VandeVondele <Joost.VandeVondele@gmail.com>
Sat, 5 Nov 2022 08:15:53 +0000 (09:15 +0100)
Normalizes the internal value as reported by evaluate or search
to the UCI centipawn result used in output. This value is derived from
the win_rate_model() such that Stockfish outputs an advantage of
"100 centipawns" for a position if the engine has a 50% probability to win
from this position in selfplay at fishtest LTC time control.

The reason to introduce this normalization is that our evaluation is, since NNUE,
no longer related to the classical parameter PawnValueEg (=208). This leads to
the current evaluation changing quite a bit from release to release, for example,
the eval needed to have 50% win probability at fishtest LTC (in cp and internal Value):

June 2020  :   113cp (237)
June 2021  :   115cp (240)
April 2022 :   134cp (279)
July 2022  :   167cp (348)

With this patch, a 100cp advantage will have a fixed interpretation,
i.e. a 50% win chance. To keep this value steady, it will be needed to update the win_rate_model()
from time to time, based on fishtest data. This analysis can be performed with
a set of scripts currently available at https://github.com/vondele/WLD_model

fixes https://github.com/official-stockfish/Stockfish/issues/4155
closes https://github.com/official-stockfish/Stockfish/pull/4216

No functional change

src/nnue/evaluate_nnue.cpp
src/uci.cpp
src/uci.h

index ba2ed36746c00cb2bf4da9c1fd2457c4309350cd..4715fed07d6c71e27e0d1fa69169f12f9f853017 100644 (file)
@@ -220,7 +220,7 @@ namespace Stockfish::Eval::NNUE {
 
     buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
 
-    int cp = std::abs(100 * v / PawnValueEg);
+    int cp = std::abs(100 * v / UCI::NormalizeToPawnValue);
     if (cp >= 10000)
     {
         buffer[1] = '0' + cp / 10000; cp %= 10000;
@@ -251,7 +251,7 @@ namespace Stockfish::Eval::NNUE {
 
     buffer[0] = (v < 0 ? '-' : v > 0 ? '+' : ' ');
 
-    double cp = 1.0 * std::abs(int(v)) / PawnValueEg;
+    double cp = 1.0 * std::abs(int(v)) / UCI::NormalizeToPawnValue;
     sprintf(&buffer[1], "%6.2f", cp);
   }
 
index d5e2c2c358600d759c765b5a4467665505c9ec08..19e2b0cb920341fcf911b083057447de87721c02 100644 (file)
@@ -207,13 +207,17 @@ namespace {
      // The coefficients of a third-order polynomial fit is based on the fishtest data
      // for two parameters that need to transform eval to the argument of a logistic
      // function.
-     double as[] = { 0.50379905,  -4.12755858,  18.95487051, 152.00733652};
-     double bs[] = {-1.71790378,  10.71543602, -17.05515898,  41.15680404};
+     constexpr double as[] = {   1.04790516,   -8.58534089,   39.42615625,  316.17524816};
+     constexpr double bs[] = {  -3.57324784,   22.28816201,  -35.47480551,   85.60617701 };
+
+     // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64
+     static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3]));
+
      double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
      double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
 
      // Transform the eval to centipawns with limited range
-     double x = std::clamp(double(100 * v) / PawnValueEg, -2000.0, 2000.0);
+     double x = std::clamp(double(v), -4000.0, 4000.0);
 
      // Return the win rate in per mille units rounded to the nearest value
      return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
@@ -312,7 +316,7 @@ string UCI::value(Value v) {
   stringstream ss;
 
   if (abs(v) < VALUE_MATE_IN_MAX_PLY)
-      ss << "cp " << v * 100 / PawnValueEg;
+      ss << "cp " << v * 100 / NormalizeToPawnValue;
   else
       ss << "mate " << (v > 0 ? VALUE_MATE - v + 1 : -VALUE_MATE - v) / 2;
 
index 76a893f90c4f0067b58cad0b7df91c7cdefd2806..f5f2c385c6c65e41a99f998292ab68e391333213 100644 (file)
--- a/src/uci.h
+++ b/src/uci.h
@@ -30,6 +30,13 @@ class Position;
 
 namespace UCI {
 
+// Normalizes the internal value as reported by evaluate or search
+// to the UCI centipawn result used in output. This value is derived from
+// the win_rate_model() such that Stockfish outputs an advantage of
+// "100 centipawns" for a position if the engine has a 50% probability to win
+// from this position in selfplay at fishtest LTC time control.
+const int NormalizeToPawnValue = 348;
+
 class Option;
 
 /// Define a custom comparator, because the UCI options should be case-insensitive