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 **
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.
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.
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.
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.
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:
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. **
Recommended Posts