Table of Contents

How to export a SavedModel in TensorFlow

A trained TensorFlow model needs a portable handoff point before it can move from training code into serving, batch inference, conversion, or release testing. A SavedModel directory carries the inference graph, variables, assets, and callable signatures that those downstream runtimes can load without rebuilding the original Python model definition.

Current Keras models can use model.export() when the target is an inference-focused SavedModel instead of a full .keras training archive. The export should be checked from a separate load path with tf.saved_model.load() and inspected with saved_model_cli before another runtime depends on the tensor names and shapes.

The export is a directory tree, not one standalone file. Keep the numeric version folder shape when the model may later move into TensorFlow Serving, and use explicit serving signatures only when the default serve and serving_default endpoints do not give clients stable enough field names.

Steps to export a TensorFlow model as a SavedModel:

  1. Open a terminal in the Python environment that can import TensorFlow and run saved_model_cli.

    Validation used TensorFlow 2.21.0 and Keras 3.15.0 from the campaign CPU image.

  2. Save the export workflow as
    export_savedmodel_demo.py

    .

    export_savedmodel_demo.py
    import os
    import pathlib
    import shutil
     
    os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2"
     
    import tensorflow as tf
     
    tf.get_logger().setLevel("ERROR")
    tf.keras.utils.set_random_seed(7)
     
    export_dir = pathlib.Path("exported/number_classifier/1")
    model_root = export_dir.parents[1]
    if model_root.exists():
        shutil.rmtree(model_root)
     
    features = tf.constant(
        [
            [0.0, 0.0, 0.0, 0.0],
            [0.0, 1.0, 0.0, 1.0],
            [1.0, 0.0, 1.0, 0.0],
            [1.0, 1.0, 1.0, 1.0],
            [0.2, 0.9, 0.1, 0.8],
            [0.8, 0.2, 0.9, 0.1],
        ],
        dtype=tf.float32,
    )
    labels = tf.constant([[0.0], [0.0], [1.0], [1.0], [0.0], [1.0]], dtype=tf.float32)
     
    inputs = tf.keras.Input(shape=(4,), name="features")
    x = tf.keras.layers.Dense(8, activation="relu")(inputs)
    outputs = tf.keras.layers.Dense(1, activation="sigmoid")(x)
    model = tf.keras.Model(inputs=inputs, outputs=outputs, name="number_classifier")
     
    model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])
    model.fit(features, labels, epochs=12, verbose=0)
    model.export(export_dir)
     
    artifact = tf.saved_model.load(str(export_dir))
    signature = artifact.signatures["serving_default"]
    _, keyword_inputs = signature.structured_input_signature
    input_name, input_spec = next(iter(keyword_inputs.items()))
     
    sample_batch = tf.constant(
        [
            [0.1, 0.9, 0.2, 0.8],
            [0.9, 0.1, 0.8, 0.2],
        ],
        dtype=input_spec.dtype,
    )
    prediction = signature(**{input_name: sample_batch})
     
    print(f"TensorFlow {tf.__version__}")
    print(f"Keras {tf.keras.__version__}")
    print(f"SavedModel dir: {export_dir}")
    print(f"Contains SavedModel: {tf.saved_model.contains_saved_model(str(export_dir))}")
    print(f"Signature names: {sorted(artifact.signatures.keys())}")
    print(f"Input key: {input_name} shape={tuple(input_spec.shape)} dtype={input_spec.dtype.name}")
    print(f"Output keys: {sorted(prediction.keys())}")
    print(f"Output shape: {tuple(next(iter(prediction.values())).shape)}")

    The numeric path exported/number_classifier/1 mirrors the directory shape expected by TensorFlow Serving, where the model base path contains one or more version folders.

  3. Run the script to write the SavedModel directory and load it through the exported serving signature.
    $ python3 export_savedmodel_demo.py
    Saved artifact at 'exported/number_classifier/1'. The following endpoints are available:
    
    * Endpoint 'serve'
      args_0 (POSITIONAL_ONLY): TensorSpec(shape=(None, 4), dtype=tf.float32, name='features')
    Output Type:
      TensorSpec(shape=(None, 1), dtype=tf.float32, name=None)
    ##### snipped #####
    TensorFlow 2.21.0
    Keras 3.15.0
    SavedModel dir: exported/number_classifier/1
    Contains SavedModel: True
    Signature names: ['serve', 'serving_default']
    Input key: features shape=(None, 4) dtype=float32
    Output keys: ['output_0']
    Output shape: (2, 1)

    Contains SavedModel: True confirms that the directory is a SavedModel, and the signature, input, and output lines show the contract another caller must use.

  4. List the version directory and confirm the expected SavedModel files are present.
    $ ls exported/number_classifier/1
    assets
    fingerprint.pb
    saved_model.pb
    variables

    Copy the whole version directory, not only saved_model.pb, because variables and assets can live outside the protobuf file.

  5. Inspect the default serving signature before handing the export to another runtime.
    $ saved_model_cli show --dir exported/number_classifier/1 --tag_set serve --signature_def serving_default
    The given SavedModel SignatureDef contains the following input(s):
      inputs['features'] tensor_info:
          dtype: DT_FLOAT
          shape: (-1, 4)
          name: serving_default_features:0
    The given SavedModel SignatureDef contains the following output(s):
      outputs['output_0'] tensor_info:
          dtype: DT_FLOAT
          shape: (-1, 1)
          name: StatefulPartitionedCall_1:0
    Method name is: tensorflow/serving/predict

    The input key features and output key output_0 are the values to map into a serving request, batch inference call, or client adapter.
    Related: How to inspect a TensorFlow SavedModel with saved_model_cli
    Related: How to deploy TensorFlow Serving with Docker

  6. Remove the demo script after recording the export contract.
    $ rm export_savedmodel_demo.py

    Keep the exported exported/number_classifier/1 directory if it will be passed to serving, conversion, or release validation.

SavedModel export considerations: