TensorFlow SavedModel Format
SavedModel is TensorFlow's universal format for saving a complete, self-contained model package. Unlike the Keras .keras format (which is Keras-specific), SavedModel works across every TensorFlow runtime — Python, C++, Java, JavaScript, mobile, and server-side REST APIs. A SavedModel contains the computation graph, the trained weights, and the serving signatures. Any system that understands TensorFlow can load and run it without the original training code.
What a SavedModel Contains
my_model/ ← Root directory ├── saved_model.pb ← Computation graph (Protocol Buffer format) │ Contains the TF graph operations and │ function signatures for serving ├── variables/ │ ├── variables.index ← Index of variable names and locations │ └── variables.data-00000 ← Actual weight values (can be multiple shards) └── assets/ ← Optional: vocab files, lookup tables, etc.
Saving a Model as SavedModel
import tensorflow as tf
# Save in SavedModel format
model.save('my_saved_model/')
# Equivalently with tf.saved_model
tf.saved_model.save(model, 'my_saved_model/')
Loading a SavedModel
# Load with tf.keras — returns a Keras model object
loaded_model = tf.keras.models.load_model('my_saved_model/')
# Make predictions
predictions = loaded_model.predict(x_test)
loaded_model.evaluate(x_test, y_test)
# Load with tf.saved_model — returns a lower-level SavedModel object
raw_model = tf.saved_model.load('my_saved_model/')
# Inspect serving signatures
print(list(raw_model.signatures.keys()))
# ['serving_default']
# Call the serving function directly
serving_fn = raw_model.signatures['serving_default']
result = serving_fn(input_1=tf.constant(x_test[:5]))
print(result)
Serving Signatures
A serving signature defines the input/output interface of the model when deployed. It tells the serving system exactly what tensor names and shapes to expect and what names and shapes to return.
# Inspect the serving signature
serving_fn = raw_model.signatures['serving_default']
print(serving_fn.structured_input_signature)
print(serving_fn.structured_outputs)
# Example output:
# Input: {'input_1': TensorSpec(shape=(None, 224, 224, 3), dtype=tf.float32)}
# Output: {'output_0': TensorSpec(shape=(None, 10), dtype=tf.float32)}
Saving a Custom Serving Function
You can attach a custom serving function that handles preprocessing, so callers send raw inputs (like raw pixel values or raw text) instead of preprocessed tensors.
import tensorflow as tf
class ClassifierWithPreprocessing(tf.Module):
def __init__(self, model):
self.model = model
@tf.function(input_signature=[
tf.TensorSpec(shape=[None, None, None, 3], dtype=tf.uint8, name='image')
])
def serve(self, image):
# Preprocessing happens inside the serving function
image = tf.cast(image, tf.float32) / 255.0
image = tf.image.resize(image, [224, 224])
return {'predictions': self.model(image, training=False)}
# Wrap and save
wrapper = ClassifierWithPreprocessing(model)
tf.saved_model.save(
wrapper,
'model_with_preprocessing/',
signatures={'serving_default': wrapper.serve}
)
# At inference time, caller sends raw uint8 images
caller_model = tf.saved_model.load('model_with_preprocessing/')
raw_image = tf.constant(some_uint8_image[np.newaxis]) # (1, H, W, 3) uint8
result = caller_model.serve(image=raw_image)
print(result['predictions'])
SavedModel for TensorFlow Serving
TensorFlow Serving is a production server that loads SavedModels and serves predictions over a REST or gRPC API. It handles batching, versioning, and hardware acceleration automatically.
# Export versioned model for TF Serving
# TF Serving expects: model_name/version_number/
import os
MODEL_DIR = 'tf_serving/my_classifier'
VERSION = 1
export_path = os.path.join(MODEL_DIR, str(VERSION))
model.save(export_path)
# Directory structure:
# tf_serving/my_classifier/1/saved_model.pb
# 1/variables/...
# Start TF Serving (Docker command):
# docker run -p 8501:8501 \
# --mount type=bind,source=/path/to/tf_serving,target=/models \
# -e MODEL_NAME=my_classifier \
# tensorflow/serving
# Query via REST API:
import requests, json, numpy as np
payload = json.dumps({
'instances': x_test[:5].tolist()
})
response = requests.post(
'http://localhost:8501/v1/models/my_classifier:predict',
data=payload
)
print(response.json())
Converting SavedModel to Other Formats
# Convert to TFLite for mobile
converter = tf.lite.TFLiteConverter.from_saved_model('my_saved_model/')
tflite_model = converter.convert()
with open('model.tflite', 'wb') as f:
f.write(tflite_model)
# Convert to TensorFlow.js for browser
# (requires: pip install tensorflowjs)
# tensorflowjs_converter \
# --input_format=tf_saved_model \
# my_saved_model/ \
# tfjs_model/
# Convert to ONNX for cross-framework compatibility
# (requires: pip install tf2onnx)
# python -m tf2onnx.convert \
# --saved-model my_saved_model/ \
# --output model.onnx
SavedModel vs Other Formats
Format Extension When to Use ────────────────────────────────────────────────────────────────── SavedModel directory Production, TF Serving, cross-platform .keras .keras Keras projects, training resumption HDF5 .h5 Legacy projects, simple sharing TFLite .tflite Mobile, embedded, edge deployment TF.js directory Browser-based inference ONNX .onnx Cross-framework (PyTorch ↔ TF) ──────────────────────────────────────────────────────────────────
Verifying a SavedModel
# Use the saved_model_cli tool to inspect without Python
# saved_model_cli show --dir my_saved_model/ --all
# In Python:
imported = tf.saved_model.load('my_saved_model/')
print(dir(imported))
# Check all callable functions
for name in dir(imported):
attr = getattr(imported, name)
if callable(attr):
print(name, type(attr))
SavedModel is the production backbone of TensorFlow. When a model needs to move from a training notebook to a deployed API, a mobile app, a browser, or any environment that is not Python, SavedModel is the universal bridge. Mastering it ensures your trained models can reach users on any platform. The next topic covers TensorFlow Serving — the production server that exposes SavedModels as REST and gRPC APIs at scale.
