"_impl/keras/datasets/mnist.py",
"_impl/keras/datasets/reuters.py",
"_impl/keras/engine/__init__.py",
- "_impl/keras/engine/topology.py",
+ "_impl/keras/engine/base_layer.py",
+ "_impl/keras/engine/input_layer.py",
+ "_impl/keras/engine/network.py",
+ "_impl/keras/engine/saving.py",
+ "_impl/keras/engine/sequential.py",
"_impl/keras/engine/training.py",
"_impl/keras/engine/training_eager.py",
"_impl/keras/estimator.py",
srcs_version = "PY2AND3",
deps = [
":keras",
- "//tensorflow/python:array_ops",
"//tensorflow/python:client_testlib",
- "//tensorflow/python:dtypes",
+ "//third_party/py/numpy",
+ ],
+)
+
+py_test(
+ name = "saving_test",
+ size = "small",
+ srcs = ["_impl/keras/engine/saving_test.py"],
+ srcs_version = "PY2AND3",
+ deps = [
+ ":keras",
+ "//tensorflow/python:client_testlib",
+ "//third_party/py/numpy",
+ ],
+)
+
+py_test(
+ name = "sequential_test",
+ size = "small",
+ srcs = ["_impl/keras/engine/sequential_test.py"],
+ srcs_version = "PY2AND3",
+ deps = [
+ ":keras",
+ "//tensorflow/python:client_testlib",
"//third_party/py/numpy",
],
)
from tensorflow.python.keras._impl.keras.applications import imagenet_utils
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Activation
from tensorflow.python.keras._impl.keras.layers import AveragePooling2D
from tensorflow.python.keras._impl.keras.layers import BatchNormalization
from tensorflow.python.keras._impl.keras.applications import imagenet_utils
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Activation
from tensorflow.python.keras._impl.keras.layers import AveragePooling2D
from tensorflow.python.keras._impl.keras.layers import BatchNormalization
from tensorflow.python.keras._impl.keras.applications import imagenet_utils
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Activation
from tensorflow.python.keras._impl.keras.layers import AveragePooling2D
from tensorflow.python.keras._impl.keras.layers import BatchNormalization
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
from tensorflow.python.keras._impl.keras.engine import InputSpec
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Activation
from tensorflow.python.keras._impl.keras.layers import BatchNormalization
from tensorflow.python.keras._impl.keras.layers import Conv2D
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
from tensorflow.python.keras._impl.keras.applications.inception_v3 import preprocess_input
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Activation
from tensorflow.python.keras._impl.keras.layers import add
from tensorflow.python.keras._impl.keras.layers import AveragePooling2D
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import preprocess_input
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Activation
from tensorflow.python.keras._impl.keras.layers import AveragePooling2D
from tensorflow.python.keras._impl.keras.layers import BatchNormalization
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import preprocess_input
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Conv2D
from tensorflow.python.keras._impl.keras.layers import Dense
from tensorflow.python.keras._impl.keras.layers import Flatten
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import preprocess_input
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Conv2D
from tensorflow.python.keras._impl.keras.layers import Dense
from tensorflow.python.keras._impl.keras.layers import Flatten
from tensorflow.python.keras._impl.keras.applications import imagenet_utils
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import _obtain_input_shape
from tensorflow.python.keras._impl.keras.applications.imagenet_utils import decode_predictions
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
from tensorflow.python.keras._impl.keras.layers import Activation
from tensorflow.python.keras._impl.keras.layers import BatchNormalization
from tensorflow.python.keras._impl.keras.layers import Conv2D
from __future__ import division
from __future__ import print_function
-from tensorflow.python.keras._impl.keras.engine.topology import get_source_inputs
-from tensorflow.python.keras._impl.keras.engine.topology import Input
-from tensorflow.python.keras._impl.keras.engine.topology import InputLayer
-from tensorflow.python.keras._impl.keras.engine.topology import InputSpec
-from tensorflow.python.keras._impl.keras.engine.topology import Layer
+from tensorflow.python.keras._impl.keras.engine.base_layer import InputSpec
+from tensorflow.python.keras._impl.keras.engine.base_layer import Layer
+from tensorflow.python.keras._impl.keras.engine.input_layer import Input
+from tensorflow.python.keras._impl.keras.engine.input_layer import InputLayer
+from tensorflow.python.keras._impl.keras.engine.network import get_source_inputs
+from tensorflow.python.keras._impl.keras.engine.network import Network
from tensorflow.python.keras._impl.keras.engine.training import Model
-
-
-# Note: topology.Node is an internal class,
-# it isn't meant to be used by Keras users.
--- /dev/null
+# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+# pylint: disable=protected-access
+"""Base layer code (`Layer`).
+"""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+from six.moves import zip # pylint: disable=redefined-builtin
+
+from tensorflow.python.eager import context
+from tensorflow.python.framework import tensor_shape
+from tensorflow.python.keras._impl.keras import backend as K
+from tensorflow.python.keras._impl.keras import constraints
+from tensorflow.python.keras._impl.keras import initializers
+from tensorflow.python.keras._impl.keras import regularizers
+from tensorflow.python.keras._impl.keras.utils import generic_utils
+from tensorflow.python.layers import base as tf_base_layers
+from tensorflow.python.platform import tf_logging as logging
+from tensorflow.python.util.tf_export import tf_export
+
+
+# pylint: disable=invalid-name
+InputSpec = tf_base_layers.InputSpec
+Node = tf_base_layers.Node
+TFBaseLayer = tf_base_layers.Layer
+# pylint: enable=invalid-name
+
+
+@tf_export('keras.layers.Layer')
+class Layer(tf_base_layers.Layer):
+ """Abstract base layer class.
+
+ # Properties
+ name: String, must be unique within a model.
+ input_spec: List of InputSpec class instances
+ each entry describes one required input:
+ - ndim
+ - dtype
+ A layer with `n` input tensors must have
+ an `input_spec` of length `n`.
+ trainable: Boolean, whether the layer weights
+ will be updated during training.
+ uses_learning_phase: Whether any operation
+ of the layer uses `K.in_training_phase()`
+ or `K.in_test_phase()`.
+ input_shape: Shape tuple. Provided for convenience,
+ but note that there may be cases in which this
+ attribute is ill-defined (e.g. a shared layer
+ with multiple input shapes), in which case
+ requesting `input_shape` will raise an Exception.
+ Prefer using `layer.get_input_shape_for(input_shape)`,
+ or `layer.get_input_shape_at(node_index)`.
+ output_shape: Shape tuple. See above.
+ inbound_nodes: List of nodes.
+ outbound_nodes: List of nodes.
+ input, output: Input/output tensor(s). Note that if the layer is used
+ more than once (shared layer), this is ill-defined
+ and will raise an exception. In such cases, use
+ `layer.get_input_at(node_index)`.
+ input_mask, output_mask: Same as above, for masks.
+ trainable_weights: List of variables.
+ non_trainable_weights: List of variables.
+ weights: The concatenation of the lists trainable_weights and
+ non_trainable_weights (in this order).
+
+ # Methods
+ call(x, mask=None): Where the layer's logic lives.
+ __call__(x, mask=None): Wrapper around the layer logic (`call`).
+ If x is a Keras tensor:
+ - Connect current layer with last layer from tensor:
+ `self._add_inbound_node(last_layer)`
+ - Add layer to tensor history
+ If layer is not built:
+ - Build from inputs shape
+ get_weights()
+ set_weights(weights)
+ get_config()
+ count_params()
+ compute_output_shape(input_shape)
+ compute_mask(x, mask)
+ get_input_at(node_index)
+ get_output_at(node_index)
+ get_input_shape_at(node_index)
+ get_output_shape_at(node_index)
+ get_input_mask_at(node_index)
+ get_output_mask_at(node_index)
+
+ # Class Methods
+ from_config(config)
+
+ # Internal methods:
+ build(input_shape)
+ _add_inbound_node(layer, index=0)
+ """
+
+ def __init__(self, **kwargs):
+ # These properties should be set by the user via keyword arguments.
+ # note that 'dtype', 'input_shape' and 'batch_input_shape'
+ # are only applicable to input layers: do not pass these keywords
+ # to non-input layers.
+ allowed_kwargs = {
+ 'activity_regularizer',
+ 'input_shape',
+ 'batch_input_shape',
+ 'batch_size',
+ 'dtype',
+ 'name',
+ 'trainable',
+ 'weights',
+ }
+ # Validate optional keyword arguments.
+ for kwarg in kwargs:
+ if kwarg not in allowed_kwargs:
+ raise TypeError('Keyword argument not understood:', kwarg)
+
+ # Get layer name.
+ name = kwargs.get('name')
+
+ # Get `trainable` status.
+ trainable = kwargs.get('trainable', True)
+
+ # Get `dtype`.
+ dtype = kwargs.get('dtype')
+ if dtype is None:
+ dtype = K.floatx()
+
+ # Call super, which will set all properties common to Keras layers
+ # and core TF layers.
+ super(Layer, self).__init__(
+ name=name, dtype=dtype, trainable=trainable,
+ activity_regularizer=kwargs.get('activity_regularizer'))
+
+ # Add properties that are Keras-only for now.
+ self.supports_masking = False
+
+ # Manage input shape information if passed.
+ if 'input_shape' in kwargs or 'batch_input_shape' in kwargs:
+ # In this case we will later create an input layer
+ # to insert before the current layer
+ if 'batch_input_shape' in kwargs:
+ batch_input_shape = tuple(kwargs['batch_input_shape'])
+ elif 'input_shape' in kwargs:
+ if 'batch_size' in kwargs:
+ batch_size = kwargs['batch_size']
+ else:
+ batch_size = None
+ batch_input_shape = (batch_size,) + tuple(kwargs['input_shape'])
+ self._batch_input_shape = batch_input_shape
+
+ # Manage initial weight values if passed.
+ if 'weights' in kwargs:
+ self._initial_weights = kwargs['weights']
+ else:
+ self._initial_weights = None
+
+ def add_weight(self,
+ name,
+ shape,
+ dtype=None,
+ initializer=None,
+ regularizer=None,
+ trainable=True,
+ constraint=None):
+ """Adds a weight variable to the layer.
+
+ Arguments:
+ name: String, the name for the weight variable.
+ shape: The shape tuple of the weight.
+ dtype: The dtype of the weight.
+ initializer: An Initializer instance (callable).
+ regularizer: An optional Regularizer instance.
+ trainable: A boolean, whether the weight should
+ be trained via backprop or not (assuming
+ that the layer itself is also trainable).
+ constraint: An optional Constraint instance.
+
+ Returns:
+ The created weight variable.
+ """
+ if dtype is None:
+ dtype = K.floatx()
+ weight = self.add_variable(name, shape,
+ dtype=dtype,
+ initializer=initializers.get(initializer),
+ regularizer=regularizers.get(regularizer),
+ constraint=constraints.get(constraint),
+ trainable=trainable)
+ return weight
+
+ def call(self, inputs, **kwargs): # pylint: disable=unused-argument
+ """This is where the layer's logic lives.
+
+ Arguments:
+ inputs: Input tensor, or list/tuple of input tensors.
+ **kwargs: Additional keyword arguments.
+
+ Returns:
+ A tensor or list/tuple of tensors.
+ """
+ return inputs
+
+ def __call__(self, inputs, **kwargs):
+ """Wrapper around self.call(), for handling internal references.
+
+ If a Keras tensor is passed:
+ - We call self._add_inbound_node().
+ - If necessary, we `build` the layer to match
+ the shape of the input(s).
+ - We update the _keras_history of the output tensor(s)
+ with the current layer.
+ This is done as part of _add_inbound_node().
+
+ Arguments:
+ inputs: Can be a tensor or list/tuple of tensors.
+ **kwargs: Additional keyword arguments to be passed to `call()`.
+
+ Returns:
+ Output of the layer's `call` method.
+
+ Raises:
+ ValueError: in case the layer is missing shape information
+ for its `build` call.
+ """
+ # Actually call the layer (optionally building it).
+ output = super(Layer, self).__call__(inputs, **kwargs)
+ if context.in_eager_mode():
+ return output
+
+ # Un-built subclassed network: build it
+ if hasattr(self, '_set_inputs') and not self.inputs:
+ self._set_inputs(inputs, training=kwargs.get('training'))
+
+ # Update learning phase info.
+ output_tensors = generic_utils.to_list(output)
+ uses_lp = any(
+ [getattr(x, '_uses_learning_phase', False)
+ for x in generic_utils.to_list(inputs)])
+ uses_lp = getattr(self, 'uses_learning_phase', False) or uses_lp
+ for i in range(len(output_tensors)):
+ output_tensors[i]._uses_learning_phase = getattr(
+ output_tensors[i], '_uses_learning_phase', False) or uses_lp
+
+ # Optionally load weight values that were specified at layer instantiation.
+ if hasattr(self, '_initial_weights') and self._initial_weights is not None:
+ self.set_weights(self._initial_weights)
+ del self._initial_weights
+ return output
+
+ def compute_output_shape(self, input_shape):
+ """Computes the output shape of the layer.
+
+ Assumes that the layer will be built
+ to match that input shape provided.
+
+ Arguments:
+ input_shape: Shape tuple (tuple of integers)
+ or list of shape tuples (one per output tensor of the layer).
+ Shape tuples can include None for free dimensions,
+ instead of an integer.
+
+ Returns:
+ An input shape tuple.
+ """
+ logging.warning(
+ 'All custom layers should implement the '
+ '`compute_output_shape` method. This layer (' + self.name + ') '
+ 'is relying on the base `Layer.compute_output_shape` implementation, '
+ 'which will start raising a `NotImplementedError` '
+ 'as of July 1st, 2018.')
+ return input_shape
+
+ def compute_mask(self, inputs, mask=None): # pylint: disable=unused-argument
+ """Computes an output mask tensor.
+
+ Arguments:
+ inputs: Tensor or list of tensors.
+ mask: Tensor or list of tensors.
+
+ Returns:
+ None or a tensor (or list of tensors,
+ one per output tensor of the layer).
+ """
+ if not self.supports_masking:
+ if mask is not None:
+ if isinstance(mask, list):
+ if any(m is not None for m in mask):
+ raise TypeError('Layer ' + self.name + ' does not support masking, '
+ 'but was passed an input_mask: ' + str(mask))
+ else:
+ raise TypeError('Layer ' + self.name + ' does not support masking, '
+ 'but was passed an input_mask: ' + str(mask))
+ # masking not explicitly supported: return None as mask
+ return None
+ # if masking is explicitly supported, by default
+ # carry over the input mask
+ return mask
+
+ def get_input_mask_at(self, node_index):
+ """Retrieves the input mask tensor(s) of a layer at a given node.
+
+ Arguments:
+ node_index: Integer, index of the node
+ from which to retrieve the attribute.
+ E.g. `node_index=0` will correspond to the
+ first time the layer was called.
+
+ Returns:
+ A mask tensor
+ (or list of tensors if the layer has multiple inputs).
+ """
+ inputs = self.get_input_at(node_index)
+ if isinstance(inputs, list):
+ return [getattr(x, '_keras_mask', None) for x in inputs]
+ else:
+ return getattr(inputs, '_keras_mask', None)
+
+ def get_output_mask_at(self, node_index):
+ """Retrieves the output mask tensor(s) of a layer at a given node.
+
+ Arguments:
+ node_index: Integer, index of the node
+ from which to retrieve the attribute.
+ E.g. `node_index=0` will correspond to the
+ first time the layer was called.
+
+ Returns:
+ A mask tensor
+ (or list of tensors if the layer has multiple outputs).
+ """
+ output = self.get_output_at(node_index)
+ if isinstance(output, list):
+ return [getattr(x, '_keras_mask', None) for x in output]
+ else:
+ return getattr(output, '_keras_mask', None)
+
+ @property
+ def input_mask(self):
+ """Retrieves the input mask tensor(s) of a layer.
+
+ Only applicable if the layer has exactly one inbound node,
+ i.e. if it is connected to one incoming layer.
+
+ Returns:
+ Input mask tensor (potentially None) or list of input
+ mask tensors.
+
+ Raises:
+ AttributeError: if the layer is connected to
+ more than one incoming layers.
+ """
+ inputs = self.input
+ if isinstance(inputs, list):
+ return [getattr(x, '_keras_mask', None) for x in inputs]
+ else:
+ return getattr(inputs, '_keras_mask', None)
+
+ @property
+ def output_mask(self):
+ """Retrieves the output mask tensor(s) of a layer.
+
+ Only applicable if the layer has exactly one inbound node,
+ i.e. if it is connected to one incoming layer.
+
+ Returns:
+ Output mask tensor (potentially None) or list of output
+ mask tensors.
+
+ Raises:
+ AttributeError: if the layer is connected to
+ more than one incoming layers.
+ """
+ output = self.output
+ if isinstance(output, list):
+ return [getattr(x, '_keras_mask', None) for x in output]
+ else:
+ return getattr(output, '_keras_mask', None)
+
+ def set_weights(self, weights):
+ """Sets the weights of the layer, from Numpy arrays.
+
+ Arguments:
+ weights: a list of Numpy arrays. The number
+ of arrays and their shape must match
+ number of the dimensions of the weights
+ of the layer (i.e. it should match the
+ output of `get_weights`).
+
+ Raises:
+ ValueError: If the provided weights list does not match the
+ layer's specifications.
+ """
+ params = self.weights
+ if len(params) != len(weights):
+ raise ValueError('You called `set_weights(weights)` on layer "' +
+ self.name + '" with a weight list of length ' +
+ str(len(weights)) + ', but the layer was expecting ' +
+ str(len(params)) + ' weights. Provided weights: ' +
+ str(weights)[:50] + '...')
+ if not params:
+ return
+ weight_value_tuples = []
+ param_values = K.batch_get_value(params)
+ for pv, p, w in zip(param_values, params, weights):
+ if pv.shape != w.shape:
+ raise ValueError('Layer weight shape ' + str(pv.shape) +
+ ' not compatible with '
+ 'provided weight shape ' + str(w.shape))
+ weight_value_tuples.append((p, w))
+ K.batch_set_value(weight_value_tuples)
+
+ def get_weights(self):
+ """Returns the current weights of the layer.
+
+ Returns:
+ Weights values as a list of numpy arrays.
+ """
+ params = self.weights
+ return K.batch_get_value(params)
+
+ def get_config(self):
+ """Returns the config of the layer.
+
+ A layer config is a Python dictionary (serializable)
+ containing the configuration of a layer.
+ The same layer can be reinstantiated later
+ (without its trained weights) from this configuration.
+
+ The config of a layer does not include connectivity
+ information, nor the layer class name. These are handled
+ by `Network` (one layer of abstraction above).
+
+ Returns:
+ Python dictionary.
+ """
+ config = {'name': self.name, 'trainable': self.trainable}
+ if hasattr(self, '_batch_input_shape'):
+ config['batch_input_shape'] = self._batch_input_shape
+ if hasattr(self, 'dtype'):
+ config['dtype'] = self.dtype
+ return config
+
+ @classmethod
+ def from_config(cls, config):
+ """Creates a layer from its config.
+
+ This method is the reverse of `get_config`,
+ capable of instantiating the same layer from the config
+ dictionary. It does not handle layer connectivity
+ (handled by Network), nor weights (handled by `set_weights`).
+
+ Arguments:
+ config: A Python dictionary, typically the
+ output of get_config.
+
+ Returns:
+ A layer instance.
+ """
+ return cls(**config)
+
+ @tf_base_layers.Layer.activity_regularizer.setter
+ def activity_regularizer(self, activity_regularizer):
+ self._activity_regularizer = activity_regularizer
+
+
+def shape_type_conversion(fn):
+ """Decorator that handles tuple/TensorShape conversion.
+
+ Used in `compute_output_shape` and `build`.
+
+ Arguments:
+ fn: function to wrap.
+
+ Returns:
+ Wrapped function.
+ """
+
+ def wrapper(instance, input_shape):
+ if input_shape is not None:
+ if isinstance(input_shape, list):
+ input_shape = [
+ tuple(tensor_shape.TensorShape(x).as_list()) for x in input_shape]
+ else:
+ input_shape = tuple(tensor_shape.TensorShape(input_shape).as_list())
+ output_shape = fn(instance, input_shape)
+ if output_shape is not None:
+ if isinstance(output_shape, list):
+ return [tensor_shape.TensorShape(x) for x in output_shape]
+ return tensor_shape.TensorShape(output_shape)
+
+ return wrapper
--- /dev/null
+# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+# pylint: disable=protected-access
+"""Input layer code (`Input` and `InputLayer`).
+"""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+from tensorflow.python.eager import context
+from tensorflow.python.framework import tensor_shape
+from tensorflow.python.keras._impl.keras import backend as K
+from tensorflow.python.keras._impl.keras.engine import base_layer
+from tensorflow.python.layers import base as tf_base_layers
+from tensorflow.python.ops import array_ops
+from tensorflow.python.util.tf_export import tf_export
+
+
+class InputLayer(base_layer.Layer):
+ """Layer to be used as an entry point into a Network (a graph of layers).
+
+ It can either wrap an existing tensor (pass an `input_tensor` argument)
+ or create its a placeholder tensor (pass arguments `input_shape`, and
+ optionally, `dtype`).
+
+ It is generally recommend to use the functional layer API via `Input`,
+ (which creates an `InputLayer`) without directly using `InputLayer`.
+
+ Arguments:
+ input_shape: Shape tuple (not including the batch axis), or `TensorShape`
+ instance (not including the batch axis).
+ batch_size: Optional input batch size (integer or None).
+ dtype: Datatype of the input.
+ input_tensor: Optional tensor to use as layer input
+ instead of creating a placeholder.
+ sparse: Boolean, whether the placeholder created
+ is meant to be sparse.
+ name: Name of the layer (string).
+ """
+
+ def __init__(self,
+ input_shape=None,
+ batch_size=None,
+ dtype=None,
+ input_tensor=None,
+ sparse=False,
+ name=None,
+ **kwargs):
+ if 'batch_input_shape' in kwargs:
+ batch_input_shape = kwargs.pop('batch_input_shape')
+ if input_shape and batch_input_shape:
+ raise ValueError('Only provide the input_shape OR '
+ 'batch_input_shape argument to '
+ 'InputLayer, not both at the same time.')
+ batch_size = batch_input_shape[0]
+ input_shape = batch_input_shape[1:]
+ if kwargs:
+ raise ValueError('Unrecognized keyword arguments:', kwargs.keys())
+
+ if not name:
+ prefix = 'input'
+ name = prefix + '_' + str(K.get_uid(prefix))
+
+ if not dtype:
+ if input_tensor is None:
+ dtype = K.floatx()
+ else:
+ dtype = K.dtype(input_tensor)
+ super(InputLayer, self).__init__(dtype=dtype, name=name)
+ self.built = True
+ self.sparse = sparse
+ self.batch_size = batch_size
+
+ if isinstance(input_shape, tensor_shape.TensorShape):
+ input_shape = tuple(input_shape.as_list())
+
+ if input_tensor is None:
+ if input_shape is not None:
+ batch_input_shape = (batch_size,) + tuple(input_shape)
+ else:
+ batch_input_shape = None
+
+ if context.in_eager_mode():
+ # In eager mode, create a temporary placeholder to call the layer on.
+ input_tensor = tf_base_layers._DeferredTensor( # pylint: disable=protected-access
+ shape=batch_input_shape,
+ dtype=dtype,
+ name=self.name)
+ else:
+ # In graph mode, create a graph placeholder to call the layer on.
+ if sparse:
+ input_tensor = array_ops.sparse_placeholder(
+ shape=batch_input_shape,
+ dtype=dtype,
+ name=self.name)
+ else:
+ input_tensor = array_ops.placeholder(
+ shape=batch_input_shape,
+ dtype=dtype,
+ name=self.name)
+
+ # For compatibility with Keras API.
+ self.is_placeholder = True
+ self._batch_input_shape = batch_input_shape
+ else:
+ # For compatibility with Keras API.
+ self.is_placeholder = False
+ self._batch_input_shape = tuple(input_tensor.get_shape().as_list())
+
+ # Create an input node to add to self.outbound_node
+ # and set output_tensors' _keras_history.
+ input_tensor._keras_history = (self, 0, 0) # pylint: disable=protected-access
+ tf_base_layers.Node(
+ self,
+ inbound_layers=[],
+ node_indices=[],
+ tensor_indices=[],
+ input_tensors=[input_tensor],
+ output_tensors=[input_tensor])
+
+ def get_config(self):
+ config = {
+ 'batch_input_shape': self._batch_input_shape,
+ 'dtype': self.dtype,
+ 'sparse': self.sparse,
+ 'name': self.name
+ }
+ return config
+
+
+@tf_export('keras.layers.Input', 'keras.Input')
+def Input( # pylint: disable=invalid-name
+ shape=None,
+ batch_size=None,
+ name=None,
+ dtype=None,
+ sparse=False,
+ tensor=None,
+ **kwargs):
+ """`Input()` is used to instantiate a Keras tensor.
+
+ A Keras tensor is a tensor object from the underlying backend
+ (Theano or TensorFlow), which we augment with certain
+ attributes that allow us to build a Keras model
+ just by knowing the inputs and outputs of the model.
+
+ For instance, if a, b and c are Keras tensors,
+ it becomes possible to do:
+ `model = Model(input=[a, b], output=c)`
+
+ The added Keras attribute is:
+ `_keras_history`: Last layer applied to the tensor.
+ the entire layer graph is retrievable from that layer,
+ recursively.
+
+ Arguments:
+ shape: A shape tuple (integers), not including the batch size.
+ For instance, `shape=(32,)` indicates that the expected input
+ will be batches of 32-dimensional vectors.
+ batch_size: optional static batch size (integer).
+ name: An optional name string for the layer.
+ Should be unique in a model (do not reuse the same name twice).
+ It will be autogenerated if it isn't provided.
+ dtype: The data type expected by the input, as a string
+ (`float32`, `float64`, `int32`...)
+ sparse: A boolean specifying whether the placeholder
+ to be created is sparse.
+ tensor: Optional existing tensor to wrap into the `Input` layer.
+ If set, the layer will not create a placeholder tensor.
+ **kwargs: deprecated arguments support.
+
+ Returns:
+ A tensor.
+
+ Example:
+
+ ```python
+ # this is a logistic regression in Keras
+ x = Input(shape=(32,))
+ y = Dense(16, activation='softmax')(x)
+ model = Model(x, y)
+ ```
+
+ Raises:
+ ValueError: in case of invalid arguments.
+ """
+ if 'batch_shape' in kwargs:
+ batch_shape = kwargs.pop('batch_shape')
+ if shape and batch_shape:
+ raise ValueError('Only provide the shape OR '
+ 'batch_shape argument to '
+ 'Input, not both at the same time.')
+ batch_size = batch_shape[0]
+ shape = batch_shape[1:]
+ if kwargs:
+ raise ValueError('Unrecognized keyword arguments:', kwargs.keys())
+
+ if dtype is None:
+ dtype = K.floatx()
+ if not shape and tensor is None:
+ raise ValueError('Please provide to Input either a `shape`'
+ ' or a `tensor` argument. Note that '
+ '`shape` does not include the batch '
+ 'dimension.')
+ input_layer = InputLayer(
+ input_shape=shape,
+ batch_size=batch_size,
+ name=name,
+ dtype=dtype,
+ sparse=sparse,
+ input_tensor=tensor)
+ # Return tensor including `_keras_history`.
+ # Note that in this case train_output and test_output are the same pointer.
+ outputs = input_layer._inbound_nodes[0].output_tensors
+ if len(outputs) == 1:
+ return outputs[0]
+ else:
+ return outputs
# limitations under the License.
# ==============================================================================
# pylint: disable=protected-access
-"""Base layer code and base model (Network) code.
+"""A `Network` is way to compose layers: the topological form of a `Model`.
"""
from __future__ import absolute_import
from __future__ import division
from tensorflow.python.framework import ops
from tensorflow.python.framework import tensor_shape
from tensorflow.python.keras._impl.keras import backend as K
-from tensorflow.python.keras._impl.keras import constraints
-from tensorflow.python.keras._impl.keras import initializers
-from tensorflow.python.keras._impl.keras import regularizers
-from tensorflow.python.keras._impl.keras.utils import conv_utils
+from tensorflow.python.keras._impl.keras.engine import base_layer
+from tensorflow.python.keras._impl.keras.engine import saving
+from tensorflow.python.keras._impl.keras.utils import generic_utils
from tensorflow.python.keras._impl.keras.utils.io_utils import ask_to_proceed_with_overwrite
from tensorflow.python.keras._impl.keras.utils.layer_utils import print_summary as print_layer_summary
from tensorflow.python.layers import base as tf_base_layers
from tensorflow.python.layers import utils as tf_layers_util
-from tensorflow.python.ops import array_ops
from tensorflow.python.platform import tf_logging as logging
from tensorflow.python.util import nest
from tensorflow.python.util import tf_inspect
-from tensorflow.python.util.tf_export import tf_export
# pylint: disable=g-import-not-at-top
yaml = None
# pylint: enable=g-import-not-at-top
-# pylint: disable=invalid-name
-InputSpec = tf_base_layers.InputSpec
-Node = tf_base_layers.Node
-TFBaseLayer = tf_base_layers.Layer
-# pylint: enable=invalid-name
-
-
-@tf_export('keras.layers.Layer')
-class Layer(tf_base_layers.Layer):
- """Abstract base layer class.
-
- # Properties
- name: String, must be unique within a model.
- input_spec: List of InputSpec class instances
- each entry describes one required input:
- - ndim
- - dtype
- A layer with `n` input tensors must have
- an `input_spec` of length `n`.
- trainable: Boolean, whether the layer weights
- will be updated during training.
- uses_learning_phase: Whether any operation
- of the layer uses `K.in_training_phase()`
- or `K.in_test_phase()`.
- input_shape: Shape tuple. Provided for convenience,
- but note that there may be cases in which this
- attribute is ill-defined (e.g. a shared layer
- with multiple input shapes), in which case
- requesting `input_shape` will raise an Exception.
- Prefer using `layer.get_input_shape_for(input_shape)`,
- or `layer.get_input_shape_at(node_index)`.
- output_shape: Shape tuple. See above.
- inbound_nodes: List of nodes.
- outbound_nodes: List of nodes.
- input, output: Input/output tensor(s). Note that if the layer is used
- more than once (shared layer), this is ill-defined
- and will raise an exception. In such cases, use
- `layer.get_input_at(node_index)`.
- input_mask, output_mask: Same as above, for masks.
- trainable_weights: List of variables.
- non_trainable_weights: List of variables.
- weights: The concatenation of the lists trainable_weights and
- non_trainable_weights (in this order).
-
- # Methods
- call(x, mask=None): Where the layer's logic lives.
- __call__(x, mask=None): Wrapper around the layer logic (`call`).
- If x is a Keras tensor:
- - Connect current layer with last layer from tensor:
- `self._add_inbound_node(last_layer)`
- - Add layer to tensor history
- If layer is not built:
- - Build from inputs shape
- get_weights()
- set_weights(weights)
- get_config()
- count_params()
- compute_output_shape(input_shape)
- compute_mask(x, mask)
- get_input_at(node_index)
- get_output_at(node_index)
- get_input_shape_at(node_index)
- get_output_shape_at(node_index)
- get_input_mask_at(node_index)
- get_output_mask_at(node_index)
-
- # Class Methods
- from_config(config)
-
- # Internal methods:
- build(input_shape)
- _add_inbound_node(layer, index=0)
- """
-
- def __init__(self, **kwargs):
- # These properties should be set by the user via keyword arguments.
- # note that 'dtype', 'input_shape' and 'batch_input_shape'
- # are only applicable to input layers: do not pass these keywords
- # to non-input layers.
- allowed_kwargs = {
- 'activity_regularizer',
- 'input_shape',
- 'batch_input_shape',
- 'batch_size',
- 'dtype',
- 'name',
- 'trainable',
- 'weights',
- }
- # Validate optional keyword arguments.
- for kwarg in kwargs:
- if kwarg not in allowed_kwargs:
- raise TypeError('Keyword argument not understood:', kwarg)
-
- # Get layer name.
- name = kwargs.get('name')
-
- # Get `trainable` status.
- trainable = kwargs.get('trainable', True)
-
- # Get `dtype`.
- dtype = kwargs.get('dtype')
- if dtype is None:
- dtype = K.floatx()
-
- # Call super, which will set all properties common to Keras layers
- # and core TF layers.
- super(Layer, self).__init__(
- name=name, dtype=dtype, trainable=trainable,
- activity_regularizer=kwargs.get('activity_regularizer'))
-
- # Add properties that are Keras-only for now.
- self.supports_masking = False
-
- # Manage input shape information if passed.
- if 'input_shape' in kwargs or 'batch_input_shape' in kwargs:
- # In this case we will later create an input layer
- # to insert before the current layer
- if 'batch_input_shape' in kwargs:
- batch_input_shape = tuple(kwargs['batch_input_shape'])
- elif 'input_shape' in kwargs:
- if 'batch_size' in kwargs:
- batch_size = kwargs['batch_size']
- else:
- batch_size = None
- batch_input_shape = (batch_size,) + tuple(kwargs['input_shape'])
- self._batch_input_shape = batch_input_shape
-
- # Manage initial weight values if passed.
- if 'weights' in kwargs:
- self._initial_weights = kwargs['weights']
- else:
- self._initial_weights = None
-
- def add_weight(self,
- name,
- shape,
- dtype=None,
- initializer=None,
- regularizer=None,
- trainable=True,
- constraint=None):
- """Adds a weight variable to the layer.
-
- Arguments:
- name: String, the name for the weight variable.
- shape: The shape tuple of the weight.
- dtype: The dtype of the weight.
- initializer: An Initializer instance (callable).
- regularizer: An optional Regularizer instance.
- trainable: A boolean, whether the weight should
- be trained via backprop or not (assuming
- that the layer itself is also trainable).
- constraint: An optional Constraint instance.
-
- Returns:
- The created weight variable.
- """
- if dtype is None:
- dtype = K.floatx()
- weight = self.add_variable(name, shape,
- dtype=dtype,
- initializer=initializers.get(initializer),
- regularizer=regularizers.get(regularizer),
- constraint=constraints.get(constraint),
- trainable=trainable)
- return weight
-
- def call(self, inputs, **kwargs): # pylint: disable=unused-argument
- """This is where the layer's logic lives.
-
- Arguments:
- inputs: Input tensor, or list/tuple of input tensors.
- **kwargs: Additional keyword arguments.
-
- Returns:
- A tensor or list/tuple of tensors.
- """
- return inputs
-
- def __call__(self, inputs, **kwargs):
- """Wrapper around self.call(), for handling internal references.
-
- If a Keras tensor is passed:
- - We call self._add_inbound_node().
- - If necessary, we `build` the layer to match
- the shape of the input(s).
- - We update the _keras_history of the output tensor(s)
- with the current layer.
- This is done as part of _add_inbound_node().
-
- Arguments:
- inputs: Can be a tensor or list/tuple of tensors.
- **kwargs: Additional keyword arguments to be passed to `call()`.
-
- Returns:
- Output of the layer's `call` method.
-
- Raises:
- ValueError: in case the layer is missing shape information
- for its `build` call.
- """
- # Actually call the layer (optionally building it).
- output = super(Layer, self).__call__(inputs, **kwargs)
- if context.in_eager_mode():
- return output
-
- # Un-built subclassed network: build it
- if isinstance(self, Network) and not self.inputs:
- self._set_inputs(inputs, training=kwargs.get('training'))
-
- # Update learning phase info.
- output_tensors = to_list(output)
- uses_lp = any(
- [getattr(x, '_uses_learning_phase', False) for x in to_list(inputs)])
- uses_lp = getattr(self, 'uses_learning_phase', False) or uses_lp
- for i in range(len(output_tensors)):
- output_tensors[i]._uses_learning_phase = getattr(
- output_tensors[i], '_uses_learning_phase', False) or uses_lp
-
- # Optionally load weight values that were specified at layer instantiation.
- if hasattr(self, '_initial_weights') and self._initial_weights is not None:
- self.set_weights(self._initial_weights)
- del self._initial_weights
- return output
-
- def compute_output_shape(self, input_shape):
- """Computes the output shape of the layer.
-
- Assumes that the layer will be built
- to match that input shape provided.
-
- Arguments:
- input_shape: Shape tuple (tuple of integers)
- or list of shape tuples (one per output tensor of the layer).
- Shape tuples can include None for free dimensions,
- instead of an integer.
-
- Returns:
- An input shape tuple.
- """
- logging.warning(
- 'All custom layers should implement the '
- '`compute_output_shape` method. This layer (' + self.name + ') '
- 'is relying on the base `Layer.compute_output_shape` implementation, '
- 'which will start raising a `NotImplementedError` '
- 'as of July 1st, 2018.')
- return input_shape
-
- def compute_mask(self, inputs, mask=None): # pylint: disable=unused-argument
- """Computes an output mask tensor.
-
- Arguments:
- inputs: Tensor or list of tensors.
- mask: Tensor or list of tensors.
-
- Returns:
- None or a tensor (or list of tensors,
- one per output tensor of the layer).
- """
- if not self.supports_masking:
- if mask is not None:
- if isinstance(mask, list):
- if any(m is not None for m in mask):
- raise TypeError('Layer ' + self.name + ' does not support masking, '
- 'but was passed an input_mask: ' + str(mask))
- else:
- raise TypeError('Layer ' + self.name + ' does not support masking, '
- 'but was passed an input_mask: ' + str(mask))
- # masking not explicitly supported: return None as mask
- return None
- # if masking is explicitly supported, by default
- # carry over the input mask
- return mask
-
- def get_input_mask_at(self, node_index):
- """Retrieves the input mask tensor(s) of a layer at a given node.
-
- Arguments:
- node_index: Integer, index of the node
- from which to retrieve the attribute.
- E.g. `node_index=0` will correspond to the
- first time the layer was called.
-
- Returns:
- A mask tensor
- (or list of tensors if the layer has multiple inputs).
- """
- inputs = self.get_input_at(node_index)
- if isinstance(inputs, list):
- return [getattr(x, '_keras_mask', None) for x in inputs]
- else:
- return getattr(inputs, '_keras_mask', None)
-
- def get_output_mask_at(self, node_index):
- """Retrieves the output mask tensor(s) of a layer at a given node.
-
- Arguments:
- node_index: Integer, index of the node
- from which to retrieve the attribute.
- E.g. `node_index=0` will correspond to the
- first time the layer was called.
-
- Returns:
- A mask tensor
- (or list of tensors if the layer has multiple outputs).
- """
- output = self.get_output_at(node_index)
- if isinstance(output, list):
- return [getattr(x, '_keras_mask', None) for x in output]
- else:
- return getattr(output, '_keras_mask', None)
-
- @property
- def input_mask(self):
- """Retrieves the input mask tensor(s) of a layer.
-
- Only applicable if the layer has exactly one inbound node,
- i.e. if it is connected to one incoming layer.
-
- Returns:
- Input mask tensor (potentially None) or list of input
- mask tensors.
-
- Raises:
- AttributeError: if the layer is connected to
- more than one incoming layers.
- """
- inputs = self.input
- if isinstance(inputs, list):
- return [getattr(x, '_keras_mask', None) for x in inputs]
- else:
- return getattr(inputs, '_keras_mask', None)
-
- @property
- def output_mask(self):
- """Retrieves the output mask tensor(s) of a layer.
-
- Only applicable if the layer has exactly one inbound node,
- i.e. if it is connected to one incoming layer.
-
- Returns:
- Output mask tensor (potentially None) or list of output
- mask tensors.
-
- Raises:
- AttributeError: if the layer is connected to
- more than one incoming layers.
- """
- output = self.output
- if isinstance(output, list):
- return [getattr(x, '_keras_mask', None) for x in output]
- else:
- return getattr(output, '_keras_mask', None)
-
- def set_weights(self, weights):
- """Sets the weights of the layer, from Numpy arrays.
-
- Arguments:
- weights: a list of Numpy arrays. The number
- of arrays and their shape must match
- number of the dimensions of the weights
- of the layer (i.e. it should match the
- output of `get_weights`).
-
- Raises:
- ValueError: If the provided weights list does not match the
- layer's specifications.
- """
- params = self.weights
- if len(params) != len(weights):
- raise ValueError('You called `set_weights(weights)` on layer "' +
- self.name + '" with a weight list of length ' +
- str(len(weights)) + ', but the layer was expecting ' +
- str(len(params)) + ' weights. Provided weights: ' +
- str(weights)[:50] + '...')
- if not params:
- return
- weight_value_tuples = []
- param_values = K.batch_get_value(params)
- for pv, p, w in zip(param_values, params, weights):
- if pv.shape != w.shape:
- raise ValueError('Layer weight shape ' + str(pv.shape) +
- ' not compatible with '
- 'provided weight shape ' + str(w.shape))
- weight_value_tuples.append((p, w))
- K.batch_set_value(weight_value_tuples)
-
- def get_weights(self):
- """Returns the current weights of the layer.
-
- Returns:
- Weights values as a list of numpy arrays.
- """
- params = self.weights
- return K.batch_get_value(params)
-
- def get_config(self):
- """Returns the config of the layer.
-
- A layer config is a Python dictionary (serializable)
- containing the configuration of a layer.
- The same layer can be reinstantiated later
- (without its trained weights) from this configuration.
-
- The config of a layer does not include connectivity
- information, nor the layer class name. These are handled
- by `Network` (one layer of abstraction above).
-
- Returns:
- Python dictionary.
- """
- config = {'name': self.name, 'trainable': self.trainable}
- if hasattr(self, '_batch_input_shape'):
- config['batch_input_shape'] = self._batch_input_shape
- if hasattr(self, 'dtype'):
- config['dtype'] = self.dtype
- return config
-
- @classmethod
- def from_config(cls, config):
- """Creates a layer from its config.
-
- This method is the reverse of `get_config`,
- capable of instantiating the same layer from the config
- dictionary. It does not handle layer connectivity
- (handled by Network), nor weights (handled by `set_weights`).
-
- Arguments:
- config: A Python dictionary, typically the
- output of get_config.
-
- Returns:
- A layer instance.
- """
- return cls(**config)
-
- @tf_base_layers.Layer.activity_regularizer.setter
- def activity_regularizer(self, activity_regularizer):
- self._activity_regularizer = activity_regularizer
+class Network(base_layer.Layer):
+ """A `Network` is a composition of layers.
-class InputLayer(Layer):
- """Layer to be used as an entry point into a Network (a graph of layers).
-
- It can either wrap an existing tensor (pass an `input_tensor` argument)
- or create its a placeholder tensor (pass arguments `input_shape`, and
- optionally, `dtype`).
-
- It is generally recommend to use the functional layer API via `Input`,
- (which creates an `InputLayer`) without directly using `InputLayer`.
-
- Arguments:
- input_shape: Shape tuple (not including the batch axis), or `TensorShape`
- instance (not including the batch axis).
- batch_size: Optional input batch size (integer or None).
- dtype: Datatype of the input.
- input_tensor: Optional tensor to use as layer input
- instead of creating a placeholder.
- sparse: Boolean, whether the placeholder created
- is meant to be sparse.
- name: Name of the layer (string).
- """
-
- def __init__(self,
- input_shape=None,
- batch_size=None,
- dtype=None,
- input_tensor=None,
- sparse=False,
- name=None,
- **kwargs):
- if 'batch_input_shape' in kwargs:
- batch_input_shape = kwargs.pop('batch_input_shape')
- if input_shape and batch_input_shape:
- raise ValueError('Only provide the input_shape OR '
- 'batch_input_shape argument to '
- 'InputLayer, not both at the same time.')
- batch_size = batch_input_shape[0]
- input_shape = batch_input_shape[1:]
- if kwargs:
- raise ValueError('Unrecognized keyword arguments:', kwargs.keys())
-
- if not name:
- prefix = 'input'
- name = prefix + '_' + str(K.get_uid(prefix))
-
- if not dtype:
- if input_tensor is None:
- dtype = K.floatx()
- else:
- dtype = K.dtype(input_tensor)
- super(InputLayer, self).__init__(dtype=dtype, name=name)
- self.built = True
- self.sparse = sparse
- self.batch_size = batch_size
-
- if isinstance(input_shape, tensor_shape.TensorShape):
- input_shape = tuple(input_shape.as_list())
-
- if input_tensor is None:
- if input_shape is not None:
- batch_input_shape = (batch_size,) + tuple(input_shape)
- else:
- batch_input_shape = None
-
- if context.in_eager_mode():
- # In eager mode, create a temporary placeholder to call the layer on.
- input_tensor = tf_base_layers._DeferredTensor( # pylint: disable=protected-access
- shape=batch_input_shape,
- dtype=dtype,
- name=self.name)
- else:
- # In graph mode, create a graph placeholder to call the layer on.
- if sparse:
- input_tensor = array_ops.sparse_placeholder(
- shape=batch_input_shape,
- dtype=dtype,
- name=self.name)
- else:
- input_tensor = array_ops.placeholder(
- shape=batch_input_shape,
- dtype=dtype,
- name=self.name)
-
- # For compatibility with Keras API.
- self.is_placeholder = True
- self._batch_input_shape = batch_input_shape
- else:
- # For compatibility with Keras API.
- self.is_placeholder = False
- self._batch_input_shape = tuple(input_tensor.get_shape().as_list())
-
- # Create an input node to add to self.outbound_node
- # and set output_tensors' _keras_history.
- input_tensor._keras_history = (self, 0, 0) # pylint: disable=protected-access
- tf_base_layers.Node(
- self,
- inbound_layers=[],
- node_indices=[],
- tensor_indices=[],
- input_tensors=[input_tensor],
- output_tensors=[input_tensor])
-
- def get_config(self):
- config = {
- 'batch_input_shape': self._batch_input_shape,
- 'dtype': self.dtype,
- 'sparse': self.sparse,
- 'name': self.name
- }
- return config
-
-
-@tf_export('keras.layers.Input', 'keras.Input')
-def Input( # pylint: disable=invalid-name
- shape=None,
- batch_size=None,
- name=None,
- dtype=None,
- sparse=False,
- tensor=None,
- **kwargs):
- """`Input()` is used to instantiate a Keras tensor.
-
- A Keras tensor is a tensor object from the underlying backend
- (Theano or TensorFlow), which we augment with certain
- attributes that allow us to build a Keras model
- just by knowing the inputs and outputs of the model.
-
- For instance, if a, b and c are Keras tensors,
- it becomes possible to do:
- `model = Model(input=[a, b], output=c)`
-
- The added Keras attribute is:
- `_keras_history`: Last layer applied to the tensor.
- the entire layer graph is retrievable from that layer,
- recursively.
-
- Arguments:
- shape: A shape tuple (integers), not including the batch size.
- For instance, `shape=(32,)` indicates that the expected input
- will be batches of 32-dimensional vectors.
- batch_size: optional static batch size (integer).
- name: An optional name string for the layer.
- Should be unique in a model (do not reuse the same name twice).
- It will be autogenerated if it isn't provided.
- dtype: The data type expected by the input, as a string
- (`float32`, `float64`, `int32`...)
- sparse: A boolean specifying whether the placeholder
- to be created is sparse.
- tensor: Optional existing tensor to wrap into the `Input` layer.
- If set, the layer will not create a placeholder tensor.
- **kwargs: deprecated arguments support.
-
- Returns:
- A tensor.
-
- Example:
-
- ```python
- # this is a logistic regression in Keras
- x = Input(shape=(32,))
- y = Dense(16, activation='softmax')(x)
- model = Model(x, y)
- ```
-
- Raises:
- ValueError: in case of invalid arguments.
- """
- if 'batch_shape' in kwargs:
- batch_shape = kwargs.pop('batch_shape')
- if shape and batch_shape:
- raise ValueError('Only provide the shape OR '
- 'batch_shape argument to '
- 'Input, not both at the same time.')
- batch_size = batch_shape[0]
- shape = batch_shape[1:]
- if kwargs:
- raise ValueError('Unrecognized keyword arguments:', kwargs.keys())
-
- if dtype is None:
- dtype = K.floatx()
- if not shape and tensor is None:
- raise ValueError('Please provide to Input either a `shape`'
- ' or a `tensor` argument. Note that '
- '`shape` does not include the batch '
- 'dimension.')
- input_layer = InputLayer(
- input_shape=shape,
- batch_size=batch_size,
- name=name,
- dtype=dtype,
- sparse=sparse,
- input_tensor=tensor)
- # Return tensor including `_keras_history`.
- # Note that in this case train_output and test_output are the same pointer.
- outputs = input_layer._inbound_nodes[0].output_tensors
- if len(outputs) == 1:
- return outputs[0]
- else:
- return outputs
-
-
-class Network(Layer):
- """A Network is a directed acyclic graph of layers.
-
- It is the topological form of a "model". A Model
- is simply a Network with added training routines.
-
- # Properties
- name
- inputs
- outputs
- input_layers
- output_layers
- input_spec (list of class instances)
- each entry describes one required input:
- - ndim
- - dtype
- trainable (boolean)
- input_shape
- output_shape
- inbound_nodes: list of nodes
- outbound_nodes: list of nodes
- trainable_weights (list of variables)
- non_trainable_weights (list of variables)
-
- # Methods
- summary
- get_layer
- get_weights
- set_weights
- get_config
- compute_output_shape
-
- # Class Methods
- from_config
+ It is the topological form of a "model". A `Model`
+ is simply a `Network` with added training routines.
"""
def __init__(self, *args, **kwargs): # pylint: disable=super-init-not-called
if not self._is_graph_network:
return None
- inputs = to_list(inputs)
+ inputs = generic_utils.to_list(inputs)
if mask is None:
masks = [None for _ in range(len(inputs))]
else:
- masks = to_list(mask)
+ masks = generic_utils.to_list(mask)
cache_key = (tf_layers_util.object_list_uid(inputs)
+ '_' + tf_layers_util.object_list_uid(masks))
if cache_key in self._output_mask_cache:
if not proceed:
return
with h5py.File(filepath, 'w') as f:
- save_weights_to_hdf5_group(f, self.layers)
+ saving.save_weights_to_hdf5_group(f, self.layers)
def load_weights(self, filepath, by_name=False):
"""Loads all layer weights from a HDF5 save file.
if 'layer_names' not in f.attrs and 'model_weights' in f:
f = f['model_weights']
if by_name:
- load_weights_from_hdf5_group_by_name(f, self.layers)
+ saving.load_weights_from_hdf5_group_by_name(f, self.layers)
else:
- load_weights_from_hdf5_group(f, self.layers)
+ saving.load_weights_from_hdf5_group(f, self.layers)
def _updated_config(self):
"""Util hared between different serialization methods.
return source_tensors
-def to_list(x):
- """Normalizes a list/tensor into a list.
-
- If a tensor is passed, we return
- a list of size 1 containing the tensor.
-
- Arguments:
- x: target object to be normalized.
-
- Returns:
- A list.
- """
- if isinstance(x, list):
- return x
- return [x]
-
-
-def save_weights_to_hdf5_group(f, layers):
- from tensorflow.python.keras._impl.keras import __version__ as keras_version # pylint: disable=g-import-not-at-top
-
- f.attrs['layer_names'] = [layer.name.encode('utf8') for layer in layers]
- f.attrs['backend'] = K.backend().encode('utf8')
- f.attrs['keras_version'] = str(keras_version).encode('utf8')
-
- for layer in layers:
- g = f.create_group(layer.name)
- symbolic_weights = layer.weights
- weight_values = K.batch_get_value(symbolic_weights)
- weight_names = []
- for i, (w, val) in enumerate(zip(symbolic_weights, weight_values)):
- if hasattr(w, 'name') and w.name:
- name = str(w.name)
- else:
- name = 'param_' + str(i)
- weight_names.append(name.encode('utf8'))
- g.attrs['weight_names'] = weight_names
- for name, val in zip(weight_names, weight_values):
- param_dset = g.create_dataset(name, val.shape, dtype=val.dtype)
- if not val.shape:
- # scalar
- param_dset[()] = val
- else:
- param_dset[:] = val
-
-
-def preprocess_weights_for_loading(layer,
- weights,
- original_keras_version=None,
- original_backend=None):
- """Converts layers weights from Keras 1 format to Keras 2.
-
- Arguments:
- layer: Layer instance.
- weights: List of weights values (Numpy arrays).
- original_keras_version: Keras version for the weights, as a string.
- original_backend: Keras backend the weights were trained with,
- as a string.
-
- Returns:
- A list of weights values (Numpy arrays).
- """
- if layer.__class__.__name__ == 'Bidirectional':
- num_weights_per_layer = len(weights) // 2
- forward_weights = preprocess_weights_for_loading(
- layer.forward_layer, weights[:num_weights_per_layer],
- original_keras_version, original_backend)
- backward_weights = preprocess_weights_for_loading(
- layer.backward_layer, weights[num_weights_per_layer:],
- original_keras_version, original_backend)
- weights = forward_weights + backward_weights
-
- if original_keras_version == '1':
- if layer.__class__.__name__ == 'TimeDistributed':
- weights = preprocess_weights_for_loading(
- layer.layer, weights, original_keras_version, original_backend)
-
- if layer.__class__.__name__ == 'Conv1D':
- shape = weights[0].shape
- # Handle Keras 1.1 format
- if shape[:2] != (layer.kernel_size[0], 1) or shape[3] != layer.filters:
- # Legacy shape:
- # (filters, input_dim, filter_length, 1)
- assert shape[0] == layer.filters and shape[2:] == (layer.kernel_size[0],
- 1)
- weights[0] = np.transpose(weights[0], (2, 3, 1, 0))
- weights[0] = weights[0][:, 0, :, :]
-
- if layer.__class__.__name__ == 'Conv2D':
- if layer.data_format == 'channels_first':
- # old: (filters, stack_size, kernel_rows, kernel_cols)
- # new: (kernel_rows, kernel_cols, stack_size, filters)
- weights[0] = np.transpose(weights[0], (2, 3, 1, 0))
-
- if layer.__class__.__name__ == 'Conv2DTranspose':
- if layer.data_format == 'channels_last':
- # old: (kernel_rows, kernel_cols, stack_size, filters)
- # new: (kernel_rows, kernel_cols, filters, stack_size)
- weights[0] = np.transpose(weights[0], (0, 1, 3, 2))
- if layer.data_format == 'channels_first':
- # old: (filters, stack_size, kernel_rows, kernel_cols)
- # new: (kernel_rows, kernel_cols, filters, stack_size)
- weights[0] = np.transpose(weights[0], (2, 3, 0, 1))
-
- if layer.__class__.__name__ == 'Conv3D':
- if layer.data_format == 'channels_first':
- # old: (filters, stack_size, ...)
- # new: (..., stack_size, filters)
- weights[0] = np.transpose(weights[0], (2, 3, 4, 1, 0))
-
- if layer.__class__.__name__ == 'GRU':
- if len(weights) == 9:
- kernel = np.concatenate([weights[0], weights[3], weights[6]], axis=-1)
- recurrent_kernel = np.concatenate(
- [weights[1], weights[4], weights[7]], axis=-1)
- bias = np.concatenate([weights[2], weights[5], weights[8]], axis=-1)
- weights = [kernel, recurrent_kernel, bias]
-
- if layer.__class__.__name__ == 'LSTM':
- if len(weights) == 12:
- # old: i, c, f, o
- # new: i, f, c, o
- kernel = np.concatenate(
- [weights[0], weights[6], weights[3], weights[9]], axis=-1)
- recurrent_kernel = np.concatenate(
- [weights[1], weights[7], weights[4], weights[10]], axis=-1)
- bias = np.concatenate(
- [weights[2], weights[8], weights[5], weights[11]], axis=-1)
- weights = [kernel, recurrent_kernel, bias]
-
- if layer.__class__.__name__ == 'ConvLSTM2D':
- if len(weights) == 12:
- kernel = np.concatenate(
- [weights[0], weights[6], weights[3], weights[9]], axis=-1)
- recurrent_kernel = np.concatenate(
- [weights[1], weights[7], weights[4], weights[10]], axis=-1)
- bias = np.concatenate(
- [weights[2], weights[8], weights[5], weights[11]], axis=-1)
- if layer.data_format == 'channels_first':
- # old: (filters, stack_size, kernel_rows, kernel_cols)
- # new: (kernel_rows, kernel_cols, stack_size, filters)
- kernel = np.transpose(kernel, (2, 3, 1, 0))
- recurrent_kernel = np.transpose(recurrent_kernel, (2, 3, 1, 0))
- weights = [kernel, recurrent_kernel, bias]
-
- if layer.__class__.__name__ in ['Model', 'Sequential']:
- new_weights = []
- # trainable weights
- for sublayer in layer.layers:
- num_weights = len(sublayer.trainable_weights)
- if num_weights > 0:
- new_weights.extend(
- preprocess_weights_for_loading(
- layer=sublayer,
- weights=weights[:num_weights],
- original_keras_version=original_keras_version,
- original_backend=original_backend))
- weights = weights[num_weights:]
-
- # non-trainable weights
- for sublayer in layer.layers:
- num_weights = len([
- l for l in sublayer.weights if l not in sublayer.trainable_weights
- ])
- if num_weights > 0:
- new_weights.extend(
- preprocess_weights_for_loading(
- layer=sublayer,
- weights=weights[:num_weights],
- original_keras_version=original_keras_version,
- original_backend=original_backend))
- weights = weights[num_weights:]
- weights = new_weights
-
- conv_layers = ['Conv1D', 'Conv2D', 'Conv3D', 'Conv2DTranspose', 'ConvLSTM2D']
- if layer.__class__.__name__ in conv_layers:
- if original_backend == 'theano':
- weights[0] = conv_utils.convert_kernel(weights[0])
- if layer.__class__.__name__ == 'ConvLSTM2D':
- weights[1] = conv_utils.convert_kernel(weights[1])
- if K.int_shape(layer.weights[0]) != weights[0].shape:
- weights[0] = np.transpose(weights[0], (3, 2, 0, 1))
- if layer.__class__.__name__ == 'ConvLSTM2D':
- weights[1] = np.transpose(weights[1], (3, 2, 0, 1))
-
- # Convert the weights of CuDNNLSTM so that they could be loaded into LSTM
- if layer.__class__.__name__ == 'LSTM' and len(weights) == 3:
- # Determine if loading a CuDNNLSTM layer from the number of bias weights:
- # CuDNNLSTM has (units * 8) weights; while LSTM has (units * 4)
- # if there's no bias weight in the file, skip this conversion
- units = weights[1].shape[0]
- bias = weights[2]
- if len(bias) == units * 8:
- # reshape the kernels
- kernels = np.split(weights[0], 4, axis=1)
- kernels = [
- kernel.reshape(-1).reshape(kernel.shape, order='F')
- for kernel in kernels
- ]
- weights[0] = np.concatenate(kernels, axis=1)
-
- # transpose the recurrent kernels
- recurrent_kernels = np.split(weights[1], 4, axis=1)
- recurrent_kernels = [kernel.T for kernel in recurrent_kernels]
- weights[1] = np.concatenate(recurrent_kernels, axis=1)
-
- # split the bias into half and merge
- weights[2] = bias[:units * 4] + bias[units * 4:]
-
- return weights
-
-
-def load_weights_from_hdf5_group(f, layers):
- """Implements topological (order-based) weight loading.
-
- Arguments:
- f: A pointer to a HDF5 group.
- layers: a list of target layers.
-
- Raises:
- ValueError: in case of mismatch between provided layers
- and weights file.
- """
- if 'keras_version' in f.attrs:
- original_keras_version = f.attrs['keras_version'].decode('utf8')
- else:
- original_keras_version = '1'
- if 'backend' in f.attrs:
- original_backend = f.attrs['backend'].decode('utf8')
- else:
- original_backend = None
-
- filtered_layers = []
- for layer in layers:
- weights = layer.weights
- if weights:
- filtered_layers.append(layer)
-
- layer_names = [n.decode('utf8') for n in f.attrs['layer_names']]
- filtered_layer_names = []
- for name in layer_names:
- g = f[name]
- weight_names = [n.decode('utf8') for n in g.attrs['weight_names']]
- if weight_names:
- filtered_layer_names.append(name)
- layer_names = filtered_layer_names
- if len(layer_names) != len(filtered_layers):
- raise ValueError('You are trying to load a weight file '
- 'containing ' + str(len(layer_names)) +
- ' layers into a model with ' + str(len(filtered_layers)) +
- ' layers.')
-
- # We batch weight value assignments in a single backend call
- # which provides a speedup in TensorFlow.
- weight_value_tuples = []
- for k, name in enumerate(layer_names):
- g = f[name]
- weight_names = [n.decode('utf8') for n in g.attrs['weight_names']]
- weight_values = [g[weight_name] for weight_name in weight_names]
- layer = filtered_layers[k]
- symbolic_weights = layer.weights
- weight_values = preprocess_weights_for_loading(
- layer, weight_values, original_keras_version, original_backend)
- if len(weight_values) != len(symbolic_weights):
- raise ValueError('Layer #' + str(k) + ' (named "' + layer.name +
- '" in the current model) was found to '
- 'correspond to layer ' + name + ' in the save file. '
- 'However the new layer ' + layer.name + ' expects ' +
- str(len(symbolic_weights)) +
- ' weights, but the saved weights have ' +
- str(len(weight_values)) + ' elements.')
- weight_value_tuples += zip(symbolic_weights, weight_values)
- K.batch_set_value(weight_value_tuples)
-
-
-def load_weights_from_hdf5_group_by_name(f, layers):
- """Implements name-based weight loading.
-
- (instead of topological weight loading).
-
- Layers that have no matching name are skipped.
-
- Arguments:
- f: A pointer to a HDF5 group.
- layers: a list of target layers.
-
- Raises:
- ValueError: in case of mismatch between provided layers
- and weights file.
- """
- if 'keras_version' in f.attrs:
- original_keras_version = f.attrs['keras_version'].decode('utf8')
- else:
- original_keras_version = '1'
- if 'backend' in f.attrs:
- original_backend = f.attrs['backend'].decode('utf8')
- else:
- original_backend = None
-
- # New file format.
- layer_names = [n.decode('utf8') for n in f.attrs['layer_names']]
-
- # Reverse index of layer name to list of layers with name.
- index = {}
- for layer in layers:
- if layer.name:
- index.setdefault(layer.name, []).append(layer)
-
- # We batch weight value assignments in a single backend call
- # which provides a speedup in TensorFlow.
- weight_value_tuples = []
- for k, name in enumerate(layer_names):
- g = f[name]
- weight_names = [n.decode('utf8') for n in g.attrs['weight_names']]
- weight_values = [g[weight_name] for weight_name in weight_names]
-
- for layer in index.get(name, []):
- symbolic_weights = layer.weights
- weight_values = preprocess_weights_for_loading(
- layer, weight_values, original_keras_version, original_backend)
- if len(weight_values) != len(symbolic_weights):
- raise ValueError('Layer #' + str(k) + ' (named "' + layer.name +
- '") expects ' + str(len(symbolic_weights)) +
- ' weight(s), but the saved weights' + ' have ' +
- str(len(weight_values)) + ' element(s).')
- # Set values.
- for i in range(len(weight_values)):
- weight_value_tuples.append((symbolic_weights[i], weight_values[i]))
- K.batch_set_value(weight_value_tuples)
-
-
-def shape_type_conversion(fn):
- """Decorator that handles tuple/TensorShape conversion.
-
- Used in `compute_output_shape` and `build`.
-
- Arguments:
- fn: function to wrap.
-
- Returns:
- Wrapped function.
- """
-
- def wrapper(instance, input_shape):
- if input_shape is not None:
- if isinstance(input_shape, list):
- input_shape = [
- tuple(tensor_shape.TensorShape(x).as_list()) for x in input_shape]
- else:
- input_shape = tuple(tensor_shape.TensorShape(input_shape).as_list())
- output_shape = fn(instance, input_shape)
- if output_shape is not None:
- if isinstance(output_shape, list):
- return [tensor_shape.TensorShape(x) for x in output_shape]
- return tensor_shape.TensorShape(output_shape)
-
- return wrapper
-
-
def _make_node_key(layer_name, node_index):
return layer_name + '_ib-' + str(node_index)
--- /dev/null
+# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+# pylint: disable=protected-access
+"""Model saving utilities.
+"""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import json
+import os
+
+import numpy as np
+from six.moves import zip # pylint: disable=redefined-builtin
+
+from tensorflow.python.keras._impl.keras import backend as K
+from tensorflow.python.keras._impl.keras import optimizers
+from tensorflow.python.keras._impl.keras.utils import conv_utils
+from tensorflow.python.keras._impl.keras.utils.io_utils import ask_to_proceed_with_overwrite
+from tensorflow.python.platform import tf_logging as logging
+from tensorflow.python.util.tf_export import tf_export
+
+# pylint: disable=g-import-not-at-top
+try:
+ import h5py
+except ImportError:
+ h5py = None
+
+try:
+ import yaml
+except ImportError:
+ yaml = None
+# pylint: enable=g-import-not-at-top
+
+
+@tf_export('keras.models.save_model')
+def save_model(model, filepath, overwrite=True, include_optimizer=True):
+ """Save a model to a HDF5 file.
+
+ The saved model contains:
+ - the model's configuration (topology)
+ - the model's weights
+ - the model's optimizer's state (if any)
+
+ Thus the saved model can be reinstantiated in
+ the exact same state, without any of the code
+ used for model definition or training.
+
+ Arguments:
+ model: Keras model instance to be saved.
+ filepath: String, path where to save the model.
+ overwrite: Whether we should overwrite any existing
+ model at the target location, or instead
+ ask the user with a manual prompt.
+ include_optimizer: If True, save optimizer's state together.
+
+ Raises:
+ ImportError: if h5py is not available.
+ """
+
+ if h5py is None:
+ raise ImportError('`save_model` requires h5py.')
+
+ def get_json_type(obj):
+ """Serialize any object to a JSON-serializable structure.
+
+ Arguments:
+ obj: the object to serialize
+
+ Returns:
+ JSON-serializable structure representing `obj`.
+
+ Raises:
+ TypeError: if `obj` cannot be serialized.
+ """
+ # if obj is a serializable Keras class instance
+ # e.g. optimizer, layer
+ if hasattr(obj, 'get_config'):
+ return {'class_name': obj.__class__.__name__, 'config': obj.get_config()}
+
+ # if obj is any numpy type
+ if type(obj).__module__ == np.__name__:
+ if isinstance(obj, np.ndarray):
+ return {'type': type(obj), 'value': obj.tolist()}
+ else:
+ return obj.item()
+
+ # misc functions (e.g. loss function)
+ if callable(obj):
+ return obj.__name__
+
+ # if obj is a python 'type'
+ if type(obj).__name__ == type.__name__:
+ return obj.__name__
+
+ raise TypeError('Not JSON Serializable:', obj)
+
+ from tensorflow.python.keras._impl.keras import __version__ as keras_version # pylint: disable=g-import-not-at-top
+
+ # If file exists and should not be overwritten.
+ if not overwrite and os.path.isfile(filepath):
+ proceed = ask_to_proceed_with_overwrite(filepath)
+ if not proceed:
+ return
+
+ with h5py.File(filepath, mode='w') as f:
+ f.attrs['keras_version'] = str(keras_version).encode('utf8')
+ f.attrs['backend'] = K.backend().encode('utf8')
+ f.attrs['model_config'] = json.dumps(
+ {
+ 'class_name': model.__class__.__name__,
+ 'config': model.get_config()
+ },
+ default=get_json_type).encode('utf8')
+
+ model_weights_group = f.create_group('model_weights')
+ model_layers = model.layers
+ save_weights_to_hdf5_group(model_weights_group, model_layers)
+
+ if include_optimizer and hasattr(model, 'optimizer'):
+ if isinstance(model.optimizer, optimizers.TFOptimizer):
+ logging.warning(
+ 'TensorFlow optimizers do not '
+ 'make it possible to access '
+ 'optimizer attributes or optimizer state '
+ 'after instantiation. '
+ 'As a result, we cannot save the optimizer '
+ 'as part of the model save file.'
+ 'You will have to compile your model again after loading it. '
+ 'Prefer using a Keras optimizer instead '
+ '(see keras.io/optimizers).')
+ else:
+ f.attrs['training_config'] = json.dumps(
+ {
+ 'optimizer_config': {
+ 'class_name': model.optimizer.__class__.__name__,
+ 'config': model.optimizer.get_config()
+ },
+ 'loss': model.loss,
+ 'metrics': model.metrics,
+ 'sample_weight_mode': model.sample_weight_mode,
+ 'loss_weights': model.loss_weights,
+ },
+ default=get_json_type).encode('utf8')
+
+ # Save optimizer weights.
+ symbolic_weights = getattr(model.optimizer, 'weights')
+ if symbolic_weights:
+ optimizer_weights_group = f.create_group('optimizer_weights')
+ weight_values = K.batch_get_value(symbolic_weights)
+ weight_names = []
+ for w, val in zip(symbolic_weights, weight_values):
+ name = str(w.name)
+ weight_names.append(name.encode('utf8'))
+ optimizer_weights_group.attrs['weight_names'] = weight_names
+ for name, val in zip(weight_names, weight_values):
+ param_dset = optimizer_weights_group.create_dataset(
+ name, val.shape, dtype=val.dtype)
+ if not val.shape:
+ # scalar
+ param_dset[()] = val
+ else:
+ param_dset[:] = val
+ f.flush()
+
+
+@tf_export('keras.models.load_model')
+def load_model(filepath, custom_objects=None, compile=True): # pylint: disable=redefined-builtin
+ """Loads a model saved via `save_model`.
+
+ Arguments:
+ filepath: String, path to the saved model.
+ custom_objects: Optional dictionary mapping names
+ (strings) to custom classes or functions to be
+ considered during deserialization.
+ compile: Boolean, whether to compile the model
+ after loading.
+
+ Returns:
+ A Keras model instance. If an optimizer was found
+ as part of the saved model, the model is already
+ compiled. Otherwise, the model is uncompiled and
+ a warning will be displayed. When `compile` is set
+ to False, the compilation is omitted without any
+ warning.
+
+ Raises:
+ ImportError: if h5py is not available.
+ ValueError: In case of an invalid savefile.
+ """
+ if h5py is None:
+ raise ImportError('`load_model` requires h5py.')
+
+ if not custom_objects:
+ custom_objects = {}
+
+ def convert_custom_objects(obj):
+ """Handles custom object lookup.
+
+ Arguments:
+ obj: object, dict, or list.
+
+ Returns:
+ The same structure, where occurrences
+ of a custom object name have been replaced
+ with the custom object.
+ """
+ if isinstance(obj, list):
+ deserialized = []
+ for value in obj:
+ deserialized.append(convert_custom_objects(value))
+ return deserialized
+ if isinstance(obj, dict):
+ deserialized = {}
+ for key, value in obj.items():
+ deserialized[key] = convert_custom_objects(value)
+ return deserialized
+ if obj in custom_objects:
+ return custom_objects[obj]
+ return obj
+
+ with h5py.File(filepath, mode='r') as f:
+ # instantiate model
+ model_config = f.attrs.get('model_config')
+ if model_config is None:
+ raise ValueError('No model found in config file.')
+ model_config = json.loads(model_config.decode('utf-8'))
+ model = model_from_config(model_config, custom_objects=custom_objects)
+
+ # set weights
+ load_weights_from_hdf5_group(f['model_weights'], model.layers)
+
+ # Early return if compilation is not required.
+ if not compile:
+ return model
+
+ # instantiate optimizer
+ training_config = f.attrs.get('training_config')
+ if training_config is None:
+ logging.warning('No training configuration found in save file: '
+ 'the model was *not* compiled. Compile it manually.')
+ return model
+ training_config = json.loads(training_config.decode('utf-8'))
+ optimizer_config = training_config['optimizer_config']
+ optimizer = optimizers.deserialize(
+ optimizer_config, custom_objects=custom_objects)
+
+ # Recover loss functions and metrics.
+ loss = convert_custom_objects(training_config['loss'])
+ metrics = convert_custom_objects(training_config['metrics'])
+ sample_weight_mode = training_config['sample_weight_mode']
+ loss_weights = training_config['loss_weights']
+
+ # Compile model.
+ model.compile(
+ optimizer=optimizer,
+ loss=loss,
+ metrics=metrics,
+ loss_weights=loss_weights,
+ sample_weight_mode=sample_weight_mode)
+
+ # Set optimizer weights.
+ if 'optimizer_weights' in f:
+ # Build train function (to get weight updates).
+ model._make_train_function()
+ optimizer_weights_group = f['optimizer_weights']
+ optimizer_weight_names = [
+ n.decode('utf8')
+ for n in optimizer_weights_group.attrs['weight_names']
+ ]
+ optimizer_weight_values = [
+ optimizer_weights_group[n] for n in optimizer_weight_names
+ ]
+ try:
+ model.optimizer.set_weights(optimizer_weight_values)
+ except ValueError:
+ logging.warning('Error in loading the saved optimizer '
+ 'state. As a result, your model is '
+ 'starting with a freshly initialized '
+ 'optimizer.')
+ return model
+
+
+@tf_export('keras.models.model_from_config')
+def model_from_config(config, custom_objects=None):
+ """Instantiates a Keras model from its config.
+
+ Arguments:
+ config: Configuration dictionary.
+ custom_objects: Optional dictionary mapping names
+ (strings) to custom classes or functions to be
+ considered during deserialization.
+
+ Returns:
+ A Keras model instance (uncompiled).
+
+ Raises:
+ TypeError: if `config` is not a dictionary.
+ """
+ if isinstance(config, list):
+ raise TypeError('`model_from_config` expects a dictionary, not a list. '
+ 'Maybe you meant to use '
+ '`Sequential.from_config(config)`?')
+ from tensorflow.python.keras._impl.keras.layers import deserialize # pylint: disable=g-import-not-at-top
+ return deserialize(config, custom_objects=custom_objects)
+
+
+@tf_export('keras.models.model_from_yaml')
+def model_from_yaml(yaml_string, custom_objects=None):
+ """Parses a yaml model configuration file and returns a model instance.
+
+ Arguments:
+ yaml_string: YAML string encoding a model configuration.
+ custom_objects: Optional dictionary mapping names
+ (strings) to custom classes or functions to be
+ considered during deserialization.
+
+ Returns:
+ A Keras model instance (uncompiled).
+
+ Raises:
+ ImportError: if yaml module is not found.
+ """
+ if yaml is None:
+ raise ImportError('Requires yaml module installed.')
+ config = yaml.load(yaml_string)
+ from tensorflow.python.keras._impl.keras.layers import deserialize # pylint: disable=g-import-not-at-top
+ return deserialize(config, custom_objects=custom_objects)
+
+
+@tf_export('keras.models.model_from_json')
+def model_from_json(json_string, custom_objects=None):
+ """Parses a JSON model configuration file and returns a model instance.
+
+ Arguments:
+ json_string: JSON string encoding a model configuration.
+ custom_objects: Optional dictionary mapping names
+ (strings) to custom classes or functions to be
+ considered during deserialization.
+
+ Returns:
+ A Keras model instance (uncompiled).
+ """
+ config = json.loads(json_string)
+ from tensorflow.python.keras._impl.keras.layers import deserialize # pylint: disable=g-import-not-at-top
+ return deserialize(config, custom_objects=custom_objects)
+
+
+def save_weights_to_hdf5_group(f, layers):
+ from tensorflow.python.keras._impl.keras import __version__ as keras_version # pylint: disable=g-import-not-at-top
+
+ f.attrs['layer_names'] = [layer.name.encode('utf8') for layer in layers]
+ f.attrs['backend'] = K.backend().encode('utf8')
+ f.attrs['keras_version'] = str(keras_version).encode('utf8')
+
+ for layer in layers:
+ g = f.create_group(layer.name)
+ symbolic_weights = layer.weights
+ weight_values = K.batch_get_value(symbolic_weights)
+ weight_names = []
+ for i, (w, val) in enumerate(zip(symbolic_weights, weight_values)):
+ if hasattr(w, 'name') and w.name:
+ name = str(w.name)
+ else:
+ name = 'param_' + str(i)
+ weight_names.append(name.encode('utf8'))
+ g.attrs['weight_names'] = weight_names
+ for name, val in zip(weight_names, weight_values):
+ param_dset = g.create_dataset(name, val.shape, dtype=val.dtype)
+ if not val.shape:
+ # scalar
+ param_dset[()] = val
+ else:
+ param_dset[:] = val
+
+
+def preprocess_weights_for_loading(layer,
+ weights,
+ original_keras_version=None,
+ original_backend=None):
+ """Converts layers weights from Keras 1 format to Keras 2.
+
+ Arguments:
+ layer: Layer instance.
+ weights: List of weights values (Numpy arrays).
+ original_keras_version: Keras version for the weights, as a string.
+ original_backend: Keras backend the weights were trained with,
+ as a string.
+
+ Returns:
+ A list of weights values (Numpy arrays).
+ """
+ if layer.__class__.__name__ == 'Bidirectional':
+ num_weights_per_layer = len(weights) // 2
+ forward_weights = preprocess_weights_for_loading(
+ layer.forward_layer, weights[:num_weights_per_layer],
+ original_keras_version, original_backend)
+ backward_weights = preprocess_weights_for_loading(
+ layer.backward_layer, weights[num_weights_per_layer:],
+ original_keras_version, original_backend)
+ weights = forward_weights + backward_weights
+
+ if original_keras_version == '1':
+ if layer.__class__.__name__ == 'TimeDistributed':
+ weights = preprocess_weights_for_loading(
+ layer.layer, weights, original_keras_version, original_backend)
+
+ if layer.__class__.__name__ == 'Conv1D':
+ shape = weights[0].shape
+ # Handle Keras 1.1 format
+ if shape[:2] != (layer.kernel_size[0], 1) or shape[3] != layer.filters:
+ # Legacy shape:
+ # (filters, input_dim, filter_length, 1)
+ assert shape[0] == layer.filters and shape[2:] == (layer.kernel_size[0],
+ 1)
+ weights[0] = np.transpose(weights[0], (2, 3, 1, 0))
+ weights[0] = weights[0][:, 0, :, :]
+
+ if layer.__class__.__name__ == 'Conv2D':
+ if layer.data_format == 'channels_first':
+ # old: (filters, stack_size, kernel_rows, kernel_cols)
+ # new: (kernel_rows, kernel_cols, stack_size, filters)
+ weights[0] = np.transpose(weights[0], (2, 3, 1, 0))
+
+ if layer.__class__.__name__ == 'Conv2DTranspose':
+ if layer.data_format == 'channels_last':
+ # old: (kernel_rows, kernel_cols, stack_size, filters)
+ # new: (kernel_rows, kernel_cols, filters, stack_size)
+ weights[0] = np.transpose(weights[0], (0, 1, 3, 2))
+ if layer.data_format == 'channels_first':
+ # old: (filters, stack_size, kernel_rows, kernel_cols)
+ # new: (kernel_rows, kernel_cols, filters, stack_size)
+ weights[0] = np.transpose(weights[0], (2, 3, 0, 1))
+
+ if layer.__class__.__name__ == 'Conv3D':
+ if layer.data_format == 'channels_first':
+ # old: (filters, stack_size, ...)
+ # new: (..., stack_size, filters)
+ weights[0] = np.transpose(weights[0], (2, 3, 4, 1, 0))
+
+ if layer.__class__.__name__ == 'GRU':
+ if len(weights) == 9:
+ kernel = np.concatenate([weights[0], weights[3], weights[6]], axis=-1)
+ recurrent_kernel = np.concatenate(
+ [weights[1], weights[4], weights[7]], axis=-1)
+ bias = np.concatenate([weights[2], weights[5], weights[8]], axis=-1)
+ weights = [kernel, recurrent_kernel, bias]
+
+ if layer.__class__.__name__ == 'LSTM':
+ if len(weights) == 12:
+ # old: i, c, f, o
+ # new: i, f, c, o
+ kernel = np.concatenate(
+ [weights[0], weights[6], weights[3], weights[9]], axis=-1)
+ recurrent_kernel = np.concatenate(
+ [weights[1], weights[7], weights[4], weights[10]], axis=-1)
+ bias = np.concatenate(
+ [weights[2], weights[8], weights[5], weights[11]], axis=-1)
+ weights = [kernel, recurrent_kernel, bias]
+
+ if layer.__class__.__name__ == 'ConvLSTM2D':
+ if len(weights) == 12:
+ kernel = np.concatenate(
+ [weights[0], weights[6], weights[3], weights[9]], axis=-1)
+ recurrent_kernel = np.concatenate(
+ [weights[1], weights[7], weights[4], weights[10]], axis=-1)
+ bias = np.concatenate(
+ [weights[2], weights[8], weights[5], weights[11]], axis=-1)
+ if layer.data_format == 'channels_first':
+ # old: (filters, stack_size, kernel_rows, kernel_cols)
+ # new: (kernel_rows, kernel_cols, stack_size, filters)
+ kernel = np.transpose(kernel, (2, 3, 1, 0))
+ recurrent_kernel = np.transpose(recurrent_kernel, (2, 3, 1, 0))
+ weights = [kernel, recurrent_kernel, bias]
+
+ if layer.__class__.__name__ in ['Model', 'Sequential']:
+ new_weights = []
+ # trainable weights
+ for sublayer in layer.layers:
+ num_weights = len(sublayer.trainable_weights)
+ if num_weights > 0:
+ new_weights.extend(
+ preprocess_weights_for_loading(
+ layer=sublayer,
+ weights=weights[:num_weights],
+ original_keras_version=original_keras_version,
+ original_backend=original_backend))
+ weights = weights[num_weights:]
+
+ # non-trainable weights
+ for sublayer in layer.layers:
+ num_weights = len([
+ l for l in sublayer.weights if l not in sublayer.trainable_weights
+ ])
+ if num_weights > 0:
+ new_weights.extend(
+ preprocess_weights_for_loading(
+ layer=sublayer,
+ weights=weights[:num_weights],
+ original_keras_version=original_keras_version,
+ original_backend=original_backend))
+ weights = weights[num_weights:]
+ weights = new_weights
+
+ conv_layers = ['Conv1D', 'Conv2D', 'Conv3D', 'Conv2DTranspose', 'ConvLSTM2D']
+ if layer.__class__.__name__ in conv_layers:
+ if original_backend == 'theano':
+ weights[0] = conv_utils.convert_kernel(weights[0])
+ if layer.__class__.__name__ == 'ConvLSTM2D':
+ weights[1] = conv_utils.convert_kernel(weights[1])
+ if K.int_shape(layer.weights[0]) != weights[0].shape:
+ weights[0] = np.transpose(weights[0], (3, 2, 0, 1))
+ if layer.__class__.__name__ == 'ConvLSTM2D':
+ weights[1] = np.transpose(weights[1], (3, 2, 0, 1))
+
+ # Convert the weights of CuDNNLSTM so that they could be loaded into LSTM
+ if layer.__class__.__name__ == 'LSTM' and len(weights) == 3:
+ # Determine if loading a CuDNNLSTM layer from the number of bias weights:
+ # CuDNNLSTM has (units * 8) weights; while LSTM has (units * 4)
+ # if there's no bias weight in the file, skip this conversion
+ units = weights[1].shape[0]
+ bias = weights[2]
+ if len(bias) == units * 8:
+ # reshape the kernels
+ kernels = np.split(weights[0], 4, axis=1)
+ kernels = [
+ kernel.reshape(-1).reshape(kernel.shape, order='F')
+ for kernel in kernels
+ ]
+ weights[0] = np.concatenate(kernels, axis=1)
+
+ # transpose the recurrent kernels
+ recurrent_kernels = np.split(weights[1], 4, axis=1)
+ recurrent_kernels = [kernel.T for kernel in recurrent_kernels]
+ weights[1] = np.concatenate(recurrent_kernels, axis=1)
+
+ # split the bias into half and merge
+ weights[2] = bias[:units * 4] + bias[units * 4:]
+
+ return weights
+
+
+def load_weights_from_hdf5_group(f, layers):
+ """Implements topological (order-based) weight loading.
+
+ Arguments:
+ f: A pointer to a HDF5 group.
+ layers: a list of target layers.
+
+ Raises:
+ ValueError: in case of mismatch between provided layers
+ and weights file.
+ """
+ if 'keras_version' in f.attrs:
+ original_keras_version = f.attrs['keras_version'].decode('utf8')
+ else:
+ original_keras_version = '1'
+ if 'backend' in f.attrs:
+ original_backend = f.attrs['backend'].decode('utf8')
+ else:
+ original_backend = None
+
+ filtered_layers = []
+ for layer in layers:
+ weights = layer.weights
+ if weights:
+ filtered_layers.append(layer)
+
+ layer_names = [n.decode('utf8') for n in f.attrs['layer_names']]
+ filtered_layer_names = []
+ for name in layer_names:
+ g = f[name]
+ weight_names = [n.decode('utf8') for n in g.attrs['weight_names']]
+ if weight_names:
+ filtered_layer_names.append(name)
+ layer_names = filtered_layer_names
+ if len(layer_names) != len(filtered_layers):
+ raise ValueError('You are trying to load a weight file '
+ 'containing ' + str(len(layer_names)) +
+ ' layers into a model with ' + str(len(filtered_layers)) +
+ ' layers.')
+
+ # We batch weight value assignments in a single backend call
+ # which provides a speedup in TensorFlow.
+ weight_value_tuples = []
+ for k, name in enumerate(layer_names):
+ g = f[name]
+ weight_names = [n.decode('utf8') for n in g.attrs['weight_names']]
+ weight_values = [g[weight_name] for weight_name in weight_names]
+ layer = filtered_layers[k]
+ symbolic_weights = layer.weights
+ weight_values = preprocess_weights_for_loading(
+ layer, weight_values, original_keras_version, original_backend)
+ if len(weight_values) != len(symbolic_weights):
+ raise ValueError('Layer #' + str(k) + ' (named "' + layer.name +
+ '" in the current model) was found to '
+ 'correspond to layer ' + name + ' in the save file. '
+ 'However the new layer ' + layer.name + ' expects ' +
+ str(len(symbolic_weights)) +
+ ' weights, but the saved weights have ' +
+ str(len(weight_values)) + ' elements.')
+ weight_value_tuples += zip(symbolic_weights, weight_values)
+ K.batch_set_value(weight_value_tuples)
+
+
+def load_weights_from_hdf5_group_by_name(f, layers):
+ """Implements name-based weight loading.
+
+ (instead of topological weight loading).
+
+ Layers that have no matching name are skipped.
+
+ Arguments:
+ f: A pointer to a HDF5 group.
+ layers: a list of target layers.
+
+ Raises:
+ ValueError: in case of mismatch between provided layers
+ and weights file.
+ """
+ if 'keras_version' in f.attrs:
+ original_keras_version = f.attrs['keras_version'].decode('utf8')
+ else:
+ original_keras_version = '1'
+ if 'backend' in f.attrs:
+ original_backend = f.attrs['backend'].decode('utf8')
+ else:
+ original_backend = None
+
+ # New file format.
+ layer_names = [n.decode('utf8') for n in f.attrs['layer_names']]
+
+ # Reverse index of layer name to list of layers with name.
+ index = {}
+ for layer in layers:
+ if layer.name:
+ index.setdefault(layer.name, []).append(layer)
+
+ # We batch weight value assignments in a single backend call
+ # which provides a speedup in TensorFlow.
+ weight_value_tuples = []
+ for k, name in enumerate(layer_names):
+ g = f[name]
+ weight_names = [n.decode('utf8') for n in g.attrs['weight_names']]
+ weight_values = [g[weight_name] for weight_name in weight_names]
+
+ for layer in index.get(name, []):
+ symbolic_weights = layer.weights
+ weight_values = preprocess_weights_for_loading(
+ layer, weight_values, original_keras_version, original_backend)
+ if len(weight_values) != len(symbolic_weights):
+ raise ValueError('Layer #' + str(k) + ' (named "' + layer.name +
+ '") expects ' + str(len(symbolic_weights)) +
+ ' weight(s), but the saved weights' + ' have ' +
+ str(len(weight_values)) + ' element(s).')
+ # Set values.
+ for i in range(len(weight_values)):
+ weight_value_tuples.append((symbolic_weights[i], weight_values[i]))
+ K.batch_set_value(weight_value_tuples)
--- /dev/null
+# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#,============================================================================
+"""Tests for model saving."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import shutil
+import tempfile
+
+import numpy as np
+
+from tensorflow.python.keras._impl import keras
+from tensorflow.python.platform import test
+from tensorflow.python.training import training as training_module
+
+try:
+ import h5py # pylint:disable=g-import-not-at-top
+except ImportError:
+ h5py = None
+
+
+class TestWeightSavingAndLoading(test.TestCase):
+
+ def test_weight_loading(self):
+ with self.test_session():
+ a = keras.layers.Input(shape=(2,))
+ x = keras.layers.Dense(3)(a)
+ b = keras.layers.Dense(1)(x)
+ model = keras.models.Model(a, b)
+
+ x = np.random.random((3, 2))
+ ref_y = model.predict(x)
+ weights = model.get_weights()
+ model.set_weights(weights)
+ y = model.predict(x)
+ self.assertAllClose(ref_y, y)
+
+ with self.assertRaises(ValueError):
+ model.set_weights(weights[1:])
+ with self.assertRaises(ValueError):
+ model.set_weights(weights[::-1])
+
+ if h5py is None:
+ return # Skip rest of test if H5py isn't available.
+
+ temp_dir = self.get_temp_dir()
+ self.addCleanup(shutil.rmtree, temp_dir)
+
+ h5_path = os.path.join(temp_dir, 'test.h5')
+ model.save_weights(h5_path)
+ model.load_weights(h5_path)
+ y = model.predict(x)
+ self.assertAllClose(ref_y, y)
+
+ model.load_weights(h5_path, by_name=True)
+ y = model.predict(x)
+ self.assertAllClose(ref_y, y)
+
+ def test_weight_preprocessing(self):
+ input_dim = 3
+ output_dim = 3
+ size = 2
+ cases = [
+ [
+ (keras.layers.Bidirectional(keras.layers.SimpleRNN(2))),
+ [np.random.random((2, 1)), np.random.random((2, 1))],
+ (None, 3, 2),
+ ],
+ [
+ (keras.layers.TimeDistributed(keras.layers.Dense(1))),
+ [np.random.random((2, 1)), np.random.random((1,))],
+ (None, 3, 2),
+ ],
+ [
+ (keras.layers.Conv1D(output_dim, size, use_bias=False)),
+ [np.random.random((output_dim, input_dim, size, 1))],
+ (None, 4, input_dim),
+ ],
+ [
+ (keras.layers.Conv2D(output_dim, size,
+ use_bias=False, data_format='channels_first')),
+ [np.random.random((output_dim, input_dim, size, size))],
+ (None, input_dim, 4, 4),
+ ],
+ [
+ (keras.layers.Conv2DTranspose(output_dim, size,
+ use_bias=False,
+ data_format='channels_first')),
+ [np.random.random((output_dim, input_dim, size, size))],
+ (None, input_dim, 4, 4),
+ ],
+ [
+ (keras.layers.Conv2DTranspose(output_dim, size,
+ use_bias=False,
+ data_format='channels_last')),
+ [np.random.random((size, size, input_dim, output_dim))],
+ (None, 4, 4, input_dim),
+ ],
+ [
+ (keras.layers.Conv3D(output_dim, size,
+ use_bias=False, data_format='channels_first')),
+ [np.random.random((output_dim, input_dim, size, size, size))],
+ (None, input_dim, 4, 4, 4),
+ ],
+ [
+ (keras.layers.GRU(output_dim)),
+ [np.random.random((input_dim, output_dim)),
+ np.random.random((output_dim, output_dim)),
+ np.random.random((output_dim,)),
+ np.random.random((input_dim, output_dim)),
+ np.random.random((output_dim, output_dim)),
+ np.random.random((output_dim,)),
+ np.random.random((input_dim, output_dim)),
+ np.random.random((output_dim, output_dim)),
+ np.random.random((output_dim,))],
+ (None, 4, input_dim),
+ ],
+ [
+ (keras.layers.LSTM(output_dim)),
+ [np.random.random((input_dim, output_dim)),
+ np.random.random((output_dim, output_dim)),
+ np.random.random((output_dim,)),
+ np.random.random((input_dim, output_dim)),
+ np.random.random((output_dim, output_dim)),
+ np.random.random((output_dim,)),
+ np.random.random((input_dim, output_dim)),
+ np.random.random((output_dim, output_dim)),
+ np.random.random((output_dim,)),
+ np.random.random((input_dim, output_dim)),
+ np.random.random((output_dim, output_dim)),
+ np.random.random((output_dim,))],
+ (None, 4, input_dim),
+ ],
+ ]
+ for layer, weights, input_shape in cases:
+ layer.build(input_shape)
+ _ = keras.engine.saving.preprocess_weights_for_loading(
+ layer, weights, original_keras_version='1')
+
+ model = keras.models.Sequential([keras.layers.Dense(2, input_dim=2)])
+ _ = keras.engine.saving.preprocess_weights_for_loading(
+ model, model.weights, original_keras_version='1')
+
+ x = keras.Input((2,))
+ y = keras.layers.Dense(2)(x)
+ model = keras.models.Model(x, y)
+ _ = keras.engine.saving.preprocess_weights_for_loading(
+ model, model.weights, original_keras_version='1')
+
+ def test_sequential_weight_loading(self):
+ if h5py is None:
+ return
+
+ temp_dir = self.get_temp_dir()
+ self.addCleanup(shutil.rmtree, temp_dir)
+ h5_path = os.path.join(temp_dir, 'test.h5')
+
+ num_hidden = 5
+ input_dim = 3
+ batch_size = 5
+ num_classes = 2
+
+ with self.test_session():
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
+ model.add(keras.layers.Dense(num_classes))
+
+ x = np.random.random((batch_size, input_dim))
+ ref_y = model.predict(x)
+
+ model.save_weights(h5_path)
+
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
+ model.add(keras.layers.Dense(num_classes))
+ model.load_weights(h5_path)
+ y = model.predict(x)
+
+ self.assertAllClose(y, ref_y)
+
+
+class TestWholeModelSaving(test.TestCase):
+
+ def test_sequential_model_saving(self):
+ if h5py is None:
+ return # Skip test if models cannot be saved.
+
+ with self.test_session():
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(2, input_shape=(3,)))
+ model.add(keras.layers.RepeatVector(3))
+ model.add(keras.layers.TimeDistributed(keras.layers.Dense(3)))
+ model.compile(loss=keras.losses.MSE,
+ optimizer=keras.optimizers.RMSprop(lr=0.0001),
+ metrics=[keras.metrics.categorical_accuracy],
+ sample_weight_mode='temporal')
+ x = np.random.random((1, 3))
+ y = np.random.random((1, 3, 3))
+ model.train_on_batch(x, y)
+
+ out = model.predict(x)
+ fd, fname = tempfile.mkstemp('.h5')
+ keras.models.save_model(model, fname)
+
+ new_model = keras.models.load_model(fname)
+ os.close(fd)
+ os.remove(fname)
+
+ out2 = new_model.predict(x)
+ self.assertAllClose(out, out2, atol=1e-05)
+
+ # test that new updates are the same with both models
+ x = np.random.random((1, 3))
+ y = np.random.random((1, 3, 3))
+ model.train_on_batch(x, y)
+ new_model.train_on_batch(x, y)
+ out = model.predict(x)
+ out2 = new_model.predict(x)
+ self.assertAllClose(out, out2, atol=1e-05)
+
+ def test_sequential_model_saving_2(self):
+ if h5py is None:
+ return # Skip test if models cannot be saved.
+
+ with self.test_session():
+ # test with custom optimizer, loss
+
+ class CustomOp(keras.optimizers.RMSprop):
+ pass
+
+ def custom_loss(y_true, y_pred):
+ return keras.losses.mse(y_true, y_pred)
+
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(2, input_shape=(3,)))
+ model.add(keras.layers.Dense(3))
+ model.compile(loss=custom_loss, optimizer=CustomOp(), metrics=['acc'])
+
+ x = np.random.random((1, 3))
+ y = np.random.random((1, 3))
+ model.train_on_batch(x, y)
+
+ out = model.predict(x)
+ fd, fname = tempfile.mkstemp('.h5')
+ keras.models.save_model(model, fname)
+
+ model = keras.models.load_model(
+ fname,
+ custom_objects={'CustomOp': CustomOp,
+ 'custom_loss': custom_loss})
+ os.close(fd)
+ os.remove(fname)
+
+ out2 = model.predict(x)
+ self.assertAllClose(out, out2, atol=1e-05)
+
+ def test_functional_model_saving(self):
+ if h5py is None:
+ return # Skip test if models cannot be saved.
+
+ with self.test_session():
+ inputs = keras.layers.Input(shape=(3,))
+ x = keras.layers.Dense(2)(inputs)
+ output = keras.layers.Dense(3)(x)
+
+ model = keras.models.Model(inputs, output)
+ model.compile(loss=keras.losses.MSE,
+ optimizer=keras.optimizers.RMSprop(lr=0.0001),
+ metrics=[keras.metrics.categorical_accuracy])
+ x = np.random.random((1, 3))
+ y = np.random.random((1, 3))
+ model.train_on_batch(x, y)
+
+ out = model.predict(x)
+ fd, fname = tempfile.mkstemp('.h5')
+ keras.models.save_model(model, fname)
+
+ model = keras.models.load_model(fname)
+ os.close(fd)
+ os.remove(fname)
+
+ out2 = model.predict(x)
+ self.assertAllClose(out, out2, atol=1e-05)
+
+ def test_saving_without_compilation(self):
+ if h5py is None:
+ return # Skip test if models cannot be saved.
+
+ with self.test_session():
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(2, input_shape=(3,)))
+ model.add(keras.layers.Dense(3))
+ model.compile(loss='mse', optimizer='sgd', metrics=['acc'])
+
+ fd, fname = tempfile.mkstemp('.h5')
+ keras.models.save_model(model, fname)
+ model = keras.models.load_model(fname)
+ os.close(fd)
+ os.remove(fname)
+
+ def test_saving_with_tf_optimizer(self):
+ if h5py is None:
+ return # Skip test if models cannot be saved.
+
+ with self.test_session():
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(2, input_shape=(3,)))
+ model.add(keras.layers.Dense(3))
+ model.compile(loss='mse',
+ optimizer=training_module.AdadeltaOptimizer(0.1),
+ metrics=['acc'])
+
+ fd, fname = tempfile.mkstemp('.h5')
+ keras.models.save_model(model, fname)
+ model = keras.models.load_model(fname)
+ os.close(fd)
+ os.remove(fname)
+
+ def test_saving_right_after_compilation(self):
+ if h5py is None:
+ return # Skip test if models cannot be saved.
+
+ with self.test_session():
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(2, input_shape=(3,)))
+ model.add(keras.layers.Dense(3))
+ model.compile(loss='mse', optimizer='sgd', metrics=['acc'])
+ model.model._make_train_function()
+
+ fd, fname = tempfile.mkstemp('.h5')
+ keras.models.save_model(model, fname)
+ model = keras.models.load_model(fname)
+ os.close(fd)
+ os.remove(fname)
+
+ def test_saving_lambda_numpy_array_arguments(self):
+ if h5py is None:
+ return # Skip test if models cannot be saved.
+
+ mean = np.random.random((4, 2, 3))
+ std = np.abs(np.random.random((4, 2, 3))) + 1e-5
+ inputs = keras.layers.Input(shape=(4, 2, 3))
+ output = keras.layers.Lambda(lambda image, mu, std: (image - mu) / std,
+ arguments={'mu': mean, 'std': std})(inputs)
+ model = keras.models.Model(inputs, output)
+ model.compile(loss='mse', optimizer='sgd', metrics=['acc'])
+
+ fd, fname = tempfile.mkstemp('.h5')
+ keras.models.save_model(model, fname)
+
+ model = keras.models.load_model(fname)
+ os.close(fd)
+ os.remove(fname)
+
+ self.assertAllClose(mean, model.layers[1].arguments['mu'])
+ self.assertAllClose(std, model.layers[1].arguments['std'])
+
+
+if __name__ == '__main__':
+ test.main()
--- /dev/null
+# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+# pylint: disable=protected-access
+"""Home of the `Sequential` model.
+"""
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import copy
+import os
+
+from tensorflow.python.framework import ops
+from tensorflow.python.keras._impl.keras import backend as K
+from tensorflow.python.keras._impl.keras import layers as layer_module
+from tensorflow.python.keras._impl.keras.engine import base_layer
+from tensorflow.python.keras._impl.keras.engine import network
+from tensorflow.python.keras._impl.keras.engine import saving
+from tensorflow.python.keras._impl.keras.engine.input_layer import Input
+from tensorflow.python.keras._impl.keras.engine.input_layer import InputLayer
+from tensorflow.python.keras._impl.keras.engine.training import Model
+from tensorflow.python.keras._impl.keras.utils.io_utils import ask_to_proceed_with_overwrite
+from tensorflow.python.platform import tf_logging as logging
+from tensorflow.python.util.tf_export import tf_export
+
+try:
+ import h5py # pylint: disable=g-import-not-at-top
+except ImportError:
+ h5py = None
+
+
+@tf_export('keras.models.Sequential', 'keras.Sequential')
+class Sequential(Model):
+ """Linear stack of layers.
+
+ Arguments:
+ layers: list of layers to add to the model.
+
+ # Note
+ The first layer passed to a Sequential model
+ should have a defined input shape. What that
+ means is that it should have received an `input_shape`
+ or `batch_input_shape` argument,
+ or for some type of layers (recurrent, Dense...)
+ an `input_dim` argument.
+
+ Example:
+
+ ```python
+ model = Sequential()
+ # first layer must have a defined input shape
+ model.add(Dense(32, input_dim=500))
+ # afterwards, Keras does automatic shape inference
+ model.add(Dense(32))
+
+ # also possible (equivalent to the above):
+ model = Sequential()
+ model.add(Dense(32, input_shape=(500,)))
+ model.add(Dense(32))
+
+ # also possible (equivalent to the above):
+ model = Sequential()
+ # here the batch dimension is None,
+ # which means any batch size will be accepted by the model.
+ model.add(Dense(32, batch_input_shape=(None, 500)))
+ model.add(Dense(32))
+ ```
+ """
+
+ def __init__(self, layers=None, name=None):
+ self._is_graph_network = True
+ self._is_compiled = False
+ self._layers = [] # Stack of layers.
+ self.model = None # Internal Model instance.
+ self.inputs = [] # List of input tensors
+ self.outputs = [] # List of length 1: the output tensor (unique).
+ self._trainable = True
+ self._initial_weights = None
+ self._input_layers = []
+
+ # Model attributes.
+ self._inbound_nodes = []
+ self._outbound_nodes = []
+ self.built = False
+
+ # Set model name.
+ if not name:
+ prefix = 'sequential_'
+ name = prefix + str(K.get_uid(prefix))
+ self._name = name
+
+ # Used by Layer base class.
+ self._dtype = None
+ self._activity_regularizer = None
+
+ # The following properties are not actually used by Keras;
+ # they exist for compatibility with TF's variable scoping mechanism.
+ self._updates = []
+ self._losses = []
+ self._scope = None
+ self._reuse = None
+ self._base_name = name
+ self._graph = ops.get_default_graph()
+
+ # Add to the model any layers passed to the constructor.
+ if layers:
+ for layer in layers:
+ self.add(layer)
+
+ def add(self, layer):
+ """Adds a layer instance on top of the layer stack.
+
+ Arguments:
+ layer: layer instance.
+
+ Raises:
+ TypeError: If `layer` is not a layer instance.
+ ValueError: In case the `layer` argument does not
+ know its input shape.
+ ValueError: In case the `layer` argument has
+ multiple output tensors, or is already connected
+ somewhere else (forbidden in `Sequential` models).
+ """
+ if not isinstance(layer, (base_layer.Layer, base_layer.TFBaseLayer)):
+ raise TypeError('The added layer must be '
+ 'an instance of class Layer. '
+ 'Found: ' + str(layer))
+ if not self.outputs:
+ # First layer in model: check that it is an input layer.
+ if not isinstance(layer, InputLayer):
+ # Create an input layer.
+ # First, we need to infer its expected input shape and dtype.
+ if isinstance(layer, (Model, Sequential)):
+ # We were passed a model as first layer.
+ # This requires a specific way to figure out the
+ # input shape and dtype.
+ if not layer.layers:
+ raise ValueError('Cannot add an empty model '
+ 'to a `Sequential` model.')
+ # In case of nested models: recover the first layer
+ # of the deepest model to infer input shape and dtype.
+ first_layer = layer.layers[0]
+ while isinstance(first_layer, (Model, Sequential)):
+ first_layer = first_layer.layers[0]
+ batch_shape = first_layer._batch_input_shape
+ dtype = first_layer.dtype
+ else:
+ # We were passed a regular layer, and it should
+ # know about its input shape. Otherwise, that's an error.
+ if not hasattr(layer, '_batch_input_shape'):
+ raise ValueError('The first layer in a '
+ 'Sequential model must '
+ 'get an `input_shape` argument.')
+ batch_shape = layer._batch_input_shape
+ dtype = layer.dtype
+ # Instantiate the input layer.
+ x = Input(
+ batch_shape=batch_shape, dtype=dtype, name=layer.name + '_input')
+ # This will build the current layer
+ # and create the node connecting the current layer
+ # to the input layer we just created.
+ layer(x)
+
+ if len(layer._inbound_nodes[-1].output_tensors) != 1:
+ raise ValueError('All layers in a Sequential model '
+ 'should have a single output tensor. '
+ 'For multi-output layers, '
+ 'use the functional API.')
+
+ self.outputs = [layer._inbound_nodes[-1].output_tensors[0]]
+ self.inputs = network.get_source_inputs(self.outputs[0])
+
+ # We create an input node, which we will keep updated
+ # as we add more layers
+ base_layer.Node(
+ outbound_layer=self,
+ inbound_layers=[],
+ node_indices=[],
+ tensor_indices=[],
+ input_tensors=self.inputs,
+ output_tensors=self.outputs)
+ else:
+ output_tensor = layer(self.outputs[0])
+ if isinstance(output_tensor, list):
+ raise TypeError('All layers in a Sequential model '
+ 'should have a single output tensor. '
+ 'For multi-output layers, '
+ 'use the functional API.')
+ self.outputs = [output_tensor]
+ # update self._inbound_nodes
+ self._inbound_nodes[0].output_tensors = self.outputs
+ self._inbound_nodes[0].output_shapes = [K.int_shape(self.outputs[0])]
+
+ self._layers.append(layer)
+ self.built = False
+
+ def pop(self):
+ """Removes the last layer in the model.
+
+ Raises:
+ TypeError: if there are no layers in the model.
+ """
+ if not self.layers:
+ raise TypeError('There are no layers in the model.')
+
+ self.layers.pop()
+ if not self.layers:
+ self.outputs = []
+ self._inbound_nodes = []
+ self._outbound_nodes = []
+ else:
+ self.layers[-1]._outbound_nodes = []
+ self.outputs = [self.layers[-1].output]
+ # update self._inbound_nodes
+ self._inbound_nodes[0].output_tensors = self.outputs
+ self._inbound_nodes[0].output_shapes = [K.int_shape(self.outputs[0])]
+ self.built = False
+
+ def get_layer(self, name=None, index=None):
+ """Retrieve a layer that is part of the model.
+
+ Returns a layer based on either its name (unique)
+ or its index in the graph. Indices are based on
+ order of horizontal graph traversal (bottom-up).
+
+ Arguments:
+ name: string, name of layer.
+ index: integer, index of layer.
+
+ Returns:
+ A layer instance.
+ """
+ if not self.built:
+ self.build()
+ return self.model.get_layer(name, index)
+
+ def call(self, inputs, **kwargs):
+ if not self.built:
+ self.build()
+ return self.model.call(inputs, **kwargs)
+
+ def build(self, input_shape=None):
+ if not self.inputs or not self.outputs:
+ raise TypeError('Sequential model cannot be built: model is empty.'
+ ' Add some layers first.')
+ # actually create the model
+ self.model = Model(self.inputs, self.outputs[0], name=self.name + '_model')
+ self.model.trainable = self.trainable
+
+ # mirror model attributes
+ self.supports_masking = self.model.supports_masking
+ self._output_mask_cache = self.model._output_mask_cache
+ self._output_tensor_cache = self.model._output_tensor_cache
+ self._output_shape_cache = self.model._output_shape_cache
+ self._input_layers = self.model._input_layers
+ self._output_layers = self.model._output_layers
+ self._input_coordinates = self.model._input_coordinates
+ self._output_coordinates = self.model._output_coordinates
+ self._nodes_by_depth = self.model._nodes_by_depth
+ self._network_nodes = self.model._network_nodes
+ self.output_names = self.model.output_names
+ self.input_names = self.model.input_names
+ self._feed_input_names = self.model._feed_input_names
+ self._feed_inputs = self.model._feed_inputs
+
+ # Make sure child model callbacks
+ # will call the parent Sequential model.
+ self.model.callback_model = self
+
+ self.built = True
+
+ @property
+ def uses_learning_phase(self):
+ if not self.built:
+ self.build()
+ return self.model.uses_learning_phase
+
+ def _gather_list_attr(self, attr):
+ all_attrs = []
+ for layer in self.layers:
+ all_attrs += getattr(layer, attr, [])
+ return all_attrs
+
+ def _make_train_function(self):
+ self.model._make_train_function()
+
+ def _make_test_function(self):
+ self.model._make_test_function()
+
+ def _make_predict_function(self):
+ self.model._make_predict_function()
+
+ @property
+ def trainable(self):
+ return self._trainable
+
+ @trainable.setter
+ def trainable(self, value):
+ if self.model:
+ self.model.trainable = value
+ self._trainable = value
+
+ @property
+ def trainable_weights(self):
+ if not self.trainable:
+ return []
+ return self._gather_list_attr('trainable_weights')
+
+ @property
+ def non_trainable_weights(self):
+ weights = self._gather_list_attr('non_trainable_weights')
+ if not self.trainable:
+ trainable_weights = self._gather_list_attr('trainable_weights')
+ return trainable_weights + weights
+ return weights
+
+ @property
+ def regularizers(self):
+ if not self.built:
+ self.build()
+ return self.model.regularizers
+
+ def get_weights(self):
+ """Retrieves the weights of the model.
+
+ Returns:
+ A flat list of Numpy arrays
+ (one array per model weight).
+ """
+ if not self.built:
+ self.build()
+ return self.model.get_weights()
+
+ def set_weights(self, weights):
+ """Sets the weights of the model.
+
+ Arguments:
+ weights: Should be a list
+ of Numpy arrays with shapes and types matching
+ the output of `model.get_weights()`.
+ """
+ if not self.built:
+ self.build()
+ self.model.set_weights(weights)
+
+ def load_weights(self, filepath, by_name=False):
+ if h5py is None:
+ raise ImportError('`load_weights` requires h5py.')
+ f = h5py.File(filepath, mode='r')
+ if 'layer_names' not in f.attrs and 'model_weights' in f:
+ f = f['model_weights']
+ layers = self.layers
+ if by_name:
+ saving.load_weights_from_hdf5_group_by_name(f, layers)
+ else:
+ saving.load_weights_from_hdf5_group(f, layers)
+ if hasattr(f, 'close'):
+ f.close()
+
+ def save_weights(self, filepath, overwrite=True):
+ if h5py is None:
+ raise ImportError('`save_weights` requires h5py.')
+ # If file exists and should not be overwritten:
+ if not overwrite and os.path.isfile(filepath):
+ proceed = ask_to_proceed_with_overwrite(filepath)
+ if not proceed:
+ return
+ layers = self.layers
+ f = h5py.File(filepath, 'w')
+ saving.save_weights_to_hdf5_group(f, layers)
+ f.flush()
+ f.close()
+
+ def compile(self,
+ optimizer,
+ loss,
+ metrics=None,
+ sample_weight_mode=None,
+ weighted_metrics=None,
+ target_tensors=None,
+ **kwargs):
+ """Configures the model for training.
+
+ Arguments:
+ optimizer: String (name of optimizer) or optimizer object.
+ See [optimizers](/optimizers).
+ loss: String (name of objective function) or objective function.
+ See [losses](/losses).
+ If the model has multiple outputs, you can use a different loss
+ on each output by passing a dictionary or a list of losses.
+ The loss value that will be minimized by the model
+ will then be the sum of all individual losses.
+ metrics: List of metrics to be evaluated by the model
+ during training and testing.
+ Typically you will use `metrics=['accuracy']`.
+ To specify different metrics for different outputs of a
+ multi-output model, you could also pass a dictionary,
+ such as `metrics={'output_a': 'accuracy'}`.
+ sample_weight_mode: If you need to do timestep-wise
+ sample weighting (2D weights), set this to `"temporal"`.
+ `None` defaults to sample-wise weights (1D).
+ If the model has multiple outputs, you can use a different
+ `sample_weight_mode` on each output by passing a
+ dictionary or a list of modes.
+ weighted_metrics: list of metrics to be evaluated and weighted
+ by `sample_weight` or `class_weight` during training and testing.
+ target_tensors: By default, Keras will create a placeholder for the
+ model's target, which will be fed with the target data during
+ training. If instead you would like to use your own
+ target tensor (in turn, Keras will not expect external
+ Numpy data for these targets at training time), you
+ can specify them via the `target_tensors` argument.
+ It should be a single tensor
+ (for a single-output `Sequential` model).
+ **kwargs: These arguments are passed into `tf.Session.run`.
+
+ Example:
+ ```python
+ model = Sequential()
+ model.add(Dense(32, input_shape=(500,)))
+ model.add(Dense(10, activation='softmax'))
+ model.compile(optimizer='rmsprop',
+ loss='categorical_crossentropy',
+ metrics=['accuracy'])
+ ```
+ """
+ # create the underlying model
+ self.build()
+ # call compile method of Model class
+ self.model.compile(
+ optimizer,
+ loss,
+ metrics=metrics,
+ sample_weight_mode=sample_weight_mode,
+ weighted_metrics=weighted_metrics,
+ target_tensors=target_tensors,
+ **kwargs)
+ self.optimizer = self.model.optimizer
+ self.loss = self.model.loss
+ self.metrics = self.model.metrics
+ self.loss_weights = self.model.loss_weights
+ self.sample_weight_mode = self.model.sample_weight_mode
+ self.weighted_metrics = self.model.weighted_metrics
+ self.targets = self.model.targets
+ self.metrics_tensors = self.model.metrics_tensors
+ self.metrics_names = self.model.metrics_names
+ self.sample_weights = self.model.sample_weights
+ self.total_loss = self.model.total_loss
+
+ def fit(self,
+ x=None,
+ y=None,
+ batch_size=None,
+ epochs=1,
+ verbose=1,
+ callbacks=None,
+ validation_split=0.,
+ validation_data=None,
+ shuffle=True,
+ class_weight=None,
+ sample_weight=None,
+ initial_epoch=0,
+ steps_per_epoch=None,
+ validation_steps=None,
+ **kwargs):
+ """Trains the model for a fixed number of epochs.
+
+ Arguments:
+ x: Numpy array of training data.
+ If the input layer in the model is named, you can also pass a
+ dictionary mapping the input name to a Numpy array.
+ `x` can be `None` (default) if feeding from
+ TensorFlow data tensors.
+ y: Numpy array of target (label) data.
+ If the output layer in the model is named, you can also pass a
+ dictionary mapping the output name to a Numpy array.
+ `y` can be `None` (default) if feeding from
+ TensorFlow data tensors.
+ batch_size: Integer or `None`.
+ Number of samples per gradient update.
+ If unspecified, it will default to 32.
+ epochs: Integer. Number of epochs to train the model.
+ An epoch is an iteration over the entire `x` and `y`
+ data provided.
+ Note that in conjunction with `initial_epoch`,
+ `epochs` is to be understood as "final epoch".
+ The model is not trained for a number of iterations
+ given by `epochs`, but merely until the epoch
+ of index `epochs` is reached.
+ verbose: 0, 1, or 2. Verbosity mode.
+ 0 = silent, 1 = progress bar, 2 = one line per epoch.
+ callbacks: List of `keras.callbacks.Callback` instances.
+ List of callbacks to apply during training.
+ See [callbacks](/callbacks).
+ validation_split: Float between 0 and 1:
+ Fraction of the training data to be used as validation data.
+ The model will set apart this fraction of the training data,
+ will not train on it, and will evaluate
+ the loss and any model metrics
+ on this data at the end of each epoch.
+ The validation data is selected from the last samples
+ in the `x` and `y` data provided, before shuffling.
+ validation_data: tuple `(x_val, y_val)` or tuple
+ `(x_val, y_val, val_sample_weights)` on which to evaluate
+ the loss and any model metrics at the end of each epoch.
+ The model will not be trained on this data.
+ This will override `validation_split`.
+ shuffle: Boolean (whether to shuffle the training data
+ before each epoch) or str (for 'batch').
+ 'batch' is a special option for dealing with the
+ limitations of HDF5 data; it shuffles in batch-sized chunks.
+ Has no effect when `steps_per_epoch` is not `None`.
+ class_weight: Optional dictionary mapping class indices (integers)
+ to a weight (float) value, used for weighting the loss function
+ (during training only).
+ This can be useful to tell the model to
+ "pay more attention" to samples from
+ an under-represented class.
+ sample_weight: Optional Numpy array of weights for
+ the training samples, used for weighting the loss function
+ (during training only). You can either pass a flat (1D)
+ Numpy array with the same length as the input samples
+ (1:1 mapping between weights and samples),
+ or in the case of temporal data,
+ you can pass a 2D array with shape
+ `(samples, sequence_length)`,
+ to apply a different weight to every timestep of every sample.
+ In this case you should make sure to specify
+ `sample_weight_mode="temporal"` in `compile()`.
+ initial_epoch: Epoch at which to start training
+ (useful for resuming a previous training run).
+ steps_per_epoch: Total number of steps (batches of samples)
+ before declaring one epoch finished and starting the
+ next epoch. When training with input tensors such as
+ TensorFlow data tensors, the default `None` is equal to
+ the number of unique samples in your dataset divided by
+ the batch size, or 1 if that cannot be determined.
+ validation_steps: Only relevant if `steps_per_epoch`
+ is specified. Total number of steps (batches of samples)
+ to validate before stopping.
+ **kwargs: Used for backwards compatibility support.
+
+ Returns:
+ A `History` object. Its `History.history` attribute is
+ a record of training loss values and metrics values
+ at successive epochs, as well as validation loss values
+ and validation metrics values (if applicable).
+
+ Raises:
+ RuntimeError: If the model was never compiled.
+ ValueError: In case of mismatch between the provided input data
+ and what the model expects.
+ """
+ if not self.built:
+ raise RuntimeError('The model needs to be compiled before being used.')
+ return self.model.fit(
+ x,
+ y,
+ batch_size=batch_size,
+ epochs=epochs,
+ verbose=verbose,
+ callbacks=callbacks,
+ validation_split=validation_split,
+ validation_data=validation_data,
+ shuffle=shuffle,
+ class_weight=class_weight,
+ sample_weight=sample_weight,
+ initial_epoch=initial_epoch,
+ steps_per_epoch=steps_per_epoch,
+ validation_steps=validation_steps)
+
+ def evaluate(self, x, y, batch_size=32, verbose=1, sample_weight=None):
+ """Computes the loss on some input data, batch by batch.
+
+ Arguments:
+ x: input data, as a Numpy array or list of Numpy arrays
+ (if the model has multiple inputs).
+ y: labels, as a Numpy array.
+ batch_size: integer. Number of samples per gradient update.
+ verbose: verbosity mode, 0 or 1.
+ sample_weight: sample weights, as a Numpy array.
+
+ Returns:
+ Scalar test loss (if the model has no metrics)
+ or list of scalars (if the model computes other metrics).
+ The attribute `model.metrics_names` will give you
+ the display labels for the scalar outputs.
+
+ Raises:
+ RuntimeError: if the model was never compiled.
+ """
+ if not self.built:
+ raise RuntimeError('The model needs to be compiled before being used.')
+ return self.model.evaluate(
+ x,
+ y,
+ batch_size=batch_size,
+ verbose=verbose,
+ sample_weight=sample_weight)
+
+ def predict(self, x, batch_size=32, verbose=0):
+ """Generates output predictions for the input samples.
+
+ The input samples are processed batch by batch.
+
+ Arguments:
+ x: the input data, as a Numpy array.
+ batch_size: integer.
+ verbose: verbosity mode, 0 or 1.
+
+ Returns:
+ A Numpy array of predictions.
+ """
+ if not self.built:
+ self.build()
+ return self.model.predict(x, batch_size=batch_size, verbose=verbose)
+
+ def predict_on_batch(self, x):
+ """Returns predictions for a single batch of samples.
+
+ Arguments:
+ x: input data, as a Numpy array or list of Numpy arrays
+ (if the model has multiple inputs).
+
+ Returns:
+ A Numpy array of predictions.
+ """
+ if not self.built:
+ self.build()
+ return self.model.predict_on_batch(x)
+
+ def train_on_batch(self, x, y, class_weight=None, sample_weight=None):
+ """Single gradient update over one batch of samples.
+
+ Arguments:
+ x: input data, as a Numpy array or list of Numpy arrays
+ (if the model has multiple inputs).
+ y: labels, as a Numpy array.
+ class_weight: dictionary mapping classes to a weight value,
+ used for scaling the loss function (during training only).
+ sample_weight: sample weights, as a Numpy array.
+
+ Returns:
+ Scalar training loss (if the model has no metrics)
+ or list of scalars (if the model computes other metrics).
+ The attribute `model.metrics_names` will give you
+ the display labels for the scalar outputs.
+
+ Raises:
+ RuntimeError: if the model was never compiled.
+ """
+ if not self.built:
+ raise RuntimeError('The model needs to be compiled before being used.')
+ return self.model.train_on_batch(
+ x, y, sample_weight=sample_weight, class_weight=class_weight)
+
+ def test_on_batch(self, x, y, sample_weight=None):
+ """Evaluates the model over a single batch of samples.
+
+ Arguments:
+ x: input data, as a Numpy array or list of Numpy arrays
+ (if the model has multiple inputs).
+ y: labels, as a Numpy array.
+ sample_weight: sample weights, as a Numpy array.
+
+ Returns:
+ Scalar test loss (if the model has no metrics)
+ or list of scalars (if the model computes other metrics).
+ The attribute `model.metrics_names` will give you
+ the display labels for the scalar outputs.
+
+ Raises:
+ RuntimeError: if the model was never compiled.
+ """
+ if not self.built:
+ raise RuntimeError('The model needs to be compiled before being used.')
+ return self.model.test_on_batch(x, y, sample_weight=sample_weight)
+
+ def predict_proba(self, x, batch_size=32, verbose=0):
+ """Generates class probability predictions for the input samples.
+
+ The input samples are processed batch by batch.
+
+ Arguments:
+ x: input data, as a Numpy array or list of Numpy arrays
+ (if the model has multiple inputs).
+ batch_size: integer.
+ verbose: verbosity mode, 0 or 1.
+
+ Returns:
+ A Numpy array of probability predictions.
+ """
+ preds = self.predict(x, batch_size, verbose)
+ if preds.min() < 0. or preds.max() > 1.:
+ logging.warning('Network returning invalid probability values. '
+ 'The last layer might not normalize predictions '
+ 'into probabilities '
+ '(like softmax or sigmoid would).')
+ return preds
+
+ def predict_classes(self, x, batch_size=32, verbose=0):
+ """Generate class predictions for the input samples.
+
+ The input samples are processed batch by batch.
+
+ Arguments:
+ x: input data, as a Numpy array or list of Numpy arrays
+ (if the model has multiple inputs).
+ batch_size: integer.
+ verbose: verbosity mode, 0 or 1.
+
+ Returns:
+ A numpy array of class predictions.
+ """
+ proba = self.predict(x, batch_size=batch_size, verbose=verbose)
+ if proba.shape[-1] > 1:
+ return proba.argmax(axis=-1)
+ else:
+ return (proba > 0.5).astype('int32')
+
+ def fit_generator(self,
+ generator,
+ steps_per_epoch=None,
+ epochs=1,
+ verbose=1,
+ callbacks=None,
+ validation_data=None,
+ validation_steps=None,
+ class_weight=None,
+ max_queue_size=10,
+ workers=1,
+ use_multiprocessing=False,
+ shuffle=True,
+ initial_epoch=0,
+ **kwargs):
+ """Fits the model on data generated batch-by-batch by a Python generator.
+
+ The generator is run in parallel to the model, for efficiency.
+ For instance, this allows you to do real-time data augmentation
+ on images on CPU in parallel to training your model on GPU.
+
+ Arguments:
+ generator: A generator.
+ The output of the generator must be either
+ - a tuple (inputs, targets)
+ - a tuple (inputs, targets, sample_weights).
+ All arrays should contain the same number of samples.
+ The generator is expected to loop over its data
+ indefinitely. An epoch finishes when `steps_per_epoch`
+ batches have been seen by the model.
+ steps_per_epoch: Total number of steps (batches of samples)
+ to yield from `generator` before declaring one epoch
+ finished and starting the next epoch. It should typically
+ be equal to the number of samples of your dataset
+ divided by the batch size.
+ Optional for `Sequence`: if unspecified, will use
+ the `len(generator)` as a number of steps.
+ epochs: Integer, total number of iterations on the data.
+ Note that in conjunction with initial_epoch, the parameter
+ epochs is to be understood as "final epoch". The model is
+ not trained for n steps given by epochs, but until the
+ epoch epochs is reached.
+ verbose: Verbosity mode, 0, 1, or 2.
+ callbacks: List of callbacks to be called during training.
+ validation_data: This can be either
+ - A generator for the validation data
+ - A tuple (inputs, targets)
+ - A tuple (inputs, targets, sample_weights).
+ validation_steps: Only relevant if `validation_data`
+ is a generator.
+ Number of steps to yield from validation generator
+ at the end of every epoch. It should typically
+ be equal to the number of samples of your
+ validation dataset divided by the batch size.
+ Optional for `Sequence`: if unspecified, will use
+ the `len(validation_data)` as a number of steps.
+ class_weight: Dictionary mapping class indices to a weight
+ for the class.
+ max_queue_size: Maximum size for the generator queue
+ workers: Maximum number of processes to spin up
+ use_multiprocessing: If True, use process based threading.
+ Note that because
+ this implementation relies on multiprocessing,
+ you should not pass
+ non picklable arguments to the generator
+ as they can't be passed
+ easily to children processes.
+ shuffle: Whether to shuffle the order of the batches at
+ the beginning of each epoch. Only used with instances
+ of `Sequence` (keras.utils.Sequence).
+ initial_epoch: Epoch at which to start training
+ (useful for resuming a previous training run)
+ **kwargs: support for legacy arguments.
+
+ Returns:
+ A `History` object.
+
+ Raises:
+ RuntimeError: if the model was never compiled.
+ ValueError: In case the generator yields
+ data in an invalid format.
+
+ Example:
+
+ ```python
+ def generate_arrays_from_file(path):
+ while 1:
+ f = open(path)
+ for line in f:
+ # create Numpy arrays of input data
+ # and labels, from each line in the file
+ x, y = process_line(line)
+ yield (x, y)
+ f.close()
+
+ model.fit_generator(generate_arrays_from_file('/my_file.txt'),
+ steps_per_epoch=1000, epochs=10)
+ ```
+ """
+ # Legacy support
+ if 'max_q_size' in kwargs:
+ max_queue_size = kwargs.pop('max_q_size')
+ logging.warning('The argument `max_q_size` has been renamed '
+ '`max_queue_size`. Update your method calls accordingly.')
+ if 'pickle_safe' in kwargs:
+ use_multiprocessing = kwargs.pop('pickle_safe')
+ logging.warning('The argument `pickle_safe` has been renamed '
+ '`use_multiprocessing`. '
+ 'Update your method calls accordingly.')
+ if kwargs:
+ raise ValueError('Unrecognized keyword arguments: ' + str(kwargs))
+
+ if not self.built:
+ raise RuntimeError('The model needs to be compiled before being used.')
+ return self.model.fit_generator(
+ generator,
+ steps_per_epoch,
+ epochs,
+ verbose=verbose,
+ callbacks=callbacks,
+ validation_data=validation_data,
+ validation_steps=validation_steps,
+ class_weight=class_weight,
+ max_queue_size=max_queue_size,
+ workers=workers,
+ use_multiprocessing=use_multiprocessing,
+ shuffle=shuffle,
+ initial_epoch=initial_epoch)
+
+ def evaluate_generator(self,
+ generator,
+ steps=None,
+ max_queue_size=10,
+ workers=1,
+ use_multiprocessing=False,
+ **kwargs):
+ """Evaluates the model on a data generator.
+
+ The generator should return the same kind of data
+ as accepted by `test_on_batch`.
+
+ Arguments:
+ generator: Generator yielding tuples (inputs, targets)
+ or (inputs, targets, sample_weights)
+ steps: Total number of steps (batches of samples)
+ to yield from `generator` before stopping.
+ Optional for `Sequence`: if unspecified, will use
+ the `len(generator)` as a number of steps.
+ max_queue_size: maximum size for the generator queue
+ workers: maximum number of processes to spin up
+ use_multiprocessing: if True, use process based threading.
+ Note that because this implementation
+ relies on multiprocessing, you should not pass
+ non picklable arguments to the generator
+ as they can't be passed easily to children processes.
+ **kwargs: support for legacy arguments.
+
+ Returns:
+ Scalar test loss (if the model has no metrics)
+ or list of scalars (if the model computes other metrics).
+ The attribute `model.metrics_names` will give you
+ the display labels for the scalar outputs.
+
+ Raises:
+ RuntimeError: if the model was never compiled.
+ ValueError: In case the generator yields
+ data in an invalid format.
+ """
+ # Legacy support
+ if 'max_q_size' in kwargs:
+ max_queue_size = kwargs.pop('max_q_size')
+ logging.warning('The argument `max_q_size` has been renamed '
+ '`max_queue_size`. Update your method calls accordingly.')
+ if 'pickle_safe' in kwargs:
+ use_multiprocessing = kwargs.pop('pickle_safe')
+ logging.warning('The argument `pickle_safe` has been renamed '
+ '`use_multiprocessing`. '
+ 'Update your method calls accordingly.')
+ if kwargs:
+ raise ValueError('Unrecognized keyword arguments: ' + str(kwargs))
+
+ if not self.built:
+ raise RuntimeError('The model needs to be compiled before being used.')
+ return self.model.evaluate_generator(
+ generator,
+ steps,
+ max_queue_size=max_queue_size,
+ workers=workers,
+ use_multiprocessing=use_multiprocessing)
+
+ def predict_generator(self,
+ generator,
+ steps=None,
+ max_queue_size=10,
+ workers=1,
+ use_multiprocessing=False,
+ verbose=0,
+ **kwargs):
+ """Generates predictions for the input samples from a data generator.
+
+ The generator should return the same kind of data as accepted by
+ `predict_on_batch`.
+
+ Arguments:
+ generator: generator yielding batches of input samples.
+ steps: Total number of steps (batches of samples)
+ to yield from `generator` before stopping.
+ Optional for `Sequence`: if unspecified, will use
+ the `len(generator)` as a number of steps.
+ max_queue_size: maximum size for the generator queue
+ workers: maximum number of processes to spin up
+ use_multiprocessing: if True, use process based threading.
+ Note that because this implementation
+ relies on multiprocessing, you should not pass
+ non picklable arguments to the generator
+ as they can't be passed easily to children processes.
+ verbose: verbosity mode, 0 or 1.
+ **kwargs: support for legacy arguments.
+
+ Returns:
+ A Numpy array of predictions.
+
+ Raises:
+ ValueError: In case the generator yields
+ data in an invalid format.
+ """
+ # Legacy support
+ if 'max_q_size' in kwargs:
+ max_queue_size = kwargs.pop('max_q_size')
+ logging.warning('The argument `max_q_size` has been renamed '
+ '`max_queue_size`. Update your method calls accordingly.')
+ if 'pickle_safe' in kwargs:
+ use_multiprocessing = kwargs.pop('pickle_safe')
+ logging.warning('The argument `pickle_safe` has been renamed '
+ '`use_multiprocessing`. '
+ 'Update your method calls accordingly.')
+ if kwargs:
+ raise ValueError('Unrecognized keyword arguments: ' + str(kwargs))
+
+ if not self.built:
+ self.build()
+ return self.model.predict_generator(
+ generator,
+ steps,
+ max_queue_size=max_queue_size,
+ workers=workers,
+ use_multiprocessing=use_multiprocessing,
+ verbose=verbose)
+
+ def get_config(self):
+ config = []
+ for layer in self.layers:
+ config.append({
+ 'class_name': layer.__class__.__name__,
+ 'config': layer.get_config()
+ })
+ return copy.deepcopy(config)
+
+ @classmethod
+ def from_config(cls, config, custom_objects=None):
+ model = cls()
+ for conf in config:
+ layer = layer_module.deserialize(conf, custom_objects=custom_objects)
+ model.add(layer)
+ return model
--- /dev/null
+# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==============================================================================
+"""Tests specific to `Sequential` model."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import numpy as np
+
+from tensorflow.python.keras._impl import keras
+from tensorflow.python.platform import test
+
+
+class TestSequential(test.TestCase):
+ """Most Sequential model API tests are covered in `training_test.py`.
+ """
+
+ def test_basic_methods(self):
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(1, input_dim=2))
+ model.add(keras.layers.Dropout(0.3, name='dp'))
+ model.add(keras.layers.Dense(2, kernel_regularizer='l2',
+ kernel_constraint='max_norm'))
+ model.build()
+ self.assertEqual(model.state_updates, model.model.state_updates)
+ self.assertEqual(model.get_layer(name='dp').name, 'dp')
+
+ def test_sequential_pop(self):
+ num_hidden = 5
+ input_dim = 3
+ batch_size = 5
+ num_classes = 2
+ with self.test_session():
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
+ model.add(keras.layers.Dense(num_classes))
+ model.compile(loss='mse', optimizer='sgd')
+ x = np.random.random((batch_size, input_dim))
+ y = np.random.random((batch_size, num_classes))
+ model.fit(x, y, epochs=1)
+ model.pop()
+ self.assertEqual(len(model.layers), 1)
+ self.assertEqual(model.output_shape, (None, num_hidden))
+ model.compile(loss='mse', optimizer='sgd')
+ y = np.random.random((batch_size, num_hidden))
+ model.fit(x, y, epochs=1)
+
+ # Test popping single-layer model
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
+ model.pop()
+ self.assertEqual(len(model.layers), 0)
+ self.assertEqual(len(model.outputs), 0)
+
+ # Invalid use case
+ model = keras.models.Sequential()
+ with self.assertRaises(TypeError):
+ model.pop()
+
+ def test_invalid_use_cases(self):
+ with self.test_session():
+ # Added objects must be layer instances
+ with self.assertRaises(TypeError):
+ model = keras.models.Sequential()
+ model.add(None)
+
+ # Added layers must have an inputs shape
+ with self.assertRaises(ValueError):
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(1))
+
+ # Added layers cannot have multiple outputs
+ class MyLayer(keras.layers.Layer):
+
+ def call(self, inputs):
+ return [3 * inputs, 2 * inputs]
+
+ def compute_output_shape(self, input_shape):
+ return [input_shape, input_shape]
+
+ with self.assertRaises(ValueError):
+ model = keras.models.Sequential()
+ model.add(MyLayer(input_shape=(3,)))
+ with self.assertRaises(TypeError):
+ model = keras.models.Sequential()
+ model.add(keras.layers.Dense(1, input_dim=1))
+ model.add(MyLayer())
+
+ # Building empty model
+ model = keras.models.Sequential()
+ with self.assertRaises(TypeError):
+ model.build()
+
+ def test_nested_sequential_trainability(self):
+ input_dim = 20
+ num_units = 10
+ num_classes = 2
+
+ inner_model = keras.models.Sequential()
+ inner_model.add(keras.layers.Dense(num_units, input_shape=(input_dim,)))
+
+ model = keras.models.Sequential()
+ model.add(inner_model)
+ model.add(keras.layers.Dense(num_classes))
+
+ self.assertEqual(len(model.trainable_weights), 4)
+ inner_model.trainable = False
+ self.assertEqual(len(model.trainable_weights), 2)
+ inner_model.trainable = True
+ self.assertEqual(len(model.trainable_weights), 4)
+
+ def test_sequential_update_disabling(self):
+ val_a = np.random.random((10, 4))
+ val_out = np.random.random((10, 4))
+
+ with self.test_session():
+ model = keras.models.Sequential()
+ model.add(keras.layers.BatchNormalization(input_shape=(4,)))
+
+ model.trainable = False
+ assert not model.updates
+
+ model.compile('sgd', 'mse')
+ assert not model.updates
+ assert not model.model.updates
+
+ x1 = model.predict(val_a)
+ model.train_on_batch(val_a, val_out)
+ x2 = model.predict(val_a)
+ self.assertAllClose(x1, x2, atol=1e-7)
+
+ model.trainable = True
+ model.compile('sgd', 'mse')
+ assert model.updates
+ assert model.model.updates
+
+ model.train_on_batch(val_a, val_out)
+ x2 = model.predict(val_a)
+ assert np.abs(np.sum(x1 - x2)) > 1e-5
from __future__ import division
from __future__ import print_function
-import os
-import shutil
-
import numpy as np
from tensorflow.python.eager import context
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import test_util
from tensorflow.python.keras._impl import keras
-from tensorflow.python.layers import base as base_layers
+from tensorflow.python.layers import base as tf_base_layers
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import state_ops
except ImportError:
yaml = None
-try:
- import h5py # pylint:disable=g-import-not-at-top
-except ImportError:
- h5py = None
-
class TopologyConstructionTest(test.TestCase):
self.assertEqual(len(layer.get_updates_for(x2)), 1)
self.assertEqual(len(layer.get_updates_for(None)), 1)
- network = keras.engine.topology.Network(x2, y2)
+ network = keras.engine.Network(x2, y2)
self.assertEqual(len(network.updates), 2)
self.assertEqual(len(network.get_updates_for(x1)), 0)
self.assertEqual(len(network.get_updates_for(x2)), 1)
self.assertEqual(len(layer.get_losses_for(x2)), 1)
self.assertEqual(len(layer.get_losses_for(None)), 1)
- network = keras.engine.topology.Network(x2, y2)
+ network = keras.engine.Network(x2, y2)
self.assertEqual(len(network.losses), 2)
self.assertEqual(len(network.get_losses_for(x1)), 0)
self.assertEqual(len(network.get_losses_for(x2)), 1)
x = keras.Input(shape=(32,))
dense = keras.layers.Dense(2)
y = dense(x)
- network = keras.engine.topology.Network(x, y, name='dense_network')
+ network = keras.engine.Network(x, y, name='dense_network')
# test basic attributes
self.assertEqual(network.name, 'dense_network')
self.assertListEqual([x.shape for x in fn_outputs], [(10, 64), (10, 5)])
# test get_source_inputs
- self.assertListEqual(keras.engine.topology.get_source_inputs(c), [a, b])
+ self.assertListEqual(keras.engine.network.get_source_inputs(c), [a, b])
# serialization / deserialization
json_config = model.to_json()
if context.in_graph_mode():
x = keras.Input(shape=(32,))
y = MaskedLayer()(x) # pylint: disable=not-callable
- network = keras.engine.topology.Network(x, y)
+ network = keras.engine.Network(x, y)
# test callability on Input
x_2 = keras.Input(shape=(32,))
self.assertEqual(np.min(preds), 0.) # At least one unit was dropped.
-class TestSaving(test.TestCase):
-
- def test_weight_loading(self):
- with self.test_session():
- a = keras.layers.Input(shape=(2,))
- x = keras.layers.Dense(3)(a)
- b = keras.layers.Dense(1)(x)
- model = keras.models.Model(a, b)
-
- x = np.random.random((3, 2))
- ref_y = model.predict(x)
- weights = model.get_weights()
- model.set_weights(weights)
- y = model.predict(x)
- self.assertAllClose(ref_y, y)
-
- with self.assertRaises(ValueError):
- model.set_weights(weights[1:])
- with self.assertRaises(ValueError):
- model.set_weights(weights[::-1])
-
- if h5py is None:
- return # Skip rest of test if H5py isn't available.
-
- temp_dir = self.get_temp_dir()
- self.addCleanup(shutil.rmtree, temp_dir)
-
- h5_path = os.path.join(temp_dir, 'test.h5')
- model.save_weights(h5_path)
- model.load_weights(h5_path)
- y = model.predict(x)
- self.assertAllClose(ref_y, y)
-
- model.load_weights(h5_path, by_name=True)
- y = model.predict(x)
- self.assertAllClose(ref_y, y)
-
- def test_weight_preprocessing(self):
- input_dim = 3
- output_dim = 3
- size = 2
- cases = [
- [
- (keras.layers.Bidirectional(keras.layers.SimpleRNN(2))),
- [np.random.random((2, 1)), np.random.random((2, 1))],
- (None, 3, 2),
- ],
- [
- (keras.layers.TimeDistributed(keras.layers.Dense(1))),
- [np.random.random((2, 1)), np.random.random((1,))],
- (None, 3, 2),
- ],
- [
- (keras.layers.Conv1D(output_dim, size, use_bias=False)),
- [np.random.random((output_dim, input_dim, size, 1))],
- (None, 4, input_dim),
- ],
- [
- (keras.layers.Conv2D(output_dim, size,
- use_bias=False, data_format='channels_first')),
- [np.random.random((output_dim, input_dim, size, size))],
- (None, input_dim, 4, 4),
- ],
- [
- (keras.layers.Conv2DTranspose(output_dim, size,
- use_bias=False,
- data_format='channels_first')),
- [np.random.random((output_dim, input_dim, size, size))],
- (None, input_dim, 4, 4),
- ],
- [
- (keras.layers.Conv2DTranspose(output_dim, size,
- use_bias=False,
- data_format='channels_last')),
- [np.random.random((size, size, input_dim, output_dim))],
- (None, 4, 4, input_dim),
- ],
- [
- (keras.layers.Conv3D(output_dim, size,
- use_bias=False, data_format='channels_first')),
- [np.random.random((output_dim, input_dim, size, size, size))],
- (None, input_dim, 4, 4, 4),
- ],
- [
- (keras.layers.GRU(output_dim)),
- [np.random.random((input_dim, output_dim)),
- np.random.random((output_dim, output_dim)),
- np.random.random((output_dim,)),
- np.random.random((input_dim, output_dim)),
- np.random.random((output_dim, output_dim)),
- np.random.random((output_dim,)),
- np.random.random((input_dim, output_dim)),
- np.random.random((output_dim, output_dim)),
- np.random.random((output_dim,))],
- (None, 4, input_dim),
- ],
- [
- (keras.layers.LSTM(output_dim)),
- [np.random.random((input_dim, output_dim)),
- np.random.random((output_dim, output_dim)),
- np.random.random((output_dim,)),
- np.random.random((input_dim, output_dim)),
- np.random.random((output_dim, output_dim)),
- np.random.random((output_dim,)),
- np.random.random((input_dim, output_dim)),
- np.random.random((output_dim, output_dim)),
- np.random.random((output_dim,)),
- np.random.random((input_dim, output_dim)),
- np.random.random((output_dim, output_dim)),
- np.random.random((output_dim,))],
- (None, 4, input_dim),
- ],
- ]
- for layer, weights, input_shape in cases:
- layer.build(input_shape)
- _ = keras.engine.topology.preprocess_weights_for_loading(
- layer, weights, original_keras_version='1')
-
- model = keras.models.Sequential([keras.layers.Dense(2, input_dim=2)])
- _ = keras.engine.topology.preprocess_weights_for_loading(
- model, model.weights, original_keras_version='1')
-
- x = keras.Input((2,))
- y = keras.layers.Dense(2)(x)
- model = keras.models.Model(x, y)
- _ = keras.engine.topology.preprocess_weights_for_loading(
- model, model.weights, original_keras_version='1')
-
-
class DeferredModeTest(test.TestCase):
def testDeferredTensorAttributes(self):
- x = base_layers._DeferredTensor(shape=(None, 2), dtype='float32', name='x')
+ x = tf_base_layers._DeferredTensor(shape=(None, 2),
+ dtype='float32',
+ name='x')
self.assertEqual(str(x),
'DeferredTensor(\'x\', shape=(?, 2), dtype=float32)')
self.assertEqual(repr(x),
@test_util.run_in_graph_and_eager_modes()
def testSimpleNetworkBuilding(self):
- inputs = keras.engine.topology.Input(shape=(32,))
+ inputs = keras.engine.Input(shape=(32,))
if context.in_eager_mode():
- self.assertIsInstance(inputs, base_layers._DeferredTensor)
+ self.assertIsInstance(inputs, tf_base_layers._DeferredTensor)
self.assertEqual(inputs.dtype.name, 'float32')
self.assertEqual(inputs.shape.as_list(), [None, 32])
x = keras.layers.Dense(2)(inputs)
if context.in_eager_mode():
- self.assertIsInstance(x, base_layers._DeferredTensor)
+ self.assertIsInstance(x, tf_base_layers._DeferredTensor)
self.assertEqual(x.dtype.name, 'float32')
self.assertEqual(x.shape.as_list(), [None, 2])
outputs = keras.layers.Dense(4)(x)
- network = keras.engine.topology.Network(inputs, outputs)
- self.assertIsInstance(network, keras.engine.topology.Network)
+ network = keras.engine.Network(inputs, outputs)
+ self.assertIsInstance(network, keras.engine.Network)
if context.in_eager_mode():
# It should be possible to call such a network on EagerTensors.
@test_util.run_in_graph_and_eager_modes()
def testMultiIONetworkbuilding(self):
- input_a = keras.engine.topology.Input(shape=(32,))
- input_b = keras.engine.topology.Input(shape=(16,))
+ input_a = keras.engine.Input(shape=(32,))
+ input_b = keras.engine.Input(shape=(16,))
a = keras.layers.Dense(16)(input_a)
class AddLayer(keras.layers.Layer):
c = AddLayer()([a, input_b]) # pylint: disable=not-callable
c = keras.layers.Dense(2)(c)
- network = keras.engine.topology.Network([input_a, input_b], [a, c])
+ network = keras.engine.Network([input_a, input_b], [a, c])
if context.in_eager_mode():
a_val = constant_op.constant(
np.random.random((10, 32)).astype('float32'))
from tensorflow.python.keras._impl.keras import metrics as metrics_module
from tensorflow.python.keras._impl.keras import optimizers
from tensorflow.python.keras._impl.keras.engine import training_eager
-from tensorflow.python.keras._impl.keras.engine.topology import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import Network
+from tensorflow.python.keras._impl.keras.engine.base_layer import Layer
+from tensorflow.python.keras._impl.keras.engine.network import Network
from tensorflow.python.keras._impl.keras.utils.data_utils import GeneratorEnqueuer
from tensorflow.python.keras._impl.keras.utils.data_utils import OrderedEnqueuer
from tensorflow.python.keras._impl.keras.utils.data_utils import Sequence
from tensorflow.python.keras._impl.keras import regularizers
from tensorflow.python.keras._impl.keras.engine import InputSpec
from tensorflow.python.keras._impl.keras.engine import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.util.tf_export import tf_export
from tensorflow.python.keras._impl.keras import initializers
from tensorflow.python.keras._impl.keras import regularizers
from tensorflow.python.keras._impl.keras.engine import InputSpec
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.keras._impl.keras.layers.recurrent import Recurrent
from tensorflow.python.keras._impl.keras.utils import conv_utils
from tensorflow.python.util.tf_export import tf_export
from tensorflow.python.keras._impl.keras import initializers
from tensorflow.python.keras._impl.keras import regularizers
from tensorflow.python.keras._impl.keras.engine import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.util.tf_export import tf_export
from tensorflow.python.keras._impl.keras import regularizers
from tensorflow.python.keras._impl.keras.engine import InputSpec
from tensorflow.python.keras._impl.keras.engine import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.keras._impl.keras.utils import conv_utils
from tensorflow.python.util.tf_export import tf_export
from __future__ import print_function
from tensorflow.python.keras._impl.keras import backend as K
-from tensorflow.python.keras._impl.keras.engine.topology import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import Layer
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.util.tf_export import tf_export
from tensorflow.python.keras._impl.keras import backend as K
from tensorflow.python.keras._impl.keras.engine import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.util.tf_export import tf_export
from tensorflow.python.keras._impl.keras import regularizers
from tensorflow.python.keras._impl.keras.engine import InputSpec
from tensorflow.python.keras._impl.keras.engine import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.keras._impl.keras.utils.generic_utils import has_arg
from tensorflow.python.platform import tf_logging as logging
from tensorflow.python.util.tf_export import tf_export
from tensorflow.python.keras._impl.keras import backend as K
from tensorflow.python.keras._impl.keras.engine import InputSpec
from tensorflow.python.keras._impl.keras.engine import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import shape_type_conversion
+from tensorflow.python.keras._impl.keras.engine.base_layer import shape_type_conversion
from tensorflow.python.keras._impl.keras.utils.generic_utils import has_arg
from tensorflow.python.layers import utils as tf_layers_util
from tensorflow.python.util.tf_export import tf_export
# limitations under the License.
# ==============================================================================
# pylint: disable=protected-access
-"""Home of the Sequential model, and the `save_model`/`load_model` functions.
+"""Code for model cloning, plus model-related API entries.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
-import copy
-import json
-import os
-
-import numpy as np
-
-from tensorflow.python.framework import ops
from tensorflow.python.keras._impl.keras import backend as K
-from tensorflow.python.keras._impl.keras import layers as layer_module
-from tensorflow.python.keras._impl.keras import optimizers
-from tensorflow.python.keras._impl.keras.engine import topology
-from tensorflow.python.keras._impl.keras.engine.topology import Input
-from tensorflow.python.keras._impl.keras.engine.topology import InputLayer
-from tensorflow.python.keras._impl.keras.engine.topology import Layer
-from tensorflow.python.keras._impl.keras.engine.topology import TFBaseLayer
-from tensorflow.python.keras._impl.keras.engine.training import Model
+from tensorflow.python.keras._impl.keras.engine import saving
+from tensorflow.python.keras._impl.keras.engine import sequential
+from tensorflow.python.keras._impl.keras.engine import training
+from tensorflow.python.keras._impl.keras.engine.input_layer import Input
+from tensorflow.python.keras._impl.keras.engine.input_layer import InputLayer
+from tensorflow.python.keras._impl.keras.utils import generic_utils
from tensorflow.python.keras._impl.keras.utils.generic_utils import has_arg
-from tensorflow.python.keras._impl.keras.utils.io_utils import ask_to_proceed_with_overwrite
-from tensorflow.python.platform import tf_logging as logging
-from tensorflow.python.util.tf_export import tf_export
-
-
-# pylint: disable=g-import-not-at-top
-try:
- import h5py
-except ImportError:
- h5py = None
-
-try:
- import yaml
-except ImportError:
- yaml = None
-# pylint: enable=g-import-not-at-top
-
-
-@tf_export('keras.models.save_model')
-def save_model(model, filepath, overwrite=True, include_optimizer=True):
- """Save a model to a HDF5 file.
-
- The saved model contains:
- - the model's configuration (topology)
- - the model's weights
- - the model's optimizer's state (if any)
-
- Thus the saved model can be reinstantiated in
- the exact same state, without any of the code
- used for model definition or training.
-
- Arguments:
- model: Keras model instance to be saved.
- filepath: String, path where to save the model.
- overwrite: Whether we should overwrite any existing
- model at the target location, or instead
- ask the user with a manual prompt.
- include_optimizer: If True, save optimizer's state together.
-
- Raises:
- ImportError: if h5py is not available.
- """
-
- if h5py is None:
- raise ImportError('`save_model` requires h5py.')
-
- def get_json_type(obj):
- """Serialize any object to a JSON-serializable structure.
-
- Arguments:
- obj: the object to serialize
-
- Returns:
- JSON-serializable structure representing `obj`.
-
- Raises:
- TypeError: if `obj` cannot be serialized.
- """
- # if obj is a serializable Keras class instance
- # e.g. optimizer, layer
- if hasattr(obj, 'get_config'):
- return {'class_name': obj.__class__.__name__, 'config': obj.get_config()}
-
- # if obj is any numpy type
- if type(obj).__module__ == np.__name__:
- if isinstance(obj, np.ndarray):
- return {'type': type(obj), 'value': obj.tolist()}
- else:
- return obj.item()
-
- # misc functions (e.g. loss function)
- if callable(obj):
- return obj.__name__
-
- # if obj is a python 'type'
- if type(obj).__name__ == type.__name__:
- return obj.__name__
-
- raise TypeError('Not JSON Serializable:', obj)
-
- from tensorflow.python.keras._impl.keras import __version__ as keras_version # pylint: disable=g-import-not-at-top
-
- # If file exists and should not be overwritten.
- if not overwrite and os.path.isfile(filepath):
- proceed = ask_to_proceed_with_overwrite(filepath)
- if not proceed:
- return
-
- with h5py.File(filepath, mode='w') as f:
- f.attrs['keras_version'] = str(keras_version).encode('utf8')
- f.attrs['backend'] = K.backend().encode('utf8')
- f.attrs['model_config'] = json.dumps(
- {
- 'class_name': model.__class__.__name__,
- 'config': model.get_config()
- },
- default=get_json_type).encode('utf8')
-
- model_weights_group = f.create_group('model_weights')
- model_layers = model.layers
- topology.save_weights_to_hdf5_group(model_weights_group, model_layers)
-
- if include_optimizer and hasattr(model, 'optimizer'):
- if isinstance(model.optimizer, optimizers.TFOptimizer):
- logging.warning(
- 'TensorFlow optimizers do not '
- 'make it possible to access '
- 'optimizer attributes or optimizer state '
- 'after instantiation. '
- 'As a result, we cannot save the optimizer '
- 'as part of the model save file.'
- 'You will have to compile your model again after loading it. '
- 'Prefer using a Keras optimizer instead '
- '(see keras.io/optimizers).')
- else:
- f.attrs['training_config'] = json.dumps(
- {
- 'optimizer_config': {
- 'class_name': model.optimizer.__class__.__name__,
- 'config': model.optimizer.get_config()
- },
- 'loss': model.loss,
- 'metrics': model.metrics,
- 'sample_weight_mode': model.sample_weight_mode,
- 'loss_weights': model.loss_weights,
- },
- default=get_json_type).encode('utf8')
-
- # Save optimizer weights.
- symbolic_weights = getattr(model.optimizer, 'weights')
- if symbolic_weights:
- optimizer_weights_group = f.create_group('optimizer_weights')
- weight_values = K.batch_get_value(symbolic_weights)
- weight_names = []
- for w, val in zip(symbolic_weights, weight_values):
- name = str(w.name)
- weight_names.append(name.encode('utf8'))
- optimizer_weights_group.attrs['weight_names'] = weight_names
- for name, val in zip(weight_names, weight_values):
- param_dset = optimizer_weights_group.create_dataset(
- name, val.shape, dtype=val.dtype)
- if not val.shape:
- # scalar
- param_dset[()] = val
- else:
- param_dset[:] = val
- f.flush()
-
-
-@tf_export('keras.models.load_model')
-def load_model(filepath, custom_objects=None, compile=True): # pylint: disable=redefined-builtin
- """Loads a model saved via `save_model`.
-
- Arguments:
- filepath: String, path to the saved model.
- custom_objects: Optional dictionary mapping names
- (strings) to custom classes or functions to be
- considered during deserialization.
- compile: Boolean, whether to compile the model
- after loading.
-
- Returns:
- A Keras model instance. If an optimizer was found
- as part of the saved model, the model is already
- compiled. Otherwise, the model is uncompiled and
- a warning will be displayed. When `compile` is set
- to False, the compilation is omitted without any
- warning.
-
- Raises:
- ImportError: if h5py is not available.
- ValueError: In case of an invalid savefile.
- """
- if h5py is None:
- raise ImportError('`load_model` requires h5py.')
-
- if not custom_objects:
- custom_objects = {}
-
- def convert_custom_objects(obj):
- """Handles custom object lookup.
-
- Arguments:
- obj: object, dict, or list.
-
- Returns:
- The same structure, where occurrences
- of a custom object name have been replaced
- with the custom object.
- """
- if isinstance(obj, list):
- deserialized = []
- for value in obj:
- deserialized.append(convert_custom_objects(value))
- return deserialized
- if isinstance(obj, dict):
- deserialized = {}
- for key, value in obj.items():
- deserialized[key] = convert_custom_objects(value)
- return deserialized
- if obj in custom_objects:
- return custom_objects[obj]
- return obj
-
- with h5py.File(filepath, mode='r') as f:
- # instantiate model
- model_config = f.attrs.get('model_config')
- if model_config is None:
- raise ValueError('No model found in config file.')
- model_config = json.loads(model_config.decode('utf-8'))
- model = model_from_config(model_config, custom_objects=custom_objects)
-
- # set weights
- topology.load_weights_from_hdf5_group(f['model_weights'], model.layers)
-
- # Early return if compilation is not required.
- if not compile:
- return model
-
- # instantiate optimizer
- training_config = f.attrs.get('training_config')
- if training_config is None:
- logging.warning('No training configuration found in save file: '
- 'the model was *not* compiled. Compile it manually.')
- return model
- training_config = json.loads(training_config.decode('utf-8'))
- optimizer_config = training_config['optimizer_config']
- optimizer = optimizers.deserialize(
- optimizer_config, custom_objects=custom_objects)
-
- # Recover loss functions and metrics.
- loss = convert_custom_objects(training_config['loss'])
- metrics = convert_custom_objects(training_config['metrics'])
- sample_weight_mode = training_config['sample_weight_mode']
- loss_weights = training_config['loss_weights']
-
- # Compile model.
- model.compile(
- optimizer=optimizer,
- loss=loss,
- metrics=metrics,
- loss_weights=loss_weights,
- sample_weight_mode=sample_weight_mode)
-
- # Set optimizer weights.
- if 'optimizer_weights' in f:
- # Build train function (to get weight updates).
- if isinstance(model, Sequential):
- model.model._make_train_function()
- else:
- model._make_train_function()
- optimizer_weights_group = f['optimizer_weights']
- optimizer_weight_names = [
- n.decode('utf8')
- for n in optimizer_weights_group.attrs['weight_names']
- ]
- optimizer_weight_values = [
- optimizer_weights_group[n] for n in optimizer_weight_names
- ]
- try:
- model.optimizer.set_weights(optimizer_weight_values)
- except ValueError:
- logging.warning('Error in loading the saved optimizer '
- 'state. As a result, your model is '
- 'starting with a freshly initialized '
- 'optimizer.')
- return model
-
-
-@tf_export('keras.models.model_from_config')
-def model_from_config(config, custom_objects=None):
- """Instantiates a Keras model from its config.
-
- Arguments:
- config: Configuration dictionary.
- custom_objects: Optional dictionary mapping names
- (strings) to custom classes or functions to be
- considered during deserialization.
-
- Returns:
- A Keras model instance (uncompiled).
-
- Raises:
- TypeError: if `config` is not a dictionary.
- """
- if isinstance(config, list):
- raise TypeError('`model_from_config` expects a dictionary, not a list. '
- 'Maybe you meant to use '
- '`Sequential.from_config(config)`?')
- return layer_module.deserialize(config, custom_objects=custom_objects)
-
-
-@tf_export('keras.models.model_from_yaml')
-def model_from_yaml(yaml_string, custom_objects=None):
- """Parses a yaml model configuration file and returns a model instance.
-
- Arguments:
- yaml_string: YAML string encoding a model configuration.
- custom_objects: Optional dictionary mapping names
- (strings) to custom classes or functions to be
- considered during deserialization.
-
- Returns:
- A Keras model instance (uncompiled).
-
- Raises:
- ImportError: if yaml module is not found.
- """
- if yaml is None:
- raise ImportError('Requires yaml module installed.')
- config = yaml.load(yaml_string)
- return layer_module.deserialize(config, custom_objects=custom_objects)
-
-
-@tf_export('keras.models.model_from_json')
-def model_from_json(json_string, custom_objects=None):
- """Parses a JSON model configuration file and returns a model instance.
-
- Arguments:
- json_string: JSON string encoding a model configuration.
- custom_objects: Optional dictionary mapping names
- (strings) to custom classes or functions to be
- considered during deserialization.
-
- Returns:
- A Keras model instance (uncompiled).
- """
- config = json.loads(json_string)
- return layer_module.deserialize(config, custom_objects=custom_objects)
-
-
-@tf_export('keras.models.Sequential', 'keras.Sequential')
-class Sequential(Model):
- """Linear stack of layers.
-
- Arguments:
- layers: list of layers to add to the model.
-
- # Note
- The first layer passed to a Sequential model
- should have a defined input shape. What that
- means is that it should have received an `input_shape`
- or `batch_input_shape` argument,
- or for some type of layers (recurrent, Dense...)
- an `input_dim` argument.
-
- Example:
-
- ```python
- model = Sequential()
- # first layer must have a defined input shape
- model.add(Dense(32, input_dim=500))
- # afterwards, Keras does automatic shape inference
- model.add(Dense(32))
-
- # also possible (equivalent to the above):
- model = Sequential()
- model.add(Dense(32, input_shape=(500,)))
- model.add(Dense(32))
-
- # also possible (equivalent to the above):
- model = Sequential()
- # here the batch dimension is None,
- # which means any batch size will be accepted by the model.
- model.add(Dense(32, batch_input_shape=(None, 500)))
- model.add(Dense(32))
- ```
- """
-
- def __init__(self, layers=None, name=None):
- self._is_graph_network = True
- self._is_compiled = False
- self._layers = [] # Stack of layers.
- self.model = None # Internal Model instance.
- self.inputs = [] # List of input tensors
- self.outputs = [] # List of length 1: the output tensor (unique).
- self._trainable = True
- self._initial_weights = None
- self._input_layers = []
-
- # Model attributes.
- self._inbound_nodes = []
- self._outbound_nodes = []
- self.built = False
-
- # Set model name.
- if not name:
- prefix = 'sequential_'
- name = prefix + str(K.get_uid(prefix))
- self._name = name
-
- # Used by Layer base class.
- self._dtype = None
- self._activity_regularizer = None
-
- # The following properties are not actually used by Keras;
- # they exist for compatibility with TF's variable scoping mechanism.
- self._updates = []
- self._losses = []
- self._scope = None
- self._reuse = None
- self._base_name = name
- self._graph = ops.get_default_graph()
-
- # Add to the model any layers passed to the constructor.
- if layers:
- for layer in layers:
- self.add(layer)
-
- def add(self, layer):
- """Adds a layer instance on top of the layer stack.
-
- Arguments:
- layer: layer instance.
-
- Raises:
- TypeError: If `layer` is not a layer instance.
- ValueError: In case the `layer` argument does not
- know its input shape.
- ValueError: In case the `layer` argument has
- multiple output tensors, or is already connected
- somewhere else (forbidden in `Sequential` models).
- """
- if not isinstance(layer, (Layer, TFBaseLayer)):
- raise TypeError('The added layer must be '
- 'an instance of class Layer. '
- 'Found: ' + str(layer))
- if not self.outputs:
- # First layer in model: check that it is an input layer.
- if not isinstance(layer, InputLayer):
- # Create an input layer.
- # First, we need to infer its expected input shape and dtype.
- if isinstance(layer, (Model, Sequential)):
- # We were passed a model as first layer.
- # This requires a specific way to figure out the
- # input shape and dtype.
- if not layer.layers:
- raise ValueError('Cannot add an empty model '
- 'to a `Sequential` model.')
- # In case of nested models: recover the first layer
- # of the deepest model to infer input shape and dtype.
- first_layer = layer.layers[0]
- while isinstance(first_layer, (Model, Sequential)):
- first_layer = first_layer.layers[0]
- batch_shape = first_layer._batch_input_shape
- dtype = first_layer.dtype
- else:
- # We were passed a regular layer, and it should
- # know about its input shape. Otherwise, that's an error.
- if not hasattr(layer, '_batch_input_shape'):
- raise ValueError('The first layer in a '
- 'Sequential model must '
- 'get an `input_shape` argument.')
- batch_shape = layer._batch_input_shape
- dtype = layer.dtype
- # Instantiate the input layer.
- x = Input(
- batch_shape=batch_shape, dtype=dtype, name=layer.name + '_input')
- # This will build the current layer
- # and create the node connecting the current layer
- # to the input layer we just created.
- layer(x)
-
- if len(layer._inbound_nodes[-1].output_tensors) != 1:
- raise ValueError('All layers in a Sequential model '
- 'should have a single output tensor. '
- 'For multi-output layers, '
- 'use the functional API.')
-
- self.outputs = [layer._inbound_nodes[-1].output_tensors[0]]
- self.inputs = topology.get_source_inputs(self.outputs[0])
-
- # We create an input node, which we will keep updated
- # as we add more layers
- topology.Node(
- outbound_layer=self,
- inbound_layers=[],
- node_indices=[],
- tensor_indices=[],
- input_tensors=self.inputs,
- output_tensors=self.outputs)
- else:
- output_tensor = layer(self.outputs[0])
- if isinstance(output_tensor, list):
- raise TypeError('All layers in a Sequential model '
- 'should have a single output tensor. '
- 'For multi-output layers, '
- 'use the functional API.')
- self.outputs = [output_tensor]
- # update self._inbound_nodes
- self._inbound_nodes[0].output_tensors = self.outputs
- self._inbound_nodes[0].output_shapes = [K.int_shape(self.outputs[0])]
-
- self._layers.append(layer)
- self.built = False
-
- def pop(self):
- """Removes the last layer in the model.
-
- Raises:
- TypeError: if there are no layers in the model.
- """
- if not self.layers:
- raise TypeError('There are no layers in the model.')
-
- self.layers.pop()
- if not self.layers:
- self.outputs = []
- self._inbound_nodes = []
- self._outbound_nodes = []
- else:
- self.layers[-1]._outbound_nodes = []
- self.outputs = [self.layers[-1].output]
- # update self._inbound_nodes
- self._inbound_nodes[0].output_tensors = self.outputs
- self._inbound_nodes[0].output_shapes = [K.int_shape(self.outputs[0])]
- self.built = False
-
- def get_layer(self, name=None, index=None):
- """Retrieve a layer that is part of the model.
-
- Returns a layer based on either its name (unique)
- or its index in the graph. Indices are based on
- order of horizontal graph traversal (bottom-up).
-
- Arguments:
- name: string, name of layer.
- index: integer, index of layer.
-
- Returns:
- A layer instance.
- """
- if not self.built:
- self.build()
- return self.model.get_layer(name, index)
-
- def call(self, inputs, **kwargs):
- if not self.built:
- self.build()
- return self.model.call(inputs, **kwargs)
-
- def build(self, input_shape=None):
- if not self.inputs or not self.outputs:
- raise TypeError('Sequential model cannot be built: model is empty.'
- ' Add some layers first.')
- # actually create the model
- self.model = Model(self.inputs, self.outputs[0], name=self.name + '_model')
- self.model.trainable = self.trainable
-
- # mirror model attributes
- self.supports_masking = self.model.supports_masking
- self._output_mask_cache = self.model._output_mask_cache
- self._output_tensor_cache = self.model._output_tensor_cache
- self._output_shape_cache = self.model._output_shape_cache
- self._input_layers = self.model._input_layers
- self._output_layers = self.model._output_layers
- self._input_coordinates = self.model._input_coordinates
- self._output_coordinates = self.model._output_coordinates
- self._nodes_by_depth = self.model._nodes_by_depth
- self._network_nodes = self.model._network_nodes
- self.output_names = self.model.output_names
- self.input_names = self.model.input_names
- self._feed_input_names = self.model._feed_input_names
- self._feed_inputs = self.model._feed_inputs
-
- # Make sure child model callbacks
- # will call the parent Sequential model.
- self.model.callback_model = self
-
- self.built = True
-
- @property
- def uses_learning_phase(self):
- if not self.built:
- self.build()
- return self.model.uses_learning_phase
-
- def _gather_list_attr(self, attr):
- all_attrs = []
- for layer in self.layers:
- all_attrs += getattr(layer, attr, [])
- return all_attrs
-
- @property
- def trainable(self):
- return self._trainable
-
- @trainable.setter
- def trainable(self, value):
- if self.model:
- self.model.trainable = value
- self._trainable = value
-
- @property
- def trainable_weights(self):
- if not self.trainable:
- return []
- return self._gather_list_attr('trainable_weights')
-
- @property
- def non_trainable_weights(self):
- weights = self._gather_list_attr('non_trainable_weights')
- if not self.trainable:
- trainable_weights = self._gather_list_attr('trainable_weights')
- return trainable_weights + weights
- return weights
-
- @property
- def regularizers(self):
- if not self.built:
- self.build()
- return self.model.regularizers
-
- def get_weights(self):
- """Retrieves the weights of the model.
-
- Returns:
- A flat list of Numpy arrays
- (one array per model weight).
- """
- if not self.built:
- self.build()
- return self.model.get_weights()
-
- def set_weights(self, weights):
- """Sets the weights of the model.
-
- Arguments:
- weights: Should be a list
- of Numpy arrays with shapes and types matching
- the output of `model.get_weights()`.
- """
- if not self.built:
- self.build()
- self.model.set_weights(weights)
-
- def load_weights(self, filepath, by_name=False):
- if h5py is None:
- raise ImportError('`load_weights` requires h5py.')
- f = h5py.File(filepath, mode='r')
- if 'layer_names' not in f.attrs and 'model_weights' in f:
- f = f['model_weights']
- layers = self.layers
- if by_name:
- topology.load_weights_from_hdf5_group_by_name(f, layers)
- else:
- topology.load_weights_from_hdf5_group(f, layers)
- if hasattr(f, 'close'):
- f.close()
-
- def save_weights(self, filepath, overwrite=True):
- if h5py is None:
- raise ImportError('`save_weights` requires h5py.')
- # If file exists and should not be overwritten:
- if not overwrite and os.path.isfile(filepath):
- proceed = ask_to_proceed_with_overwrite(filepath)
- if not proceed:
- return
- layers = self.layers
- f = h5py.File(filepath, 'w')
- topology.save_weights_to_hdf5_group(f, layers)
- f.flush()
- f.close()
-
- def compile(self,
- optimizer,
- loss,
- metrics=None,
- sample_weight_mode=None,
- weighted_metrics=None,
- target_tensors=None,
- **kwargs):
- """Configures the model for training.
-
- Arguments:
- optimizer: String (name of optimizer) or optimizer object.
- See [optimizers](/optimizers).
- loss: String (name of objective function) or objective function.
- See [losses](/losses).
- If the model has multiple outputs, you can use a different loss
- on each output by passing a dictionary or a list of losses.
- The loss value that will be minimized by the model
- will then be the sum of all individual losses.
- metrics: List of metrics to be evaluated by the model
- during training and testing.
- Typically you will use `metrics=['accuracy']`.
- To specify different metrics for different outputs of a
- multi-output model, you could also pass a dictionary,
- such as `metrics={'output_a': 'accuracy'}`.
- sample_weight_mode: If you need to do timestep-wise
- sample weighting (2D weights), set this to `"temporal"`.
- `None` defaults to sample-wise weights (1D).
- If the model has multiple outputs, you can use a different
- `sample_weight_mode` on each output by passing a
- dictionary or a list of modes.
- weighted_metrics: list of metrics to be evaluated and weighted
- by `sample_weight` or `class_weight` during training and testing.
- target_tensors: By default, Keras will create a placeholder for the
- model's target, which will be fed with the target data during
- training. If instead you would like to use your own
- target tensor (in turn, Keras will not expect external
- Numpy data for these targets at training time), you
- can specify them via the `target_tensors` argument.
- It should be a single tensor
- (for a single-output `Sequential` model).
- **kwargs: These arguments are passed into `tf.Session.run`.
-
- Example:
- ```python
- model = Sequential()
- model.add(Dense(32, input_shape=(500,)))
- model.add(Dense(10, activation='softmax'))
- model.compile(optimizer='rmsprop',
- loss='categorical_crossentropy',
- metrics=['accuracy'])
- ```
- """
- # create the underlying model
- self.build()
- # call compile method of Model class
- self.model.compile(
- optimizer,
- loss,
- metrics=metrics,
- sample_weight_mode=sample_weight_mode,
- weighted_metrics=weighted_metrics,
- target_tensors=target_tensors,
- **kwargs)
- self.optimizer = self.model.optimizer
- self.loss = self.model.loss
- self.metrics = self.model.metrics
- self.loss_weights = self.model.loss_weights
- self.sample_weight_mode = self.model.sample_weight_mode
- self.weighted_metrics = self.model.weighted_metrics
- self.targets = self.model.targets
- self.metrics_tensors = self.model.metrics_tensors
- self.metrics_names = self.model.metrics_names
- self.sample_weights = self.model.sample_weights
- self.total_loss = self.model.total_loss
-
- def fit(self,
- x=None,
- y=None,
- batch_size=None,
- epochs=1,
- verbose=1,
- callbacks=None,
- validation_split=0.,
- validation_data=None,
- shuffle=True,
- class_weight=None,
- sample_weight=None,
- initial_epoch=0,
- steps_per_epoch=None,
- validation_steps=None,
- **kwargs):
- """Trains the model for a fixed number of epochs.
-
- Arguments:
- x: Numpy array of training data.
- If the input layer in the model is named, you can also pass a
- dictionary mapping the input name to a Numpy array.
- `x` can be `None` (default) if feeding from
- TensorFlow data tensors.
- y: Numpy array of target (label) data.
- If the output layer in the model is named, you can also pass a
- dictionary mapping the output name to a Numpy array.
- `y` can be `None` (default) if feeding from
- TensorFlow data tensors.
- batch_size: Integer or `None`.
- Number of samples per gradient update.
- If unspecified, it will default to 32.
- epochs: Integer. Number of epochs to train the model.
- An epoch is an iteration over the entire `x` and `y`
- data provided.
- Note that in conjunction with `initial_epoch`,
- `epochs` is to be understood as "final epoch".
- The model is not trained for a number of iterations
- given by `epochs`, but merely until the epoch
- of index `epochs` is reached.
- verbose: 0, 1, or 2. Verbosity mode.
- 0 = silent, 1 = progress bar, 2 = one line per epoch.
- callbacks: List of `keras.callbacks.Callback` instances.
- List of callbacks to apply during training.
- See [callbacks](/callbacks).
- validation_split: Float between 0 and 1:
- Fraction of the training data to be used as validation data.
- The model will set apart this fraction of the training data,
- will not train on it, and will evaluate
- the loss and any model metrics
- on this data at the end of each epoch.
- The validation data is selected from the last samples
- in the `x` and `y` data provided, before shuffling.
- validation_data: tuple `(x_val, y_val)` or tuple
- `(x_val, y_val, val_sample_weights)` on which to evaluate
- the loss and any model metrics at the end of each epoch.
- The model will not be trained on this data.
- This will override `validation_split`.
- shuffle: Boolean (whether to shuffle the training data
- before each epoch) or str (for 'batch').
- 'batch' is a special option for dealing with the
- limitations of HDF5 data; it shuffles in batch-sized chunks.
- Has no effect when `steps_per_epoch` is not `None`.
- class_weight: Optional dictionary mapping class indices (integers)
- to a weight (float) value, used for weighting the loss function
- (during training only).
- This can be useful to tell the model to
- "pay more attention" to samples from
- an under-represented class.
- sample_weight: Optional Numpy array of weights for
- the training samples, used for weighting the loss function
- (during training only). You can either pass a flat (1D)
- Numpy array with the same length as the input samples
- (1:1 mapping between weights and samples),
- or in the case of temporal data,
- you can pass a 2D array with shape
- `(samples, sequence_length)`,
- to apply a different weight to every timestep of every sample.
- In this case you should make sure to specify
- `sample_weight_mode="temporal"` in `compile()`.
- initial_epoch: Epoch at which to start training
- (useful for resuming a previous training run).
- steps_per_epoch: Total number of steps (batches of samples)
- before declaring one epoch finished and starting the
- next epoch. When training with input tensors such as
- TensorFlow data tensors, the default `None` is equal to
- the number of unique samples in your dataset divided by
- the batch size, or 1 if that cannot be determined.
- validation_steps: Only relevant if `steps_per_epoch`
- is specified. Total number of steps (batches of samples)
- to validate before stopping.
- **kwargs: Used for backwards compatibility support.
-
- Returns:
- A `History` object. Its `History.history` attribute is
- a record of training loss values and metrics values
- at successive epochs, as well as validation loss values
- and validation metrics values (if applicable).
-
- Raises:
- RuntimeError: If the model was never compiled.
- ValueError: In case of mismatch between the provided input data
- and what the model expects.
- """
- if not self.built:
- raise RuntimeError('The model needs to be compiled before being used.')
- return self.model.fit(
- x,
- y,
- batch_size=batch_size,
- epochs=epochs,
- verbose=verbose,
- callbacks=callbacks,
- validation_split=validation_split,
- validation_data=validation_data,
- shuffle=shuffle,
- class_weight=class_weight,
- sample_weight=sample_weight,
- initial_epoch=initial_epoch,
- steps_per_epoch=steps_per_epoch,
- validation_steps=validation_steps)
-
- def evaluate(self, x, y, batch_size=32, verbose=1, sample_weight=None):
- """Computes the loss on some input data, batch by batch.
-
- Arguments:
- x: input data, as a Numpy array or list of Numpy arrays
- (if the model has multiple inputs).
- y: labels, as a Numpy array.
- batch_size: integer. Number of samples per gradient update.
- verbose: verbosity mode, 0 or 1.
- sample_weight: sample weights, as a Numpy array.
-
- Returns:
- Scalar test loss (if the model has no metrics)
- or list of scalars (if the model computes other metrics).
- The attribute `model.metrics_names` will give you
- the display labels for the scalar outputs.
-
- Raises:
- RuntimeError: if the model was never compiled.
- """
- if not self.built:
- raise RuntimeError('The model needs to be compiled before being used.')
- return self.model.evaluate(
- x,
- y,
- batch_size=batch_size,
- verbose=verbose,
- sample_weight=sample_weight)
-
- def predict(self, x, batch_size=32, verbose=0):
- """Generates output predictions for the input samples.
-
- The input samples are processed batch by batch.
-
- Arguments:
- x: the input data, as a Numpy array.
- batch_size: integer.
- verbose: verbosity mode, 0 or 1.
-
- Returns:
- A Numpy array of predictions.
- """
- if not self.built:
- self.build()
- return self.model.predict(x, batch_size=batch_size, verbose=verbose)
-
- def predict_on_batch(self, x):
- """Returns predictions for a single batch of samples.
-
- Arguments:
- x: input data, as a Numpy array or list of Numpy arrays
- (if the model has multiple inputs).
-
- Returns:
- A Numpy array of predictions.
- """
- if not self.built:
- self.build()
- return self.model.predict_on_batch(x)
-
- def train_on_batch(self, x, y, class_weight=None, sample_weight=None):
- """Single gradient update over one batch of samples.
-
- Arguments:
- x: input data, as a Numpy array or list of Numpy arrays
- (if the model has multiple inputs).
- y: labels, as a Numpy array.
- class_weight: dictionary mapping classes to a weight value,
- used for scaling the loss function (during training only).
- sample_weight: sample weights, as a Numpy array.
-
- Returns:
- Scalar training loss (if the model has no metrics)
- or list of scalars (if the model computes other metrics).
- The attribute `model.metrics_names` will give you
- the display labels for the scalar outputs.
-
- Raises:
- RuntimeError: if the model was never compiled.
- """
- if not self.built:
- raise RuntimeError('The model needs to be compiled before being used.')
- return self.model.train_on_batch(
- x, y, sample_weight=sample_weight, class_weight=class_weight)
-
- def test_on_batch(self, x, y, sample_weight=None):
- """Evaluates the model over a single batch of samples.
-
- Arguments:
- x: input data, as a Numpy array or list of Numpy arrays
- (if the model has multiple inputs).
- y: labels, as a Numpy array.
- sample_weight: sample weights, as a Numpy array.
-
- Returns:
- Scalar test loss (if the model has no metrics)
- or list of scalars (if the model computes other metrics).
- The attribute `model.metrics_names` will give you
- the display labels for the scalar outputs.
-
- Raises:
- RuntimeError: if the model was never compiled.
- """
- if not self.built:
- raise RuntimeError('The model needs to be compiled before being used.')
- return self.model.test_on_batch(x, y, sample_weight=sample_weight)
-
- def predict_proba(self, x, batch_size=32, verbose=0):
- """Generates class probability predictions for the input samples.
-
- The input samples are processed batch by batch.
-
- Arguments:
- x: input data, as a Numpy array or list of Numpy arrays
- (if the model has multiple inputs).
- batch_size: integer.
- verbose: verbosity mode, 0 or 1.
-
- Returns:
- A Numpy array of probability predictions.
- """
- preds = self.predict(x, batch_size, verbose)
- if preds.min() < 0. or preds.max() > 1.:
- logging.warning('Network returning invalid probability values. '
- 'The last layer might not normalize predictions '
- 'into probabilities '
- '(like softmax or sigmoid would).')
- return preds
-
- def predict_classes(self, x, batch_size=32, verbose=0):
- """Generate class predictions for the input samples.
-
- The input samples are processed batch by batch.
-
- Arguments:
- x: input data, as a Numpy array or list of Numpy arrays
- (if the model has multiple inputs).
- batch_size: integer.
- verbose: verbosity mode, 0 or 1.
-
- Returns:
- A numpy array of class predictions.
- """
- proba = self.predict(x, batch_size=batch_size, verbose=verbose)
- if proba.shape[-1] > 1:
- return proba.argmax(axis=-1)
- else:
- return (proba > 0.5).astype('int32')
-
- def fit_generator(self,
- generator,
- steps_per_epoch=None,
- epochs=1,
- verbose=1,
- callbacks=None,
- validation_data=None,
- validation_steps=None,
- class_weight=None,
- max_queue_size=10,
- workers=1,
- use_multiprocessing=False,
- shuffle=True,
- initial_epoch=0,
- **kwargs):
- """Fits the model on data generated batch-by-batch by a Python generator.
-
- The generator is run in parallel to the model, for efficiency.
- For instance, this allows you to do real-time data augmentation
- on images on CPU in parallel to training your model on GPU.
-
- Arguments:
- generator: A generator.
- The output of the generator must be either
- - a tuple (inputs, targets)
- - a tuple (inputs, targets, sample_weights).
- All arrays should contain the same number of samples.
- The generator is expected to loop over its data
- indefinitely. An epoch finishes when `steps_per_epoch`
- batches have been seen by the model.
- steps_per_epoch: Total number of steps (batches of samples)
- to yield from `generator` before declaring one epoch
- finished and starting the next epoch. It should typically
- be equal to the number of samples of your dataset
- divided by the batch size.
- Optional for `Sequence`: if unspecified, will use
- the `len(generator)` as a number of steps.
- epochs: Integer, total number of iterations on the data.
- Note that in conjunction with initial_epoch, the parameter
- epochs is to be understood as "final epoch". The model is
- not trained for n steps given by epochs, but until the
- epoch epochs is reached.
- verbose: Verbosity mode, 0, 1, or 2.
- callbacks: List of callbacks to be called during training.
- validation_data: This can be either
- - A generator for the validation data
- - A tuple (inputs, targets)
- - A tuple (inputs, targets, sample_weights).
- validation_steps: Only relevant if `validation_data`
- is a generator.
- Number of steps to yield from validation generator
- at the end of every epoch. It should typically
- be equal to the number of samples of your
- validation dataset divided by the batch size.
- Optional for `Sequence`: if unspecified, will use
- the `len(validation_data)` as a number of steps.
- class_weight: Dictionary mapping class indices to a weight
- for the class.
- max_queue_size: Maximum size for the generator queue
- workers: Maximum number of processes to spin up
- use_multiprocessing: If True, use process based threading.
- Note that because
- this implementation relies on multiprocessing,
- you should not pass
- non picklable arguments to the generator
- as they can't be passed
- easily to children processes.
- shuffle: Whether to shuffle the order of the batches at
- the beginning of each epoch. Only used with instances
- of `Sequence` (keras.utils.Sequence).
- initial_epoch: Epoch at which to start training
- (useful for resuming a previous training run)
- **kwargs: support for legacy arguments.
-
- Returns:
- A `History` object.
-
- Raises:
- RuntimeError: if the model was never compiled.
- ValueError: In case the generator yields
- data in an invalid format.
-
- Example:
-
- ```python
- def generate_arrays_from_file(path):
- while 1:
- f = open(path)
- for line in f:
- # create Numpy arrays of input data
- # and labels, from each line in the file
- x, y = process_line(line)
- yield (x, y)
- f.close()
-
- model.fit_generator(generate_arrays_from_file('/my_file.txt'),
- steps_per_epoch=1000, epochs=10)
- ```
- """
- # Legacy support
- if 'max_q_size' in kwargs:
- max_queue_size = kwargs.pop('max_q_size')
- logging.warning('The argument `max_q_size` has been renamed '
- '`max_queue_size`. Update your method calls accordingly.')
- if 'pickle_safe' in kwargs:
- use_multiprocessing = kwargs.pop('pickle_safe')
- logging.warning('The argument `pickle_safe` has been renamed '
- '`use_multiprocessing`. '
- 'Update your method calls accordingly.')
- if kwargs:
- raise ValueError('Unrecognized keyword arguments: ' + str(kwargs))
-
- if not self.built:
- raise RuntimeError('The model needs to be compiled before being used.')
- return self.model.fit_generator(
- generator,
- steps_per_epoch,
- epochs,
- verbose=verbose,
- callbacks=callbacks,
- validation_data=validation_data,
- validation_steps=validation_steps,
- class_weight=class_weight,
- max_queue_size=max_queue_size,
- workers=workers,
- use_multiprocessing=use_multiprocessing,
- shuffle=shuffle,
- initial_epoch=initial_epoch)
-
- def evaluate_generator(self,
- generator,
- steps=None,
- max_queue_size=10,
- workers=1,
- use_multiprocessing=False,
- **kwargs):
- """Evaluates the model on a data generator.
-
- The generator should return the same kind of data
- as accepted by `test_on_batch`.
-
- Arguments:
- generator: Generator yielding tuples (inputs, targets)
- or (inputs, targets, sample_weights)
- steps: Total number of steps (batches of samples)
- to yield from `generator` before stopping.
- Optional for `Sequence`: if unspecified, will use
- the `len(generator)` as a number of steps.
- max_queue_size: maximum size for the generator queue
- workers: maximum number of processes to spin up
- use_multiprocessing: if True, use process based threading.
- Note that because this implementation
- relies on multiprocessing, you should not pass
- non picklable arguments to the generator
- as they can't be passed easily to children processes.
- **kwargs: support for legacy arguments.
-
- Returns:
- Scalar test loss (if the model has no metrics)
- or list of scalars (if the model computes other metrics).
- The attribute `model.metrics_names` will give you
- the display labels for the scalar outputs.
-
- Raises:
- RuntimeError: if the model was never compiled.
- ValueError: In case the generator yields
- data in an invalid format.
- """
- # Legacy support
- if 'max_q_size' in kwargs:
- max_queue_size = kwargs.pop('max_q_size')
- logging.warning('The argument `max_q_size` has been renamed '
- '`max_queue_size`. Update your method calls accordingly.')
- if 'pickle_safe' in kwargs:
- use_multiprocessing = kwargs.pop('pickle_safe')
- logging.warning('The argument `pickle_safe` has been renamed '
- '`use_multiprocessing`. '
- 'Update your method calls accordingly.')
- if kwargs:
- raise ValueError('Unrecognized keyword arguments: ' + str(kwargs))
-
- if not self.built:
- raise RuntimeError('The model needs to be compiled before being used.')
- return self.model.evaluate_generator(
- generator,
- steps,
- max_queue_size=max_queue_size,
- workers=workers,
- use_multiprocessing=use_multiprocessing)
-
- def predict_generator(self,
- generator,
- steps=None,
- max_queue_size=10,
- workers=1,
- use_multiprocessing=False,
- verbose=0,
- **kwargs):
- """Generates predictions for the input samples from a data generator.
-
- The generator should return the same kind of data as accepted by
- `predict_on_batch`.
-
- Arguments:
- generator: generator yielding batches of input samples.
- steps: Total number of steps (batches of samples)
- to yield from `generator` before stopping.
- Optional for `Sequence`: if unspecified, will use
- the `len(generator)` as a number of steps.
- max_queue_size: maximum size for the generator queue
- workers: maximum number of processes to spin up
- use_multiprocessing: if True, use process based threading.
- Note that because this implementation
- relies on multiprocessing, you should not pass
- non picklable arguments to the generator
- as they can't be passed easily to children processes.
- verbose: verbosity mode, 0 or 1.
- **kwargs: support for legacy arguments.
-
- Returns:
- A Numpy array of predictions.
-
- Raises:
- ValueError: In case the generator yields
- data in an invalid format.
- """
- # Legacy support
- if 'max_q_size' in kwargs:
- max_queue_size = kwargs.pop('max_q_size')
- logging.warning('The argument `max_q_size` has been renamed '
- '`max_queue_size`. Update your method calls accordingly.')
- if 'pickle_safe' in kwargs:
- use_multiprocessing = kwargs.pop('pickle_safe')
- logging.warning('The argument `pickle_safe` has been renamed '
- '`use_multiprocessing`. '
- 'Update your method calls accordingly.')
- if kwargs:
- raise ValueError('Unrecognized keyword arguments: ' + str(kwargs))
-
- if not self.built:
- self.build()
- return self.model.predict_generator(
- generator,
- steps,
- max_queue_size=max_queue_size,
- workers=workers,
- use_multiprocessing=use_multiprocessing,
- verbose=verbose)
- def get_config(self):
- config = []
- for layer in self.layers:
- config.append({
- 'class_name': layer.__class__.__name__,
- 'config': layer.get_config()
- })
- return copy.deepcopy(config)
- @classmethod
- def from_config(cls, config, custom_objects=None):
- model = cls()
- for conf in config:
- layer = layer_module.deserialize(conf, custom_objects=custom_objects)
- model.add(layer)
- return model
+# API entries importable from `keras.models`:
+Model = training.Model # pylint: disable=invalid-name
+Sequential = sequential.Sequential # pylint: disable=invalid-name
+save_model = saving.save_model
+load_model = saving.load_model
+model_from_config = saving.model_from_config
+model_from_yaml = saving.model_from_yaml
+model_from_json = saving.model_from_json
def _clone_functional_model(model, input_tensors=None):
else:
# Make sure that all input tensors come from a Keras layer.
# If tensor comes from an input layer: cache the input layer.
- input_tensors = topology.to_list(input_tensors)
+ input_tensors = generic_utils.to_list(input_tensors)
input_tensors_ = []
for i, x in enumerate(input_tensors):
if not K.is_keras_tensor(x):
# Reuse previously cloned layer.
layer = layer_map[layer]
# Don't call InputLayer multiple times.
- if isinstance(layer, topology.InputLayer):
+ if isinstance(layer, InputLayer):
continue
# Gather inputs to call the new layer.
if has_arg(layer.call, 'mask'):
if 'mask' not in kwargs:
kwargs['mask'] = computed_mask
- output_tensors = topology.to_list(layer(computed_tensor, **kwargs))
- output_masks = topology.to_list(
+ output_tensors = generic_utils.to_list(layer(computed_tensor,
+ **kwargs))
+ output_masks = generic_utils.to_list(
layer.compute_mask(computed_tensor, computed_mask))
computed_tensors = [computed_tensor]
computed_masks = [computed_mask]
if has_arg(layer.call, 'mask'):
if 'mask' not in kwargs:
kwargs['mask'] = computed_masks
- output_tensors = topology.to_list(layer(computed_tensors, **kwargs))
- output_masks = topology.to_list(
+ output_tensors = generic_utils.to_list(layer(computed_tensors,
+ **kwargs))
+ output_masks = generic_utils.to_list(
layer.compute_mask(computed_tensors, computed_masks))
# Update tensor_map.
for x, y, mask in zip(reference_output_tensors, output_tensors,
if input_tensors is None:
return Sequential(layers=layers, name=model.name)
else:
- if len(topology.to_list(input_tensors)) != 1:
+ if len(generic_utils.to_list(input_tensors)) != 1:
raise ValueError('To clone a `Sequential` model, we expect '
' at most one tensor '
'as part of `input_tensors`.')
- x = topology.to_list(input_tensors)[0]
+ x = generic_utils.to_list(input_tensors)[0]
if K.is_keras_tensor(x):
origin_layer = x._keras_history[0]
- if isinstance(origin_layer, topology.InputLayer):
+ if isinstance(origin_layer, InputLayer):
return Sequential(layers=[origin_layer] + layers, name=model.name)
else:
raise ValueError('Cannot clone a `Sequential` model on top '
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
-"""Tests for training routines."""
+"""Tests for `models.py` (model cloning, mainly)."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
-import os
-import shutil
-import tempfile
-
import numpy as np
from tensorflow.python.keras._impl import keras
from tensorflow.python.platform import test
-from tensorflow.python.training import training as training_module
-
-try:
- import h5py # pylint:disable=g-import-not-at-top
-except ImportError:
- h5py = None
-
-
-class TestModelSaving(test.TestCase):
-
- def test_sequential_model_saving(self):
- if h5py is None:
- return # Skip test if models cannot be saved.
-
- with self.test_session():
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(2, input_shape=(3,)))
- model.add(keras.layers.RepeatVector(3))
- model.add(keras.layers.TimeDistributed(keras.layers.Dense(3)))
- model.compile(loss=keras.losses.MSE,
- optimizer=keras.optimizers.RMSprop(lr=0.0001),
- metrics=[keras.metrics.categorical_accuracy],
- sample_weight_mode='temporal')
- x = np.random.random((1, 3))
- y = np.random.random((1, 3, 3))
- model.train_on_batch(x, y)
-
- out = model.predict(x)
- fd, fname = tempfile.mkstemp('.h5')
- keras.models.save_model(model, fname)
-
- new_model = keras.models.load_model(fname)
- os.close(fd)
- os.remove(fname)
-
- out2 = new_model.predict(x)
- self.assertAllClose(out, out2, atol=1e-05)
-
- # test that new updates are the same with both models
- x = np.random.random((1, 3))
- y = np.random.random((1, 3, 3))
- model.train_on_batch(x, y)
- new_model.train_on_batch(x, y)
- out = model.predict(x)
- out2 = new_model.predict(x)
- self.assertAllClose(out, out2, atol=1e-05)
-
- def test_sequential_model_saving_2(self):
- if h5py is None:
- return # Skip test if models cannot be saved.
-
- with self.test_session():
- # test with custom optimizer, loss
-
- class CustomOp(keras.optimizers.RMSprop):
- pass
-
- def custom_loss(y_true, y_pred):
- return keras.losses.mse(y_true, y_pred)
-
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(2, input_shape=(3,)))
- model.add(keras.layers.Dense(3))
- model.compile(loss=custom_loss, optimizer=CustomOp(), metrics=['acc'])
-
- x = np.random.random((1, 3))
- y = np.random.random((1, 3))
- model.train_on_batch(x, y)
-
- out = model.predict(x)
- fd, fname = tempfile.mkstemp('.h5')
- keras.models.save_model(model, fname)
-
- model = keras.models.load_model(
- fname,
- custom_objects={'CustomOp': CustomOp,
- 'custom_loss': custom_loss})
- os.close(fd)
- os.remove(fname)
-
- out2 = model.predict(x)
- self.assertAllClose(out, out2, atol=1e-05)
-
- def test_functional_model_saving(self):
- if h5py is None:
- return # Skip test if models cannot be saved.
-
- with self.test_session():
- inputs = keras.layers.Input(shape=(3,))
- x = keras.layers.Dense(2)(inputs)
- output = keras.layers.Dense(3)(x)
-
- model = keras.models.Model(inputs, output)
- model.compile(loss=keras.losses.MSE,
- optimizer=keras.optimizers.RMSprop(lr=0.0001),
- metrics=[keras.metrics.categorical_accuracy])
- x = np.random.random((1, 3))
- y = np.random.random((1, 3))
- model.train_on_batch(x, y)
-
- out = model.predict(x)
- fd, fname = tempfile.mkstemp('.h5')
- keras.models.save_model(model, fname)
-
- model = keras.models.load_model(fname)
- os.close(fd)
- os.remove(fname)
-
- out2 = model.predict(x)
- self.assertAllClose(out, out2, atol=1e-05)
-
- def test_saving_without_compilation(self):
- if h5py is None:
- return # Skip test if models cannot be saved.
-
- with self.test_session():
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(2, input_shape=(3,)))
- model.add(keras.layers.Dense(3))
- model.compile(loss='mse', optimizer='sgd', metrics=['acc'])
-
- fd, fname = tempfile.mkstemp('.h5')
- keras.models.save_model(model, fname)
- model = keras.models.load_model(fname)
- os.close(fd)
- os.remove(fname)
-
- def test_saving_with_tf_optimizer(self):
- if h5py is None:
- return # Skip test if models cannot be saved.
-
- with self.test_session():
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(2, input_shape=(3,)))
- model.add(keras.layers.Dense(3))
- model.compile(loss='mse',
- optimizer=training_module.AdadeltaOptimizer(0.1),
- metrics=['acc'])
-
- fd, fname = tempfile.mkstemp('.h5')
- keras.models.save_model(model, fname)
- model = keras.models.load_model(fname)
- os.close(fd)
- os.remove(fname)
-
- def test_saving_right_after_compilation(self):
- if h5py is None:
- return # Skip test if models cannot be saved.
-
- with self.test_session():
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(2, input_shape=(3,)))
- model.add(keras.layers.Dense(3))
- model.compile(loss='mse', optimizer='sgd', metrics=['acc'])
- model.model._make_train_function()
-
- fd, fname = tempfile.mkstemp('.h5')
- keras.models.save_model(model, fname)
- model = keras.models.load_model(fname)
- os.close(fd)
- os.remove(fname)
-
- def test_saving_lambda_numpy_array_arguments(self):
- if h5py is None:
- return # Skip test if models cannot be saved.
-
- mean = np.random.random((4, 2, 3))
- std = np.abs(np.random.random((4, 2, 3))) + 1e-5
- inputs = keras.layers.Input(shape=(4, 2, 3))
- output = keras.layers.Lambda(lambda image, mu, std: (image - mu) / std,
- arguments={'mu': mean, 'std': std})(inputs)
- model = keras.models.Model(inputs, output)
- model.compile(loss='mse', optimizer='sgd', metrics=['acc'])
-
- fd, fname = tempfile.mkstemp('.h5')
- keras.models.save_model(model, fname)
-
- model = keras.models.load_model(fname)
- os.close(fd)
- os.remove(fname)
-
- self.assertAllClose(mean, model.layers[1].arguments['mu'])
- self.assertAllClose(std, model.layers[1].arguments['std'])
-
-
-class TestSequential(test.TestCase):
- """Most Sequential model API tests are covered in `training_test.py`.
- """
-
- def test_basic_methods(self):
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(1, input_dim=2))
- model.add(keras.layers.Dropout(0.3, name='dp'))
- model.add(keras.layers.Dense(2, kernel_regularizer='l2',
- kernel_constraint='max_norm'))
- model.build()
- self.assertEqual(model.state_updates, model.model.state_updates)
- self.assertEqual(model.get_layer(name='dp').name, 'dp')
-
- def test_sequential_pop(self):
- num_hidden = 5
- input_dim = 3
- batch_size = 5
- num_classes = 2
- with self.test_session():
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
- model.add(keras.layers.Dense(num_classes))
- model.compile(loss='mse', optimizer='sgd')
- x = np.random.random((batch_size, input_dim))
- y = np.random.random((batch_size, num_classes))
- model.fit(x, y, epochs=1)
- model.pop()
- self.assertEqual(len(model.layers), 1)
- self.assertEqual(model.output_shape, (None, num_hidden))
- model.compile(loss='mse', optimizer='sgd')
- y = np.random.random((batch_size, num_hidden))
- model.fit(x, y, epochs=1)
-
- # Test popping single-layer model
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
- model.pop()
- self.assertEqual(len(model.layers), 0)
- self.assertEqual(len(model.outputs), 0)
-
- # Invalid use case
- model = keras.models.Sequential()
- with self.assertRaises(TypeError):
- model.pop()
-
- def test_sequential_weight_loading(self):
- if h5py is None:
- return
-
- temp_dir = self.get_temp_dir()
- self.addCleanup(shutil.rmtree, temp_dir)
- h5_path = os.path.join(temp_dir, 'test.h5')
-
- num_hidden = 5
- input_dim = 3
- batch_size = 5
- num_classes = 2
-
- with self.test_session():
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
- model.add(keras.layers.Dense(num_classes))
-
- x = np.random.random((batch_size, input_dim))
- ref_y = model.predict(x)
-
- model.save_weights(h5_path)
-
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(num_hidden, input_dim=input_dim))
- model.add(keras.layers.Dense(num_classes))
- model.load_weights(h5_path)
- y = model.predict(x)
-
- self.assertAllClose(y, ref_y)
-
- def test_invalid_use_cases(self):
- with self.test_session():
- # Added objects must be layer instances
- with self.assertRaises(TypeError):
- model = keras.models.Sequential()
- model.add(None)
-
- # Added layers must have an inputs shape
- with self.assertRaises(ValueError):
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(1))
-
- # Added layers cannot have multiple outputs
- class MyLayer(keras.layers.Layer):
-
- def call(self, inputs):
- return [3 * inputs, 2 * inputs]
-
- def compute_output_shape(self, input_shape):
- return [input_shape, input_shape]
-
- with self.assertRaises(ValueError):
- model = keras.models.Sequential()
- model.add(MyLayer(input_shape=(3,)))
- with self.assertRaises(TypeError):
- model = keras.models.Sequential()
- model.add(keras.layers.Dense(1, input_dim=1))
- model.add(MyLayer())
-
- # Building empty model
- model = keras.models.Sequential()
- with self.assertRaises(TypeError):
- model.build()
-
- def test_nested_sequential_trainability(self):
- input_dim = 20
- num_units = 10
- num_classes = 2
-
- inner_model = keras.models.Sequential()
- inner_model.add(keras.layers.Dense(num_units, input_shape=(input_dim,)))
-
- model = keras.models.Sequential()
- model.add(inner_model)
- model.add(keras.layers.Dense(num_classes))
-
- self.assertEqual(len(model.trainable_weights), 4)
- inner_model.trainable = False
- self.assertEqual(len(model.trainable_weights), 2)
- inner_model.trainable = True
- self.assertEqual(len(model.trainable_weights), 4)
-
- def test_sequential_update_disabling(self):
- val_a = np.random.random((10, 4))
- val_out = np.random.random((10, 4))
-
- with self.test_session():
- model = keras.models.Sequential()
- model.add(keras.layers.BatchNormalization(input_shape=(4,)))
-
- model.trainable = False
- assert not model.updates
-
- model.compile('sgd', 'mse')
- assert not model.updates
- assert not model.model.updates
-
- x1 = model.predict(val_a)
- model.train_on_batch(val_a, val_out)
- x2 = model.predict(val_a)
- self.assertAllClose(x1, x2, atol=1e-7)
-
- model.trainable = True
- model.compile('sgd', 'mse')
- assert model.updates
- assert model.model.updates
-
- model.train_on_batch(val_a, val_out)
- x2 = model.predict(val_a)
- assert np.abs(np.sum(x1 - x2)) > 1e-5
class TestModelCloning(test.TestCase):
return arrays[start:stop]
else:
return [None]
+
+
+def to_list(x):
+ """Normalizes a list/tensor into a list.
+
+ If a tensor is passed, we return
+ a list of size 1 containing the tensor.
+
+ Arguments:
+ x: target object to be normalized.
+
+ Returns:
+ A list.
+ """
+ if isinstance(x, list):
+ return x
+ return [x]
path: "tensorflow.keras.Model"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.training.Model\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Network\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.network.Network\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.Sequential"
tf_class {
- is_instance: "<class \'tensorflow.python.keras._impl.keras.models.Sequential\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.sequential.Sequential\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.training.Model\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Network\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.network.Network\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Activation"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Activation\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.ActivityRegularization"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.ActivityRegularization\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge.Add\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge._Merge\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.AlphaDropout"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.noise.AlphaDropout\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.AveragePooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.AveragePooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.AveragePooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.AveragePooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.AveragePooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.AveragePooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge.Average\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge._Merge\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.AveragePooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.AveragePooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.AveragePooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.AveragePooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.AveragePooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.AveragePooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.normalization.BatchNormalization\'>"
is_instance: "<class \'tensorflow.python.layers.normalization.BatchNormalization\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.wrappers.Bidirectional\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.wrappers.Wrapper\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge.Concatenate\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge._Merge\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional_recurrent.ConvLSTM2D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional_recurrent.ConvRecurrent2D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.Recurrent\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Conv1D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv1D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv2DTranspose\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Conv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv3DTranspose\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv3D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Conv3D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv3D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Conv1D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv1D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv2DTranspose\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Conv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv3DTranspose\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv3D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Conv3D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional.Conv3D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Cropping1D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Cropping1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Cropping2D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Cropping2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Cropping3D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.Cropping3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Dense\'>"
is_instance: "<class \'tensorflow.python.layers.core.Dense\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge.Dot\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge._Merge\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Dropout\'>"
is_instance: "<class \'tensorflow.python.layers.core.Dropout\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.ELU"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.advanced_activations.ELU\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Embedding"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.embeddings.Embedding\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Flatten\'>"
is_instance: "<class \'tensorflow.python.layers.core.Flatten\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.GRUCell"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.GRUCell\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.GRU\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.RNN\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.GaussianDropout"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.noise.GaussianDropout\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.GaussianNoise"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.noise.GaussianNoise\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalAveragePooling1D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalAveragePooling2D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalAveragePooling3D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalAveragePooling1D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalAveragePooling2D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalAveragePooling3D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalMaxPooling1D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalMaxPooling2D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalMaxPooling3D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalMaxPooling1D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalMaxPooling2D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.GlobalMaxPooling3D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling._GlobalPooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.InputLayer"
tf_class {
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.InputLayer\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.input_layer.InputLayer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.LSTMCell"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.LSTMCell\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.LSTM\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.RNN\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Lambda"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Lambda\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Layer"
tf_class {
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.LeakyReLU"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.advanced_activations.LeakyReLU\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.LocallyConnected1D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.local.LocallyConnected1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.LocallyConnected2D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.local.LocallyConnected2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Masking"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Masking\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.MaxPooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.MaxPooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.MaxPooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.MaxPooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.MaxPooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.MaxPooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.MaxPooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.MaxPooling1D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.MaxPooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.MaxPooling2D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.pooling.MaxPooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling.MaxPooling3D\'>"
is_instance: "<class \'tensorflow.python.layers.pooling._Pooling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge.Maximum\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge._Merge\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge.Multiply\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.merge._Merge\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.PReLU"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.advanced_activations.PReLU\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Permute"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Permute\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.RNN"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.RNN\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.RepeatVector"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.RepeatVector\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Reshape"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Reshape\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.SeparableConv1D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._SeparableConv\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.SeparableConv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._SeparableConv\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.SeparableConv1D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._SeparableConv\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.layers.convolutional.SeparableConv2D\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._SeparableConv\'>"
is_instance: "<class \'tensorflow.python.layers.convolutional._Conv\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.SimpleRNNCell"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.SimpleRNNCell\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.SimpleRNN\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.RNN\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Softmax"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.advanced_activations.Softmax\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.SpatialDropout1D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Dropout\'>"
is_instance: "<class \'tensorflow.python.layers.core.Dropout\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.SpatialDropout2D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Dropout\'>"
is_instance: "<class \'tensorflow.python.layers.core.Dropout\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.SpatialDropout3D\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.core.Dropout\'>"
is_instance: "<class \'tensorflow.python.layers.core.Dropout\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.StackedRNNCells"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.recurrent.StackedRNNCells\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.ThresholdedReLU"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.advanced_activations.ThresholdedReLU\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.wrappers.TimeDistributed\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.wrappers.Wrapper\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.UpSampling1D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.UpSampling1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.UpSampling2D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.UpSampling2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.UpSampling3D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.UpSampling3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.Wrapper"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.wrappers.Wrapper\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.ZeroPadding1D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.ZeroPadding1D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.ZeroPadding2D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.ZeroPadding2D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.layers.ZeroPadding3D"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.layers.convolutional.ZeroPadding3D\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.models.Model"
tf_class {
is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.training.Model\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Network\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.network.Network\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {
path: "tensorflow.keras.models.Sequential"
tf_class {
- is_instance: "<class \'tensorflow.python.keras._impl.keras.models.Sequential\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.sequential.Sequential\'>"
is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.training.Model\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Network\'>"
- is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.topology.Layer\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.network.Network\'>"
+ is_instance: "<class \'tensorflow.python.keras._impl.keras.engine.base_layer.Layer\'>"
is_instance: "<class \'tensorflow.python.layers.base.Layer\'>"
is_instance: "<type \'object\'>"
member {