diff --git a/CHANGELOG.md b/CHANGELOG.md index 94a83b51d..89939c54d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,21 @@ To release a new version, please update the changelog as followed: +## [2.1.0] - 2019-5-25 + +### Changed +- change the format of network config, change related code and files; change layer act (PR #980) + +### Added + +### Dependencies Update + +### Fixed + +### Contributors +- @warshallrho: #PR980 + + ## [2.0.1] - 2019-5-17 @@ -460,4 +475,4 @@ To many PR for this update, please check [here](https://github.com/tensorlayer/t [1.10.0]: https://github.com/tensorlayer/tensorlayer/compare/1.9.1...1.10.0 [1.9.1]: https://github.com/tensorlayer/tensorlayer/compare/1.9.0...1.9.1 [1.9.0]: https://github.com/tensorlayer/tensorlayer/compare/1.8.5...1.9.0 -[1.8.5]: https://github.com/tensorlayer/tensorlayer/compare/1.8.4...1.8.5 +[1.8.5]: https://github.com/tensorlayer/tensorlayer/compare/1.8.4...1.8.5 \ No newline at end of file diff --git a/docs/user/get_start_model.rst b/docs/user/get_start_model.rst index 7545eac0b..0ff3459cd 100644 --- a/docs/user/get_start_model.rst +++ b/docs/user/get_start_model.rst @@ -211,10 +211,9 @@ z = f(x*W+b) class Dense(Layer): def __init__(self, n_units, act=None, in_channels=None, name=None): - super(Dense, self).__init__(name) + super(Dense, self).__init__(name, act=act) self.n_units = n_units - self.act = act self.in_channels = in_channels # for dynamic model, it needs the input shape to get the shape of W diff --git a/examples/text_generation/tutorial_generate_text.py b/examples/text_generation/tutorial_generate_text.py index 50c320632..d157b1ed5 100644 --- a/examples/text_generation/tutorial_generate_text.py +++ b/examples/text_generation/tutorial_generate_text.py @@ -329,4 +329,4 @@ def main_lstm_generate_text(): # main_restore_embedding_layer() # How to generate text from a given context - main_lstm_generate_text() + main_lstm_generate_text() \ No newline at end of file diff --git a/tensorlayer/db.py b/tensorlayer/db.py index cb8db8e10..7ca3e7bb5 100644 --- a/tensorlayer/db.py +++ b/tensorlayer/db.py @@ -13,7 +13,7 @@ import tensorflow as tf from tensorlayer import logging -from tensorlayer.files import net2static_graph, static_graph2net, assign_weights +from tensorlayer.files import static_graph2net, assign_weights from tensorlayer.files import save_weights_to_hdf5, load_hdf5_to_weights from tensorlayer.files import del_folder, exists_or_mkdir @@ -153,7 +153,7 @@ def save_model(self, network=None, model_name='model', **kwargs): s = time.time() # kwargs.update({'architecture': network.all_graphs, 'time': datetime.utcnow()}) - kwargs.update({'architecture': net2static_graph(network), 'time': datetime.utcnow()}) + kwargs.update({'architecture': network.config, 'time': datetime.utcnow()}) try: params_id = self.model_fs.put(self._serialization(params)) diff --git a/tensorlayer/files/utils.py b/tensorlayer/files/utils.py index 72fcb1824..60521c309 100644 --- a/tensorlayer/files/utils.py +++ b/tensorlayer/files/utils.py @@ -31,6 +31,7 @@ from tensorflow.python.util.tf_export import keras_export from tensorflow.python.util import serialization import json +import datetime # from six.moves import zip @@ -73,7 +74,7 @@ 'load_hdf5_to_weights', 'save_hdf5_graph', 'load_hdf5_graph', - 'net2static_graph', + # 'net2static_graph', 'static_graph2net', # 'save_pkl_graph', # 'load_pkl_graph', @@ -92,29 +93,29 @@ def str2func(s): return expr -def net2static_graph(network): - saved_file = dict() - if network._NameNone is True: - saved_file.update({"name": None}) - else: - saved_file.update({"name": network.name}) - if not isinstance(network.inputs, list): - saved_file.update({"inputs": network.inputs._info[0].name}) - else: - saved_inputs = [] - for saved_input in network.inputs: - saved_inputs.append(saved_input._info[0].name) - saved_file.update({"inputs": saved_inputs}) - if not isinstance(network.outputs, list): - saved_file.update({"outputs": network.outputs._info[0].name}) - else: - saved_outputs = [] - for saved_output in network.outputs: - saved_outputs.append(saved_output._info[0].name) - saved_file.update({"outputs": saved_outputs}) - saved_file.update({"config": network.config}) - - return saved_file +# def net2static_graph(network): +# saved_file = dict() +# # if network._NameNone is True: +# # saved_file.update({"name": None}) +# # else: +# # saved_file.update({"name": network.name}) +# # if not isinstance(network.inputs, list): +# # saved_file.update({"inputs": network.inputs._info[0].name}) +# # else: +# # saved_inputs = [] +# # for saved_input in network.inputs: +# # saved_inputs.append(saved_input._info[0].name) +# # saved_file.update({"inputs": saved_inputs}) +# # if not isinstance(network.outputs, list): +# # saved_file.update({"outputs": network.outputs._info[0].name}) +# # else: +# # saved_outputs = [] +# # for saved_output in network.outputs: +# # saved_outputs.append(saved_output._info[0].name) +# # saved_file.update({"outputs": saved_outputs}) +# saved_file.update({"config": network.config}) +# +# return saved_file @keras_export('keras.models.save_model') @@ -149,7 +150,7 @@ def load_keras_model(model_config): return model -def save_hdf5_graph(network, filepath='model.hdf5', save_weights=False): +def save_hdf5_graph(network, filepath='model.hdf5', save_weights=False, customized_data=None): """Save the architecture of TL model into a hdf5 file. Support saving model weights. Parameters @@ -160,6 +161,8 @@ def save_hdf5_graph(network, filepath='model.hdf5', save_weights=False): The name of model file. save_weights : bool Whether to save model weights. + customized_data : dict + The user customized meta data. Examples -------- @@ -177,11 +180,22 @@ def save_hdf5_graph(network, filepath='model.hdf5', save_weights=False): logging.info("[*] Saving TL model into {}, saving weights={}".format(filepath, save_weights)) - saved_file = net2static_graph(network) - saved_file_str = str(saved_file) + model_config = network.config # net2static_graph(network) + model_config_str = str(model_config) + customized_data_str = str(customized_data) + version_info = { + "tensorlayer_version": tl.__version__, + "backend": "tensorflow", + "backend_version": tf.__version__, + "training_device": "gpu", + "save_date": datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + } + version_info_str = str(version_info) with h5py.File(filepath, 'w') as f: - f.attrs["model_structure"] = saved_file_str.encode('utf8') + f.attrs["model_config"] = model_config_str.encode('utf8') + f.attrs["customized_data"] = customized_data_str.encode('utf8') + f.attrs["version_info"] = version_info_str.encode('utf8') if save_weights: _save_weights_to_hdf5_group(f, network.all_layers) f.flush() @@ -237,29 +251,15 @@ def eval_layer(layer_kwargs): raise RuntimeError("Unknown layer type.") -def static_graph2net(saved_file): +def static_graph2net(model_config): layer_dict = {} - model_name = saved_file['name'] - inputs_tensors = saved_file['inputs'] - outputs_tensors = saved_file['outputs'] - all_args = saved_file['config'] - tf_version = saved_file['config'].pop(0)['tf_version'] - tl_version = saved_file['config'].pop(0)['tl_version'] - if tf_version != tf.__version__: - logging.warning( - "Saved model uses tensorflow version {}, but now you are using tensorflow version {}".format( - tf_version, tf.__version__ - ) - ) - if tl_version != tl.__version__: - logging.warning( - "Saved model uses tensorlayer version {}, but now you are using tensorlayer version {}".format( - tl_version, tl.__version__ - ) - ) + model_name = model_config["name"] + inputs_tensors = model_config["inputs"] + outputs_tensors = model_config["outputs"] + all_args = model_config["model_architecture"] for idx, layer_kwargs in enumerate(all_args): - layer_class = layer_kwargs['class'] # class of current layer - prev_layers = layer_kwargs.pop('prev_layer') # name of previous layers + layer_class = layer_kwargs["class"] # class of current layer + prev_layers = layer_kwargs.pop("prev_layer") # name of previous layers net = eval_layer(layer_kwargs) if layer_class in tl.layers.inputs.__all__: net = net._nodes[0].out_tensors[0] @@ -312,11 +312,30 @@ def load_hdf5_graph(filepath='model.hdf5', load_weights=False): - see ``tl.files.save_hdf5_graph`` """ logging.info("[*] Loading TL model from {}, loading weights={}".format(filepath, load_weights)) + f = h5py.File(filepath, 'r') - saved_file_str = f.attrs["model_structure"].decode('utf8') - saved_file = eval(saved_file_str) - M = static_graph2net(saved_file) + version_info_str = f.attrs["version_info"].decode('utf8') + version_info = eval(version_info_str) + backend_version = version_info["backend_version"] + tensorlayer_version = version_info["tensorlayer_version"] + if backend_version != tf.__version__: + logging.warning( + "Saved model uses tensorflow version {}, but now you are using tensorflow version {}".format( + backend_version, tf.__version__ + ) + ) + if tensorlayer_version != tl.__version__: + logging.warning( + "Saved model uses tensorlayer version {}, but now you are using tensorlayer version {}".format( + tensorlayer_version, tl.__version__ + ) + ) + + model_config_str = f.attrs["model_config"].decode('utf8') + model_config = eval(model_config_str) + + M = static_graph2net(model_config) if load_weights: if not ('layer_names' in f.attrs.keys()): raise RuntimeError("Saved model does not contain weights.") @@ -329,55 +348,55 @@ def load_hdf5_graph(filepath='model.hdf5', load_weights=False): return M -def load_pkl_graph(name='model.pkl'): - """Restore TL model archtecture from a a pickle file. No parameters be restored. - - Parameters - ----------- - name : str - The name of graph file. - - Returns - -------- - network : TensorLayer Model. - - Examples - -------- - >>> # It is better to use load_hdf5_graph - """ - logging.info("[*] Loading TL graph from {}".format(name)) - with open(name, 'rb') as file: - saved_file = pickle.load(file) - - M = static_graph2net(saved_file) - - return M - - -def save_pkl_graph(network, name='model.pkl'): - """Save the architecture of TL model into a pickle file. No parameters be saved. - - Parameters - ----------- - network : TensorLayer layer - The network to save. - name : str - The name of graph file. - - Example - -------- - >>> # It is better to use save_hdf5_graph - """ - if network.outputs is None: - raise AssertionError("save_graph not support dynamic mode yet") - - logging.info("[*] Saving TL graph into {}".format(name)) - - saved_file = net2static_graph(network) - - with open(name, 'wb') as file: - pickle.dump(saved_file, file, protocol=pickle.HIGHEST_PROTOCOL) - logging.info("[*] Saved graph") +# def load_pkl_graph(name='model.pkl'): +# """Restore TL model archtecture from a a pickle file. No parameters be restored. +# +# Parameters +# ----------- +# name : str +# The name of graph file. +# +# Returns +# -------- +# network : TensorLayer Model. +# +# Examples +# -------- +# >>> # It is better to use load_hdf5_graph +# """ +# logging.info("[*] Loading TL graph from {}".format(name)) +# with open(name, 'rb') as file: +# saved_file = pickle.load(file) +# +# M = static_graph2net(saved_file) +# +# return M +# +# +# def save_pkl_graph(network, name='model.pkl'): +# """Save the architecture of TL model into a pickle file. No parameters be saved. +# +# Parameters +# ----------- +# network : TensorLayer layer +# The network to save. +# name : str +# The name of graph file. +# +# Example +# -------- +# >>> # It is better to use save_hdf5_graph +# """ +# if network.outputs is None: +# raise AssertionError("save_graph not support dynamic mode yet") +# +# logging.info("[*] Saving TL graph into {}".format(name)) +# +# saved_file = net2static_graph(network) +# +# with open(name, 'wb') as file: +# pickle.dump(saved_file, file, protocol=pickle.HIGHEST_PROTOCOL) +# logging.info("[*] Saved graph") # Load dataset functions diff --git a/tensorlayer/layers/convolution/binary_conv.py b/tensorlayer/layers/convolution/binary_conv.py index 23448cf6f..cf55127d5 100644 --- a/tensorlayer/layers/convolution/binary_conv.py +++ b/tensorlayer/layers/convolution/binary_conv.py @@ -75,11 +75,10 @@ def __init__( in_channels=None, name=None # 'binary_cnn2d', ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.strides = self._strides = strides - self.act = act self.padding = padding self.use_gemm = use_gemm self.data_format = data_format diff --git a/tensorlayer/layers/convolution/deformable_conv.py b/tensorlayer/layers/convolution/deformable_conv.py index 5f75bbe15..616803ba1 100644 --- a/tensorlayer/layers/convolution/deformable_conv.py +++ b/tensorlayer/layers/convolution/deformable_conv.py @@ -83,12 +83,11 @@ def __init__( in_channels=None, name=None # 'deformable_conv_2d', ): - super().__init__(name) + super().__init__(name, act=act) self.offset_layer = offset_layer self.n_filter = n_filter self.filter_size = filter_size - self.act = act self.padding = padding self.W_init = W_init self.b_init = b_init diff --git a/tensorlayer/layers/convolution/depthwise_conv.py b/tensorlayer/layers/convolution/depthwise_conv.py index d6136ede3..b11233c27 100644 --- a/tensorlayer/layers/convolution/depthwise_conv.py +++ b/tensorlayer/layers/convolution/depthwise_conv.py @@ -82,10 +82,9 @@ def __init__( in_channels=None, name=None # 'depthwise_conv2d' ): - super().__init__(name) + super().__init__(name, act=act) self.filter_size = filter_size self.strides = self._strides = strides - self.act = act self.padding = padding self.dilation_rate = self._dilation_rate = dilation_rate self.data_format = data_format diff --git a/tensorlayer/layers/convolution/dorefa_conv.py b/tensorlayer/layers/convolution/dorefa_conv.py index ed9b32dd8..dc7979967 100644 --- a/tensorlayer/layers/convolution/dorefa_conv.py +++ b/tensorlayer/layers/convolution/dorefa_conv.py @@ -81,13 +81,12 @@ def __init__( in_channels=None, name=None # 'dorefa_cnn2d', ): - super().__init__(name) + super().__init__(name, act=act) self.bitW = bitW self.bitA = bitA self.n_filter = n_filter self.filter_size = filter_size self.strides = self._strides = strides - self.act = act self.padding = padding self.use_gemm = use_gemm self.data_format = data_format diff --git a/tensorlayer/layers/convolution/expert_conv.py b/tensorlayer/layers/convolution/expert_conv.py index d7e59a0e8..50ea12cb9 100644 --- a/tensorlayer/layers/convolution/expert_conv.py +++ b/tensorlayer/layers/convolution/expert_conv.py @@ -71,8 +71,7 @@ def __init__( b_init=tl.initializers.constant(value=0.0), name=None # 'cnn1d_layer', ): - super().__init__(name) - self.act = act + super().__init__(name, act=act) self.n_filter = shape[-1] self.filter_size = shape[0] self.shape = shape @@ -191,8 +190,7 @@ def __init__( b_init=tl.initializers.constant(value=0.0), name=None # 'cnn2d_layer', ): - super().__init__(name) - self.act = act + super().__init__(name, act=act) self.n_filter = shape[-1] self.filter_size = (shape[0], shape[1]) self.shape = shape @@ -310,8 +308,7 @@ def __init__( b_init=tl.initializers.constant(value=0.0), name=None # 'cnn3d_layer' ): - super().__init__(name) - self.act = act + super().__init__(name, act=act) self.n_filter = shape[-1] self.filter_size = (shape[0], shape[1], shape[2]) self.shape = shape diff --git a/tensorlayer/layers/convolution/expert_deconv.py b/tensorlayer/layers/convolution/expert_deconv.py index cb5cd6773..f23c752ad 100644 --- a/tensorlayer/layers/convolution/expert_deconv.py +++ b/tensorlayer/layers/convolution/expert_deconv.py @@ -80,8 +80,7 @@ def __init__( b_init=tl.initializers.constant(value=0.0), name=None # 'decnn1d_layer', ): - super().__init__(name) - self.act = act + super().__init__(name, act=act) self.shape = shape self.outputs_shape = outputs_shape self.strides = strides @@ -215,8 +214,7 @@ def __init__( b_init=tl.initializers.constant(value=0.0), name=None # 'decnn2d_layer', ): - super().__init__(name) - self.act = act + super().__init__(name, act=act) self.shape = shape self.outputs_shape = outputs_shape self.strides = strides @@ -342,8 +340,7 @@ def __init__( b_init=tl.initializers.constant(value=0.0), name=None # 'decnn3d_layer', ): - super().__init__(name) - self.act = act + super().__init__(name, act=act) self.shape = shape self.outputs_shape = outputs_shape self.strides = strides diff --git a/tensorlayer/layers/convolution/group_conv.py b/tensorlayer/layers/convolution/group_conv.py index 34d8c10e6..bc35d4e00 100644 --- a/tensorlayer/layers/convolution/group_conv.py +++ b/tensorlayer/layers/convolution/group_conv.py @@ -73,12 +73,11 @@ def __init__( in_channels=None, name=None # 'groupconv', ): # Windaway - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.strides = self._strides = strides self.n_group = n_group - self.act = act self.padding = padding self.data_format = data_format self.dilation_rate = self._dilation_rate = dilation_rate diff --git a/tensorlayer/layers/convolution/quan_conv.py b/tensorlayer/layers/convolution/quan_conv.py index e235dfeb4..75ee3943c 100644 --- a/tensorlayer/layers/convolution/quan_conv.py +++ b/tensorlayer/layers/convolution/quan_conv.py @@ -82,13 +82,12 @@ def __init__( in_channels=None, name=None # 'quan_cnn2d', ): - super().__init__(name) + super().__init__(name, act=act) self.bitW = bitW self.bitA = bitA self.n_filter = n_filter self.filter_size = filter_size self.strides = self._strides = strides - self.act = act self.padding = padding self.use_gemm = use_gemm self.data_format = data_format diff --git a/tensorlayer/layers/convolution/separable_conv.py b/tensorlayer/layers/convolution/separable_conv.py index b6ae62446..ca1c66d49 100644 --- a/tensorlayer/layers/convolution/separable_conv.py +++ b/tensorlayer/layers/convolution/separable_conv.py @@ -84,11 +84,10 @@ def __init__( in_channels=None, name=None # 'seperable1d', ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.strides = strides - self.act = act self.padding = padding self.data_format = data_format self.dilation_rate = dilation_rate @@ -232,11 +231,10 @@ def __init__( in_channels=None, name=None # 'seperable2d', ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.strides = strides - self.act = act self.padding = padding self.data_format = data_format self.dilation_rate = dilation_rate diff --git a/tensorlayer/layers/convolution/simplified_conv.py b/tensorlayer/layers/convolution/simplified_conv.py index c00ff8fe7..536d4e52e 100644 --- a/tensorlayer/layers/convolution/simplified_conv.py +++ b/tensorlayer/layers/convolution/simplified_conv.py @@ -70,11 +70,10 @@ def __init__( in_channels=None, name=None # 'conv1d' ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.stride = stride - self.act = act self.padding = padding self.data_format = data_format self.dilation_rate = dilation_rate @@ -201,11 +200,10 @@ def __init__( in_channels=None, name=None # 'conv2d', ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self._strides = self.strides = strides - self.act = act self.padding = padding self.data_format = data_format self._dilation_rate = self.dilation_rate = dilation_rate @@ -335,11 +333,10 @@ def __init__( in_channels=None, name=None # 'conv3d', ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self._strides = self.strides = strides - self.act = act self.padding = padding self.data_format = data_format self._dilation_rate = self.dilation_rate = dilation_rate diff --git a/tensorlayer/layers/convolution/simplified_deconv.py b/tensorlayer/layers/convolution/simplified_deconv.py index 847062859..57beff0f4 100644 --- a/tensorlayer/layers/convolution/simplified_deconv.py +++ b/tensorlayer/layers/convolution/simplified_deconv.py @@ -71,12 +71,11 @@ def __init__( in_channels=None, name=None # 'decnn2d' ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.strides = strides self.padding = padding - self.act = act self.data_format = data_format self.dilation_rate = dilation_rate self.W_init = W_init @@ -199,12 +198,11 @@ def __init__( in_channels=None, name=None # 'decnn3d' ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.strides = strides self.padding = padding - self.act = act self.data_format = data_format self.W_init = W_init self.b_init = b_init diff --git a/tensorlayer/layers/convolution/super_resolution.py b/tensorlayer/layers/convolution/super_resolution.py index 35fee8722..21d765e03 100644 --- a/tensorlayer/layers/convolution/super_resolution.py +++ b/tensorlayer/layers/convolution/super_resolution.py @@ -53,9 +53,8 @@ def __init__( in_channels=None, name=None # 'subpixel_conv1d' ): - super().__init__(name) + super().__init__(name, act=act) self.scale = scale - self.act = act self.in_channels = in_channels self.out_channels = int(self.in_channels / self.scale) @@ -151,10 +150,9 @@ def __init__( in_channels=None, name=None # 'subpixel_conv2d' ): - super().__init__(name) + super().__init__(name, act=act) self.scale = scale self.n_out_channels = n_out_channels - self.act = act self.in_channels = in_channels if self.in_channels is not None: diff --git a/tensorlayer/layers/convolution/ternary_conv.py b/tensorlayer/layers/convolution/ternary_conv.py index 9a97c7bec..33e01507c 100644 --- a/tensorlayer/layers/convolution/ternary_conv.py +++ b/tensorlayer/layers/convolution/ternary_conv.py @@ -75,11 +75,10 @@ def __init__( in_channels=None, name=None # 'ternary_cnn2d', ): - super().__init__(name) + super().__init__(name, act=act) self.n_filter = n_filter self.filter_size = filter_size self.strides = self._strides = strides - self.act = act self.padding = padding self.use_gemm = use_gemm self.data_format = data_format diff --git a/tensorlayer/layers/core.py b/tensorlayer/layers/core.py index ce98f156c..d98ea312e 100644 --- a/tensorlayer/layers/core.py +++ b/tensorlayer/layers/core.py @@ -17,6 +17,36 @@ _global_layer_name_dict = {} # TODO: better implementation? +_act_dict = { + "relu": tf.nn.relu, + "relu6": tf.nn.relu6, + "leaky_relu": tf.nn.leaky_relu, + "lrelu": tf.nn.leaky_relu, + "softplus": tf.nn.softplus, + "tanh": tf.nn.tanh, + "sigmoid": tf.nn.sigmoid, +} + + +def str2act(act): + if len(act) > 5 and act[0:5] == "lrelu": + try: + alpha = float(act[5:]) + return lambda x: tf.nn.leaky_relu(x, alpha=alpha) + except Exception as e: + raise Exception("{} can not be parsed as a float".format(act[5:])) + + if len(act) > 10 and act[0:10] == "leaky_relu": + try: + alpha = float(act[10:]) + return lambda x: tf.nn.leaky_relu(x, alpha=alpha) + except Exception as e: + raise Exception("{} can not be parsed as a float".format(act[10:])) + + if act not in _act_dict.keys(): + raise Exception("Unsupported act: {}".format(act)) + return _act_dict[act] + class Layer(object): """The basic :class:`Layer` class represents a single layer of a neural network. @@ -47,11 +77,12 @@ class Layer(object): """ - def __init__(self, name=None, *args, **kwargs): + def __init__(self, name=None, act=None, *args, **kwargs): """ Initializing the Layer. :param name: str or None + :param name: str or function or None """ # Layer constants @@ -84,6 +115,10 @@ def __init__(self, name=None, *args, **kwargs): _global_layer_name_dict[name] = 0 self.name = name + if isinstance(act, str): + self.act = str2act(act) + else: + self.act = act # Layer building state self._built = False @@ -479,7 +514,8 @@ def _release_memory(self): def get_args(self): init_args = {} init_args.update({"layer_type": "modellayer"}) - init_args["model"] = utils.net2static_graph(self.layer_args["model"]) + # init_args["model"] = utils.net2static_graph(self.layer_args["model"]) + init_args["model"] = self.layer_args["model"].config return init_args diff --git a/tensorlayer/layers/dense/base_dense.py b/tensorlayer/layers/dense/base_dense.py index a5b800f04..e4c96af50 100644 --- a/tensorlayer/layers/dense/base_dense.py +++ b/tensorlayer/layers/dense/base_dense.py @@ -63,10 +63,9 @@ def __init__( name=None, # 'dense', ): - super(Dense, self).__init__(name) + super(Dense, self).__init__(name, act=act) self.n_units = n_units - self.act = act self.W_init = W_init self.b_init = b_init self.in_channels = in_channels diff --git a/tensorlayer/layers/dense/binary_dense.py b/tensorlayer/layers/dense/binary_dense.py index 4067ac4c3..77adde3b0 100644 --- a/tensorlayer/layers/dense/binary_dense.py +++ b/tensorlayer/layers/dense/binary_dense.py @@ -49,9 +49,8 @@ def __init__( in_channels=None, name=None, #'binary_dense', ): - super().__init__(name) + super().__init__(name, act=act) self.n_units = n_units - self.act = act self.use_gemm = use_gemm self.W_init = W_init self.b_init = b_init diff --git a/tensorlayer/layers/dense/dorefa_dense.py b/tensorlayer/layers/dense/dorefa_dense.py index 80ae3365c..07e9c339e 100644 --- a/tensorlayer/layers/dense/dorefa_dense.py +++ b/tensorlayer/layers/dense/dorefa_dense.py @@ -56,11 +56,10 @@ def __init__( in_channels=None, name=None, #'dorefa_dense', ): - super().__init__(name) + super().__init__(name, act=act) self.bitW = bitW self.bitA = bitA self.n_units = n_units - self.act = act self.use_gemm = use_gemm self.W_init = W_init self.b_init = b_init diff --git a/tensorlayer/layers/dense/dropconnect.py b/tensorlayer/layers/dense/dropconnect.py index d68e6c762..449221f42 100644 --- a/tensorlayer/layers/dense/dropconnect.py +++ b/tensorlayer/layers/dense/dropconnect.py @@ -65,14 +65,13 @@ def __init__( in_channels=None, name=None, # 'dropconnect', ): - super().__init__(name) + super().__init__(name, act=act) if isinstance(keep, numbers.Real) and not (keep > 0 and keep <= 1): raise ValueError("keep must be a scalar tensor or a float in the " "range (0, 1], got %g" % keep) self.keep = keep self.n_units = n_units - self.act = act self.W_init = W_init self.b_init = b_init self.in_channels = in_channels diff --git a/tensorlayer/layers/dense/quan_dense.py b/tensorlayer/layers/dense/quan_dense.py index 5a2513259..f5c42492f 100644 --- a/tensorlayer/layers/dense/quan_dense.py +++ b/tensorlayer/layers/dense/quan_dense.py @@ -54,9 +54,8 @@ def __init__( in_channels=None, name=None, #'quan_dense', ): - super().__init__(name) + super().__init__(name, act=act) self.n_units = n_units - self.act = act self.bitW = bitW self.bitA = bitA self.use_gemm = use_gemm diff --git a/tensorlayer/layers/dense/ternary_dense.py b/tensorlayer/layers/dense/ternary_dense.py index 27efb9090..778636155 100644 --- a/tensorlayer/layers/dense/ternary_dense.py +++ b/tensorlayer/layers/dense/ternary_dense.py @@ -49,9 +49,8 @@ def __init__( in_channels=None, name=None, #'ternary_dense', ): - super().__init__(name) + super().__init__(name, act=act) self.n_units = n_units - self.act = act self.use_gemm = use_gemm self.W_init = W_init self.b_init = b_init diff --git a/tensorlayer/layers/merge.py b/tensorlayer/layers/merge.py index 346a65962..6f49374ca 100644 --- a/tensorlayer/layers/merge.py +++ b/tensorlayer/layers/merge.py @@ -111,9 +111,8 @@ def __init__( name=None, #'elementwise', ): - super(Elementwise, self).__init__(name) + super(Elementwise, self).__init__(name, act=act) self.combine_fn = combine_fn - self.act = act self.build(None) self._built = True diff --git a/tensorlayer/layers/normalization.py b/tensorlayer/layers/normalization.py index d8cec274c..1f2b25f81 100644 --- a/tensorlayer/layers/normalization.py +++ b/tensorlayer/layers/normalization.py @@ -195,8 +195,7 @@ def __init__( data_format='channels_last', name=None, ): - super(BatchNorm, self).__init__(name=name) - self.act = act + super(BatchNorm, self).__init__(name=name, act=act) self.decay = decay self.epsilon = epsilon self.data_format = data_format @@ -441,8 +440,7 @@ def __init__( gamma_init=tl.initializers.random_normal(mean=1.0, stddev=0.002), num_features=None, data_format='channels_last', name=None ): - super(InstanceNorm, self).__init__(name=name) - self.act = act + super(InstanceNorm, self).__init__(name=name, act=act) self.epsilon = epsilon self.beta_init = beta_init self.gamma_init = gamma_init @@ -651,10 +649,9 @@ def __init__( ): # super(LayerNorm, self).__init__(prev_layer=prev_layer, act=act, name=name) - super(LayerNorm, self).__init__(name) + super(LayerNorm, self).__init__(name, act=act) self.center = center self.scale = scale - self.act = act self.epsilon = epsilon self.begin_norm_axis = begin_norm_axis self.begin_params_axis = begin_params_axis @@ -730,10 +727,9 @@ class GroupNorm(Layer): def __init__(self, groups=32, epsilon=1e-06, act=None, data_format='channels_last', name=None): #'groupnorm'): # super(GroupNorm, self).__init__(prev_layer=prev_layer, act=act, name=name) - super().__init__(name) + super().__init__(name, act=act) self.groups = groups self.epsilon = epsilon - self.act = act self.data_format = data_format logging.info( @@ -847,8 +843,7 @@ def __init__( name=None, #'switchnorm', ): # super(SwitchNorm, self).__init__(prev_layer=prev_layer, act=act, name=name) - super().__init__(name) - self.act = act + super().__init__(name, act=act) self.epsilon = epsilon self.beta_init = beta_init self.gamma_init = gamma_init diff --git a/tensorlayer/models/core.py b/tensorlayer/models/core.py index c811b9648..c1197ed55 100644 --- a/tensorlayer/models/core.py +++ b/tensorlayer/models/core.py @@ -437,15 +437,43 @@ def config(self): if self._config is not None and len(self._config) > 0: return self._config else: - _config = [] - _config.append({"tf_version": tf.__version__}) - _config.append({"tl_version": tl.__version__}) + # _config = [] + _config = {} + if self._NameNone is True: + _config.update({"name": None}) + else: + _config.update({"name": self.name}) + # versionInfo = { + # "tensorlayer_version": tl.__version__, + # "backend": "tensorflow", + # "backend_version": tf.__version__, + # "training_device": "gpu", + # } + # _config.update(versionInfo) # if self.outputs is None: # raise RuntimeError( # "Dynamic mode does not support config yet." # ) + model_architecture = [] for layer in self.all_layers: - _config.append(layer.config) + model_architecture.append(layer.config) + _config["model_architecture"] = model_architecture + if self.inputs is not None: + if not isinstance(self.inputs, list): + _config.update({"inputs": self.inputs._info[0].name}) + else: + config_inputs = [] + for config_input in self.inputs: + config_inputs.append(config_input._info[0].name) + _config.update({"inputs": config_inputs}) + if self.outputs is not None: + if not isinstance(self.outputs, list): + _config.update({"outputs": self.outputs._info[0].name}) + else: + config_outputs = [] + for config_output in self.outputs: + config_outputs.append(config_output._info[0].name) + _config.update({"outputs": config_outputs}) if self._nodes_fixed or self.outputs is None: self._config = _config @@ -714,7 +742,7 @@ def release_memory(self): for layer in self.all_layers: layer._release_memory() - def save(self, filepath, save_weights=True): + def save(self, filepath, save_weights=True, customized_data=None): """ Save model into a given file. This function save can save both the architecture of neural networks and weights (optional). @@ -726,6 +754,8 @@ def save(self, filepath, save_weights=True): Filename into which the model will be saved. save_weights : bool Whether to save model weights. + customized_data : dict + The user customized meta data. Examples -------- @@ -739,7 +769,9 @@ def save(self, filepath, save_weights=True): raise RuntimeError( "Model save() not support dynamic mode yet.\nHint: you can use Model save_weights() to save the weights in dynamic mode." ) - utils.save_hdf5_graph(network=self, filepath=filepath, save_weights=save_weights) + utils.save_hdf5_graph( + network=self, filepath=filepath, save_weights=save_weights, customized_data=customized_data + ) @staticmethod def load(filepath, load_weights=True): diff --git a/tests/layers/test_layers_convolution.py b/tests/layers/test_layers_convolution.py index 0f5979d5b..b768600de 100644 --- a/tests/layers/test_layers_convolution.py +++ b/tests/layers/test_layers_convolution.py @@ -33,7 +33,7 @@ def setUpClass(cls): )(cls.n2) cls.n4 = tl.layers.SeparableConv1d( - n_filter=32, filter_size=3, strides=2, padding='SAME', act=tf.nn.relu, name='separable_1d' + n_filter=32, filter_size=3, strides=2, padding='SAME', act='relu', name='separable_1d' )(cls.n3) cls.n5 = tl.layers.SubpixelConv1d(scale=2, act=tf.nn.relu, in_channels=32, name='subpixel_1d')(cls.n4) @@ -462,6 +462,29 @@ def test_layer_n4(self): # self.assertEqual(self.net2.count_params(), 19392) # self.assertEqual(self.net2.outputs.get_shape().as_list()[1:], [299, 299, 64]) + +class Exception_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing exception in activation #####") + + def test_exception(cls): + + cls.batch_size = 5 + cls.inputs_shape = [cls.batch_size, 400, 400, 3] + cls.input_layer = Input(cls.inputs_shape, name='input_layer') + + try: + cls.n1 = tl.layers.Conv2dLayer( + act='activation', shape=(5, 5, 3, 32), strides=(1, 2, 2, 1), padding='SAME', + b_init=tf.constant_initializer(value=0.0), name='conv2dlayer' + )(cls.input_layer) + except Exception as e: + cls.assertIsInstance(e, Exception) + print(e) + + if __name__ == '__main__': tl.logging.set_verbosity(tl.logging.DEBUG) diff --git a/tests/layers/test_layers_core_act.py b/tests/layers/test_layers_core_act.py new file mode 100644 index 000000000..0da41fea0 --- /dev/null +++ b/tests/layers/test_layers_core_act.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os +import unittest + +os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' + +import tensorflow as tf +import tensorlayer as tl +from tensorlayer.layers import * +from tensorlayer.models import * + +from tests.utils import CustomTestCase + + +class Layer_Convolution_2D_Test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing activation #####") + + @classmethod + def tearDownClass(cls): + pass + # tf.reset_default_graph() + + def test_layer_core_act(cls): + + cls.batch_size = 5 + cls.inputs_shape = [cls.batch_size, 400, 400, 3] + cls.input_layer = Input(cls.inputs_shape, name='input_layer') + + cls.n1 = tl.layers.Conv2dLayer( + act=tf.nn.relu, shape=(5, 5, 3, 32), strides=(1, 2, 2, 1), padding='SAME', + b_init=tf.constant_initializer(value=0.0), name='conv2dlayer' + )(cls.input_layer) + + cls.n2 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="relu", name='conv2d')(cls.n1) + + cls.n3 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="leaky_relu", + b_init=None)(cls.n2) + + cls.n4 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="lrelu", b_init=None)(cls.n2) + + cls.n5 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="sigmoid", + in_channels=32)(cls.n4) + + cls.n6 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="tanh", in_channels=32)(cls.n5) + + cls.n7 = tl.layers.Conv2d( + n_filter=32, filter_size=(3, 3), strides=(2, 2), act="leaky_relu0.22", in_channels=32 + )(cls.n6) + + cls.n8 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="lrelu0.22", + in_channels=32)(cls.n7) + + cls.n9 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="softplus", + in_channels=32)(cls.n8) + + cls.n10 = tl.layers.Conv2d(n_filter=32, filter_size=(3, 3), strides=(2, 2), act="relu6", in_channels=32)(cls.n9) + + cls.model = Model(cls.input_layer, cls.n8) + + +class Exception_test(CustomTestCase): + + @classmethod + def setUpClass(cls): + print("##### begin testing exception in activation #####") + + def test_exception(cls): + + cls.batch_size = 5 + cls.inputs_shape = [cls.batch_size, 400, 400, 3] + cls.input_layer = Input(cls.inputs_shape, name='input_layer') + + try: + cls.n1 = tl.layers.Conv2dLayer( + act='activation', shape=(5, 5, 3, 32), strides=(1, 2, 2, 1), padding='SAME', + b_init=tf.constant_initializer(value=0.0), name='conv2dlayer' + )(cls.input_layer) + except Exception as e: + cls.assertIsInstance(e, Exception) + print(e) + + try: + cls.n2 = tl.layers.Conv2dLayer( + act='leaky_relu0.2x', shape=(5, 5, 3, 32), strides=(1, 2, 2, 1), padding='SAME', + b_init=tf.constant_initializer(value=0.0), name='conv2dlayer' + )(cls.input_layer) + except Exception as e: + cls.assertIsInstance(e, Exception) + print(e) + + try: + cls.n3 = tl.layers.Conv2dLayer( + act='lrelu0.2x', shape=(5, 5, 3, 32), strides=(1, 2, 2, 1), padding='SAME', + b_init=tf.constant_initializer(value=0.0), name='conv2dlayer' + )(cls.input_layer) + except Exception as e: + cls.assertIsInstance(e, Exception) + print(e) + + +if __name__ == '__main__': + + tl.logging.set_verbosity(tl.logging.DEBUG) + + unittest.main() diff --git a/tests/models/test_model_save_graph.py b/tests/models/test_model_save_graph.py index 95229938b..057ac921f 100644 --- a/tests/models/test_model_save_graph.py +++ b/tests/models/test_model_save_graph.py @@ -178,7 +178,7 @@ class Reuse_ModelLayer_test(CustomTestCase): @classmethod def setUpClass(cls): - print("##### begin testing save_graph, load_graph, including ModelLayer and reuse #####") + print("##### begin testing save_graph, load_graph, including ModelLayer and reuse #####") def test_save(self): input_shape = (None, 784) @@ -195,7 +195,7 @@ class Vgg_LayerList_test(CustomTestCase): @classmethod def setUpClass(cls): - print("##### begin testing save_graph, load_graph, including LayerList #####") + print("##### begin testing save_graph, load_graph, including LayerList #####") def test_save(self): M1 = tl.models.vgg16(mode='static') @@ -211,7 +211,7 @@ class List_inputs_outputs_test(CustomTestCase): @classmethod def setUpClass(cls): - print("##### begin testing model with list inputs and outputs #####") + print("##### begin testing model with list inputs and outputs #####") def test_list_inputs_outputs(self): ni_1 = Input(shape=[4, 16]) @@ -344,7 +344,7 @@ class ElementWise_lambda_test(CustomTestCase): @classmethod def setUpClass(cls): - print("##### begin testing elementwise lambda layer #####") + print("##### begin testing elementwise lambda layer #####") def test_elementwise_no_para_with_args(self): # z = mean + noise * tf.exp(std * 0.5) + foo @@ -467,7 +467,7 @@ class Dynamic_config_test(CustomTestCase): @classmethod def setUpClass(cls): - print("##### begin testing exception in dynamic mode #####") + print("##### begin testing exception in dynamic mode #####") def test_dynamic_config(self): M1 = basic_dynamic_model() @@ -480,7 +480,7 @@ class Exception_test(CustomTestCase): @classmethod def setUpClass(cls): - print("##### begin testing exception in dynamic mode #####") + print("##### begin testing exception in dynamic mode #####") def test_exception(self): M1 = basic_dynamic_model()