hmmm that's more or less what I do too, without success. Even starting from stupid piece value, the algorithm is stuck very soon ...
Code: Select all
namespace Texel {
class TexelInput {
public:
TexelInput(Position * p, int r) :p(p), result(r) {} // not taking position ownership
~TexelInput() {}
Position * p;
int result;
TexelInput(const TexelInput & t) {
p = t.p;
result = t.result;
}
TexelInput & operator =(const TexelInput & t) {
p = t.p;
result = t.result;
return *this;
}
TexelInput & operator =(TexelInput && t) {
p = t.p;
result = t.result;
return *this;
}
};
template < typename T >
class TexelParam {
public:
TexelParam(const std::string & a, const T& inf, const T& sup) :accessor(a), inf(inf), sup(sup) {}
std::string accessor;
T inf;
T sup;
T& operator()() {
return Definitions::GetValue<T>(accessor);
}
void Set(const T & value) {
Definitions::SetValue(accessor, std::to_string(value));
}
};
template<class Iter >
Iter random_unique(Iter begin, Iter end, size_t num_random) {
size_t left = std::distance(begin, end);
while (num_random--) {
Iter r = begin;
std::advance(r, rand() % left);
std::swap(*begin, *r);
++begin;
--left;
}
return begin;
}
double Sigmoid(Position * p) {
static Searcher searcher;
const double s = searcher.NegaQuiesce(-Definitions::scores.infScore,Definitions::scores.infScore,50,0,*p,p->Turn(),true,TimeMan::eTC_off);
static const double K = 0.75;
return 1. / (1. + std::pow(10, -K * s / 400.));
}
double E(const std::vector<Texel::TexelInput> &data, size_t miniBatchSize) {
double e = 0;
for (size_t k = 0; k < miniBatchSize; ++k) {
e += std::pow(double((data[k].result+1)*0.5 - Sigmoid(data[k].p)),2);
}
e /= data.size();
return e;
}
void Randomize(std::vector<Texel::TexelInput> & data, size_t miniBatchSize) {
random_unique(data.begin(), data.end(), miniBatchSize);
}
std::vector<double> ComputeGradient(std::vector<TexelParam<ScoreType> > x0, std::vector<Texel::TexelInput> &data, size_t gradientBatchSize) {
LOG(logINFO) << "Computing gradient";
std::vector<double> g;
const ScoreType delta = 1;
for (size_t k = 0; k < x0.size(); ++k) {
LOG(logINFO) << "... " << k;
double grad = 0;
ScoreType oldvalue = x0[k]();
x0[k].Set(oldvalue + delta);
grad = E(data, gradientBatchSize);
x0[k].Set(oldvalue - delta);
grad += E(data, gradientBatchSize);
x0[k].Set(oldvalue);
grad -= 2*E(data, gradientBatchSize);
g.push_back(grad/(2*delta));
LOG(logINFO) << "Gradient " << k << " " << grad;
}
double norm = 0;
for (size_t k = 0; k < x0.size(); ++k) {
norm += g[k] * g[k];
}
norm = sqrt(norm);
for (size_t k = 0; k < x0.size(); ++k) {
g[k] /= norm;
LOG(logINFO) << "Gradient normalized " << k << " " << g[k];
}
return g;
}
std::vector<TexelParam<ScoreType> > TexelOptimizeGD(const std::vector<TexelParam<ScoreType> >& initialGuess, std::vector<Texel::TexelInput> &data) {
Definitions::ttConfig.do_transpositionTableEval = false;
Definitions::ttConfig.do_transpositionTableEvalPawn = false;
Definitions::ttConfig.do_transpositionTableQuiesce = false;
Definitions::SetValue("pawnValue", std::to_string(100));
Definitions::SetValue("knightValue", std::to_string(1000));
Definitions::SetValue("bishopValue", std::to_string(1000));
Definitions::SetValue("rookValue", std::to_string(1000));
Definitions::SetValue("queenValue", std::to_string(1000));
size_t errorBatchSize = 1024*64;
Randomize(data, errorBatchSize);
std::vector<TexelParam<ScoreType> > bestParam = initialGuess;
bool improved = true;
while (improved) {
improved = false;
std::vector<double> g = ComputeGradient(bestParam, data, errorBatchSize);
double delta = 50;
LOG(logINFO) << "Line search";
double curE = E(data, errorBatchSize);
double bestELS = curE;
while (delta >= 2) { // stupid line search
LOG(logINFO) << "Applying gradient, delta = " << delta;
///@todo check inf/sup
for (size_t k = 0; k < bestParam.size(); ++k) {
ScoreType oldValue = bestParam[k]();
bestParam[k].Set(oldValue - ScoreType(delta * g[k]));
}
LOG(logINFO) << "Computing new error";
curE = E(data, errorBatchSize);
LOG(logINFO) << "LS : " << delta << " " << curE << " best was : " << bestELS;
if (curE <= bestELS) {
improved = true;
bestELS = curE;
break;
}
else {
// reseting last iteration
for (size_t k = 0; k < bestParam.size(); ++k) {
ScoreType oldValue = bestParam[k]();
bestParam[k].Set(oldValue + ScoreType(delta * g[k]));
}
}
delta /= 2;
}
// randomize for next iteration
Randomize(data, errorBatchSize);
LOG(logINFO) << "Current values :";
for (size_t k = 0; k < bestParam.size(); ++k) {
LOG(logINFO) << bestParam[k].accessor << " " << bestParam[k]();
}
}
return bestParam;
}
}
void TexelTuning(const std::string & filename) {
std::ifstream stream(filename);
std::string line;
std::vector<Texel::TexelInput> data;
LOG(logINFO) << "Running texel tuning with file " << filename;
int count = 0;
while (std::getline(stream, line)){
//std::cout << line << std::endl;
nlohmann::json o;
try {
o = nlohmann::json::parse(line);
}
catch (...) {
LOG(logFATAL) << "Cannot parse json " << line;
}
//std::cout << o << std::endl;
std::string fen = o["fen"];
Position * p = new Position(fen);
if (std::abs(o["result"].get<int>()) < 800) {
data.push_back(Texel::TexelInput(p, o["result"]));
}
++count;
if (count % 10000 == 0) {
LOG(logINFO) << count << " position read";
}
}
LOG(logINFO) << "Data size : " << data.size();
std::vector<Texel::TexelParam<ScoreType> > guess;
guess.push_back(Texel::TexelParam<ScoreType>("knightValue", 100, 2000));
guess.push_back(Texel::TexelParam<ScoreType>("bishopValue", 100, 2000));
guess.push_back(Texel::TexelParam<ScoreType>("rookValue", 100, 2000));
guess.push_back(Texel::TexelParam<ScoreType>("queenValue", 100, 2000));
//guess.push_back(Texel::TexelParam<ScoreType>("badBishopMalus", 100, 2000));
//guess.push_back(Texel::TexelParam<ScoreType>("bishopPairBonus", 100, 2000));
//guess.push_back(Texel::TexelParam<ScoreType>("knightPairMalus", 100, 2000));
//guess.push_back(Texel::TexelParam<ScoreType>("rookPairMalus", 100, 2000));
//guess.push_back(Texel::TexelParam<ScoreType>("isolatedPawnMalus", 100, 2000));
//guess.push_back(Texel::TexelParam<ScoreType>("doublePawnMalus", 100, 2000));
//guess.push_back(Texel::TexelParam<ScoreType>("bonusCandidatePassedPawn", 100, 2000));
LOG(logINFO) << "Initial values :";
for (size_t k = 0; k < guess.size(); ++k) {
LOG(logINFO) << guess[k].accessor << " " << guess[k]();
}
std::vector<Texel::TexelParam<ScoreType> > optim = Texel::TexelOptimizeGD(guess, data);
LOG(logINFO) << "Optimized values :";
for (size_t k = 0; k < optim.size(); ++k) {
LOG(logINFO) << optim[k].accessor << " " << optim[k]();
}
for (size_t k = 0; k < data.size(); ++k) {
delete data[k].p;
}
}
There is probably something stupid in that ...