[PYTHON] Machen wir Othellos KI mit Chainer-Teil 1-

Einführung

Zuvor schrieb ich einen Artikel "Machen wir mit Pylearn 2 eine dreiäugige KI". Zu diesem Zeitpunkt habe ich das Problem einfach gemacht und in drei Reihen angeordnet, aber dieses Mal werde ich das ursprüngliche Ziel in Frage stellen, Othellos KI mit einem neuronalen Netzwerk zu erstellen. Bitte verzeihen Sie es, obwohl es ein Artikel mit vielen Versuchen und Fehlern sein kann. Dieses Mal werde ich Chainer als Bibliothek für Deep Learning verwenden.

Der Artikel besteht aus zwei Teilen.

Wir werden AI im zweiten Teil implementieren, aber wenn es nicht funktioniert, müssen wir die Konfiguration des neuronalen Netzwerks überdenken ... ** → Operation bestätigt und Abschluss **

Vorbereitung der Lehrerdaten

Ich habe die Score-Daten von [hier] ausgeliehen (https://www.skatgame.net/mburo/ggs/game-archive/Othello/). Eine große Menge von Kampfdaten, die im ggf-Format gespeichert sind. Es ist im Textformat lesbar.

MLP-Design

Entscheiden Sie zuerst die Eingabe und Ausgabe. Da die KI von Othello erstellt wird, befindet sich die Eingabe im Status der Karte und der nächste Zug ist die Ausgabe. Es ist das folgende Bild, wenn es in der Abbildung geschrieben ist. reversi-nn.png

Insbesondere wird es in das neuronale Netz fallen gelassen. Die Eingabe ist der Zustand der Karte, dh ein 8 x 8-Vektor. Die Elemente des Vektors sind '0': keine, '1': schwarz und '2': weiß. Es gibt 64 Arten von Ausgaben, da sie danach klassifiziert sind, wo auf dem Brett der nächste Zug ausgeführt wird. Es gibt jedoch Fälle, in denen sie übergeben werden können, sodass insgesamt 65 Möglichkeiten bestehen. Die Boardposition wird durch 0 bis 63 angezeigt und Pass ist 64. board_num.png

Der Rest ist das Design der verborgenen Schicht. Derzeit beträgt die Anzahl der Neuronen in beiden Schichten 100. Wie kann ich hier theoretisch gestalten ... Die Figur ist wie folgt.

reversi-nn_02.png

Implementierung

Hier ist ein Python-Programm, das Folgendes ausführt:

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'

Ich werde es kurz erklären. Bitte lesen Sie es in Übereinstimmung mit (1) bis (5) im Code. (1) Ketten-MLP-Definition Definieren Sie die Struktur von MLP in der MLP-Klasse. Wie oben erwähnt, ist es definiert als Eingabe = 8 × 8 = 64, Ausgabe = 65 und 2 verborgene Schichten (100 Neuronen). Die Aktivierungsfunktion und die Schichtstruktur sind in \ __ call \ __ definiert. Die Classifier-Klasse definiert die Softmax-Klassifizierung.

(2) Lesen und Umwandlung von Schach Es ist eine lange Schleife, aber sie liest die Partitur und konvertiert sie in Eingabe (8x8) und Ausgabe (0-64). Was an der Programmierung schwierig war, war hier am schwierigsten ...

(3) Schulung des MLP-Modells Geben Sie die konvertierte Punktzahl für das Training in das Modell ein. Sowohl Eingabe als auch Ausgabe können mit Dataset.TupleDataset im Numpy-Array-Format festgelegt werden. Achten Sie zu diesem Zeitpunkt auf den Typ. Der Rest ist wie der Quellcode

halten. Das Training wird mit Trainer.Run durchgeführt.

(4) Speichern des Modells Speichern Sie das trainierte Modell. NPZ-Format. Es wird verwendet, wenn es im zweiten Teil im Othello-Spiel implementiert wird.

(5) Einfacher Test Versuchen Sie, den nächsten Schritt mit dem trainierten Modell korrekt abzuleiten.

Ein Beispiel für das Ausführungsergebnis ist wie folgt. Wir verwenden'Othello.01e4.ggf'as Lehrerdaten und erstellen KI nur mit den Gewinndaten des ersten Zuges (schwarz).

$ 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]

Ich schreibe, um GPU nicht für das Training zu verwenden. Ich denke, es wird ungefähr eine Stunde dauern, bis ein schlechter PC läuft. Da der Fortschrittsbalken von der Funktion Chainer angezeigt wird, prüfen Sie bitte, ob er in angemessener Zeit abgeschlossen sein wird, wenn Sie dies in Ihrer eigenen Umgebung tun. Im Beispiel wird auch Othello.01e4.ggf verwendet. Da jedoch zwei Daten (Zeilen 85 und 2699) vom erwarteten Format abweichen, habe ich sie gelöscht und ausgeführt.

Wenn Sie sich das in den Ergebnissen angezeigte Vorhersagebeispiel ansehen, können Sie sehen, dass die KI trifft: prediction-example.png

Es gibt nur zwei Möglichkeiten, aber es scheint, dass ein einfacher Test den nächsten Schritt erfolgreich abgeleitet hat. In Teil 2 werden wir dieses trainierte Modell tatsächlich als KI im Othello-Spiel implementieren und die Funktionsweise von Othello überprüfen. ** → Operation bestätigt und Schlussfolgerung wurde gezogen. **

Referenz

Recommended Posts

Machen wir Othellos KI mit Chainer-Teil 1-
Machen wir Othellos KI mit Chainer-Teil 2-
Machen wir Othello mit wxPython
Machen wir mit Pylearn 2 eine dreiäugige KI
Erstelle Puyopuyo AI mit Python
Lassen Sie uns eine GUI mit Python erstellen.
Machen wir einen Blockbruch mit wxPython
Lassen Sie uns ein Diagramm mit Python erstellen! !!
Machen wir mit xCAT einen Spacon
Lassen Sie uns mit Python ein Shiritori-Spiel machen
Lassen Sie uns mit Python langsam sprechen
Lassen Sie uns mit PLY 1 eine einfache Sprache erstellen
[Python] Lassen Sie uns matplotlib mit Japanisch kompatibel machen
Erstellen Sie ein Webframework mit Python! (1)
Machen wir einen Twitter-Bot mit Python!
Erstellen Sie ein Webframework mit Python! (2)
Ersetzen wir UWSC durch Python (5) Machen wir einen Roboter
Bilden Sie Lambda-Schichten mit Lambda
Machen Sie eine Yuma mit Discord.py
[Lass uns mit Python spielen] Ein Haushaltsbuch erstellen
Versuchen Sie, ein einfaches Spiel mit Python 3 und iPhone zu erstellen
Visualisieren Sie Ansprüche mit AI
Lassen Sie uns das Abhängigkeitsmanagement mit pip etwas einfacher machen
Lassen Sie uns eine Mac-App mit Tkinter und py2app erstellen
Versuchen Sie, mit Rhinoceros / Grasshopper / GHPython ein sphärisches Gitter zu erstellen
[Super einfach] Machen wir einen LINE BOT mit Python.
Erstellen Sie Folien mit iPython
Ich habe versucht, Othello AI zu machen, dass ich 7,2 Millionen Hände durch tiefes Lernen mit Chainer gelernt habe
Eine Geschichte über den Wettbewerb mit einem Freund in Othello AI Preparation
Lassen Sie uns ein Diagramm erstellen, auf das mit IPython geklickt werden kann
Spielen wir mit der 4. Dimension der 4. Dimension
Machen wir einen Discord Bot.
Spielen wir mit Amedas Daten - Teil 1
Machen Sie mit Kivy SF-ähnliche Knöpfe
Ich habe versucht, Othello AI mit Tensorflow zu machen, ohne die Theorie des maschinellen Lernens zu verstehen ~ Einführung ~
Lassen Sie uns ein Errbot-Plugin erstellen
Spielen wir mit Amedas Daten - Teil 4
Machen Sie es mit der Syntax einfach
Machen Sie eine Lotterie mit Python
Schreiben wir Python mitinema4d.
Lassen Sie uns R-CNN mit Sklearn-theano machen
Spielen wir mit Amedas Daten - Teil 3
Spielen wir mit Amedas Daten - Teil 2
Lassen Sie uns Git-Cat mit Python bauen
Othello gemacht mit Python (wie GUI)
Machen Sie ein Feuer mit kdeplot
Machen Sie Slack Chatbot mit Errbot
Ich habe versucht, Othello AI mit Tensorflow zu erstellen, ohne die Theorie des maschinellen Lernens zu verstehen ~ Implementierung ~
Lassen Sie uns eine WEB-Anwendung für das Telefonbuch mit Flasche Teil 1 erstellen
Lassen Sie uns mit Pylearn2 eine Drei-Wege-KI erstellen - Modell speichern und laden -
Machen wir einen Fahrradcomputer mit Raspberry Pi Zero (W, WH)
Lassen Sie uns eine WEB-Anwendung für das Telefonbuch mit Flasche Teil 2 erstellen
Lassen Sie uns eine WEB-Anwendung für das Telefonbuch mit Flasche Teil 3 erstellen
Lassen Sie uns eine WEB-Anwendung für das Telefonbuch mit Flasche Teil 4 erstellen
Lassen Sie uns einen Web-Chat mit WebSocket mit AWS serverless (Python) durchführen!
Machen wir ein IoT-Shirt mit Lambda, Kinesis, Raspberry Pi [Teil 1]
Ich habe versucht, Othello AI mit Tensorflow zu erstellen, ohne die Theorie des maschinellen Lernens zu verstehen ~ Battle Edition ~