Saving Custom Model cannot be done with `model.save()`

Question:

My python version 3.6.5

tensorflow version 2.3.0

Simple Custom Model

import tensorflow as tf
import tensorflow.keras as keras
class x(keras.layers.Layer):
    def build(self, input_shape):
        self.add_weight()

inputs = keras.layers.Input(1)
outputs = x()(inputs)
model = keras.models.Model(inputs, outputs)
model.save("temp_model")

And it fails with AttributeError: 'NoneType' object has no attribute 'replace'

What is wrong?

For details,

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-160-d7ce2ee17e65> in <module>
      6 outputs = x()(inputs)
      7 model = keras.models.Model(inputs, outputs)
----> 8 model.save("temp_model")

~AppDataRoamingPythonPython36site-packagestensorflowpythonkerasenginetraining.py in save(self, filepath, overwrite, include_optimizer, save_format, signatures, options)
   1977     """
   1978     save.save_model(self, filepath, overwrite, include_optimizer, save_format,
-> 1979                     signatures, options)
   1980 
   1981   def save_weights(self,

~AppDataRoamingPythonPython36site-packagestensorflowpythonkerassavingsave.py in save_model(model, filepath, overwrite, include_optimizer, save_format, signatures, options)
    132   else:
    133     saved_model_save.save(model, filepath, overwrite, include_optimizer,
--> 134                           signatures, options)
    135 
    136 

~AppDataRoamingPythonPython36site-packagestensorflowpythonkerassavingsaved_modelsave.py in save(model, filepath, overwrite, include_optimizer, signatures, options)
     78     # we use the default replica context here.
     79     with distribution_strategy_context._get_default_replica_context():  # pylint: disable=protected-access
---> 80       save_lib.save(model, filepath, signatures, options)
     81 
     82   if not include_optimizer:

~AppDataRoamingPythonPython36site-packagestensorflowpythonsaved_modelsave.py in save(obj, export_dir, signatures, options)
    974 
    975   _, exported_graph, object_saver, asset_info = _build_meta_graph(
--> 976       obj, export_dir, signatures, options, meta_graph_def)
    977   saved_model.saved_model_schema_version = constants.SAVED_MODEL_SCHEMA_VERSION
    978 

~AppDataRoamingPythonPython36site-packagestensorflowpythonsaved_modelsave.py in _build_meta_graph(obj, export_dir, signatures, options, meta_graph_def)
   1059   # Note we run this twice since, while constructing the view the first time
   1060   # there can be side effects of creating variables.
-> 1061   _ = _SaveableView(checkpoint_graph_view)
   1062   saveable_view = _SaveableView(checkpoint_graph_view, wrapped_functions)
   1063   object_saver = util.TrackableSaver(checkpoint_graph_view)

~AppDataRoamingPythonPython36site-packagestensorflowpythonsaved_modelsave.py in __init__(self, checkpoint_view, wrapped_functions)
    176     self.checkpoint_view = checkpoint_view
    177     trackable_objects, node_ids, slot_variables = (
--> 178         self.checkpoint_view.objects_ids_and_slot_variables())
    179     self.nodes = trackable_objects
    180     self.node_ids = node_ids

~AppDataRoamingPythonPython36site-packagestensorflowpythontrainingtrackinggraph_view.py in objects_ids_and_slot_variables(self)
    424     object_names = object_identity.ObjectIdentityDictionary()
    425     for obj, path in path_to_root.items():
--> 426       object_names[obj] = _object_prefix_from_path(path)
    427     node_ids = object_identity.ObjectIdentityDictionary()
    428     for node_id, node in enumerate(trackable_objects):

~AppDataRoamingPythonPython36site-packagestensorflowpythontrainingtrackinggraph_view.py in _object_prefix_from_path(path_to_root)
     62   return "/".join(
     63       (_escape_local_name(trackable.name)
---> 64        for trackable in path_to_root))
     65 
     66 

~AppDataRoamingPythonPython36site-packagestensorflowpythontrainingtrackinggraph_view.py in <genexpr>(.0)
     62   return "/".join(
     63       (_escape_local_name(trackable.name)
---> 64        for trackable in path_to_root))
     65 
     66 

~AppDataRoamingPythonPython36site-packagestensorflowpythontrainingtrackinggraph_view.py in _escape_local_name(name)
     55   # edges traversed to reach the variable, so we escape forward slashes in
     56   # names.
---> 57   return (name.replace(_ESCAPE_CHAR, _ESCAPE_CHAR + _ESCAPE_CHAR)
     58           .replace(r"/", _ESCAPE_CHAR + "S"))
     59 

AttributeError: 'NoneType' object has no attribute 'replace' 

I quoted this simple model from here,

and I saw it’s been handled here,

but I can still see this in my computer and other’s as well

Asked By: HyeonPhil Youn

||

Answers:

This seems to be a bug in tensorflow. Simply give a name to the weights you create and the problem is gone:

self.add_weight(name='name')
Answered By: Feri

In my case, the solution consisted of two parts worked as following:

  1. To add a unique name to each layer, including custom layers, for example:
keras.layers.Dense(name=str(uuid.uuid4()), input_shape=self.inputShape,
                         units=self.inputShape[1], activation="relu")
  1. To switch to the fileName.h5 format for the models saving, for example:
model.save('myModelName.h5', save_format="h5")
  1. To load the model with the reference to custom_objects if any:
model = keras.models.load_model(pathToModel, custom_objects={
          'MyCustomLayer': MyCustomLayer})

My environment:

  • python: ‘3.10.8’
  • sys.version: ‘3.10.8 (main, Nov 24 2022, 08:09:04) [Clang 14.0.6 ]
  • tensorFlowVersion: ‘2.11.0’
Answered By: Roman
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.