Skip to content

Instantly share code, notes, and snippets.

@Ravenslofty
Created February 7, 2017 23:39
Show Gist options
  • Select an option

  • Save Ravenslofty/22662adee3307e27ade766a4aa9d33a2 to your computer and use it in GitHub Desktop.

Select an option

Save Ravenslofty/22662adee3307e27ade766a4aa9d33a2 to your computer and use it in GitHub Desktop.
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Dan Ravensloft <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "definitions.h"
#include "functions.h"
struct TunePos {
struct Board b;
float s;
};
double K;
/*
* Logistic regression - essentially fitting the eval to the game result.
*
* For the sake of experimentation, I have included a few different functions
* to be fitted:
* - base-e sigmoid vs base-10 sigmoid.
* - Texel, and I believe Gaviota fit to a base-10 sigmoid.
* - Demolito fits to a base-e sigmoid.
* - I have found that base-e sigmoid needs a much higher K than base-10 sigmoid.
* Compare K = 1.5 for base-10 to K = 3.5 for base-e (MSE, QS).
* - Whether it is "better" is to be tested.
* - mean absolute error vs mean squared error.
* - fitting eval vs fitting quiescence search score.
* - Gaviota fits the eval score.
* - Texel fits the quiescence search score.
* - Eval runs about 3-4 times faster than quiescence search.
*/
//#define NATURAL_EXP
#define SQUARED_ERROR
#define EVAL_ERROR
#ifdef NATURAL_EXP
double K_start = 0.0f;
double K_finish = 5.0f;
double K_tol = 0.1f;
double ScoreToSigmoid(int score)
{
return 1 / (1 + exp((-K * score)/400));
}
#else
double K_start = 0.1f;
double K_finish = 3.0f;
double K_tol = 0.1f;
double ScoreToSigmoid(double score)
{
return 1 / (1 + pow(10.0f, (-(K * score)/400)));
}
#endif
#ifdef SQUARED_ERROR
double Residual(int score, float result)
{
double sigmoid = result - ScoreToSigmoid(score);
return sigmoid * sigmoid;
}
double ResidualMSE(int score, float result)
{
double sigmoid = result - ScoreToSigmoid(score);
return sigmoid * sigmoid;
}
#else
double Residual(int score, float result)
{
double sigmoid = result - ScoreToSigmoid(score);
return sigmoid < 0 ? -sigmoid : sigmoid;
}
double ResidualMSE(int score, float result)
{
double sigmoid = result - ScoreToSigmoid(score);
return sigmoid * sigmoid;
}
#endif
#ifdef EVAL_ERROR
double Score(struct Board * b)
{
double score = _Eval(b);
if (b->side == BLACK) {
score = -score;
}
return score;
}
#else
int Score(struct Board * b)
{
int score = Quies(b, -10000, +10000, 1);
if (b->side == BLACK) {
score = -score;
}
return score;
}
#endif // EVAL_ERROR
#define DATA_SIZE 725000
#define TUNE_SIZE 543750
struct TunePos data[DATA_SIZE];
double sigmoids[DATA_SIZE];
double Error(int start, int finish)
{
InitPST();
double q = 0.0f;
for (int i = start; i < finish; i++) {
sigmoids[i] = ScoreToSigmoid(Score(&data[i].b));
q += Residual(Score(&data[i].b), data[i].s);
}
return q / (finish - start);
}
double Verify(int start, int finish)
{
double q = 0.0f;
for (int i = start; i < finish; i++) {
q += ResidualMSE(Score(&data[i].b), data[i].s);
}
return q / (finish - start);
}
void Tune()
{
printf("Loading data...");
/* Load data into memory */
FILE * positions = fopen("D:\\Dan\\Documents\\tuning\\tuning.epd", "r");
FILE * results = fopen("D:\\Dan\\Documents\\tuning\\results.txt", "r");
for (int i = 0; i < DATA_SIZE; i++) {
char line[128];
fgets(line, 128, positions);
ParseFEN(line, &data[i].b);
fgets(line, 128, results);
data[i].s = atof(line);
}
fclose(positions);
fclose(results);
printf("Done!\n");
/* Prepare for tuning. */
double best_error = 1.0f, verif_error;
K = 1.0f;
printf("=========================\n");
/* ??? */
int improvement = 1, iteration = 0;
double e;
const int size = 209;
double * params[209] = {&PawnValue, &KnightValue, &BishopValue, &RookValue, &QueenValue,
&BishopPairBonusMG, &BishopPairBonusEG, &MobilityPoint, &TempoBonusMG, &TempoBonusEG, &UnstoppablePassedPawnBonus,
&PassedPawnBonus[1],&PassedPawnBonus[2],&PassedPawnBonus[3],&PassedPawnBonus[4],&PassedPawnBonus[5],&PassedPawnBonus[6],
&PawnRankMG[0], &PawnRankMG[1], &PawnRankMG[2], &PawnRankMG[3], &PawnRankMG[4], &PawnRankMG[5], &PawnRankMG[6], &PawnRankMG[7],
&PawnFileMG[0], &PawnFileMG[1], &PawnFileMG[2], &PawnFileMG[3], &PawnFileMG[4], &PawnFileMG[5], &PawnFileMG[6], &PawnFileMG[7],
&PawnRankEG[0], &PawnRankEG[1], &PawnRankEG[2], &PawnRankEG[3], &PawnRankEG[4], &PawnRankEG[5], &PawnRankEG[6], &PawnRankEG[7],
&PawnFileEG[0], &PawnFileEG[1], &PawnFileEG[2], &PawnFileEG[3], &PawnFileEG[4], &PawnFileEG[5], &PawnFileEG[6], &PawnFileEG[7],
&KnightRankMG[0], &KnightRankMG[1], &KnightRankMG[2], &KnightRankMG[3], &KnightRankMG[4], &KnightRankMG[5], &KnightRankMG[6], &KnightRankMG[7],
&KnightFileMG[0], &KnightFileMG[1], &KnightFileMG[2], &KnightFileMG[3], &KnightFileMG[4], &KnightFileMG[5], &KnightFileMG[6], &KnightFileMG[7],
&KnightRankEG[0], &KnightRankEG[1], &KnightRankEG[2], &KnightRankEG[3], &KnightRankEG[4], &KnightRankEG[5], &KnightRankEG[6], &KnightRankEG[7],
&KnightFileEG[0], &KnightFileEG[1], &KnightFileEG[2], &KnightFileEG[3], &KnightFileEG[4], &KnightFileEG[5], &KnightFileEG[6], &KnightFileEG[7],
&BishopRankMG[0], &BishopRankMG[1], &BishopRankMG[2], &BishopRankMG[3], &BishopRankMG[4], &BishopRankMG[5], &BishopRankMG[6], &BishopRankMG[7],
&BishopFileMG[0], &BishopFileMG[1], &BishopFileMG[2], &BishopFileMG[3], &BishopFileMG[4], &BishopFileMG[5], &BishopFileMG[6], &BishopFileMG[7],
&BishopRankEG[0], &BishopRankEG[1], &BishopRankEG[2], &BishopRankEG[3], &BishopRankEG[4], &BishopRankEG[5], &BishopRankEG[6], &BishopRankEG[7],
&BishopFileEG[0], &BishopFileEG[1], &BishopFileEG[2], &BishopFileEG[3], &BishopFileEG[4], &BishopFileEG[5], &BishopFileEG[6], &BishopFileEG[7],
&RookRankMG[0], &RookRankMG[1], &RookRankMG[2], &RookRankMG[3], &RookRankMG[4], &RookRankMG[5], &RookRankMG[6], &RookRankMG[7],
&RookFileMG[0], &RookFileMG[1], &RookFileMG[2], &RookFileMG[3], &RookFileMG[4], &RookFileMG[5], &RookFileMG[6], &RookFileMG[7],
&RookRankEG[0], &RookRankEG[1], &RookRankEG[2], &RookRankEG[3], &RookRankEG[4], &RookRankEG[5], &RookRankEG[6], &RookRankEG[7],
&RookFileEG[0], &RookFileEG[1], &RookFileEG[2], &RookFileEG[3], &RookFileEG[4], &RookFileEG[5], &RookFileEG[6], &RookFileEG[7],
&QueenRankMG[0], &QueenRankMG[1], &QueenRankMG[2], &QueenRankMG[3], &QueenRankMG[4], &QueenRankMG[5], &QueenRankMG[6], &QueenRankMG[7],
&QueenFileMG[0], &QueenFileMG[1], &QueenFileMG[2], &QueenFileMG[3], &QueenFileMG[4], &QueenFileMG[5], &QueenFileMG[6], &QueenFileMG[7],
&QueenRankEG[0], &QueenRankEG[1], &QueenRankEG[2], &QueenRankEG[3], &QueenRankEG[4], &QueenRankEG[5], &QueenRankEG[6], &QueenRankEG[7],
&QueenFileEG[0], &QueenFileEG[1], &QueenFileEG[2], &QueenFileEG[3], &QueenFileEG[4], &QueenFileEG[5], &QueenFileEG[6], &QueenFileEG[7],
&KingRankMG[0], &KingRankMG[1], &KingRankMG[2], &KingRankMG[3], &KingRankMG[4], &KingRankMG[5], &KingRankMG[6], &KingRankMG[7],
&KingFileMG[0], &KingFileMG[1], &KingFileMG[2], &KingFileMG[3], &KingFileMG[4], &KingFileMG[5], &KingFileMG[6], &KingFileMG[7],
&KingRankEG[0], &KingRankEG[1], &KingRankEG[2], &KingRankEG[3], &KingRankEG[4], &KingRankEG[5], &KingRankEG[6], &KingRankEG[7],
&KingFileEG[0], &KingFileEG[1], &KingFileEG[2], &KingFileEG[3], &KingFileEG[4], &KingFileEG[5], &KingFileEG[6], &KingFileEG[7]};
double step[209] = {[0 ... 208] = 1.0};
double acceleration = 1.5;
double candidates[5] = {-1.5, (-1/1.5), 0, (1/1.5), 1.5};
FILE * f = fopen("results.csv", "w");
fprintf(f, "%d,%.17g,%.17g,", iteration, best_error, verif_error);
for (int j = 0; j < size; j++) {
fprintf(f, "%d,", *params[j]);
}
fprintf(f, "\n");
fclose(f);
while (improvement) {
improvement = 0;
iteration++;
double before = Error(0, TUNE_SIZE);
for (int i = 0; i < size; i++) {
double besterr = 100.0f, backup = *params[i];
int best = -1;
for (int j = 0; j < 5; j++) {
*params[i] = backup + step[i] * candidates[j];
e = Error(0, TUNE_SIZE);
if (e < besterr) {
besterr = e;
best = j;
printf("params[%d] = %.17g, e = %.17g\n", i, *params[i], e);
}
}
if (best == 2) {
*params[i] = backup;
step[i] /= acceleration;
} else {
*params[i] = backup + step[i] * candidates[best];
step[i] *= candidates[best];
}
}
if (((e = Error(0, TUNE_SIZE)) - before) < 0) {
improvement = 1;
best_error = e;
}
f = fopen("results.csv", "a");
fprintf(f, "%d,%.17g,%.17g,", iteration, best_error, Verify(TUNE_SIZE, DATA_SIZE));
for (int j = 0; j < size; j++) {
fprintf(f, "%.17g,", *params[j]);
}
fprintf(f, "\n");
fclose(f);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment