Commit 4c23e435 authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Use the solo game for the perceptron bootstrap

parent c03de337
......@@ -2242,14 +2242,22 @@ able to learn. This perceptron is /static/, because no learning will
be remembered the next time you will call this function.
#+end_deftypefun
#+attr_texinfo: :options {TarotPerceptron *} tarot_perceptron_dup (const TarotPerceptron *@var{perceptron})
#+attr_texinfo: :options {void} tarot_perceptron_eval (TarotPerceptron *@var{perceptron}, const TarotGame *@var{base}, size_t @var{n}, TarotGameEvent **@var{candidates}, size_t @var{start}, size_t @var{max}, double *@var{scores})
#+begin_deftypefun
Return an allocated copy of /perceptron/.
Starting at /base/, for each of the /n/ /candidates/, make a
prediction using /perceptron/. Discard the predictions for the
/start/ first /candidates/, then store the following /max/ predictions
inside /scores/.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_perceptron_set_learning_rate (TarotPerceptron *perceptron, double learning_rate)
#+attr_texinfo: :options {void} tarot_perceptron_learn (TarotPerceptron *@var{perceptron}, const TarotGame *@var{base}, const TarotGameEvent *@var{event}, double @var{final_score})
#+begin_deftypefun
Change the /learning rate/ in /perceptron/.
Learn that playing /event/ in /base/ leads to the given /final score/.
#+end_deftypefun
#+attr_texinfo: :options {TarotPerceptron *} tarot_perceptron_dup (const TarotPerceptron *@var{perceptron})
#+begin_deftypefun
Return an allocated copy of /perceptron/.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_perceptron_free (TarotPerceptron *@var{perceptron})
......@@ -2258,6 +2266,11 @@ Delete a /perceptron/ allocated by =tarot_perceptron_alloc=,
=tarot_perceptron_dup= or =tarot_perceptron_static_default=.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_perceptron_set_learning_rate (TarotPerceptron *perceptron, double learning_rate)
#+begin_deftypefun
Change the /learning rate/ in /perceptron/.
#+end_deftypefun
#+attr_texinfo: :options {void} tarot_perceptron_load (TarotPerceptron *@var{perceptron}, size_t @var{start}, size_t @var{nweights}, const double *@var{parameters})
#+begin_deftypefun
Load the internal /parameters/ (size of /nweights/) into /perceptron/.
......@@ -2274,38 +2287,6 @@ function does all the allocations for you, and simply sets /*n/ to the
total number of parameters and copy them in the newly-allocated
/*data/.
#+end_deftypefun
#+attr_texinfo: :options {double} tarot_perceptron_learn (TarotPerceptron *@var{perceptron}, const TarotGame *@var{state}, const TarotGameEvent *@var{action}, double @var{score})
#+begin_deftypefun
#+attr_texinfo: @deftypefunx {void} tarot_perceptron_learn_game (TarotPerceptron *@var{perceptron}, const TarotGame *@var{full_game})
Teach /perceptron/ that doing the given /action/ in the given /state/
results in the given final /score/. For the =_game= function, learn
from all actions in the /fullgame/. Return the mean update to the
internal parameters. If the return value is /Not a Number/, then it
means that the learning has failed. Try again with a lower learning
rate. If the return value is very close to 0, then the algorithm did
not learn anything from that.
#+end_deftypefun
#+attr_texinfo: :options {double} tarot_perceptron_predict (TarotPerceptron *@var{perceptron}, const TarotGame *@var{state}, const TarotGameEvent *@var{action})
#+begin_deftypefun
Return the score predicted by /perceptron/ for doing the given
/action/ in the given /state/.
#+end_deftypefun
#+attr_texinfo: :options {const TarotGameEvent *} tarot_perceptron_best (TarotPerceptron *@var{perceptron}, const TarotGame *@var{state}, double *@var{score})
#+begin_deftypefun
Return the predicted best action in /state/ according to /perceptron/,
and set /score/ to the predicted score. If nothing is to be played in
/state/, then return =NULL= (in which case /score/ may not be set).
#+end_deftypefun
#+attr_texinfo: :options {double} tarot_perceptron_validate (TarotPerceptron *@var{perceptron}, const TarotGame *@var{validation_game})
#+begin_deftypefun
Analyze the /validation game/ and return the mean error at each event
between what the /perceptron/ would predict and the actual score for
the player.
#+end_deftypefun
** A general interface for AI predictors
#+attr_texinfo: :options {struct} TarotAi
#+begin_deftp
......
......@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: tarot 0.4.2.83-ebdc-dirty\n"
"Report-Msgid-Bugs-To: vivien@planete-kraus.eu\n"
"POT-Creation-Date: 2019-12-07 13:07+0100\n"
"PO-Revision-Date: 2019-12-07 13:07+0100\n"
"POT-Creation-Date: 2019-12-15 16:44+0100\n"
"PO-Revision-Date: 2019-12-15 16:50+0100\n"
"Last-Translator: Vivien Kraus <vivien@planete-kraus.eu>\n"
"Language-Team: French\n"
"Language: fr\n"
......@@ -48,7 +48,7 @@ msgstr "Affiche l'information de version et quitte"
#: src/tarot-app/tarot_deal.c:70 src/tarot-app/tarot_mcts.c:88
#: src/tarot-app/tarot_stacking.c:80 src/tarot-app/tarot_features.c:62
#: src/tarot-app/tarot_cnn_features.c:58
#: src/tarot-app/tarot_perceptron_bootstrap.c:92
#: src/tarot-app/tarot_perceptron_bootstrap.c:93
msgid "version"
msgstr "version"
......@@ -56,7 +56,7 @@ msgstr "version"
#: src/tarot-app/tarot_deal.c:112 src/tarot-app/tarot_mcts.c:136
#: src/tarot-app/tarot_stacking.c:112 src/tarot-app/tarot_features.c:93
#: src/tarot-app/tarot_cnn_features.c:89
#: src/tarot-app/tarot_perceptron_bootstrap.c:131
#: src/tarot-app/tarot_perceptron_bootstrap.c:132
#, c-format
msgid ""
"%s (libtarot %s)\n"
......@@ -1201,7 +1201,7 @@ msgstr ""
#: src/tarot-app/tarot_deal.c:69 src/tarot-app/tarot_mcts.c:87
#: src/tarot-app/tarot_stacking.c:79 src/tarot-app/tarot_features.c:61
#: src/tarot-app/tarot_cnn_features.c:57
#: src/tarot-app/tarot_perceptron_bootstrap.c:91
#: src/tarot-app/tarot_perceptron_bootstrap.c:92
msgid "help"
msgstr "aide"
......@@ -1218,7 +1218,7 @@ msgid "no-call-allowed"
msgstr "appel-interdit"
#: src/tarot-app/tarot_deal.c:74 src/tarot-app/tarot_mcts.c:90
#: src/tarot-app/tarot_perceptron_bootstrap.c:96
#: src/tarot-app/tarot_perceptron_bootstrap.c:97
msgid "seed"
msgstr "graine"
......@@ -1569,24 +1569,24 @@ msgstr "Poignée"
msgid "Discarding"
msgstr "Écart"
#: src/tarot-app/tarot_perceptron_bootstrap.c:68
#: src/tarot-app/tarot_perceptron_bootstrap.c:69
#, c-format
msgid "Mean update\tValidation score\tMaximum weight\n"
msgstr "Mise à jour moyenne\tScore de validation\tPoids maximum\n"
msgid "Fading accumulated metric\tMaximum weight\n"
msgstr "Métrique accumulée évanescente\tPoids maximum\n"
#: src/tarot-app/tarot_perceptron_bootstrap.c:93
#: src/tarot-app/tarot_perceptron_bootstrap.c:94
msgid "learning-rate"
msgstr "taux-d-apprentissage"
#: src/tarot-app/tarot_perceptron_bootstrap.c:94
#: src/tarot-app/tarot_perceptron_bootstrap.c:95
msgid "exploration"
msgstr "exploration"
#: src/tarot-app/tarot_perceptron_bootstrap.c:95
#: src/tarot-app/tarot_perceptron_bootstrap.c:96
msgid "continue"
msgstr "continuer"
#: src/tarot-app/tarot_perceptron_bootstrap.c:117
#: src/tarot-app/tarot_perceptron_bootstrap.c:118
#, c-format
msgid ""
"Usage: tarot-perceptron-bootstrap [OPTIONS]... [HIDDEN_SIZE]...\n"
......@@ -1609,14 +1609,14 @@ msgstr ""
"- -s GRAINE, --seed=GRAINE : définit la graine aléatoire. GRAINE ne\n"
" peut pas être une chaîne vide.\n"
#: src/tarot-app/tarot_perceptron_bootstrap.c:143
#: src/tarot-app/tarot_perceptron_bootstrap.c:144
#, c-format
msgid "Error: the learning rate should be a positive number, not '%s'.\n"
msgstr ""
"Erreur : le taux d'apprentissage doit être un nombre positif, et pas "
"« %s ».\n"
#: src/tarot-app/tarot_perceptron_bootstrap.c:155
#: src/tarot-app/tarot_perceptron_bootstrap.c:156
#, c-format
msgid ""
"Error: the exploration probability should be a probability (between 0 and "
......@@ -1625,18 +1625,18 @@ msgstr ""
"Erreur : la probabilité d'exploration doit être une probabilité (entre\n"
"0 et 1), et pas « %s ».\n"
#: src/tarot-app/tarot_perceptron_bootstrap.c:170
#: src/tarot-app/tarot_perceptron_bootstrap.c:171
#, c-format
msgid "Error: the seed must not be empty.\n"
msgstr "Erreur : la graine ne doit pas être vide.\n"
#: src/tarot-app/tarot_perceptron_bootstrap.c:191
#: src/tarot-app/tarot_perceptron_bootstrap.c:192
#, c-format
msgid "Error: if you are continuing learning, do not specify the structure!\n"
msgstr ""
"Erreur : si vous continuez l'apprentissage, ne spécifiez pas la structure !\n"
#: src/tarot-app/tarot_perceptron_bootstrap.c:206
#: src/tarot-app/tarot_perceptron_bootstrap.c:207
#, c-format
msgid ""
"Error: the %d-th dimension should be a strictly positive integer, not '%s'.\n"
......
......@@ -126,13 +126,9 @@ ai_perceptron_eval (void *state, const TarotGame * base, size_t n_candidates,
TarotGameEvent ** candidates, size_t start, size_t max,
double *scores)
{
size_t i;
TarotPerceptron *perceptron = (TarotPerceptron *) state;
for (i = 0; i < max && i + start < n_candidates; i++)
{
scores[i] =
tarot_perceptron_predict (perceptron, base, candidates[i + start]);
}
tarot_perceptron_eval (perceptron, base, n_candidates, candidates, start,
max, scores);
}
static inline void
......
......@@ -2069,10 +2069,7 @@ features_strongest_player_try_bid (const TarotGame * game)
candidates[1].u.bid = TAROT_PUSH;
candidates[2].u.bid = TAROT_STRAIGHT_KEEP;
candidates[3].u.bid = TAROT_DOUBLE_KEEP;
for (i = 0; i < 4; i++)
{
scores[i] = tarot_perceptron_predict (perceptron, game, pointers[i]);
}
tarot_perceptron_eval (perceptron, game, 4, pointers, 0, 4, scores);
for (i = 0; i < 4; i++)
{
if (scores[i] > max)
......@@ -2180,6 +2177,7 @@ predictor_perceptron_select_call (const TarotGame * game, TarotCard * call,
};
TarotNumber minimum, candidate;
TarotGameEvent e;
TarotGameEvent *pointer_e = &e;
TarotPlayer player;
int ret = 1;
int isuit;
......@@ -2203,7 +2201,8 @@ predictor_perceptron_select_call (const TarotGame * game, TarotCard * call,
assert (0);
}
event_set_call (&e, c);
candidate_score = tarot_perceptron_predict (perceptron, game, &e);
tarot_perceptron_eval (perceptron, game, 1, &pointer_e, 0, 1,
&candidate_score);
if (candidate_score > *best_score)
{
*call = c;
......
......@@ -92,40 +92,22 @@ tarot_perceptron_save_alloc (const TarotPerceptron * perceptron, size_t *n,
}
}
double
tarot_perceptron_learn (TarotPerceptron * perceptron, const TarotGame * state,
const TarotGameEvent * action, double score)
{
return perceptron_learn (perceptron, state, action, score);
}
double
tarot_perceptron_learn_game (TarotPerceptron * perceptron,
const TarotGame * full_game)
{
return perceptron_learn_game (perceptron, full_game);
}
double
tarot_perceptron_predict (TarotPerceptron * perceptron,
const TarotGame * state,
const TarotGameEvent * action)
{
return perceptron_predict (perceptron, state, action);
}
const TarotGameEvent *
tarot_perceptron_best (TarotPerceptron * perceptron, const TarotGame * state,
double *score)
void
tarot_perceptron_learn (TarotPerceptron * perceptron, const TarotGame * base,
const TarotGameEvent * event, double final_score)
{
return perceptron_best (perceptron, state, score);
perceptron_learn (perceptron, base, event, final_score);
}
double
tarot_perceptron_validate (TarotPerceptron * perceptron,
const TarotGame * validation_game)
void
tarot_perceptron_eval (TarotPerceptron * perceptron,
const TarotGame * base,
size_t n_candidates,
TarotGameEvent ** candidates,
size_t start, size_t max, double *scores)
{
return perceptron_validate (perceptron, validation_game);
return perceptron_eval (perceptron, base, n_candidates, candidates, start,
max, scores);
}
#include <tarot/perceptron_private_impl.h>
......
......@@ -37,17 +37,35 @@ extern "C"
double learning_rate);
/**
* tarot_perceptron_free:
* tarot_perceptron_static_default: (constructor):
*/
void tarot_perceptron_free (TarotPerceptron * perceptron);
TarotPerceptron *tarot_perceptron_static_default ();
/**
* tarot_perceptron_static_default: (constructor):
* tarot_perceptron_eval:
* @candidates: (array length=n_candidates):
* @scores: (array length=max):
*/
TarotPerceptron *tarot_perceptron_static_default ();
void tarot_perceptron_eval (TarotPerceptron * perceptron,
const TarotGame * base, size_t n_candidates,
TarotGameEvent ** candidates, size_t start,
size_t max, double *scores);
/**
* tarot_perceptron_learn:
*/
void tarot_perceptron_learn (TarotPerceptron * perceptron,
const TarotGame * base,
const TarotGameEvent * event,
double final_score);
TarotPerceptron *tarot_perceptron_dup (const TarotPerceptron * perceptron);
/**
* tarot_perceptron_free:
*/
void tarot_perceptron_free (TarotPerceptron * perceptron);
void tarot_perceptron_set_learning_rate (TarotPerceptron * perceptron,
double learning_rate);
......@@ -72,34 +90,6 @@ extern "C"
void tarot_perceptron_save_alloc (const TarotPerceptron * perceptron,
size_t *n, double **data);
/**
* tarot_perceptron_learn:
*/
double tarot_perceptron_learn (TarotPerceptron * perceptron,
const TarotGame * state,
const TarotGameEvent * action, double score);
/**
* tarot_perceptron_learn_game:
*/
double tarot_perceptron_learn_game (TarotPerceptron * perceptron,
const TarotGame * full_game);
double tarot_perceptron_predict (TarotPerceptron * perceptron,
const TarotGame * state,
const TarotGameEvent * action);
/**
* tarot_perceptron_best:
* @score: (out):
*/
const TarotGameEvent *tarot_perceptron_best (TarotPerceptron * perceptron,
const TarotGame * state,
double *score);
double tarot_perceptron_validate (TarotPerceptron * perceptron,
const TarotGame * validation_game);
#ifdef __cplusplus
}
#endif /* __cplusplus */
......
......@@ -45,28 +45,25 @@ extern "C"
size_t start, size_t max,
double *parameters);
static size_t perceptron_encode (const TarotGame * state,
const TarotGameEvent * action,
size_t start, size_t max, double *input);
static size_t perceptron_encode_base (const TarotGame * base,
size_t start,
size_t max, double *input);
static size_t perceptron_encode_action (const TarotGame * base,
const TarotGameEvent * action,
size_t start,
size_t max, double *input);
static double perceptron_learn (TarotPerceptron * perceptron,
const TarotGame * state,
const TarotGameEvent * action,
double score);
static double perceptron_learn_game (TarotPerceptron * perceptron,
const TarotGame * full_game);
static double perceptron_predict (TarotPerceptron * perceptron,
const TarotGame * state,
const TarotGameEvent * action);
static const TarotGameEvent *perceptron_best (TarotPerceptron * perceptron,
const TarotGame * state,
double *score);
static double perceptron_validate (TarotPerceptron * perceptron,
const TarotGame * validation_game);
static void perceptron_eval (TarotPerceptron * perceptron,
const TarotGame * state,
size_t n_candidates,
TarotGameEvent ** candidates,
size_t start, size_t max, double *scores);
#ifdef __cplusplus
}
......
......@@ -21,6 +21,7 @@ static size_t n_hidden_layers;
static size_t *hidden_sizes;
static double learning_rate;
static double exploration;
static double metric;
static int cont = 0;
static struct yarrow256_ctx generator;
......@@ -65,7 +66,7 @@ main (int argc, char *argv[])
tarot_perceptron_load (perceptron, 0, n_weights, weights);
free (weights);
}
fprintf (stderr, _("Mean update\tValidation score\tMaximum weight\n"));
fprintf (stderr, _("Fading accumulated metric\tMaximum weight\n"));
while (1)
{
self_learn (perceptron);
......@@ -210,303 +211,191 @@ Copyright © 2019 Vivien Kraus\nThis is free software; see the source for copyin
}
}
static TarotGameEvent *
random_move (const TarotGame * game)
static inline void
custom_ai_eval (void *data, const TarotGame * base, size_t n_candidates,
TarotGameEvent ** candidates, size_t start, size_t max,
double *scores)
{
TarotBid mini_bid;
int decl_allowed;
TarotPlayer next;
TarotNumber mini_call;
size_t n_cards;
size_t n_prio;
size_t n_additional;
TarotCard *prio;
TarotCard *additional;
if (tarot_game_get_hint_bid (game, &next, &mini_bid) == TAROT_GAME_OK)
TarotPerceptron *p = (TarotPerceptron *) data;
tarot_perceptron_eval (p, base, n_candidates, candidates, start, max,
scores);
}
static inline void
custom_ai_learn (void *data, const TarotGame * base,
const TarotGameEvent * event, double final_score)
{
TarotPerceptron *p = (TarotPerceptron *) data;
/* We need to validate before learning */
double predicted;
TarotGameEvent *evaluated = (TarotGameEvent *) event;
double diff;
static int has_metric = 0;
tarot_perceptron_eval (p, base, 1, &evaluated, 0, 1, &predicted);
diff = final_score - predicted;
if (diff < 0)
{
TarotBid bids[] = {
TAROT_PASS,
TAROT_TAKE,
TAROT_TAKE,
TAROT_TAKE,
TAROT_PUSH,
TAROT_PUSH,
TAROT_STRAIGHT_KEEP,
TAROT_DOUBLE_KEEP
};
size_t position;
uint8_t position_data[sizeof (position)];
yarrow256_random (&generator, sizeof (position_data), position_data);
memcpy (&position, position_data, sizeof (position));
position %= (sizeof (bids) / sizeof (bids[0]));
if (bids[position] >= mini_bid)
{
return tarot_game_event_alloc_bid (bids[position]);
}
else
{
return tarot_game_event_alloc_bid (TAROT_PASS);
}
diff = -diff;
}
else if (tarot_game_get_hint_decl (game, &next, &decl_allowed) ==
TAROT_GAME_OK)
if (!has_metric)
{
uint8_t no_decl;
/* Even in high-exploration scenarios, we want to avoid slam
* declarations. */
yarrow256_random (&generator, sizeof (no_decl), &no_decl);
no_decl %= 16;
if (no_decl == 0 && decl_allowed)
{
return tarot_game_event_alloc_decl (1);
}
else
{
return tarot_game_event_alloc_decl (0);
}
}
else if (tarot_game_get_hint_call (game, &next, &mini_call) ==
TAROT_GAME_OK)
{
static const TarotSuit suits[] = {
TAROT_HEARTS, TAROT_CLUBS, TAROT_DIAMONDS, TAROT_SPADES
};
uint8_t suit_i;
TarotCard c;
yarrow256_random (&generator, sizeof (suit_i), &suit_i);
suit_i %= (sizeof (suits) / sizeof (suits[0]));
if (tarot_of (mini_call, suits[suit_i], &c) != 0)
{
assert (0);
}
return tarot_game_event_alloc_call (c);
metric = diff;
has_metric = 1;
}
else
if (tarot_game_get_hint_discard_alloc
(game, &next, &n_cards, &n_prio, &prio, &n_additional,
&additional) == TAROT_GAME_OK)
{
TarotCard selected[78];
size_t n_selected = 0;
size_t n_selectable = n_prio;
TarotCard *selectable = prio;
size_t i;
if (n_prio < n_cards)
{
n_selectable = n_additional;
selectable = additional;
for (n_selected = 0; n_selected < n_prio; n_selected++)
{
selected[n_selected] = prio[n_selected];
}
}
for (i = n_selected; i < n_cards; i++)
{
size_t position;
uint8_t position_data[sizeof (position)];
yarrow256_random (&generator, sizeof (position), position_data);
memcpy (&position, position_data, sizeof (position));
position %= n_selectable;
selected[i] = selectable[position];
selectable[position] = selectable[--n_selectable];
}
free (prio);
free (additional);
return tarot_game_event_alloc_discard (n_cards, selected);
metric = 0.999 * metric + 0.001 * diff;
}
else if (tarot_game_get_hint_card_alloc (game, &next, &n_prio, &prio) ==
TAROT_GAME_OK)
{
TarotCard selected;
size_t position;
uint8_t position_data[sizeof (position)];
yarrow256_random (&generator, sizeof (position), position_data);
memcpy (&position, position_data, sizeof (position));
position %= n_prio;
selected = prio[position];
free (prio);
return tarot_game_event_alloc_card (selected);
}
assert (0);
return NULL;
tarot_perceptron_learn (p, base, event, final_score);
}
static inline void *
custom_ai_dup (void *data)
{
/* No memory management: the perceptron is a global variable and we
* need to track its values */
return data;
}
static inline void
custom_ai_destruct (void *data)
{
(void) data;
}
static int
is_empty_game (const TarotGame * game)
static inline TarotGame *
play_game (TarotPerceptron * p, size_t n_players, int with_call)
{
size_t i_bid;
size_t n_bids;
TarotBid *bids;
if (tarot_game_get_bids_alloc (game, &n_bids, &bids) != TAROT_GAME_OK)
uint8_t next_seed[256];
TarotAi *pure =
tarot_ai_alloc (p, custom_ai_eval, custom_ai_learn, custom_ai_dup,
custom_ai_destruct);
TarotAi *fuzzy;
TarotSolo *solo;
int done = 0;