[PYTHON] Faisons l'IA d'Othello avec Chainer-Part 1-

introduction

Auparavant, j'ai écrit un article "Faisons une IA à trois yeux avec Pylearn 2". À ce stade, j'ai simplifié le problème et l'ai organisé en trois rangées, mais cette fois, je vais contester l'objectif initial de créer l'IA d'Othello avec un réseau de neurones. Veuillez le pardonner bien qu'il puisse s'agir d'un article avec de nombreux essais et erreurs. Cette fois, j'utiliserai Chainer comme bibliothèque pour le Deep Learning.

L'article se compose de deux parties.

Nous implémenterons l'IA dans la deuxième partie, mais si cela ne fonctionne pas, nous devrons reconsidérer à partir de la configuration du réseau de neurones ... ** → Opération confirmée et conclusion **

Préparation des données des enseignants

J'ai emprunté les données de score à ici. Une grande quantité de données de combat stockées au format ggf. Il est lisible au format texte.

Conception MLP

Tout d'abord, décidez de l'entrée et de la sortie. Puisque l'IA d'Othello sera créée, l'entrée sera l'état de la carte et le prochain mouvement sera la sortie. C'est l'image suivante lorsqu'elle est écrite dans la figure. reversi-nn.png

Plus précisément, il sera déposé dans le réseau neuronal. L'entrée est l'état de la carte, c'est-à-dire un vecteur 8 x 8. Les éléments du vecteur sont «0»: aucun, «1»: noir et «2»: blanc. Il existe 64 types de sortie car elle est classée en fonction de l'endroit où le prochain coup est effectué sur le tableau, mais il y a des cas où elle peut être transmise, il y a donc 65 façons au total. La position de la carte est indiquée par 0 à 63, et Pass est 64. board_num.png

Le reste est la conception de la couche cachée. Pour le moment, le nombre de neurones dans les deux couches est de 100. Comment puis-je théoriquement concevoir ici ... La figure est la suivante.

reversi-nn_02.png

la mise en oeuvre

Voici un programme python qui effectue les opérations suivantes:

build_mlp.py


#!/usr/bin/env python
# coding=utf-8
# 
# 0 : none
# 1 : black
# 2 : white
# 

import sys
import numpy as np
import chainer
from chainer import cuda, Function, gradient_check, report, training, utils, Variable
from chainer import datasets, iterators, optimizers, serializers
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
from chainer.training import extensions

gVec = [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]
gCol = ('A','B','C','D','E','F','G','H')
gRow = ('1','2','3','4','5','6','7','8')

# (1)
class MLP(Chain):
    def __init__(self):
        super(MLP, self).__init__(
                l1=L.Linear(64, 100),
                l2=L.Linear(100, 100),
                l3=L.Linear(100, 65),
        )

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        y = self.l3(h2)
        return y

class Classifier(Chain):
    def __init__(self, predictor):
        super(Classifier, self).__init__(predictor=predictor)

    def __call__(self, x, t):
        y = self.predictor(x)
        loss = F.softmax_cross_entropy(y, t)
        accuracy = F.accuracy(y, t)
        report({'loss': loss, 'accuracy': accuracy}, self)
        return loss


def print_board(board):
    for i in range(8):
        print board[i]

    print ""

def update_board(board, pos_str, clr):
    assert clr!=0, "stone color is not black or white."
    updated_board = [[0 for col in range(8)] for row in range(8)]
    rev_list = []
    pos = pos_str2pos_index(pos_str)
    for v in gVec:
        temp_list = []
        for i in range(1, 8):
            # out of board
            if pos[0]+v[0]*(i+1) > 7 or pos[1]+v[1]*(i+1) > 7 or\
               pos[0]+v[0]*(i+1) < 0 or pos[1]+v[1]*(i+1) < 0:
                continue

            if board[pos[0]+v[0]*i][pos[1]+v[1]*i] == (clr % 2 + 1):
                temp_list.append([pos[0]+v[0]*i, pos[1]+v[1]*i])

                if board[pos[0]+v[0]*(i+1)][pos[1]+v[1]*(i+1)] == clr:
                    for j in temp_list:
                        rev_list.append(j)

                    break
            else:
                break

    rev_list.append(pos)  # put stone at pos
    assert board[pos[0]][pos[1]] == 0, "put position is not empty."
    print "rev_list = " + str(rev_list)
    for i in range(0, 8):
        for j in range(0, 8):
            if [i, j] in rev_list:
                updated_board[i][j] = clr
            else:
                updated_board[i][j] = board[i][j]

    return updated_board

def who_is_winner(board):
    # ret : 0  draw
    #       1  black win
    #       2  white win
    ret = 0
    score_b = 0
    score_w = 0
    for i in range(0, 8):
        for j in range(0, 8):
            if board[i][j] == 1:
                score_b += 1
            elif board[i][j] == 2:
                score_w += 1

    if score_b > score_w:
        ret = 1
    elif score_b < score_w:
        ret = 2

    print "Black vs White : " + str(score_b) + " vs " + str(score_w)
    return ret

def pos_str2pos_index(pos_str):
    pos_index = []
    for i, c in enumerate(gRow):
        if pos_str[1] == c:
            pos_index.append(i)
    
    for i, c in enumerate(gCol):
        if pos_str[0] == c:
            pos_index.append(i)


    return pos_index

def pos_str2pos_index_flat(pos_str):
    pos_index = pos_str2pos_index(pos_str)
    index = pos_index[0] * 8 + pos_index[1]
    return index


#==== Main ====#
record_X = []    # MLP input (board list)
record_y = []    # MLP output(class(0-64) list)
temp_X = []
temp_y = []
temp2_X = []
temp2_y = []
board = []
row = []

argv = sys.argv
argc = len(argv)

if argc != 3:
    print 'Usage'
    print '    python ' + str(argv[0]) + ' <record_filename> <type>'
    print '        type : black'
    print '               black_win'
    print '               white'
    print '               white_win'
    quit()

# check type
build_type = ''
for t in ['black', 'black_win', 'white', 'white_win']:
    if argv[2] == t:
        build_type = t

if build_type == '':
    print 'record type is illegal.'
    quit()

#(2)-- load record --#
f = open(argv[1], "r")
line_cnt = 1
for line in f:
    print 'Line Count = ' + str(line_cnt)
    idx = line.find("BO[8")
    if idx == -1:
        continue

    idx += 5
    # make board initial state
    for i in range(idx, idx+9*8):
        if line[i] == '-':
            row.append(0)
        elif line[i] == 'O':
            row.append(2)
        elif line[i] == '*':
            row.append(1)

        if (i-idx)%9 == 8:
            board.append(row)
            row = []
            if len(board) == 8:
                break

    row = []
    print_board(board)
    # record progress of game
    i = idx+9*8+2
    while line[i] != ';':
        if (line[i] == 'B' or line[i] == 'W') and line[i+1] == '[':
            temp_X.append(board)
            pos_str = line[i+2] + line[i+3]
            if pos_str == "pa":    # pass
                temp_y.append(64)
                # board state is not change
                print_board(board)
            else:
                if line[i] == 'B':
                    clr = 1
                elif line[i] == 'W':
                    clr = 2
                else:
                    clr = 0
                    assert False, "Stone Color is illegal."

                pos_index_flat = pos_str2pos_index_flat(pos_str)
                temp_y.append(pos_index_flat)
                board = update_board(board, pos_str, clr)

            if (line[i] == 'B' and (build_type == 'black' or build_type == 'black_win')) or \
               (line[i] == 'W' and (build_type == 'white' or build_type == 'white_win')):
                temp2_X.append(temp_X[0])
                temp2_y.append(temp_y[0])
                print 'X = '
                print_board(temp_X[0])
                print 'y = ' + str(temp_y[0]) + ' (' + \
                               str(pos_str2pos_index(pos_str)) + ') ' + \
                               '(' + pos_str + ')'
                print ''

            temp_X = []
            temp_y = []
        
        i += 1

    print "End of game"
    print_board(board)

    winner = who_is_winner(board)
    if (winner == 1 and build_type == 'black_win') or \
       (winner == 2 and build_type == 'white_win') or \
       build_type == 'black' or build_type == 'white':
        record_X.extend(temp2_X)
        record_y.extend(temp2_y)

    board = []
    temp2_X = []
    temp2_y = []
    line_cnt += 1


#(3)-- MLP model and Training --#
X = np.array(record_X, dtype=np.float32)
y = np.array(record_y, dtype=np.int32)

train = datasets.TupleDataset(X, y)
train_iter = iterators.SerialIterator(train, batch_size=100)

model = Classifier(MLP())
optimizer = optimizers.SGD()
optimizer.setup(model)

updater = training.StandardUpdater(train_iter, optimizer)
trainer = training.Trainer(updater, (1000, 'epoch'), out='result')

trainer.extend(extensions.ProgressBar())
trainer.run()

#(4)-- save model --#
serializers.save_npz('reversi_model.npz', model)


#(5)-- prediction example --#
X1_ = [[[0,0,0,0,0,0,0,0],\
        [0,0,0,0,0,0,0,0],\
        [0,0,0,0,0,0,0,0],\
        [0,0,0,2,1,0,0,0],\
        [0,0,0,1,2,0,0,0],\
        [0,0,0,0,0,0,0,0],\
        [0,0,0,0,0,0,0,0],\
        [0,0,0,0,0,0,0,0]]]

X1 = np.array(X1_, dtype=np.float32)
y1 = F.softmax(model.predictor(X1))
print "X1 = "
print_board(X1[0])
print "y1 = " + str(y1.data.argmax(1)) + '\n' 

X2_ = [[[0,0,0,0,0,0,0,0],\
        [0,0,0,0,0,0,0,0],\
        [0,0,2,2,2,0,0,0],\
        [0,0,2,1,1,1,0,0],\
        [0,2,2,2,1,1,0,0],\
        [0,0,2,1,0,0,0,0],\
        [0,0,0,0,0,0,0,0],\
        [0,0,0,0,0,0,0,0]]]

X2 = np.array(X2_, dtype=np.float32)
y2 = F.softmax(model.predictor(X2))
print "X2 = "
print_board(X2[0])
print "y2 = " + str(y2.data.argmax(1)) + '\n'

Je vais vous expliquer brièvement. Veuillez le lire en correspondance avec (1) à (5) dans le code. (1) Définition de Chainer MLP Définissez la structure de MLP dans la classe MLP. Comme mentionné ci-dessus, il est défini comme entrée = 8x8 = 64, sortie = 65 et 2 couches cachées (100 neurones). La fonction d'activation et la structure des couches sont définies dans \ __ call \ __. La classe Classifier définit la classification Softmax.

(2) Lecture et conversion d'échecs C'est une longue boucle, mais elle lit la partition et la convertit en entrée (8x8) et en sortie (0-64). Ce qui était difficile dans la programmation était le plus difficile ici ...

(3) Formation du modèle MLP Entrez le score converti dans le modèle pour la formation. L'entrée et la sortie peuvent être définies au format de tableau Numpy avec dataset.TupleDataset. À ce stade, faites attention au type. Le reste est comme le code source

tenir. La formation est exécutée avec trainer.run.

(4) Sauvegarde du modèle Enregistrez le modèle entraîné. Format NPZ. Il sera utilisé lors de sa mise en œuvre dans le jeu Othello dans la deuxième partie.

(5) Test simple Essayez de dériver correctement le coup suivant avec le modèle entraîné.

Un exemple du résultat de l'exécution est le suivant. Nous utilisons "Othello.01e4.ggf" comme données de l'enseignant et créons l'IA uniquement avec les données gagnantes du premier coup (noir).

$ python build_mlp.py Othello.01e4.ggf black_win
...

X1 = 
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  0.  2.  1.  0.  0.  0.]
[ 0.  0.  0.  1.  2.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]

y1 = [37]

X2 = 
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  2.  2.  2.  0.  0.  0.]
[ 0.  0.  2.  1.  1.  1.  0.  0.]
[ 0.  2.  2.  2.  1.  1.  0.  0.]
[ 0.  0.  2.  1.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.  0.  0.  0.]

y2 = [25]

J'écris de ne pas utiliser le GPU pour la formation. Je pense que cela prendra environ une heure sur un mauvais PC pour fonctionner. Puisque la barre de progression est affichée par la fonction de Chainer, veuillez vous demander si elle sera terminée dans un temps décent si vous le faites dans votre propre environnement. De plus, dans l'exemple, Othello.01e4.ggf est utilisé, mais comme il y avait deux données (lignes 85 et 2699) qui s'écartaient du format attendu, je les ai supprimées et exécutées.

Si vous regardez l'exemple de prédiction affiché dans les résultats, vous pouvez voir que l'IA atteint: prediction-example.png

Il n'y a que deux façons, mais il semble qu'un simple test ait réussi à dériver le coup suivant. Dans la Partie 2, nous allons effectivement implémenter ce modèle entraîné en tant qu'IA dans le jeu Othello et vérifier le fonctionnement d'Othello. ** → Opération confirmée et conclusion atteinte. **

référence

Recommended Posts

Faisons l'IA d'Othello avec Chainer-Part 1-
Faisons l'IA d'Othello avec Chainer-Part 2-
Faisons Othello avec wxPython
Faisons une IA à trois yeux avec Pylearn 2
Créez Puyopuyo AI avec Python
Faisons une interface graphique avec python.
Faisons une rupture de bloc avec wxPython
Faisons un graphe avec python! !!
Faisons un spacon avec xCAT
Faisons un jeu de shiritori avec Python
Faisons la voix lentement avec Python
Faisons un langage simple avec PLY 1
[Python] Rendons matplotlib compatible avec le japonais
Créez un framework Web avec Python! (1)
Faisons un bot Twitter avec Python!
Créez un framework Web avec Python! (2)
Remplaçons UWSC par Python (5) Faisons un robot
Créer des couches Lambda avec Lambda
Créez un Yuma avec Discord.py
[Jouons avec Python] Créer un livre de comptes de ménage
Essayez de créer un jeu simple avec Python 3 et iPhone
Visualisez les réclamations avec l'IA
Facilitons un peu la gestion des dépendances avec pip
Créons une application Mac avec Tkinter et py2app
Essayez de créer une grille sphérique avec Rhinoceros / Grasshopper / GHPython
[Super facile] Faisons un LINE BOT avec Python.
Créer des diapositives avec iPython
J'ai essayé de faire d'Othello AI que j'ai appris 7,2 millions de mains par apprentissage profond avec Chainer
Une histoire de compétition avec un ami dans Othello AI Preparation
Faisons un diagramme sur lequel on peut cliquer avec IPython
Jouons avec la 4e dimension 4e
Faisons un robot Discord.
Jouons avec les données Amedas - Partie 1
Créez des boutons de type SF avec Kivy
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Introduction ~
Faisons un plugin Errbot
Jouons avec les données Amedas - Partie 4
Rendre avec la syntaxe facile
Faites une loterie avec Python
Écrivons python avec cinema4d.
Faisons R-CNN avec Sklearn-theano
Jouons avec les données Amedas - Partie 3
Jouons avec les données Amedas - Partie 2
Construisons git-cat avec Python
Othello fait avec python (comme GUI)
Faire un feu avec kdeplot
Créer un chatbot Slack avec Errbot
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Implémentation ~
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 1
Créons une IA à trois voies avec Pylearn2 --Save and load model -
Faisons un ordinateur de vélo avec Raspberry Pi Zero (W, WH)
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 2
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 3
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 4
Faisons une discussion WEB en utilisant WebSocket avec AWS sans serveur (Python)!
Faisons une chemise IoT avec Lambda, Kinesis, Raspberry Pi [Partie 1]
J'ai essayé de créer Othello AI avec tensorflow sans comprendre la théorie de l'apprentissage automatique ~ Battle Edition ~