How to save and load Keras models with custom objects

Custom Keras layers, losses, metrics, and activation functions are Python objects that a saved .keras archive can describe but cannot recreate without the matching Python definition. Models that include those objects need a serialization path for the object configuration and a loading path that makes the object available again.

The native .keras format stores the model architecture, weights, and compile state, but it does not embed custom Python source code. A custom class that accepts constructor arguments should implement get_config() so those values are saved with the model configuration.

The preferred loading path registers the object with @keras.saving.register_keras_serializable and imports the module containing that decorator before calling load_model(). Keeping the class in a small importable module avoids the common failure where the archive exists, but Keras cannot locate the custom class during deserialization.

Steps to save and load a Keras model with custom objects:

  1. Save the custom layer in
    custom_layers.py

    .

    custom_layers.py
    import keras
    from keras import ops
     
     
    @keras.saving.register_keras_serializable(package="Guide")
    class ScaleLayer(keras.layers.Layer):
        def __init__(self, factor=1.0, **kwargs):
            super().__init__(**kwargs)
            self.factor = factor
     
        def call(self, inputs):
            return ops.multiply(inputs, self.factor)
     
        def get_config(self):
            config = super().get_config()
            config.update({"factor": self.factor})
            return config

    get_config() stores the constructor values needed to rebuild the layer. Because factor is a plain float, no from_config() method is needed.

  2. Save a model that uses the registered layer.
    save_custom_model.py
    import numpy as np
    import keras
     
    from custom_layers import ScaleLayer
     
     
    inputs = keras.Input(shape=(3,), name="features")
    x = ScaleLayer(0.5, name="scale_features")(inputs)
    outputs = keras.layers.Dense(
        1,
        kernel_initializer="ones",
        bias_initializer="zeros",
        name="score",
    )(x)
    model = keras.Model(inputs, outputs)
     
    sample = np.ones((2, 3), dtype="float32")
    model(sample)
    model.save("custom_scale_model.keras")
     
    print("saved: custom_scale_model.keras")
    print(f"custom layer: {model.get_layer('scale_features').__class__.__name__}")
    print(f"prediction shape: {np.asarray(model(sample)).shape}")
  3. Run the save script.
    $ python3 save_custom_model.py
    saved: custom_scale_model.keras
    custom layer: ScaleLayer
    prediction shape: (2, 1)

    The saved archive contains the model config and weights. The custom layer code remains in custom_layers.py and must be importable wherever the model is loaded.

  4. Save a failing loader that omits the custom object import.
    load_without_custom_object.py
    import keras
     
     
    keras.saving.load_model("custom_scale_model.keras")
  5. Run the failing loader and confirm that Keras cannot locate the class.
    $ python3 load_without_custom_object.py
    Traceback (most recent call last):
    ##### snipped #####
    TypeError: Could not locate class 'ScaleLayer'. Make sure custom classes are decorated with `@keras.saving.register_keras_serializable()`.

    Do not set safe_mode=False just to bypass a missing custom class. Safe mode controls unsafe lambda deserialization in the Keras v3 format; it does not replace importing or registering the object.

  6. Save the corrected loader that imports the registration module before loading.
    load_custom_model.py
    import numpy as np
    import keras
     
    from custom_layers import ScaleLayer  # Registers Guide>ScaleLayer for load_model().
     
     
    sample = np.ones((2, 3), dtype="float32")
    model = keras.saving.load_model("custom_scale_model.keras")
    prediction = np.asarray(model(sample))
     
    print(f"custom layer: {model.get_layer('scale_features').__class__.__name__}")
    print(f"prediction shape: {prediction.shape}")
    print(f"first prediction: {prediction[0].tolist()}")

    If a legacy object cannot use a decorator, import the class or function and pass it as custom_objects={“ScaleLayer”: ScaleLayer} to keras.saving.load_model().

  7. Run the corrected loader and verify that the custom layer reloads.
    $ python3 load_custom_model.py
    custom layer: ScaleLayer
    prediction shape: (2, 1)
    first prediction: [1.5]
  8. Remove the temporary failing-loader probe after the corrected load works.
    $ rm load_without_custom_object.py