195 lines
5.6 KiB
Python
195 lines
5.6 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
__author__ = "Aleksey Lobanov"
|
|
__copyright__ = "Copyright 2016, Aleksey Lobanov"
|
|
__credits__ = ["Aleksey Lobanov"]
|
|
__license__ = "MIT"
|
|
__maintainer__ = "Aleksey Lobanov"
|
|
__email__ = "i@likemath.ru"
|
|
|
|
import sys
|
|
from copy import deepcopy
|
|
from datetime import datetime
|
|
import logging
|
|
|
|
import numpy as np
|
|
|
|
from sklearn.cross_validation import train_test_split
|
|
|
|
import keras
|
|
from keras.models import Sequential
|
|
from keras.layers import Dense, Dropout
|
|
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.cm as cm
|
|
import matplotlib.patches as mpatches
|
|
|
|
|
|
def initLogging():
|
|
logger = logging.getLogger()
|
|
logger.setLevel(logging.DEBUG)
|
|
|
|
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
|
|
fh = logging.FileHandler('neurallife.log')
|
|
fh.setLevel(logging.DEBUG)
|
|
fh.setFormatter(formatter)
|
|
logger.addHandler(fh)
|
|
ch = logging.StreamHandler()
|
|
ch.setLevel(logging.DEBUG)
|
|
ch.setFormatter(formatter)
|
|
logger.addHandler(ch)
|
|
|
|
|
|
def neighbors(field, i, j, fsize):
|
|
nsum = 0
|
|
for l in range(1, 10):
|
|
x = i - 1 + (l - 1) // 3
|
|
y = j - 1 + (l + 2) % 3
|
|
if -1 < x < fsize and -1 < y < fsize and field[x][y] == 1:
|
|
nsum += 1
|
|
nsum -= field[i][j]
|
|
return nsum
|
|
|
|
|
|
def neighbors_cyclo(field, i, j, fsize):
|
|
nsum = 0
|
|
for l in range(9):
|
|
x = (i - 1 + l // 3) % fsize
|
|
y = (j - 1 + l % 3) % fsize
|
|
nsum += field[x][y]
|
|
nsum -= field[i][j]
|
|
return nsum
|
|
|
|
|
|
def nextGen(field, fsize):
|
|
tmp_field = deepcopy(field)
|
|
for i in range(fsize):
|
|
for j in range(fsize):
|
|
neighb = neighbors(tmp_field, i, j, fsize)
|
|
if field[i][j] == 1 and not (2 <= neighb <= 3):
|
|
field[i][j] = 0
|
|
elif field[i][j] == 0 and neighb == 3:
|
|
field[i][j] = 1
|
|
|
|
|
|
def uniqueRows(data):
|
|
uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
|
|
return uniq.view(data.dtype).reshape(-1, data.shape[1])
|
|
|
|
|
|
def generateData(board_size, count=10**5):
|
|
assert(2**(board_size**2) >= count)
|
|
X = np.random.randint(2, size=(int(count*1.2),board_size*board_size))
|
|
X = uniqueRows(X)[:count]
|
|
Y = []
|
|
for row in X:
|
|
tmp_list = row.reshape((board_size,board_size)).tolist()
|
|
nextGen(tmp_list, board_size)
|
|
Y.append(tmp_list)
|
|
return (X, np.asarray(Y).reshape(X.shape))
|
|
|
|
|
|
def loadKeras(path):
|
|
model = keras.models.model_from_json(open(path + '.json').read())
|
|
model.load_weights(path + '.h5')
|
|
model.compile(loss='MSE', optimizer='nadam', metrics=[])
|
|
|
|
logging.debug("Keras model loaded from {}".format(path))
|
|
return model
|
|
|
|
|
|
def saveKeras(model, path):
|
|
json_architecture = model.to_json()
|
|
json_path = path + '.json'
|
|
with open(json_path, 'w') as f:
|
|
f.write(json_architecture)
|
|
weights_path = path + '.h5'
|
|
model.save_weights(weights_path, overwrite=True)
|
|
|
|
|
|
def getModel(n):
|
|
nn = Sequential()
|
|
nn.add(Dense(5*n**2, input_dim=n**2, init="normal", activation="sigmoid"))
|
|
#nn.add(Dropout(0.2)) # model without dropout because need simple
|
|
nn.add(Dense(4*n**2,init="normal", activation="sigmoid"))
|
|
#nn.add(Dropout(0.15))
|
|
nn.add(Dense(n**2,init="normal", activation="sigmoid"))
|
|
nn.compile(loss="MSE", optimizer="nadam", metrics=[])
|
|
return nn
|
|
|
|
|
|
def getAccuracies(model,x_test,y_test):
|
|
preds = model.predict(x_test)
|
|
preds = np.rint(preds).astype("int")
|
|
acc_square = 1.0 * (preds == y_test).sum() / y_test.size
|
|
acc_boards = 0
|
|
for pred, real in zip(preds, y_test):
|
|
if (pred != real).sum() == 0:
|
|
acc_boards += 1
|
|
acc_boards = 1.0 * acc_boards / y_test.shape[0]
|
|
return (acc_square, acc_boards)
|
|
|
|
|
|
|
|
META_PARAMETERS = {
|
|
5:[1600*4**i for i in range(6)],
|
|
6:[1600*4**i for i in range(6)],
|
|
8:[1600*4**i for i in range(6)],
|
|
9:[1600*4**i for i in range(6)],
|
|
10:[1600*4**i for i in range(6)],
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
initLogging()
|
|
|
|
plt.title("Neural Life")
|
|
|
|
plt.xscale("log")
|
|
|
|
plt.xlabel("Train size")
|
|
plt.ylabel("Cell accuracy")
|
|
|
|
plt_patches = []
|
|
for meta_ind,N in enumerate(sorted(META_PARAMETERS.keys())):
|
|
points_x = []
|
|
points_y = []
|
|
for data_size in META_PARAMETERS[N]:
|
|
cur_time = datetime.now()
|
|
|
|
X_train, X_test, Y_train, Y_test = train_test_split(
|
|
*generateData(N, data_size), # X and Y
|
|
test_size=0.6,
|
|
random_state=23
|
|
)
|
|
|
|
train_size = X_train.shape[0]
|
|
|
|
nn = getModel(N)
|
|
|
|
nn.fit(X_train, Y_train, nb_epoch=40, shuffle=False, verbose=0)
|
|
|
|
cellAcc, boardAcc = getAccuracies(nn, X_test, Y_test)
|
|
|
|
points_x.append(train_size)
|
|
points_y.append(cellAcc)
|
|
|
|
logging.info(("for board {}x{} with train size={} cell accuracy is {:.5f}%, " +
|
|
"board accuracy is {:.5f}% and delta with theoretical board accuracy " +
|
|
"is {:.8f}% it takes {}").format(
|
|
N,
|
|
N,
|
|
train_size,
|
|
100 * cellAcc,
|
|
100 * boardAcc,
|
|
100 * abs(boardAcc - cellAcc**(N**2)),
|
|
datetime.now() - cur_time
|
|
))
|
|
saveKeras(nn, "models/model_{}_{}".format(N,train_size))
|
|
plt.plot(points_x, points_y, "o", linestyle="-", color=cm.ocean(meta_ind/len(META_PARAMETERS)))
|
|
plt_patches.append(mpatches.Patch(color=cm.ocean(meta_ind/len(META_PARAMETERS)), label="N={}".format(N)))
|
|
|
|
plt.legend(handles=plt_patches)
|
|
plt.savefig("graphics.svg")
|