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) - the model's metadata 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 keras import __version__ as keras_version # 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 f = h5py.File(filepath, 'w') 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') if hasattr(model, 'metadata'): f.attrs['metadata'] = json.dumps(model.metadata).encode('utf8') model_weights_group = f.create_group('model_weights') if legacy_models.needs_legacy_support(model): model_layers = legacy_models.legacy_sequential_layers(model) else: 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): warnings.warn( '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 i, (w, val) in enumerate(zip(symbolic_weights, weight_values)): # Default values of symbolic_weights is /variable for theano and cntk if K.backend() == 'theano' or K.backend() == 'cntk': if hasattr(w, 'name') and w.name != "/variable": name = str(w.name) else: name = 'param_' + str(i) else: if hasattr(w, 'name') and w.name: name = str(w.name) else: name = 'param_' + str(i) 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() f.close()
def save_all_weights(model, filepath, include_optimizer=True): """ Save model weights and optimizer weights but not configuration to a HDF5 file. Functionally between `save` and `save_weights`. The HDF5 file contains: - the model's weights - the model's optimizer's state (if any) If you have a complicated model or set of models that do not serialize to JSON correctly, use this method. # Arguments model: Keras model instance to be saved. filepath: String, path where to save the model. include_optimizer: If True, save optimizer's state together. # Raises ImportError: if h5py is not available. """ if h5py is None: raise ImportError('`save_all_weights` requires h5py.') with h5py.File(filepath, 'w') as f: model_weights_group = f.create_group('model_weights') if legacy_models.needs_legacy_support(model): model_layers = legacy_models.legacy_sequential_layers(model) else: model_layers = model.layers topology.save_weights_to_hdf5_group(model_weights_group, model_layers) if include_optimizer and hasattr(model, 'optimizer') and model.optimizer: if isinstance(model.optimizer, optimizers.TFOptimizer): warnings.warn( '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: # 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 i, (w, val) in enumerate( zip(symbolic_weights, weight_values)): # Default values of symbolic_weights is /variable for theano if K.backend() == 'theano': if hasattr(w, 'name') and w.name != "/variable": name = str(w.name) else: name = 'param_' + str(i) else: if hasattr(w, 'name') and w.name: name = str(w.name) else: name = 'param_' + str(i) 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
def reduce_model(model): """Returns a dict representing the state of the model. The dict contains: - the model's configuration (topology) - the model's weights - the model's optimizer's state (if any) Thus the model can be reinstantiated in the returned dict, without any of the code used for model definition or training. # Arguments model: Keras model instance. """ def get_config(obj): # 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() } return obj attrs = {} attrs['keras_version'] = str(keras_version).encode('utf8') attrs['backend'] = K.backend().encode('utf8') round_tripped_model = model_from_config(get_config(model)) attrs['model_config'] = get_config(round_tripped_model) model_weights = {} attrs['model_weights'] = model_weights if legacy_models.needs_legacy_support(model): model_layers = legacy_models.legacy_sequential_layers(model) else: model_layers = model.layers model_weights['layer_names'] = [ layer.name.encode('utf8') for layer in model_layers ] model_weights['backend'] = K.backend().encode('utf8') model_weights['keras_version'] = str(keras_version).encode('utf8') for layer in model_layers: g = {} model_weights[layer.name] = g symbolic_weights = layer.weights weight_values = K.batch_get_value(symbolic_weights) weight_names = [] g['weight_names'] = 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) g[name] = val if hasattr(model, 'optimizer'): if isinstance(model.optimizer, optimizers.TFOptimizer): warnings.warn( '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: attrs['training_config'] = t.valmap( get_config, { 'optimizer_config': model.optimizer, 'loss': model.loss, 'metrics': model.metrics, 'sample_weight_mode': model.sample_weight_mode, 'loss_weights': model.loss_weights, }) # Save optimizer weights. symbolic_weights = getattr(model.optimizer, 'weights') if symbolic_weights: optimizer_weights = {} attrs['optimizer_weigts'] = optimizer_weights weight_values = K.batch_get_value(symbolic_weights) for i, (w, val) in enumerate(zip(symbolic_weights, weight_values)): # Default values of symbolic_weights is /variable for theano if K.backend() == 'theano': if hasattr(w, 'name') and w.name != "/variable": name = str(w.name) else: name = 'param_' + str(i) else: if hasattr(w, 'name') and w.name: name = str(w.name) else: name = 'param_' + str(i) optimizer_weights[name] = val return attrs