Commit 2b6570d6 authored by Vivien Kraus's avatar Vivien Kraus
Browse files

Use a multi-layer perceptron

This algorithm will let us do online machine learning.
parent 16cb81d7
......@@ -119,6 +119,8 @@
/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
......
[submodule ".gnulib"]
path = .gnulib
url = git://git.sv.gnu.org/gnulib.git
[submodule "src/libtarot/perceptron/julien"]
path = src/libtarot/perceptron/julien
url = https://framagit.org/gugurumbe/julien.git
.gnulib @ 3ae177f2
Subproject commit de255f87357354e0f2422d9321fe9701b776ead5
Subproject commit 3ae177f2d92775380a9eed65ee2791fdee5c1a35
......@@ -23,6 +23,10 @@ if [ -d .gnulib ]
then
git submodule update --init .gnulib || exit 1
fi
if [ -d src/libtarot/perceptron/julien ]
then
git submodule update --init src/libtarot/perceptron/julien || exit 1
fi
gnulib-tool --libtool \
--import assert git-version-gen getopt-gnu setenv stdalign xalloc setlocale \
gitlog-to-changelog git-version-gen || exit 1
......
......@@ -131,6 +131,7 @@ AM_CONDITIONAL([WITH_GOBJECT], [test "x$WITH_GOBJECT" != "xno"])
AC_SEARCH_LIBS([atan2], [m])
AC_SEARCH_LIBS([sin], [m])
AC_SEARCH_LIBS([cos], [m])
AC_SEARCH_LIBS([sqrt], [m])
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.19.8])
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$PACKAGE", [This needs to be defined in config.h manually...])
......
......@@ -2217,50 +2217,82 @@ Predict the final score when playing each of the /candidate/ events.
Write the results to /score/, starting at /start/, up to /max/
elements. Return the position of the first invalid candidate.
#+end_deftypefun
** The tensor-shaped features for convolutional learning
The memory representation of the tensor is a C array, read line by
line and then page by page.
** Online learning with a multi-layer perceptron
Static learning models are great for the AI, because we learn them at
compile time. However, it may be a good addition to have a learning
algorithm that is able to learn to know a player.
#+attr_texinfo: :options {size_t} tarot_features_cnn_add (const TarotGame *@var{game}, size_t @var{max}, double *@var{data})
#+attr_texinfo: :options {struct} TarotPerceptron
#+begin_deftp
A model that is capable of being learnt in the course of the program
(at the end of a finished game).
#+end_deftp
#+attr_texinfo: :options {TarotPerceptron *} tarot_perceptron_alloc (size_t @var{nhiddenlayers}, const size_t *@var{hiddensizes}, double @var{learningrate})
#+begin_deftypefun
Allocate a new perceptron with the given hyperparameters: the /number
of hidden layers/, their corresponding /sizes/, and the /learning
rate/.
#+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})
#+begin_deftypefun
Delete a /perceptron/ allocated by =tarot_perceptron_alloc= or
=tarot_perceptron_dup=.
#+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
Add the encoded representation of /game/ in /data/. Write at most
/max/ cells in /data/, and return the total number of cells. Please
note that all cells in /data/ are *read*, so it should be set before
calling this function.
Load the internal /parameters/ (size of /nweights/) into /perceptron/.
If /perceptron/ has /n/ parameters, then we skip the first /start/
ones.
#+end_deftypefun
This function does *not* set /data/, but it rather adds the encoding.
This is so that you could use the =calloc= function to allocate the
/data/ pointer, then the function would not overwrite the zeros. This
is theoretically better for the kernel because it can save some memory
pages in case the game is in early stage and so /data/ ends with a lot
of zeros. Although this is still very theoretical, because /data/
will need around a hundred kilobytes.
#+attr_texinfo: :options {size_t} tarot_perceptron_save (const TarotPerceptron *@var{perceptron}, size_t @var{start}, size_t @var{max}, double *@var{parameters})
#+begin_deftypefun
#+attr_texinfo: @deftypefunx {void} tarot_perceptron_save_alloc (const TarotPerceptron *@var{perceptron}, size_t *@var{n}, double **@var{data})
Get the /max/ first internal /parameters/ after skipping the /start/
first and return the total number of parameters. The =_alloc=
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 {void} tarot_features_cnn_alloc (const TarotGame *@var{game}, double **@var{data}, size_t *@var{n})
#+attr_texinfo: :options {double} tarot_perceptron_learn (TarotPerceptron *@var{perceptron}, const TarotGame *@var{state}, const TarotGameEvent *@var{action}, double @var{score})
#+begin_deftypefun
Allocate and compute the CNN features to represent /game/, /data/, and
set the dimension /n/. /data/ is allocated with the =calloc=
function, so that it may be handled more efficiently by the kernel,
and you need to =free= it.
#+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
Since the CNN predicts the score based on a current client-side game,
we can use the =TarotPredictor= API in order to optionally use the
CNN.
#+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 {size_t} tarot_cnn_predictor_construct (size_t @var{maxmem}, char *@var{mem}, size_t *@var{alignment})
#+attr_texinfo: :options {const TarotGameEvent *} tarot_perceptron_best (TarotPerceptron *@var{perceptron}, const TarotGame *@var{state}, double *@var{score})
#+begin_deftypefun
Construct a CNN predictor in-place, at /mem/, which is an array of
/maxmem/ bytes. It is supposed to be correctly aligned if /maxmem/
not null. You can query the alignment and the required size by
calling this function with an empty /mem/ / /maxmem/ and looking at
/alignment/ and the return value.
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 {TarotPredictor *} tarot_cnn_predictor_alloc (void)
#+attr_texinfo: :options {double} tarot_perceptron_validate (TarotPerceptron *@var{perceptron}, const TarotGame *@var{validation_game})
#+begin_deftypefun
Allocate a fresh predictor that will always use the CNN.
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
** High-level game insights
Given the ML building blocks, we have some opportunities for a few
......
......@@ -36,7 +36,8 @@ git-check:
$(git_clone_verbose) ($(GIT) clone $(srcdir) .gitcheck/source/ \
&& $(GIT) submodule update \
&& cd .gitcheck/source \
&& $(GIT) clone ../../.gnulib .gnulib)
&& $(GIT) clone ../../.gnulib .gnulib \
&& $(GIT) clone ../../src/libtarot/perceptron/julien src/libtarot/perceptron/julien)
$(MKDIR_P) .gitcheck/build
(cd .gitcheck/source && $(CI_ENV) ./autogen.sh)
(cd .gitcheck/build && $(CI_ENV) ../source/configure VALAFLAGS="--target-glib=2.50" CFLAGS="-Wall -Wextra -g" --enable-valgrind=yes --enable-silent-rules=yes)
......@@ -115,6 +116,8 @@ git-check:
*/.git/*);; \
*~);; \
*.po);; \
./src/libtarot/perceptron/julien/*) \
>&2 echo "Ignoring julien file $$source";; \
*) \
if $(DIFF) $$source ../../.gitcheck/clean/$$source ; \
then \
......
......@@ -8,7 +8,7 @@ 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-22 18:32+0100\n"
"POT-Creation-Date: 2019-11-24 14:47+0100\n"
"PO-Revision-Date: 2019-11-13 21:16+0100\n"
"Last-Translator: Vivien Kraus <vivien@planete-kraus.eu>\n"
"Language-Team: French\n"
......
......@@ -47,3 +47,4 @@ include %reldir%/features/Makefile.am
include %reldir%/cnn/Makefile.am
include %reldir%/solo/Makefile.am
include %reldir%/guile/Makefile.am
include %reldir%/perceptron/Makefile.am
......@@ -32,6 +32,7 @@
#include <tarot/cnn.h>
#include <tarot/solo.h>
#include <tarot/deprecated.h>
#include <tarot/perceptron.h>
#ifdef __cplusplus
extern "C"
......
......@@ -21,6 +21,7 @@
#include <libxml/parser.h>
#include <stdalign.h>
#include <nettle/yarrow.h>
#include "julien.h"
#ifdef __cplusplus
extern "C"
......@@ -393,6 +394,17 @@ extern "C"
TarotPlayer myself;
};
struct TarotPerceptron
{
size_t n_layers;
size_t *layer_sizes;
double learning_rate;
struct julien *jl;
TarotGameEvent best_event;
unsigned int event_data[78];
double best_score;
};
#ifdef __cplusplus
}
#endif /* __cplusplus */
......
......@@ -82,6 +82,7 @@ TAROT_BOXED_TYPE (Game, game)
TAROT_BOXED_TYPE (GameEvent, game_event)
TAROT_BOXED_TYPE (GameIterator, game_iterator)
TAROT_BOXED_TYPE (Mcts, mcts)
TAROT_BOXED_TYPE (Perceptron, perceptron)
TAROT_BOXED_TYPE (Predictor, predictor)
TAROT_BOXED_TYPE (Solo, solo)
TAROT_BOXED_TYPE (XmlParser, xml_parser)
......
......@@ -47,6 +47,7 @@ extern "C"
GType tarot_game_event_get_type (void);
GType tarot_game_iterator_get_type (void);
GType tarot_mcts_get_type (void);
GType tarot_perceptron_get_type (void);
GType tarot_predictor_get_type (void);
GType tarot_solo_get_type (void);
GType tarot_xml_parser_get_type (void);
......
# 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/>.
AM_CPPFLAGS += -I $(srcdir)/%reldir% -I %reldir% -I $(srcdir)/%reldir%/julien -I %reldir%/julien
tarotinclude_HEADERS += %reldir%/tarot/perceptron.h
src_libtarot_libtarot_la_SOURCES += \
%reldir%/perceptron.c \
%reldir%/tarot/perceptron_private.h \
%reldir%/tarot/perceptron_private_impl.h \
%reldir%/julien/julien.h \
%reldir%/julien/julien.c
INTROSPECTED_SOURCES += %reldir%/perceptron.c %reldir%/julien/julien.c
Subproject commit 5b0c904eeca5b6a32160e6afc2aa1456c37881dc
/* 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 <config.h>
#include <tarot/perceptron_private.h>
#include <tarot/game_private.h>
#include <tarot/features_private.h>
#include <stdlib.h>
#include <assert.h>
#include "xalloc.h"
#include "gettext.h"
#define _(String) dgettext (PACKAGE, String)
TarotPerceptron *
tarot_perceptron_alloc (size_t n_hidden_layers, const size_t *hidden_sizes,
double learning_rate)
{
TarotPerceptron *ret = xmalloc (sizeof (TarotPerceptron));
perceptron_construct (ret, n_hidden_layers, hidden_sizes, learning_rate);
return ret;
}
TarotPerceptron *
tarot_perceptron_dup (const TarotPerceptron * perceptron)
{
TarotPerceptron *ret = xmalloc (sizeof (TarotPerceptron));
perceptron_copy (ret, perceptron);
return ret;
}
void
tarot_perceptron_free (TarotPerceptron * perceptron)
{
if (perceptron != NULL)
{
perceptron_destruct (perceptron);
}
free (perceptron);
}
void
tarot_perceptron_load (TarotPerceptron * perceptron, size_t start,
size_t n_weights, const double *parameters)
{
perceptron_load (perceptron, start, n_weights, parameters);
}
size_t
tarot_perceptron_save (const TarotPerceptron * perceptron, size_t start,
size_t max, double *parameters)
{
return perceptron_save (perceptron, start, max, parameters);
}
void
tarot_perceptron_save_alloc (const TarotPerceptron * perceptron, size_t *n,
double **data)
{
*n = perceptron_save (perceptron, 0, 0, NULL);
*data = xmalloc (*n * sizeof (double));
if (perceptron_save (perceptron, 0, *n, *data) != *n)
{
assert (0);
}
}
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)
{
return perceptron_best (perceptron, state, score);
}
double
tarot_perceptron_validate (TarotPerceptron * perceptron,
const TarotGame * validation_game)
{
return perceptron_validate (perceptron, validation_game);
}
#include <tarot/perceptron_private_impl.h>
#include <tarot/game_private_impl.h>
#include <tarot/features_private_impl.h>
/* 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/>.
*/
#ifndef H_TAROT_PERCEPTRON_INCLUDED
#define H_TAROT_PERCEPTRON_INCLUDED
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
struct TarotGame;
struct TarotPerceptron;
typedef struct TarotPerceptron TarotPerceptron;
/**
* tarot_perceptron_alloc:
* @hidden_sizes: (array length=n_hidden_layers):
*/
TarotPerceptron *tarot_perceptron_alloc (size_t n_hidden_layers, const size_t *hidden_sizes, double learning_rate);
/**
* tarot_perceptron_free:
*/
void tarot_perceptron_free (TarotPerceptron *perceptron);
TarotPerceptron * tarot_perceptron_dup (const TarotPerceptron *perceptron);
/**
* tarot_perceptron_load:
* @parameters: (array length=n_weights):
*/
void tarot_perceptron_load (TarotPerceptron *perceptron, size_t start, size_t n_weights, const double *parameters);
/**
* tarot_perceptron_save:
* @parameters: (array length=max):
*/
size_t tarot_perceptron_save (const TarotPerceptron *perceptron, size_t start, size_t max, double *parameters);
/**
* tarot_perceptron_save_alloc:
* @data: (array length=n) (out):
*/
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 */
#endif /* not H_TAROT_PERCEPTRON_INCLUDED */
/* 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/>.
*/
#ifndef H_TAROT_PERCEPTRON_PRIVATE_INCLUDED
#define H_TAROT_PERCEPTRON_PRIVATE_INCLUDED
#include <tarot_private.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
static void perceptron_construct (TarotPerceptron * perceptron,
size_t n_hidden_layers,
const size_t *hidden_sizes,
double learning_rate);
static void perceptron_destruct (TarotPerceptron * perceptron);
static void perceptron_copy (TarotPerceptron * dest,
const TarotPerceptron * source);
static void perceptron_load (TarotPerceptron * perceptron, size_t start,
size_t n, const double *parameters);
static size_t perceptron_save (const TarotPerceptron * perceptron,
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 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);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* not H_TAROT_PERCEPTRON_PRIVATE_INCLUDED */
This diff is collapsed.
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