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:
- 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.
- 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.
- 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.
- 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.
- 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/predictThe 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 - 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:
- model.export() writes an inference-focused SavedModel directory, while model.save("model.keras") writes a full Keras archive intended for later Python reloads with training-side model metadata.
- tf.saved_model.save() remains the lower-level export API for tf.Module objects and workflows that need explicit signatures without using the Keras export helpers.
- saved_model_cli show --tag_set serve --signature_def serving_default exposes the input and output contract that serving clients must match.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.