def layer_test(layer_cls, kwargs={}, input_shape=None, input_dtype=None, input_data=None, expected_output=None, expected_output_dtype=None, fixed_batch_size=False): """Test routine for a layer with a single input tensor and single output tensor. Copy of the function in keras-team/keras because it's not in the public API. If we use the one from keras-team/keras it won't work with tf.keras. """ # generate input data if input_data is None: assert input_shape if not input_dtype: input_dtype = K.floatx() input_data_shape = list(input_shape) for i, e in enumerate(input_data_shape): if e is None: input_data_shape[i] = np.random.randint(1, 4) input_data = (10 * np.random.random(input_data_shape)) input_data = input_data.astype(input_dtype) else: if input_shape is None: input_shape = input_data.shape if input_dtype is None: input_dtype = input_data.dtype if expected_output_dtype is None: expected_output_dtype = input_dtype # instantiation layer = layer_cls(**kwargs) # test get_weights , set_weights at layer level weights = layer.get_weights() layer.set_weights(weights) expected_output_shape = layer.compute_output_shape(input_shape) # test in functional API if fixed_batch_size: x = Input(batch_shape=input_shape, dtype=input_dtype) else: x = Input(shape=input_shape[1:], dtype=input_dtype) y = layer(x) assert K.dtype(y) == expected_output_dtype # check with the functional API model = Model(x, y) actual_output = model.predict(input_data) actual_output_shape = actual_output.shape for expected_dim, actual_dim in zip(expected_output_shape, actual_output_shape): if expected_dim is not None: assert expected_dim == actual_dim if expected_output is not None: assert_allclose(actual_output, expected_output, rtol=1e-3) # test serialization, weight setting at model level model_config = model.get_config() custom_objects = {layer.__class__.__name__: layer.__class__} recovered_model = model.__class__.from_config(model_config, custom_objects) if model.weights: weights = model.get_weights() recovered_model.set_weights(weights) _output = recovered_model.predict(input_data) assert_allclose(_output, actual_output, rtol=1e-3) # test training mode (e.g. useful when the layer has a # different behavior at training and testing time). if has_arg(layer.call, 'training'): model.compile('rmsprop', 'mse') model.train_on_batch(input_data, actual_output) # test instantiation from layer config layer_config = layer.get_config() layer_config['batch_input_shape'] = input_shape layer = layer.__class__.from_config(layer_config) # for further checks in the caller function return actual_output
class ChessModel: def __init__(self, config: Config): self.config = config self.channels_first = True self.data_format = "channels_first" self.Input_Layer_Dim = Input((18,8,8)) 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 = ChessAPI(self) self.api.start() return [self.api.create_pipe() for _ in range(num)] def build_model(self): mc = self.config.model in_x = x = self.Input_Layer_Dim # used as the input first layer of the model x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_first_filter_size, padding='same', data_format=self.data_format, use_bias=False, kernel_regularizer=l2(mc.l2_reg), name='input_convolutional-{}-{}'.format(str(mc.cnn_first_filter_size), str(mc.cnn_filter_num)))(x) x = BatchNormalization(axis=1, name='input_batchnorm')(x) # used for forward training instead of gradient descent x = Activation("relu", name="input_relu")(x) for i in range(mc.res_layer_num): x = self._build_remaining_block(x, i+1) res_out = x # chain the res_out through the policy and value neural nets x = Conv2D(filters=mc.p_filter_size, kernel_size=mc.p_kernel_size, data_format=self.data_format, use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="policy_conv-{}-{}".format(mc.p_kernel_size, mc.p_filter_size))(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.config.num_labels, kernel_regularizer=l2(mc.l2_reg), activation="softmax", name="policy_out")(x) x = Conv2D(filters=mc.v_filter_size, kernel_size=mc.v_kernel_size, data_format=self.data_format, use_bias=False, kernel_regularizer=l2(mc.l2_reg), name="value_conv-{}-{}".format(mc.v_kernel_size, mc.v_filter_size))(res_out) x = BatchNormalization(axis=1, name="value_bactchnorm")(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="Chesster") def _build_remaining_block(self, x, index): mc = self.config.model in_x = x indexed_name="res{}".format(str(index)) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format=self.data_format, use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=indexed_name+"_conv1-{}-{}".format(str(mc.cnn_filter_size),str(mc.cnn_filter_num)))(x) x = BatchNormalization(axis=1, name=indexed_name+"_batchnorm1")(x) x = Activation("relu",name=indexed_name+"_relu1")(x) x = Conv2D(filters=mc.cnn_filter_num, kernel_size=mc.cnn_filter_size, padding="same", data_format=self.data_format, use_bias=False, kernel_regularizer=l2(mc.l2_reg), name=indexed_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=indexed_name+"_add")([in_x, x]) x = Activation("relu", name=indexed_name+"_relu2")(x) return x @staticmethod def fetch_digest(weights_path): if os.path.exists(weights_path): m = hashlib.sha256() with open(weights_path, "rb") as f: m.update(f.read()) return m.hexdigest() def save(self, config_path, weights_path): """ Saves the model. arguments: config_path: path to configuration file weight_path: path to the weights """ logger.debug("saved model to {}".format(config_path)) with open(config_path, 'wt') as f: json.dump(self.model.get_config(), f) self.model.save_weights(weights_path) self.digest = self.fetch_digest(weights_path) logger.debug("Saved model digest {}".format(self.digest)) def load(self, config_path, weights_path): if os.path.exists(config_path) and os.path.exists(weights_path): logger.debug("loading model from {}".format(config_path)) with open(config_path, 'rt') as f: self.model = Model.from_config(json.load(f)) self.model.load_weights(weights_path) self.model.make_predict_function() self.digest = self.fetch_digest(weights_path) logger.debug("loaded model digest = {}".format(self.digest)) return True else: logger.debug("model no existy..") return False
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)] # (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(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") 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 @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()