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

Setup learning

parent d6a877ea
......@@ -2,7 +2,7 @@
apt-get update
apt-get upgrade -y
apt-get install -y --no-install-recommends texlive-base texlive-latex-base git ca-certificates texlive-fonts-recommended texlive-generic-recommended autoconf automake gettext autopoint libtool texinfo gnulib autoconf-archive make gcc flex bison gperf emacs org-mode indent check gengetopt help2man libreadline-dev libunistring-dev valgrind autogen guile-2.0 guile-2.0-dev emacs-goodies-el geiser paredit-el elpa-rainbow-delimiters
apt-get install -y --no-install-recommends texlive-base texlive-latex-base git ca-certificates texlive-fonts-recommended texlive-generic-recommended autoconf automake gettext autopoint libtool texinfo gnulib autoconf-archive make gcc flex bison gperf emacs org-mode indent check gengetopt help2man libreadline-dev libunistring-dev valgrind autogen guile-2.0 guile-2.0-dev emacs-goodies-el geiser paredit-el elpa-rainbow-delimiters ess r-base r-cran-foreign r-cran-tibble r-cran-glmnet r-cran-dplyr r-cran-magrittr r-cran-tidyr
SOURCE_TOP=$(pwd)
CLEAN_TOP=$(cd .. && pwd)/clean
......
......@@ -35,7 +35,8 @@ install-html-local: install-dist_htmlDATA
%reldir%/tarot-full.html %reldir%/tarot.texi \
src/scm/tarot/utf8-everywhere.scm $(SCM_TESTS) \
src/scm/tarot.scm: \
src/scm/tarot.scm \
src/tarot-learn: \
%reldir%/tarot.org \
%reldir%/setup.el \
.version
......
......@@ -2977,6 +2977,187 @@ When exporting the features of the previously generated game, we get:
true
#+END_SRC
** Just a bit of machine learning
Now that we know how to generate games with an AI, it is interesting
to start actually learning from these games. For this we will turn to
R, since it has excellent tools. What we are going to do is learn to
predict the final score for a move.
We are going to read an ARFF file from standard input, and write a
model in the standard output.
So, first, let's read an arff file format from standard input.
#+name: r-read-arff-from-stdin
#+caption: Read an ARFF from standard input
#+BEGIN_SRC R :eval no
library ("foreign")
data <- read.arff (file ("stdin"))
score <- data$score
data$score <- NULL
#+END_SRC
Then, we have to encode the features as numbers, because integers are
not appropriate for learning. For instance, factors that have only 2
values (0 and 1) are binary values, they should be replaced by the
numerical feature composed of 0s and 1s.
#+name: r-encode-binary-factor
#+caption: Encode a binary factor
#+BEGIN_SRC R :eval no
value <- as.matrix (as.numeric (as.character (feature)))
colnames (value) <- sprintf ("%s_1", feature_name)
#+END_SRC
For ordinal feature values, we use the following encoding: we make a
binary feature for each modality, and set each binary feature from the
first to the current modality (included) to 1, the others to 0. So
the first modality is always set to 1. This is not optimal, but we do
not care.
#+name: r-encode-ordinal-factor
#+caption: Encode an ordinal factor
#+BEGIN_SRC R :eval no
lev <- sort (as.numeric (levels (feature)))
encode_one <- function (i) {
ret <- array (0, max (lev) - min (lev) + 1)
ret[1:(i - min (lev) + 1)] <- 1
ret
}
value <- t (sapply (as.numeric (as.character (feature)), encode_one))
colnames (value) <- sprintf ("%s_%d", feature_name, (min (lev):max (lev)))
#+END_SRC
Please note that we encode the feature name with the modality value,
so that we will be able to bring the pieces back together at the end,
when writing the updated model.
So the encoding of the whole dataset is:
#+name: r-encode-data
#+caption: Encode the whole data set
#+BEGIN_SRC R :eval no :noweb no-export
encode_either <- function (feature_name) {
feature <- data[[feature_name]]
if (all (sort (levels (feature)) == c ("0", "1"))) {
<<r-encode-binary-factor>>
value
} else {
<<r-encode-ordinal-factor>>
value
}
}
encoded_data <- do.call (cbind, lapply (colnames (data), encode_either))
#+END_SRC
For instance, suppose that we have table [[tbl-to-encode]] to encode. The
encoding is presented in table [[tbl-encoded]].
#+name: tbl-to-encode
#+caption: A table to encode, with a binary factor, a 2-value factor and a multiple value factor
| 1 | 3 | 5 |
| 0 | 2 | 4 |
| 1 | 2 | 3 |
| 1 | 3 | 2 |
| 0 | 2 | 3 |
| 0 | 3 | 4 |
#+BEGIN_SRC R :var input=tbl-to-encode :noweb no-export :results output drawer
data <- as.data.frame (lapply (input, as.factor))
<<r-encode-data>>
cat ("#+name: tbl-encoded\n")
cat ("#+caption: The encoded data\n")
cat (sprintf ("| %s |\n", paste (sapply (colnames (encoded_data), function (n) {
sprintf ("=%s=", n)
}), collapse = " | ")))
cat ("|---\n")
print_line <- function (i) {
sprintf ("| %s |", paste (encoded_data[i,], collapse = " | "))
}
cat (sprintf ("%s", paste (sapply (seq_len (nrow (encoded_data)), print_line),
collapse = "\n")))
#+END_SRC
#+RESULTS:
:RESULTS:
#+name: tbl-encoded
#+caption: The encoded data
| =V1_1= | =V2_2= | =V2_3= | =V3_2= | =V3_3= | =V3_4= | =V3_5= |
|--------+--------+--------+--------+--------+--------+--------|
| 1 | 1 | 1 | 1 | 1 | 1 | 1 |
| 0 | 1 | 0 | 1 | 1 | 1 | 0 |
| 1 | 1 | 0 | 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 | 1 | 1 | 0 |
:END:
Now, we can start learning the weights with a regression model, and
write our new AI model. In order to get meaningful results, and not
just "always pass", we have to penalize the games where everyone
passes. In order to do that, we change each score of 0 to a score of
-5.
#+name: r-fit-model
#+caption: Fitting a linear model on the data set
#+BEGIN_SRC R :eval no
library ("glmnet")
library ("dplyr")
library ("magrittr")
library ("tibble")
library ("tidyr")
score[score == 0] <- -5
coef <- coef (cv.glmnet (encoded_data, score, intercept = FALSE))
model <- (tibble (id = colnames (encoded_data),
coef = coef[2:nrow (coef)])
%>% separate (id, c ("key", "modality"), '_')
%>% group_by (key)
%>% arrange (modality)
%>% summarize (line = paste (coef, collapse = " "))
%>% mutate (line = sprintf ("%s = %s", key, line))
%>% select (line))
#+END_SRC
So the full program is code [[r-learn]]:
#+name: r-learn
#+caption: Learning from the game features
#+BEGIN_SRC R :eval no :tangle ../src/tarot-learn :shebang "#!/usr/bin/env Rscript" :mkdirp t :noweb yes
## This file has been generated; please do not edit
##
## Copyright (C) 2017, 2018 Vivien Kraus <vivien@planete-kraus.eu>
##
## 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, either version 3 of the
## License, or (at your option) any later version.
##
## 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/>.
sink ("/dev/null")
<<r-read-arff-from-stdin>>
<<r-encode-data>>
<<r-fit-model>>
sink ()
writeLines (model$line)
#+END_SRC
** UTF-8 everywhere
:PROPERTIES:
:header-args: :tangle ../src/scm/tarot/utf8-everywhere.scm :noweb no-export :eval no
......
......@@ -16,6 +16,7 @@
# along with this program. If not, see
# <http://www.gnu.org/licenses/>.
dist_bin_SCRIPTS += %reldir%/tarot-learn
lib_LTLIBRARIES += %reldir%/libtarot.la
LIBTAROT_VERSION_INFO=0:0:0
......
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