class ChessModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None self.queue = None def get_api_queue(self): if self.queue is None: self.queue = ChessModelAPI(self.config, self).prediction_queue return self.queue def build(self): mc = self.config.model in_x = x = Input((101, 8, 8)) # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) for _ in range(mc.res_layer_num): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=1, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="chess_model") def _build_residual_block(self, x): mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Add()([in_x, x]) x = Activation("relu")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: try: logger.debug("loading model from server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) ftp_connection.retrbinary("RETR model_best_config.json", open(config_path, 'wb').write) ftp_connection.retrbinary("RETR model_best_weight.h5", open(weight_path, 'wb').write) ftp_connection.quit() except: pass from tensorflow import get_default_graph if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.graph = get_default_graph() self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") #print(self.model.summary) return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path, weight_path): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}") mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: try: logger.debug("saving model to server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) fh = open(config_path, 'rb') ftp_connection.storbinary('STOR model_best_config.json', fh) fh.close() fh = open(weight_path, 'rb') ftp_connection.storbinary('STOR model_best_weight.h5', fh) fh.close() ftp_connection.quit() except: pass
class CChessModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None self.n_labels = len(ActionLabelsRed) self.graph = None self.api = None def build(self): mc = self.config.model in_x = x = Input((28, 10, 9)) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_first_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="input_conv-" + str(mc.cnn_first_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="input_batchnorm")(x) x = Activation("relu", name="input_relu")(x) for i in range(mc.res_layer_num): x = self._build_residual_block(x, i + 1) res_out = x # for policy output x = Conv2D(filters=32, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="policy_conv-1-2")(res_out) x = BatchNormalization(axis=1, name="policy_batchnorm")(x) x = Activation("relu", name="policy_relu")(x) x = Flatten(name="policy_flatten")(x) policy_out = Dense(self.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=4, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="value_conv-1-4")(res_out) x = BatchNormalization(axis=1, name="value_batchnorm")(x) x = Activation("relu", name="value_relu")(x) x = Flatten(name="value_flatten")(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu", name="value_dense")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="cchess_model") self.graph = tf.get_default_graph() def _build_residual_block(self, x, index): mc = self.config.model in_x = x res_name = "res" + str(index) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv1-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name=res_name + "_batchnorm1")(x) x = Activation("relu", name=res_name + "_relu1")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv2-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="res" + str(index) + "_batchnorm2")(x) x = Add(name=res_name + "_add")([in_x, x]) x = Activation("relu", name=res_name + "_relu2")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() return None def load(self, config_path, weight_path): if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) self.graph = tf.get_default_graph() logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug(f"model files does not exist at {config_path} and {weight_path}") return False def save(self, config_path, weight_path): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}") def get_pipes(self, need_reload=True): if self.api is None: self.api = CChessModelAPI(self.config, self) self.api.start(need_reload) return self.api.get_pipe(need_reload) def close_pipes(self): if self.api is not None: self.api.close() self.api = None
class ChessModel: """ The model which can be trained to take observations of a game of chess and return value and policy predictions. Attributes: :ivar Config config: configuration to use :ivar Model model: the Keras model to use for predictions :ivar digest: basically just a hash of the file containing the weights being used by this model :ivar ChessModelAPI api: the api to use to listen for and then return this models predictions (on a pipe). """ def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None self.api = None def get_pipes(self, num=1): """ Creates a list of pipes on which observations of the game state will be listened for. Whenever an observation comes in, returns policy and value network predictions on that pipe. :param int num: number of pipes to create :return str(Connection): a list of all connections to the pipes that were created """ if self.api is None: self.api = ChessModelAPI(self) self.api.start() return [self.api.create_pipe() for _ in range(num)] def build(self): """ Builds the full Keras model and stores it in self.model. """ mc = self.config.model in_x = x = Input((18, 8, 8)) # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_first_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="input_conv-" + str(mc.cnn_first_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="input_batchnorm")(x) x = Activation("relu", name="input_relu")(x) for i in range(mc.res_layer_num): x = self._build_residual_block(x, i + 1) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="policy_conv-1-2")(res_out) x = BatchNormalization(axis=1, name="policy_batchnorm")(x) x = Activation("relu", name="policy_relu")(x) x = Flatten(name="policy_flatten")(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=4, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="value_conv-1-4")(res_out) x = BatchNormalization(axis=1, name="value_batchnorm")(x) x = Activation("relu", name="value_relu")(x) x = Flatten(name="value_flatten")(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu", name="value_dense")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="chess_model") def _build_residual_block(self, x, index): mc = self.config.model in_x = x res_name = "res" + str(index) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv1-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name=res_name + "_batchnorm1")(x) x = Activation("relu", name=res_name + "_relu1")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv2-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="res" + str(index) + "_batchnorm2")(x) x = Add(name=res_name + "_add")([in_x, x]) x = Activation("relu", name=res_name + "_relu2")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): """ :param str config_path: path to the file containing the entire configuration :param str weight_path: path to the file containing the model weights :return: true iff successful in loading """ mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: try: logger.debug("loading model from server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) ftp_connection.retrbinary("RETR model_best_config.json", open(config_path, 'wb').write) ftp_connection.retrbinary("RETR model_best_weight.h5", open(weight_path, 'wb').write) ftp_connection.quit() except: pass if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.model._make_predict_function() self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path, weight_path): """ :param str config_path: path to save the entire configuration to :param str weight_path: path to save the model weights to """ logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}") mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: try: logger.debug("saving model to server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) fh = open(config_path, 'rb') ftp_connection.storbinary('STOR model_best_config.json', fh) fh.close() fh = open(weight_path, 'rb') ftp_connection.storbinary('STOR model_best_weight.h5', fh) fh.close() ftp_connection.quit() except: pass
class GameModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None def build(self): mc = self.config.model in_x = x = Input((4, 3, 3)) # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) for _ in range(mc.res_layer_num): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=4, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=16, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="game_model") #Print Model to PNG ## from keras.utils import plot_model ## plot_model(self.model, to_file='model.png', show_shapes=True) def _build_residual_block(self, x): mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Add()([in_x, x]) x = Activation("relu")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): if os.path.exists(config_path) and os.path.exists(weight_path): print("loading model from ", config_path) with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) print("loaded model digest = ", self.digest) return True else: print("model files does not exist at ", config_path, " and ", weight_path) return False def save(self, config_path, weight_path): print("save model to ", config_path) with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) print("saved model digest ", self.digest)
class ReversiModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None def build(self): mc = self.config.model in_x = x = Input((2, 8, 8)) # [own(8x8), enemy(8x8)] # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) for _ in range(mc.res_layer_num): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) # no output for 'pass' policy_out = Dense(8 * 8, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=1, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="reversi_model") def _build_residual_block(self, x): mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Add()([in_x, x]) x = Activation("relu")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path, weight_path): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}")
class ReversiModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None def build(self): mc = self.config.model in_x = x = Input(self.config.model.input_size) # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) for _ in range(mc.res_layer_num): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) policy_out = Dense(self.config.model.policy_size, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=1, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="reversi_model") def _build_residual_block(self, x): mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Add()([in_x, x]) x = Activation("relu")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() @staticmethod def load_step_info(config_path): if os.path.exists(config_path): with open(config_path, "rt") as f: try: config = json.load(f) return int(config['steps']) except JSONDecodeError: return None except ValueError: return None else: return None def load(self, config_path, weight_path): if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: config = json.load(f) if 'weight_digest' in config: exp_digest = config['weight_digest'] act_digest = self.fetch_digest(weight_path) if exp_digest != act_digest: logger.debug( f"exp weight digest {exp_digest}, act {act_digest}" ) return None try: steps = int(config['steps']) except ValueError: steps = None del config['steps'] self.model = Model.from_config(config) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return steps else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return None def save(self, config_path, weight_path, steps=0): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) config = self.model.get_config() config['steps'] = steps config['weight_digest'] = self.digest json.dump(config, f) logger.debug(f"saved model digest {self.digest}")
class ChessModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None self.api = None def get_pipes(self, num=1): if self.api is None: self.api = ChessModelAPI(self.config, self) self.api.start() return [self.api.get_pipe() for _ in range(num)] def build(self): mc = self.config.model # in_x = x = Input((18, 8, 8)) in_x = x = Input((14, 10, 9)) # change to CC # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_first_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="input_conv-" + str(mc.cnn_first_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="input_batchnorm")(x) x = Activation("relu", name="input_relu")(x) for i in range(mc.res_layer_num): x = self._build_residual_block(x, i + 1) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="policy_conv-1-2")(res_out) x = BatchNormalization(axis=1, name="policy_batchnorm")(x) x = Activation("relu", name="policy_relu")(x) x = Flatten(name="policy_flatten")(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=4, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="value_conv-1-4")(res_out) x = BatchNormalization(axis=1, name="value_batchnorm")(x) x = Activation("relu", name="value_relu")(x) x = Flatten(name="value_flatten")(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu", name="value_dense")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="chess_model") def _build_residual_block(self, x, index): mc = self.config.model in_x = x res_name = "res" + str(index) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv1-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name=res_name + "_batchnorm1")(x) x = Activation("relu", name=res_name + "_relu1")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv2-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="res" + str(index) + "_batchnorm2")(x) x = Add(name=res_name + "_add")([in_x, x]) x = Activation("relu", name=res_name + "_relu2")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: try: logger.debug("loading model from server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) ftp_connection.retrbinary("RETR model_best_config.json", open(config_path, 'wb').write) ftp_connection.retrbinary("RETR model_best_weight.h5", open(weight_path, 'wb').write) ftp_connection.quit() except: pass if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug("loading model from %s" % (config_path)) with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.model._make_predict_function() self.digest = self.fetch_digest(weight_path) logger.debug("loaded model digest = %s" % (self.digest)) return True else: logger.debug("model files does not exist at %s and %s" % (config_path, weight_path)) return False def save(self, config_path, weight_path): logger.debug("saving model to %s" % (config_path)) print('debug-3') with open(config_path, "wt") as f: print('debug-2') json.dump(self.model.get_config(), f) print('debug-1') self.model.save_weights(weight_path) print('debug-0') self.digest = self.fetch_digest(weight_path) logger.debug("saved model digest %s" % (self.digest)) print('debug') mc = self.config.model resources = self.config.resource print('debug2') if mc.distributed and config_path == resources.model_best_config_path: try: print('debug3') logger.debug("saving model to server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) fh = open(config_path, 'rb') ftp_connection.storbinary('STOR model_best_config.json', fh) fh.close() fh = open(weight_path, 'rb') ftp_connection.storbinary('STOR model_best_weight.h5', fh) fh.close() ftp_connection.quit() except: print('debug4') pass
class ChessModel: """ The model which can be trained to take observations of a game of chess and return value and policy predictions. Attributes: :ivar Config config: configuration to use :ivar Model model: the Keras model to use for predictions :ivar digest: basically just a hash of the file containing the weights being used by this model :ivar ChessModelAPI api: the api to use to listen for and then return this models predictions (on a pipe). """ def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None self.api = None def get_pipes(self, num=1): """ Creates a list of pipes on which observations of the game state will be listened for. Whenever an observation comes in, returns policy and value network predictions on that pipe. :param int num: number of pipes to create :return str(Connection): a list of all connections to the pipes that were created """ if self.api is None: self.api = ChessModelAPI(self) self.api.start() return [self.api.create_pipe() for _ in range(num)] def build(self): """ Builds the full Keras model and stores it in self.model. Why biases are set to False : https://github.com/kuangliu/pytorch-cifar/issues/52 """ logger.debug(f"Building new model.") mc = self.config.model in_x = x = Input((18, 8, 8)) # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_first_filter_size, padding="same", data_format="channels_first", use_bias=False, name="input_conv-" + str(mc.cnn_first_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="input_batchnorm")(x) x = Activation("relu", name="input_relu")(x) for i in range(mc.common_res_layer_num): x = self._build_residual_block(x, i + 1) x_policy = x x_value = x # for policy output for i in range(mc.policy_res_layer_num): x_policy = self._build_residual_block( x_policy, mc.common_res_layer_num + i + 1) x = Conv2D(filters=32, kernel_size=1, data_format="channels_first", use_bias=False, name="policy_conv-1-2")(x_policy) x = BatchNormalization(axis=1, name="policy_batchnorm")(x) x = Activation("relu", name="policy_relu")(x) x = Flatten(name="policy_flatten")(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, activation="softmax", name="policy_out")(x) for i in range(mc.value_res_layer_num): x_value = self._build_residual_block( x_value, mc.common_res_layer_num + mc.policy_res_layer_num + i + 1) # for value output x = Conv2D(filters=4, kernel_size=1, data_format="channels_first", use_bias=False, name="value_conv-1-4")(x_value) x = BatchNormalization(axis=1, name="value_batchnorm")(x) x = Activation("relu", name="value_relu")(x) x = Flatten(name="value_flatten")(x) x = Dense(mc.value_fc_size, activation="relu", name="value_dense", use_bias=False)(x) x = BatchNormalization(name="value_batchnorm_2")(x) value_out = Dense(1, activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="chess_model") def _build_residual_block(self, x, index): mc = self.config.model in_x = x res_name = "res" + str(index) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, name=res_name + "_conv1-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name=res_name + "_batchnorm1")(x) x = Activation("relu", name=res_name + "_relu1")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, name=res_name + "_conv2-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="res" + str(index) + "_batchnorm2")(x) x = Add(name=res_name + "_add")([in_x, x]) x = Activation("relu", name=res_name + "_relu2")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path=None, weight_path=None): """ :param str config_path: path to the file containing the entire configuration :param str weight_path: path to the file containing the model weights :return: true if successful in loading """ if os.path.exists(config_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) if os.path.exists(weight_path): self.model.load_weights(weight_path) self.model._make_predict_function() self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) self.build() return True def save(self, config_path, weight_path): """ :param str config_path: path to save the entire configuration to :param str weight_path: path to save the model weights to """ logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}")
class Connect4Model: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None def build(self): mc = self.config.model in_x = x = Input((2, 6, 7)) # [own(8x8), enemy(8x8)] activation = self.config.opts.activation print("activation selected is!!!!!!!!!!", activation) # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) #x = Activation("relu")(x) x = Activation(activation, name="input_" + activation)(x) print("activation selected is!!!!!!!!!!", activation) for i in range(mc.res_layer_num): x = self._build_residual_block(x, i + 1) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) #x = Activation("relu")(x) x = Activation(activation, name="policy_" + activation)(x) x = Flatten()(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=1, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) #x = Activation("relu")(x) x = Activation(activation, name="value_" + activation)(x) x = Flatten()(x) #x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation=activation, name="value_dense")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="connect4_model") def _build_residual_block(self, x, index): mc = self.config.model activation = self.config.opts.activation in_x = x res_name = "res" + str(index) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv1-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name=res_name + "_batchnorm1")(x) #x = Activation("relu")(x) x = Activation(activation, name=res_name + "_" + activation + "1")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg), name=res_name + "_conv2-" + str(mc.cnn_filter_size) + "-" + str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1)(x) x = Add(name=res_name + "_add")([in_x, x]) #x = Activation("relu")(x) x = Activation(activation, name=res_name + "_2")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: logger.debug(f"loading model from server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) ftp_connection.retrbinary("RETR model_best_config.json", open(config_path, 'wb').write) ftp_connection.retrbinary("RETR model_best_weight.h5", open(weight_path, 'wb').write) ftp_connection.quit() if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path, weight_path): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}") mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: logger.debug(f"saving model to server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) fh = open(config_path, 'rb') ftp_connection.storbinary('STOR model_best_config.json', fh) fh.close() fh = open(weight_path, 'rb') ftp_connection.storbinary('STOR model_best_weight.h5', fh) fh.close() ftp_connection.quit()
class QNetwork: def __init__(self, config: Config) -> None: self.config = config self.digest = None def build(self) -> None: mc = self.config.model in_x = x = Input((4, 5, 5)) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) for _ in range(mc.res_layer_num): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) # no output for 'pass' out = Dense(100, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="out")(x) # x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), # activation="relu")(x) # value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), # activation="tanh", name="value_out")(x) self.model = Model(in_x, out, name="slipe_model") self.model.compile(loss='mse', optimizer=Adam(lr=mc.learning_rate)) self.model.summary() def _build_residual_block(self, x): mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Add()([in_x, x]) x = Activation("relu")(x) return x # 重みの学習 def replay(self, memory: Memory, batch_size: int, gamma: float, targetQN: 'QNetwork') -> None: inputs = np.zeros((batch_size, 4, 5, 5)) targets = np.zeros((batch_size, 100)) mini_batch = memory.sample(batch_size) for i, (state_b, action_b, reward_b, next_state_b) in enumerate(mini_batch): inputs[i] = state_b # shape=(4, 5, 5) target = reward_b # type: int # if not (next_state_b == 0).all(): # 価値計算(DDQNにも対応できるように、行動決定のQネットワークと価値関数のQネットワークは分離) retmainQs = self.model.predict(next_state_b) next_action = np.argmax(retmainQs) # 最大の報酬を返す行動を選択する target = reward_b + gamma * \ targetQN.model.predict(next_state_b)[0][next_action] targets[i] = self.model.predict(state_b)[0][0] # Qネットワークの出力 # 教師信号 action_b: int <= 100 targets[i, action_b] = target # epochsは訓練データの反復回数、verbose=0は表示なしの設定 self.model.fit(inputs, targets, epochs=1, verbose=0) @staticmethod def fetch_digest(weight_path: str): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path: str, weight_path: str) -> bool: if os.path.exists(weight_path): # os.path.exists(config_path) and logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.model.compile( loss='mse', optimizer=Adam(lr=self.config.model.learning_rate)) self.model.summary() self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path: str, weight_path: str) -> None: logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}")
class ResiCNN: # My JackNet with residual block cnn_filter_num = 64 cnn_kernel_size = 3 def __init__(self, channels=3): self.model = None self.optimizer = None self.channels = channels def bulid(self): # build model image_in = Input((None, None, self.channels)) conv = Conv2D(filters=self.cnn_filter_num, kernel_size=self.cnn_kernel_size, strides=(1, 1), padding='same', data_format='channels_last')(image_in) conv = Activation('relu')(conv) x = conv for layers in range(8): x = self._build_residual_block(x) conv_out = Conv2D(filters=self.channels, kernel_size=self.cnn_kernel_size, strides=(1, 1), padding='same', data_format='channels_last')(x) output = Add()([image_in, conv_out]) self.model = Model(image_in, output, name='model') def _build_residual_block(self, x): # build residual block x_in = x x = Conv2D(filters=self.cnn_filter_num, kernel_size=self.cnn_kernel_size, strides=(1, 1), padding='same', data_format='channels_last')(x) x = BatchNormalization(axis=-1)(x) x = Activation('relu')(x) x = Conv2D(filters=self.cnn_filter_num, kernel_size=self.cnn_kernel_size, strides=(1, 1), padding='same', data_format='channels_last')(x) x = BatchNormalization(axis=-1)(x) x = Add()([x_in, x]) x = Activation("relu")(x) return x def predict(self, x): # denoise on input x if x.ndim == 3: x = x.reshape(1, x.shape[0], x.shape[1], self.channels) return self.model.predict_on_batch(x) def load(self, config_path, model_path): # load model print('restore model...') if os.path.exists(config_path) and os.path.exists(model_path): with open(config_path, 'r') as fp: self.model = Model.from_config(json.load(fp)) self.model.load_weights(model_path) return True return False def save(self, config_path, model_path): # save model with open(config_path, 'w') as fp: json.dump(self.model.get_config(), fp) self.model.save_weights(model_path) def compile(self): # choose adam optimizer and set learning rate self.optimizer = Adam(lr=1e-2) self.model.compile(optimizer=self.optimizer, loss=self.loss) def train_generator(self, data, epochs=1, steps_per_epochs=None, callbacks=None): self.model.fit_generator(iter(data), epochs=epochs, steps_per_epoch=steps_per_epochs, callbacks=callbacks) def train(self, data, epochs=1, callbacks=None): self.model.fit(x=data[0], y=data[1], epochs=epochs, batch_size=8, callbacks=callbacks) @staticmethod def loss(y_true, y_pred): # loss function, mean square error return 0.5 * K.sum(K.square(y_pred - y_true), axis=-1)
class CChessModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None self.n_labels = len(ActionLabelsRed) self.graph = None self.api = None def build(self): mc = self.config.model in_x = x = Input((14, 10, 9)) # 14 x 10 x 9 # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_first_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="input_conv-"+str(mc.cnn_first_filter_size)+"-"+str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="input_batchnorm")(x) x = Activation("relu", name="input_relu")(x) for i in range(mc.res_layer_num): x = self._build_residual_block(x, i + 1) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="policy_conv-1-2")(res_out) x = BatchNormalization(axis=1, name="policy_batchnorm")(x) x = Activation("relu", name="policy_relu")(x) x = Flatten(name="policy_flatten")(x) policy_out = Dense(self.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=4, kernel_size=1, data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="value_conv-1-4")(res_out) x = BatchNormalization(axis=1, name="value_batchnorm")(x) x = Activation("relu",name="value_relu")(x) x = Flatten(name="value_flatten")(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu", name="value_dense")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="cchess_model") self.graph = tf.get_default_graph() def _build_residual_block(self, x, index): mc = self.config.model in_x = x res_name = "res" + str(index) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name+"_conv1-"+str(mc.cnn_filter_size)+"-"+str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name=res_name+"_batchnorm1")(x) x = Activation("relu",name=res_name+"_relu1")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=res_name+"_conv2-"+str(mc.cnn_filter_size)+"-"+str(mc.cnn_filter_num))(x) x = BatchNormalization(axis=1, name="res"+str(index)+"_batchnorm2")(x) x = Add(name=res_name+"_add")([in_x, x]) x = Activation("relu", name=res_name+"_relu2")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) self.graph = tf.get_default_graph() logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug(f"model files does not exist at {config_path} and {weight_path}") return False def save(self, config_path, weight_path): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}") def get_pipes(self, num=1, api=None, need_reload=True): if self.api is None: self.api = CChessModelAPI(self.config, self) self.api.start() return self.api.get_pipe(need_reload) def close_pipes(self): if self.api is not None: self.api.close()
class ChessModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.graph = None self.digest = None self.api = None def get_pipes(self, num=1): if self.api is None: self.api = ChessModelAPI(self) self.api.start() return [self.api.get_pipe() for _ in range(num)] def build(self): mc = self.config.model in_x = x = Input((mc.input_stack_height, 8, 8)) # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg), input_shape=(mc.input_stack_height, 8, 8))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) for _ in range(mc.res_layer_num): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", use_bias=False, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=1, kernel_size=1, data_format="channels_first", use_bias=False, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) x = Dense(mc.value_fc_size, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) value_out = Dense(1, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="chess_model") self.graph = get_default_graph() def _build_residual_block(self, x): mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", use_bias=False, kernel_initializer='glorot_normal', bias_initializer='zeros', kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Add()([in_x, x]) x = Activation("relu")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.graph = get_default_graph() # self.model._make_predict_function() self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files do not exist at {config_path} and {weight_path}") return False def save(self, config_path, weight_path): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}")
class GomokuModel: def __init__(self, config: Config): ''' Manages the model saving and loading :param config: configuration settings ''' self.config = config self.model = None # type: Model self.digest = None def build(self, res_layers): ''' Constructs the ResNet model based on number of layers :param res_layers: number of layers in the model ''' mc = self.config.model in_x = x = Input((2, 8, 5)) # [own(8x8), enemy(8x8)] # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) logger.debug(f"Build Model with %d Res Blocks" % res_layers) #for _ in range(mc.res_layer_num): # build with number of res blocks for _ in range(res_layers): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) # no output for 'pass' policy_out = Dense(self.config.n_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) # for value output x = Conv2D(filters=1, kernel_size=1, data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Flatten()(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(x) self.model = Model(in_x, [policy_out, value_out], name="connect4_model") def _build_residual_block(self, x): ''' Build a single residual block ''' mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_first", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=1)(x) x = Add()([in_x, x]) x = Activation("relu")(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): ''' Load model with existing weights ''' if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {weight_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path, weight_path): ''' Save the model into specified path :param config_path: config path to save at :param weight_path: weight file path to save at ''' logger.debug(f"save model to {weight_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) #self.model.save(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}") def update_model(self, target): ''' Update the model with the target model weights :param target: the target model to copy from ''' copy_layers = target.get_num_res_layers() * 7 + 4 model_layers = len(self.model.layers) target_layers = len(target.model.layers) # set the weights of the previous layers for i in range(copy_layers): weight = target.model.layers[i].get_weights() self.model.layers[i].set_weights(weight) # set the weights of the layers after the res block for i in range(5 * 2 + 1): weight = target.model.layers[target_layers - i - 1].get_weights() self.model.layers[model_layers - i - 1].set_weights(weight) def get_num_res_layers(self): ''' Computes the number of layers in the architecture''' print('model layers %f' % len(self.model.layers)) return int((len(self.model.layers) - 4 - 5 * 2 - 1) / 7)
class ChessModel: def __init__(self, config: Config): self.config = config self.model = None # type: Model self.digest = None self.policy_out = None self.value_out = None #self.net_params = get_policy_param() def build(self): mc = self.config.model in_x = x = Input((2, 8, 8)) # [own(8x8), enemy(8x8)] # (batch, channels, height, width) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding='same', kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(momentum=.997, epsilon=1e-5)(x) x = Activation('relu')(x) for i in range(mc.res_layer_num): x = self._build_residual_block(x) res_out = x # for policy output x = Conv2D(filters=2, kernel_size=1, kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(momentum=.997, epsilon=1e-5)(x) x = Activation('relu')(x) x = Flatten()(x) policy_out = Dense(self.config.n_labels, activation='softmax', name='policy_out', kernel_regularizer=l2(mc.l2_reg))(x) #self.value_out = Dense(1, activation='tanh', name='self.value_out')(x) # for value output x = Conv2D(filters=1, kernel_size=1, kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(momentum=.997, epsilon=1e-5)(x) x = Activation('relu')(x) x = Flatten()(x) x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg))(x) x = Activation('relu')(x) value_out = Dense(1, activation='tanh', name='value_out', kernel_regularizer=l2(mc.l2_reg))(x) self.model = Model(in_x, [policy_out, value_out], name="chess_model") def _build_residual_block(self, x): mc = self.config.model in_x = x x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding='same', kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(momentum=.997, epsilon=1e-5)(x) x = Activation('relu')(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding='same', kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(momentum=.997, epsilon=1e-5)(x) x = Add()([x, in_x]) x = Activation('relu')(x) return x @staticmethod def fetch_digest(weight_path): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path, weight_path): mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: try: logger.debug(f"loading model from server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) ftp_connection.retrbinary("RETR model_best_config.json", open(config_path, 'wb').write) ftp_connection.retrbinary("RETR model_best_weight.h5", open(weight_path, 'wb').write) ftp_connection.quit() except: pass if os.path.exists(config_path) and os.path.exists(weight_path): logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path, weight_path): logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}") mc = self.config.model resources = self.config.resource if mc.distributed and config_path == resources.model_best_config_path: try: logger.debug(f"saving model to server") ftp_connection = ftplib.FTP( resources.model_best_distributed_ftp_server, resources.model_best_distributed_ftp_user, resources.model_best_distributed_ftp_password) ftp_connection.cwd( resources.model_best_distributed_ftp_remote_path) fh = open(config_path, 'rb') ftp_connection.storbinary('STOR model_best_config.json', fh) fh.close() fh = open(weight_path, 'rb') ftp_connection.storbinary('STOR model_best_weight.h5', fh) fh.close() ftp_connection.quit() except: pass
class ModelZero: def __init__(self, config: Config) -> None: self.config = config self.digest = None def build(self) -> None: mc = self.config.model in_x = x = Input(shape=(7, 5, 3)) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_last", kernel_regularizer=l2(mc.l2_reg))(x) x = Activation("relu")(x) for _ in range(mc.res_layer_num): x = self._build_residual_block(x) x = Activation("relu")(x) res_out = x # for policy output x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, data_format="channels_last", kernel_regularizer=l2(mc.l2_reg))(res_out) x = BatchNormalization(axis=3)(x) x = Activation("relu")(x) x = Flatten()(x) # no output for 'pass' x = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) policy_out = Dense(mc.value_fc_size, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) policy_out = Dense(315, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(policy_out) value_out = Dense(32, kernel_regularizer=l2(mc.l2_reg), activation="relu")(x) value_out = Dense(1, kernel_regularizer=l2(mc.l2_reg), activation="tanh", name="value_out")(value_out) self.model = Model(in_x, [policy_out, value_out], name="slipe_model") self.compile_model() # self.model.summary() def compile_model(self): self.optimizer = SGD(lr=self.config.model.learning_rate, momentum=0.9) losses = [objective_function_for_policy, objective_function_for_value] self.model.compile(optimizer=self.optimizer, loss=losses) def _build_residual_block(self, x): mc = self.config.model in_x = x x = BatchNormalization(axis=3)(x) x = Activation("relu")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_last", kernel_regularizer=l2(mc.l2_reg))(x) x = BatchNormalization(axis=3)(x) x = Activation("relu")(x) x = Dropout(1. / 3)(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format="channels_last", kernel_regularizer=l2(mc.l2_reg))(x) x = Add()([in_x, x]) return x # 重みの学習 def replay(self, wps, pi_mcts, board_logs, plus_turns, weights, batch_size: int, beta: float) -> None: inputs = np.zeros((batch_size, 7, 5, 3)) policy_true = np.zeros((batch_size, 315)) values_true = np.zeros((batch_size)) input_weights = np.zeros((batch_size)) indices = np.random.choice(np.arange(len(wps)), size=batch_size, replace=False) mini_batch = [(wps[i], pi_mcts[i], board_logs[i], plus_turns[i], weights[i]) for i in indices] for i, (winner, pi, board, plus_turn, weight) in enumerate(mini_batch): gs = GameState() gs.board = board inputs[i] = gs.to_inputs(flip=not plus_turn) # shape=(4, 5, 5) policy_true[i] = pi**beta values_true[i] = winner input_weights[i] = weight # epochsは訓練データの反復回数、verbose=0は表示なしの設定 self.model.fit(inputs, [policy_true, values_true], sample_weight=input_weights, epochs=1, verbose=0, shuffle=True) @staticmethod def fetch_digest(weight_path: str): if os.path.exists(weight_path): m = hashlib.sha256() with open(weight_path, "rb") as f: m.update(f.read()) return m.hexdigest() def load(self, config_path: str, weight_path: str) -> bool: if os.path.exists(weight_path): # os.path.exists(config_path) and logger.debug(f"loading model from {config_path}") with open(config_path, "rt") as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weight_path) self.compile_model() # self.model.summary() self.digest = self.fetch_digest(weight_path) logger.debug(f"loaded model digest = {self.digest}") return True else: logger.debug( f"model files does not exist at {config_path} and {weight_path}" ) return False def save(self, config_path: str, weight_path: str) -> None: logger.debug(f"save model to {config_path}") with open(config_path, "wt") as f: json.dump(self.model.get_config(), f) self.model.save_weights(weight_path) self.digest = self.fetch_digest(weight_path) logger.debug(f"saved model digest {self.digest}")