Commit b6fac6c1 authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Add a bootstrap learner

parent 2b6570d6
......@@ -54,6 +54,7 @@
/doc/tarot-features.man
/doc/tarot-figures
/doc/tarot-mcts.man
/doc/tarot-perceptron-bootstrap.man
/doc/tarot-stacking.man
/doc/tarot-status.man
/doc/tarot-tune
......@@ -119,8 +120,6 @@
/src/libtarot/guile/tarot/config.scm
/src/libtarot/hand/check_hand
/src/libtarot/layout/check-layout
/src/libtarot/perceptron/julien/julien.c
/src/libtarot/perceptron/julien/julien.h
/src/libtarot/pkgconfig/libtarot-gobject.pc
/src/libtarot/pkgconfig/libtarot.pc
/src/libtarot/semantic-api/api.sxml
......
......@@ -21,7 +21,8 @@ dist_man6_MANS += \
%reldir%/tarot-stacking.man \
%reldir%/tarot-features.man \
%reldir%/tarot-cnn-features.man \
%reldir%/tarot-cnn.man
%reldir%/tarot-cnn.man \
%reldir%/tarot-perceptron-bootstrap.man
MAINTAINERCLEANFILES += \
$(srcdir)/%reldir%/tarot.man \
$(srcdir)/%reldir%/tarot-status.man \
......@@ -30,7 +31,8 @@ MAINTAINERCLEANFILES += \
$(srcdir)/%reldir%/tarot-stacking.man \
$(srcdir)/%reldir%/tarot-features.man \
$(srcdir)/%reldir%/tarot-cnn-features.man \
$(srcdir)/%reldir%/tarot-cnn.man
$(srcdir)/%reldir%/tarot-cnn.man \
$(srcdir)/%reldir%/tarot-perceptron-bootstrap.man
help2man_verbose = $(help2man_verbose_@AM_V@)
help2man_verbose_ = $(help2man_verbose_@AM_DEFAULT_V@)
......@@ -67,3 +69,7 @@ $(srcdir)/%reldir%/tarot-cnn-features.man: src/tarot-app/tarot_cnn_features.c
$(srcdir)/%reldir%/tarot-cnn.man: src/tarot-app/tarot_cnn.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-cnn
$(help2man_verbose) ./pre-inst-env help2man tarot-cnn > $@
$(srcdir)/%reldir%/tarot-perceptron-bootstrap.man: src/tarot-app/tarot_perceptron_bootstrap.c
$(MAKE) $(AM_MAKEFLAGS) src/tarot-app/tarot-perceptron-bootstrap
$(help2man_verbose) ./pre-inst-env help2man tarot-perceptron-bootstrap > $@
......@@ -33,4 +33,5 @@ src/tarot-app/tarot_stacking.c
src/tarot-app/tarot_features.c
src/tarot-app/tarot_cnn_features.c
src/tarot-app/gui/window.ui
src/tarot-app/gui/widget.ui
\ No newline at end of file
src/tarot-app/gui/widget.ui
src/tarot-app/tarot_perceptron_bootstrap.c
\ No newline at end of file
......@@ -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-11-24 14:47+0100\n"
"PO-Revision-Date: 2019-11-13 21:16+0100\n"
"POT-Creation-Date: 2019-11-27 08:04+0100\n"
"PO-Revision-Date: 2019-11-24 17:44+0100\n"
"Last-Translator: Vivien Kraus <vivien@planete-kraus.eu>\n"
"Language-Team: French\n"
"Language: fr\n"
......@@ -48,6 +48,7 @@ msgstr "Affiche l'information de version et quitte"
#: src/tarot-app/tarot_deal.c:66 src/tarot-app/tarot_mcts.c:89
#: src/tarot-app/tarot_stacking.c:81 src/tarot-app/tarot_features.c:61
#: src/tarot-app/tarot_cnn_features.c:58
#: src/tarot-app/tarot_perceptron_bootstrap.c:60
msgid "version"
msgstr "version"
......@@ -55,6 +56,7 @@ msgstr "version"
#: src/tarot-app/tarot_deal.c:106 src/tarot-app/tarot_mcts.c:135
#: src/tarot-app/tarot_stacking.c:111 src/tarot-app/tarot_features.c:90
#: src/tarot-app/tarot_cnn_features.c:86
#: src/tarot-app/tarot_perceptron_bootstrap.c:94
#, c-format
msgid ""
"%s (libtarot %s)\n"
......@@ -1203,6 +1205,7 @@ msgstr ""
#: src/tarot-app/tarot_deal.c:65 src/tarot-app/tarot_mcts.c:88
#: src/tarot-app/tarot_stacking.c:80 src/tarot-app/tarot_features.c:60
#: src/tarot-app/tarot_cnn_features.c:57
#: src/tarot-app/tarot_perceptron_bootstrap.c:59
msgid "help"
msgstr "aide"
......@@ -1219,6 +1222,7 @@ msgid "no-call-allowed"
msgstr "appel-interdit"
#: src/tarot-app/tarot_deal.c:70 src/tarot-app/tarot_mcts.c:91
#: src/tarot-app/tarot_perceptron_bootstrap.c:63
msgid "seed"
msgstr "graine"
......@@ -1569,6 +1573,64 @@ msgstr "Poignée"
msgid "Discarding"
msgstr "Écart"
#: src/tarot-app/tarot_perceptron_bootstrap.c:61
msgid "learning-rate"
msgstr "taux-d-apprentissage"
#: src/tarot-app/tarot_perceptron_bootstrap.c:62
msgid "exploration"
msgstr "exploration"
#: src/tarot-app/tarot_perceptron_bootstrap.c:81
#, c-format
msgid ""
"Usage: tarot-perceptron-bootstrap [OPTIONS]... [HIDDEN_SIZE]...\n"
"Learn to play by trial and error. Options:\n"
"- -h, --help: print this message and exit;\n"
"- -v, --version: print the program version and exit;\n"
"- -r, --learning-rate=LR: define the learning rate;\n"
"- -e, --exploration=FRAC: define the probability to play at random;\n"
"- -s SEED, --seed=\"SEED\": set the seed for the random deal. SEED\n"
" must not be empty.\n"
msgstr ""
"Utilisation : tarot-perceptron-bootstrap [OPTIONS]... [TAILLE_CACHEES]...\n"
"Apprend à jouer par essai et erreur. Options :\n"
"- -h, --aide : affiche ce message et quitte ;\n"
"- -v, --version : affiche la version du programme et quitte ;\n"
"- -r TA, --taux-d-apprentissage=TA : définit le taux d'apprentissage ;\n"
"- -e FRAC, --exploration=FRAC : définit la probabilité de jouer au hasard ;\n"
"- -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:108
#, 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:120
#, c-format
msgid ""
"Error: the exploration probability should be a probability (between 0 and "
"1), not '%s'.\n"
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:131
#, 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:158
#, c-format
msgid ""
"Error: the %d-th dimension should be a strictly positive integer, not '%s'.\n"
msgstr ""
"Erreur : la %d-ième dimension doit être un entier strictement positif,\n"
"et pas « %s ».\n"
#~ msgid "Error: the MCTS parameter should be a number, not '%s'.\n"
#~ msgstr "Erreur : le paramètre MCTS doit être un nombre, et pas « %s ».\n"
......@@ -1577,21 +1639,12 @@ msgstr "Écart"
#~ msgstr ""
#~ "Erreur : le paramètre MCTS (%f) a un suffixe inattendu, « %s » : « %s ».\n"
#~ msgid "Error: the random parameter should be a number, not '%s'.\n"
#~ msgstr ""
#~ "Erreur : le paramètre aléatoire doit être un nombre, et pas « %s ».\n"
#~ msgid ""
#~ "Error: the random parameter (%f) has an unexpected suffix, '%s': '%s'.\n"
#~ msgstr ""
#~ "Erreur : le paramètre aléatoire (%f) a un suffixe inattendu, « %s » : "
#~ "« %s ».\n"
#~ msgid "Error: the random parameter shoul be between 0 and 1, not %f.\n"
#~ msgstr ""
#~ "Erreur : le paramètre aléatoire doit être un nombre entre 0 et 1, et pas "
#~ "« %f ».\n"
#~ msgid "Error: the agreement parameter should be a number, not '%s'.\n"
#~ msgstr "Erreur : le paramètre accord doit être un nombre, et pas « %s ».\n"
......
......@@ -25,3 +25,4 @@ include %reldir%/tarot-stacking.am
include %reldir%/tarot-features.am
include %reldir%/tarot-cnn-features.am
include %reldir%/tarot-cnn.am
include %reldir%/tarot-perceptron-bootstrap.am
......@@ -26,6 +26,7 @@
#include <assert.h>
#include "getopt.h"
#include <string.h>
#include <nettle/yarrow.h>
#define _(String) gettext (String)
#define N_(String) String
......
# tarot implements the rules of the tarot game
# Copyright (C) 2019 Vivien Kraus
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
bin_PROGRAMS += %reldir%/tarot-perceptron-bootstrap
%canon_reldir%_tarot_perceptron_bootstrap_SOURCES = \
%reldir%/tarot_perceptron_bootstrap.c
%canon_reldir%_tarot_perceptron_bootstrap_LDADD = \
%reldir%/libprogram-commons.la \
lib/libgnu.la \
src/libtarot/libtarot.la \
$(LTLIBINTL) \
$(LTLIBTHREAD) \
@INTL_MACOSX_LIBS@ \
$(AM_LIBS)
/* tarot implements the rules of the tarot game
* Copyright (C) 2019 Vivien Kraus
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "program_commons.h"
static size_t n_hidden_layers;
static size_t *hidden_sizes;
static double learning_rate;
static double exploration;
static struct yarrow256_ctx generator;
static void parse_options (int argc, char *argv[]);
static void self_learn (TarotPerceptron *perceptron);
int
main (int argc, char *argv[])
{
TarotPerceptron *perceptron;
tarot_program_setup ();
parse_options (argc, argv);
perceptron = tarot_perceptron_alloc (n_hidden_layers, hidden_sizes, learning_rate);
while (1)
{
self_learn (perceptron);
}
tarot_perceptron_free (perceptron);
return EXIT_SUCCESS;
}
static void
parse_options (int argc, char *argv[])
{
int c;
char default_seed[] = "default";
uint8_t *seed;
char *end;
exploration = 0.1;
learning_rate = 1e-5;
yarrow256_init (&generator, 0, NULL);
yarrow256_seed (&generator, strlen (default_seed), (void *) default_seed);
while (1)
{
static struct option long_options[] =
{
{N_ ("help"), no_argument, 0, 'h'},
{N_ ("version"), no_argument, 0, 'v'},
{N_ ("learning-rate"), required_argument, 0, 'r'},
{N_ ("exploration"), required_argument, 0, 'e'},
{N_ ("seed"), required_argument, 0, 's'},
{0, 0, 0, 0}
};
int option_index = 0;
for (option_index = 0; long_options[option_index].name != NULL; option_index++)
{
long_options[option_index].name = _ (long_options[option_index].name);
}
option_index = 0;
c = getopt_long (argc, argv, "hvr:e:s:", long_options, &option_index);
if (c == -1)
break;
switch (c)
{
case 0:
break;
case 'h':
printf (_ ("Usage: tarot-perceptron-bootstrap [OPTIONS]... [HIDDEN_SIZE]...\n\
Learn to play by trial and error. Options:\n\
- -h, --help: print this message and exit;\n\
- -v, --version: print the program version and exit;\n\
- -r, --learning-rate=LR: define the learning rate;\n\
- -e, --exploration=FRAC: define the probability to play at random;\n\
- -s SEED, --seed=\"SEED\": set the seed for the random deal. SEED\n\
must not be empty.\n\
"));
exit (EXIT_SUCCESS);
break;
case 'v':
printf (_ ("%s (libtarot %s)\n\
\n\
Copyright © 2019 Vivien Kraus\nThis is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"),
"tarot-perceptron-bootstrap",
tarot_libtarot_version ());
exit (EXIT_SUCCESS);
break;
case 'r':
learning_rate = strtod (optarg, &end);
if (learning_rate <= 0)
{
fprintf (stderr,
_
("Error: the learning rate should be a positive number, not '%s'.\n"),
optarg);
exit (EXIT_FAILURE);
}
break;
case 'e':
exploration = strtod (optarg, &end);
if (exploration < 0 || exploration > 1)
{
fprintf (stderr,
_
("Error: the exploration probability should be a probability (between 0 and 1), not '%s'.\n"),
optarg);
exit (EXIT_FAILURE);
}
break;
case 's':
seed = xmalloc (strlen (optarg) + 1);
memcpy (seed, optarg, strlen (optarg) + 1);
if (strlen (optarg) == 0)
{
fprintf (stderr, _("Error: the seed must not be empty.\n"));
exit (EXIT_FAILURE);
}
yarrow256_seed (&generator, strlen (optarg), seed);
free (seed);
break;
case '?':
/* getopt_long already printed an error message. */
exit (EXIT_FAILURE);
break;
default:
abort ();
}
}
n_hidden_layers = argc - optind;
hidden_sizes = xmalloc (n_hidden_layers * sizeof (size_t));
for (c = 0; (size_t) c < n_hidden_layers; c++)
{
hidden_sizes[c] = strtoul (argv[optind + c], &end, 10);
if (strcmp (end, "") != 0)
{
hidden_sizes[c] = 0;
}
if (hidden_sizes[c] == 0)
{
fprintf (stderr, _ ("Error: the %d-th dimension should be a strictly positive integer, not '%s'.\n"),
c, argv[optind + c]);
exit (EXIT_FAILURE);
}
}
}
static TarotPerceptron *
random_perceptron (const TarotPerceptron *base)
{
TarotPerceptron *ret = tarot_perceptron_dup (base);
size_t n_parameters;
double *parameters;
size_t size_shuffled;
tarot_perceptron_save_alloc (ret, &n_parameters, &parameters);
/* We shuffle the weights */
for (size_shuffled = 1; size_shuffled < n_parameters; size_shuffled++)
{
uint8_t position_data[sizeof (size_t)];
size_t position;
double hold = parameters[size_shuffled];
yarrow256_random (&generator, sizeof (position_data), position_data);
memcpy (&position, position_data, sizeof (size_t));
position %= (size_shuffled + 1);
parameters[size_shuffled] = parameters[position];
parameters[position] = hold;
}
tarot_perceptron_load (ret, 0, n_parameters, parameters);
free (parameters);
return ret;
}
static int
is_empty_game (const TarotGame * game)
{
size_t i_bid;
size_t n_bids;
TarotBid *bids;
if (tarot_game_get_bids_alloc (game, &n_bids, &bids) != TAROT_GAME_OK)
{
assert (0);
}
for (i_bid = 0; i_bid < n_bids; i_bid++)
{
if (bids[i_bid] != TAROT_PASS)
{
free (bids);
return 0;
}
}
free (bids);
return 1;
}
static void
self_learn (TarotPerceptron *perceptron)
{
uint8_t option = 0;
size_t n_players;
int with_call;
TarotGame *game = tarot_game_alloc ();
TarotGameEvent *setup_event, *deal_event;
uint8_t sub_seed[256];
size_t n_scores;
double metric;
double update;
yarrow256_random (&generator, sizeof (option), &option);
yarrow256_random (&generator, sizeof (sub_seed), sub_seed);
if (option % 4 == 0)
{
n_players = 3;
with_call = 0;
}
else if (option % 4 == 1)
{
n_players = 4;
with_call = 0;
}
else if (option % 4 == 2)
{
n_players = 5;
with_call = 0;
}
else
{
n_players = 5;
with_call = 1;
}
setup_event = tarot_game_event_alloc_setup (n_players, with_call);
if (tarot_game_add_event (game, setup_event) != TAROT_GAME_OK)
{
assert (0);
}
tarot_game_event_free (setup_event);
deal_event =
tarot_game_event_alloc_deal_all_random (n_players, sizeof (sub_seed),
sub_seed);
if (tarot_game_add_event (game, deal_event) != TAROT_GAME_OK)
{
tarot_game_event_free (deal_event);
tarot_game_free (game);
return;
}
tarot_game_event_free (deal_event);
while (tarot_game_get_scores (game, &n_scores, 0, 0, NULL) != TAROT_GAME_OK)
{
TarotCard *dog;
size_t n_dog;
if (tarot_game_can_autoreveal_alloc (game, &n_dog, &dog))
{
TarotGameEvent *dog_event = tarot_game_event_alloc_dog (n_dog, dog);
free (dog);
if (tarot_game_add_event (game, dog_event) != TAROT_GAME_OK)
{
assert (0);
}
tarot_game_event_free (dog_event);
}
else
{
uint8_t behavior_data[sizeof (unsigned long)];
unsigned long behavior;
unsigned long behavior_max = (unsigned long) (-1);
double behavior_dbl;
double max_dbl = behavior_max;
TarotPerceptron *p_generator = perceptron;
double score;
const TarotGameEvent *next;
yarrow256_random (&generator, sizeof (behavior_data),
behavior_data);
memcpy (&behavior, behavior_data, sizeof (behavior));
behavior_dbl = behavior;
if (behavior_dbl / max_dbl < exploration)
{
p_generator = random_perceptron (perceptron);
}
next = tarot_perceptron_best (p_generator, game, &score);
if (tarot_game_add_event (game, next) != TAROT_GAME_OK)
{
assert (0);
}
if (p_generator != perceptron)
{
tarot_perceptron_free (p_generator);
}
}
}
metric = tarot_perceptron_validate (perceptron, game);
if (!is_empty_game (game))
{
update = tarot_perceptron_learn_game (perceptron, game);
fprintf (stderr, "%g\t%g\n", update, metric);
}
tarot_game_free (game);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment