def unpack(model, training_config, weights):
    restored_model = deserialize(model)
    if training_config is not None:
        restored_model.compile(
            **saving_utils.compile_args_from_training_config(training_config))
    restored_model.set_weights(weights)
    return restored_model
Example #2
0
def load(path, compile=True):  # pylint: disable=redefined-builtin
    """Loads Keras objects from a SavedModel.

  Any Keras layer or model saved to the SavedModel will be loaded back
  as Keras objects. Other objects are loaded as regular trackable objects (same
  as `tf.saved_model.load`).

  Currently, Keras saving/loading only retains the Keras object's weights,
  losses, and call function.

  The loaded model can be re-compiled, but the original optimizer, compiled loss
  functions, and metrics are not retained. This is temporary, and `model.save`
  will soon be able to serialize compiled models.

  Args:
    path: Path to SavedModel.
    compile: If true, compile the model after loading it.

  Returns:
    Object loaded from SavedModel.
  """
    # TODO(kathywu): Add saving/loading of optimizer, compiled losses and metrics.
    # TODO(kathywu): Add code to load from objects that contain all endpoints
    model = tf_load.load_internal(path, loader_cls=KerasObjectLoader)

    if isinstance(model, RevivedModel) and compile:
        # TODO(kathywu): Use compiled objects from SavedModel, instead of
        # creating new objects from the training config.
        if model._training_config is not None:  # pylint: disable=protected-access
            model.compile(**saving_utils.compile_args_from_training_config(
                model._training_config))  # pylint: disable=protected-access

    return model
Example #3
0
    def load(self,
             compile_model: bool = False,
             custom_objects: Optional[dict] = None) -> Model:
        """
        Loads a Tensorflow model from a TileDB array.
        :param compile_model: Boolean. Whether to compile the model after loading or not.
        :param custom_objects: Optional dictionary mapping names (strings) to
        custom classes or functions to be considered during deserialization.
        :return: Model. Tensorflow model.
        """
        model_array = tiledb.open(self.uri)
        model_array_results = model_array[:]
        model_weights = pickle.loads(
            model_array_results["model_weights"].item(0))
        model_config = json.loads(model_array.meta["model_config"])

        architecture = model_config["config"]
        model_class = model_config["class_name"]

        if model_class == "Sequential":
            model = tf.keras.Sequential.from_config(architecture)
        elif model_class == "Functional":
            model = tf.keras.Model.from_config(architecture)
        else:
            raise NotImplementedError(
                "No support for Subclassed models at the moment. Your "
                "model should be either Sequential or Functional.")

        model.set_weights(model_weights)

        if compile_model:
            optimizer_weights = pickle.loads(
                model_array_results["optimizer_weights"].item(0))
            training_config = json.loads(model_array.meta["training_config"])

            # Compile model.
            model.compile(**saving_utils.compile_args_from_training_config(
                training_config, custom_objects))
            saving_utils.try_build_compiled_arguments(model)

            # Set optimizer weights.
            if optimizer_weights:
                try:
                    model.optimizer._create_all_weights(
                        model.trainable_variables)
                except (NotImplementedError, AttributeError):
                    logging.warning(
                        "Error when creating the weights of optimizer {}, making it "
                        "impossible to restore the saved optimizer state. As a result, "
                        "your model is starting with a freshly initialized optimizer."
                    )

                try:
                    model.optimizer.set_weights(optimizer_weights)
                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
Example #4
0
def unpack(model, training_config, weights):
    """Make Keras Model exportable within pipeline ("picklable") 1/2"""
    restored_model = deserialize(model)
    if training_config is not None:
        restored_model.compile(
            **saving_utils.compile_args_from_training_config(training_config))
    restored_model.set_weights(weights)
    return restored_model
Example #5
0
def load(path, compile=True, options=None):  # pylint: disable=redefined-builtin
    """Loads Keras objects from a SavedModel.

  Any Keras layer or model saved to the SavedModel will be loaded back
  as Keras objects. Other objects are loaded as regular trackable objects (same
  as `tf.saved_model.load`).

  Currently, Keras saving/loading only retains the Keras object's weights,
  losses, and call function.

  The loaded model can be re-compiled, but the original optimizer, compiled loss
  functions, and metrics are not retained. This is temporary, and `model.save`
  will soon be able to serialize compiled models.

  Args:
    path: Path to SavedModel.
    compile: If true, compile the model after loading it.
    options: Optional `tf.saved_model.LoadOptions` object that specifies
      options for loading from SavedModel.


  Returns:
    Object loaded from SavedModel.
  """
    # TODO(kathywu): Add saving/loading of optimizer, compiled losses and metrics.
    # TODO(kathywu): Add code to load from objects that contain all endpoints

    model = tf_load.load_internal(path,
                                  options=options,
                                  loader_cls=KerasObjectLoader)

    # pylint: disable=protected-access
    if isinstance(model, training_lib.Model) and compile:
        # TODO(kathywu): Use compiled objects from SavedModel, instead of
        # creating new objects from the training config.
        training_config = model._serialized_attributes['metadata'].get(
            'training_config', None)
        if training_config is not None:
            model.compile(**saving_utils.compile_args_from_training_config(
                training_config))
            saving_utils.try_build_compiled_arguments(model)
        else:
            logging.warning(
                'No training configuration found in save file, so the '
                'model was *not* compiled. Compile it manually.')
    # pylint: enable=protected-access

    # Force variables and resources to initialize.
    if not context.executing_eagerly():
        sess = backend.get_session()  # Variables are initialized by this call.
        sess.run(ops.get_collection(ops.GraphKeys.TABLE_INITIALIZERS))

    return model
Example #6
0
def unpack_keras_model(model, training_config, weights):
    """Creates a new Keras model object using the input
    parameters.

    Returns
    -------
    Model
        A copy of the input Keras Model,
        compiled if the original was compiled.
    """
    restored_model = deserialize_layer(model)
    if training_config is not None:
        restored_model.compile(
            **saving_utils.compile_args_from_training_config(training_config))
    restored_model.set_weights(weights)
    restored_model.__reduce_ex__ = pack_keras_model.__get__(restored_model)
    return restored_model
Example #7
0
        def _unpack_obj(obj):
            """Recursively unpacks objects.
            """
            if isinstance(obj, SavedKerasModel):
                restored_model = deserialize(obj.model)
                training_config = obj.training_config
                restored_model.compile(
                    **saving_utils.compile_args_from_training_config(
                        training_config))
                restored_model.set_weights(obj.weights)
                return restored_model
            if hasattr(obj, "__dict__"):
                for key, val in obj.__dict__.items():
                    obj.__dict__[key] = _unpack_obj(val)
                return obj
            if isinstance(obj, (list, tuple)):
                obj_type = type(obj)
                new_obj = obj_type([_unpack_obj(o) for o in obj])
                return new_obj

            return obj  # not much we can do at this point, cross fingers
Example #8
0
def _clone_prebuilt_model(build_fn):
    """Clones and compiles a pre-built model when build_fn is an existing
            Keras model instance.

    Arguments:
        build_fn : instance of Keras Model.

    Returns: copy of the input model with no training.
    """
    model = clone_model(build_fn)
    # clone_model does not compy over compilation parameters, do those manually
    model_metadata = saving_utils.model_metadata(build_fn)
    if "training_config" in model_metadata:
        training_config = model_metadata["training_config"]
    else:
        raise ValueError("To use %s as `build_fn`, you must compile"
                         "it first." % build_fn)

    model.compile(
        **saving_utils.compile_args_from_training_config(training_config))

    return model
Example #9
0
def load(path, compile=True, options=None):  # pylint: disable=redefined-builtin
  """Loads Keras objects from a SavedModel.

  Any Keras layer or model saved to the SavedModel will be loaded back
  as Keras objects. Other objects are loaded as regular trackable objects (same
  as `tf.saved_model.load`).

  Currently, Keras saving/loading only retains the Keras object's weights,
  losses, and call function.

  The loaded model can be re-compiled, but the original optimizer, compiled loss
  functions, and metrics are not retained. This is temporary, and `model.save`
  will soon be able to serialize compiled models.

  Args:
    path: Path to SavedModel.
    compile: If true, compile the model after loading it.
    options: Optional `tf.saved_model.LoadOptions` object that specifies
      options for loading from SavedModel.


  Returns:
    Object loaded from SavedModel.
  """
  # TODO(kathywu): Add saving/loading of optimizer, compiled losses and metrics.
  # TODO(kathywu): Add code to load from objects that contain all endpoints

  # Look for metadata file or parse the SavedModel
  metadata = saved_metadata_pb2.SavedMetadata()
  meta_graph_def = loader_impl.parse_saved_model(path).meta_graphs[0]
  object_graph_def = meta_graph_def.object_graph_def
  path_to_metadata_pb = os.path.join(path, constants.SAVED_METADATA_PATH)
  if gfile.Exists(path_to_metadata_pb):
    try:
      with gfile.GFile(path_to_metadata_pb, 'rb') as f:
        file_content = f.read()
      metadata.ParseFromString(file_content)
    except message.DecodeError as e:
      raise IOError('Cannot parse keras metadata {}: {}.'
                    .format(path_to_metadata_pb, str(e)))
  else:
    logging.warning('SavedModel saved prior to TF 2.4 detected when loading '
                    'Keras model. Please ensure that you are saving the model '
                    'with model.save() or tf.keras.models.save_model(), *NOT* '
                    'tf.saved_model.save(). To confirm, there should be a file '
                    'named "keras_metadata.pb" in the SavedModel directory.')
    _read_legacy_metadata(object_graph_def, metadata)

  if not metadata.nodes:
    # When there are no Keras objects, return the results from the core loader
    return tf_load.load(path, options=options)

  # Recreate layers and metrics using the info stored in the metadata.
  keras_loader = KerasObjectLoader(metadata, object_graph_def)
  keras_loader.load_layers(compile=compile)

  # Generate a dictionary of all loaded nodes.
  nodes_to_load = {'root': None}
  for node_id, loaded_node in keras_loader.loaded_nodes.items():
    nodes_to_load[keras_loader.get_path(node_id)] = loaded_node
  loaded = tf_load.load_partial(path, nodes_to_load, options=options)

  # Finalize the loaded layers and remove the extra tracked dependencies.
  keras_loader.finalize_objects()
  keras_loader.del_tracking()

  model = loaded['root']

  # pylint: disable=protected-access
  if isinstance(model, training_lib.Model) and compile:
    # TODO(kathywu): Use compiled objects from SavedModel, instead of
    # creating new objects from the training config.
    training_config = model._serialized_attributes['metadata'].get(
        'training_config', None)
    if training_config is not None:
      model.compile(**saving_utils.compile_args_from_training_config(
          training_config))
      saving_utils.try_build_compiled_arguments(model)
    else:
      logging.warning('No training configuration found in save file, so the '
                      'model was *not* compiled. Compile it manually.')
  # pylint: enable=protected-access

  # Force variables and resources to initialize.
  if not context.executing_eagerly():
    sess = backend.get_session()  # Variables are initialized by this call.
    sess.run(ops.get_collection(ops.GraphKeys.TABLE_INITIALIZERS))

  return model
Example #10
0
def load_model_from_hdf5(filepath, custom_objects=None, compile=True):  # pylint: disable=redefined-builtin
    """Loads a model saved via `save_model_to_hdf5`.

  Arguments:
      filepath: One of the following:
          - String, path to the saved model
          - `h5py.File` object from which to load the 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 = {}

    opened_new_file = not isinstance(filepath, h5py.File)
    if opened_new_file:
        f = h5py.File(filepath, mode='r')
    else:
        f = filepath

    model = None
    try:
        # 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_config_lib.model_from_config(
            model_config, custom_objects=custom_objects)

        # set weights
        load_weights_from_hdf5_group(f['model_weights'], model.layers)

        if compile:
            # 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'))

            # Compile model.
            model.compile(**saving_utils.compile_args_from_training_config(
                training_config, custom_objects))

            # Set optimizer weights.
            if 'optimizer_weights' in f:
                # Build train function (to get weight updates).
                # Models that aren't graph networks must wait until they are called
                # with data to _make_train_function() and so can't load optimizer
                # weights.
                if model._is_graph_network:  # pylint: disable=protected-access
                    model._make_train_function()
                    optimizer_weight_values = load_optimizer_weights_from_hdf5_group(
                        f)
                    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.')
                else:
                    logging.warning(
                        'Sequential models without an `input_shape` '
                        'passed to the first layer cannot reload their '
                        'optimizer state. As a result, your model is'
                        'starting with a freshly initialized optimizer.')

    finally:
        if opened_new_file:
            f.close()
    return model
Example #11
0
def load_model_from_hdf5(filepath, custom_objects=None, compile=True):  # pylint: disable=redefined-builtin
  """Loads a model saved via `save_model_to_hdf5`.

  Arguments:
      filepath: One of the following:
          - String, path to the saved model
          - `h5py.File` object from which to load the 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 = {}

  opened_new_file = not isinstance(filepath, h5py.File)
  if opened_new_file:
    f = h5py.File(filepath, mode='r')
  else:
    f = filepath

  model = None
  try:
    # 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_config_lib.model_from_config(model_config,
                                               custom_objects=custom_objects)

    # set weights
    load_weights_from_hdf5_group(f['model_weights'], model.layers)

    if compile:
      # instantiate optimizer
      training_config = f.attrs.get('training_config')
      if training_config is None:
        logging.warning('No training configuration found in the save file, so '
                        'the model was *not* compiled. Compile it manually.')
        return model
      training_config = json.loads(training_config.decode('utf-8'))

      # Compile model.
      model.compile(**saving_utils.compile_args_from_training_config(
          training_config, custom_objects))

      # Set optimizer weights.
      if 'optimizer_weights' in f:
        try:
          model.optimizer._create_all_weights(model.trainable_variables)
        except (NotImplementedError, AttributeError):
          logging.warning(
              'Error when creating the weights of optimizer {}, making it '
              'impossible to restore the saved optimizer state. As a result, '
              'your model is starting with a freshly initialized optimizer.')

        optimizer_weight_values = load_optimizer_weights_from_hdf5_group(f)
        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.')
  finally:
    if opened_new_file:
      f.close()
  return model
Example #12
0
def load(path, compile=True, options=None):  # pylint: disable=redefined-builtin
  """Loads Keras objects from a SavedModel.

  Any Keras layer or model saved to the SavedModel will be loaded back
  as Keras objects. Other objects are loaded as regular trackable objects (same
  as `tf.saved_model.load`).

  Currently, Keras saving/loading only retains the Keras object's weights,
  losses, and call function.

  The loaded model can be re-compiled, but the original optimizer, compiled loss
  functions, and metrics are not retained. This is temporary, and `model.save`
  will soon be able to serialize compiled models.

  Args:
    path: Path to SavedModel.
    compile: If true, compile the model after loading it.
    options: Optional `tf.saved_model.LoadOptions` object that specifies
      options for loading from SavedModel.


  Returns:
    Object loaded from SavedModel.
  """
  # TODO(kathywu): Add saving/loading of optimizer, compiled losses and metrics.
  # TODO(kathywu): Add code to load from objects that contain all endpoints

  # The Keras metadata file is not yet saved, so create it from the SavedModel.
  metadata = saved_metadata_pb2.SavedMetadata()
  meta_graph_def = loader_impl.parse_saved_model(path).meta_graphs[0]
  object_graph_def = meta_graph_def.object_graph_def
  # TODO(kathywu): When the keras metadata file is saved, load it directly
  # instead of calling the _read_legacy_metadata function.
  _read_legacy_metadata(object_graph_def, metadata)

  if not metadata.nodes:
    # When there are no Keras objects, return the results from the core loader
    return tf_load.load(path, options=options)

  # Recreate layers and metrics using the info stored in the metadata.
  keras_loader = KerasObjectLoader(metadata, object_graph_def)
  keras_loader.load_layers(compile=compile)

  # Generate a dictionary of all loaded nodes.
  nodes_to_load = {'root': None}
  for node_id, loaded_node in keras_loader.loaded_nodes.items():
    nodes_to_load[keras_loader.get_path(node_id)] = loaded_node
  loaded = tf_load.load_partial(path, nodes_to_load, options=options)

  # Finalize the loaded layers and remove the extra tracked dependencies.
  keras_loader.finalize_objects()
  keras_loader.del_tracking()

  model = loaded['root']

  # pylint: disable=protected-access
  if isinstance(model, training_lib.Model) and compile:
    # TODO(kathywu): Use compiled objects from SavedModel, instead of
    # creating new objects from the training config.
    training_config = model._serialized_attributes['metadata'].get(
        'training_config', None)
    if training_config is not None:
      model.compile(**saving_utils.compile_args_from_training_config(
          training_config))
      saving_utils.try_build_compiled_arguments(model)
    else:
      logging.warning('No training configuration found in save file, so the '
                      'model was *not* compiled. Compile it manually.')
  # pylint: enable=protected-access

  # Force variables and resources to initialize.
  if not context.executing_eagerly():
    sess = backend.get_session()  # Variables are initialized by this call.
    sess.run(ops.get_collection(ops.GraphKeys.TABLE_INITIALIZERS))

  return model
Example #13
0
# import tensorflow as tf

from tensorflow.python.keras.saving.saved_model.load import KerasObjectLoader
from tensorflow.python.saved_model.load import load_internal
from tensorflow.python.keras.saving.saved_model.load import RevivedModel
from tensorflow.python.keras.saving import saving_utils
from tensorflow.python.saved_model import loader_impl

model_path = 'output/saved_model/cls/1599723701'

loader_impl.parse_saved_model(model_path)
model = load_internal(model_path, tags=['serve'], loader_cls=KerasObjectLoader)

if not isinstance(model, RevivedModel):
    raise RuntimeError("Can not load model")

if model._training_config is None:
    raise RuntimeError("Model _training_config is None")

model.compile(
    **saving_utils.compile_args_from_training_config(model._training_config))

test_data = [[], [], [], []]

model.predict(test_data)
Example #14
0
    def load(
        self,
        *,
        timestamp: Optional[Timestamp] = None,
        compile_model: bool = False,
        custom_objects: Optional[Mapping[str, Any]] = None,
        input_shape: Optional[Tuple[int, ...]] = None,
    ) -> tf.keras.Model:
        """
        Load a Tensorflow model from a TileDB array.

        :param timestamp: Range of timestamps to load fragments of the array which live
            in the specified time range.
        :param compile_model: Whether to compile the model after loading or not.
        :param custom_objects: Mapping of names to custom classes or functions to be
            considered during deserialization.
        :param input_shape: The shape that the custom model expects as input
        :return: Tensorflow model.
        """
        # TODO: Change timestamp when issue in core is resolved

        with tiledb.open(self.uri, ctx=self.ctx,
                         timestamp=timestamp) as model_array:
            model_array_results = model_array[:]
            model_config = json.loads(model_array.meta["model_config"])
            model_class = model_config["class_name"]

            if model_class != "Sequential" and model_class != "Functional":
                with generic_utils.SharedObjectLoadingScope():
                    with generic_utils.CustomObjectScope(custom_objects or {}):
                        if hasattr(model_config, "decode"):
                            model_config = model_config.decode("utf-8")
                        model = model_config_lib.model_from_config(
                            model_config, custom_objects=custom_objects)
                        if not model.built:
                            model.build(input_shape)

                        # Load weights for layers
                        self._load_custom_subclassed_model(model, model_array)
            else:
                cls = (tf.keras.Sequential
                       if model_class == "Sequential" else tf.keras.Model)
                model = cls.from_config(model_config["config"])
                model_weights = pickle.loads(
                    model_array_results["model_weights"].item(0))
                model.set_weights(model_weights)

            if compile_model:
                optimizer_weights = pickle.loads(
                    model_array_results["optimizer_weights"].item(0))
                training_config = json.loads(
                    model_array.meta["training_config"])

                # Compile model.
                model.compile(**saving_utils.compile_args_from_training_config(
                    training_config, custom_objects))
                saving_utils.try_build_compiled_arguments(model)

                # Set optimizer weights.
                if optimizer_weights:
                    try:
                        model.optimizer._create_all_weights(
                            model.trainable_variables)
                    except (NotImplementedError, AttributeError):
                        logging.warning(
                            "Error when creating the weights of optimizer {}, making it "
                            "impossible to restore the saved optimizer state. As a result, "
                            "your model is starting with a freshly initialized optimizer."
                        )

                    try:
                        model.optimizer.set_weights(optimizer_weights)
                    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