Keras custom layers and SavedModel export: which pieces survive the round trip without custom_objects?
0 reputation · 27 Mar 2021, 07:26 UTC
0 reputation · 27 Mar 2021, 07:26 UTC
I am designing a deployment pipeline where a tf.keras model containing several custom layers must be exported via model.save() in SavedModel format and later reloaded in a separate process, potentially by a team that does not have the original layer classes importable. Assume TensorFlow 2.x with eager execution.
The documented contract is that custom layers implement get_config() (and optionally from_config()) so the model config can reconstruct them, and that tf.keras.models.load_model() needs a custom_objects mapping when the classes are not registered. What is unclear to me is the boundary between what the SavedModel format itself preserves (the traced computation graph and weights, loadable via tf.saved_model.load() with no Python class definitions) and what the Keras-level reload strictly requires.
This matters because the consuming side may only need inference signatures, while other consumers need a fully functional Keras model for continued training.
Specifically:
tf.saved_model.load() signatures even when the custom layer classes are unavailable?@tf.keras.utils.register_keras_serializable() sufficient to drop the custom_objects argument on reload, and does that registration survive across processes?get_config() alone is insufficient, e.g. layers with non-serializable constructor arguments?35275 reputation · 27 Mar 2021, 12:55 UTC
1. Inference via tf.saved_model.load()
Yes. A tf.keras model saved with model.save() writes a full TensorFlow graph (the concrete function) and all variable weights. The resulting SavedModel can be loaded with tf.saved_model.load() and its serving signatures can be called without any knowledge of the Python layer classes. The graph is static; any Python‑side logic that was not captured in the traced graph is lost.
2. Registering with @tf.keras.utils.register_keras_serializable()
Registration only makes the class discoverable by tf.keras.models.load_model() if the module that applied the decorator is imported in the loading process. The registry entry does not survive across processes automatically; you still need to import the module or supply custom_objects if the class is not importable.
3. get_config() alone is not enough for all layers
Layers that receive non‑JSON‑serializable constructor arguments (e.g., lambda functions, tf.Variable, or arbitrary Python objects) will raise a serialization error when load_model() tries to rebuild the architecture. In such cases from_config() must handle deserialization, or the constructor arguments must be made serializable.
get_config() returning only JSON‑serializable values.model.save(), run a dummy forward pass or call model.build(input_shape) so all variables are created.model.save('path', save_format='tf').tf.saved_model.load('path') and invoke the exported signature.@tf.keras.utils.register_keras_serializable() in that module) before calling tf.keras.models.load_model('path'), orcustom_objects dictionary mapping layer names to the classes.When tf.keras.models.load_model() fails, confirm whether the module containing the custom layer was imported in the loading process. If it was not, the registry entry will be missing and custom_objects becomes mandatory.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.