C.W.K.
Stream
Lesson 01 of 05 · published

Exporting JAX Models for Production

~8 min · ecosystem, jax, tutorial

Level 0Curious
0 XP0/73 lessons0/17 achievements
0/100 XP to next level100 XP to go0% complete

Training a model in JAX is one thing. Getting it into production is another. JAX provides several paths for model export, with StableHLO being the recommended format.

StableHLO Export (Recommended)

StableHLO is now the only recommended export format from JAX. It produces a portable, versioned representation of your computation that can be consumed by various compilers and runtimes. The older jax2tf with enable_xla=False is deprecated.

import jax
from jax import export
import jax.numpy as jnp
import numpy as np

# 1. Create a JIT-transformed function
@jax.jit
def predict(params, x):
    h = jax.nn.relu(x @ params['w1'] + params['b1'])
    return h @ params['w2'] + params['b2']

# 2. Define input shapes for export
params_shapes = {
    'w1': jax.ShapeDtypeStruct((784, 256), jnp.float32),
    'b1': jax.ShapeDtypeStruct((256,), jnp.float32),
    'w2': jax.ShapeDtypeStruct((256, 10), jnp.float32),
    'b2': jax.ShapeDtypeStruct((10,), jnp.float32),
}
x_shape = jax.ShapeDtypeStruct((1, 784), jnp.float32)

# 3. Export to StableHLO
exported = export.export(predict)(params_shapes, x_shape)

# 4. Get the StableHLO module (MLIR text)
stablehlo_module = exported.mlir_module()

# 5. Serialize for later use
serialized = export.export(predict)(params_shapes, x_shape).serialize()
# Can be saved to disk and loaded in a different process/language

Dynamic Batch Sizes

# Export with dynamic batch dimension
scope = export.SymbolicScope()
dynamic_x = jax.ShapeDtypeStruct(
    export.symbolic_shape("batch, 784", scope=scope),
    jnp.float32,
)

exported_dynamic = export.export(predict)(params_shapes, dynamic_x)
# Now the exported model accepts any batch size

TensorFlow SavedModel (via StableHLO)

# You can pack StableHLO into a TensorFlow SavedModel
# for serving with TensorFlow Serving
from jax.experimental.jax2tf import convert as jax2tf_convert

# Note: the recommended path is now:
# 1. Export to StableHLO via jax.export
# 2. Load StableHLO into TF SavedModel if needed for TF Serving
# 3. Or use StableHLO directly with XLA-compatible runtimes

💡 Why This Matters

StableHLO is a portability layer: it decouples the model from the framework that produced it. A StableHLO module can be compiled by XLA (Google's compiler), consumed by TensorFlow Serving, or processed by other OpenXLA-compatible tools. This means you can train in JAX and deploy anywhere that supports StableHLO — which includes Google's entire serving infrastructure. The ONNX export path (jax2onnx) exists but is still maturing.

Code

import jax
from jax import export
import jax.numpy as jnp
import numpy as np

# 1. Create a JIT-transformed function
@jax.jit
def predict(params, x):
    h = jax.nn.relu(x @ params['w1'] + params['b1'])
    return h @ params['w2'] + params['b2']

# 2. Define input shapes for export
params_shapes = {
    'w1': jax.ShapeDtypeStruct((784, 256), jnp.float32),
    'b1': jax.ShapeDtypeStruct((256,), jnp.float32),
    'w2': jax.ShapeDtypeStruct((256, 10), jnp.float32),
    'b2': jax.ShapeDtypeStruct((10,), jnp.float32),
}
x_shape = jax.ShapeDtypeStruct((1, 784), jnp.float32)

# 3. Export to StableHLO
exported = export.export(predict)(params_shapes, x_shape)

# 4. Get the StableHLO module (MLIR text)
stablehlo_module = exported.mlir_module()

# 5. Serialize for later use
serialized = export.export(predict)(params_shapes, x_shape).serialize()
# Can be saved to disk and loaded in a different process/language
# Export with dynamic batch dimension
scope = export.SymbolicScope()
dynamic_x = jax.ShapeDtypeStruct(
    export.symbolic_shape("batch, 784", scope=scope),
    jnp.float32,
)

exported_dynamic = export.export(predict)(params_shapes, dynamic_x)
# Now the exported model accepts any batch size
# You can pack StableHLO into a TensorFlow SavedModel
# for serving with TensorFlow Serving
from jax.experimental.jax2tf import convert as jax2tf_convert

# Note: the recommended path is now:
# 1. Export to StableHLO via jax.export
# 2. Load StableHLO into TF SavedModel if needed for TF Serving
# 3. Or use StableHLO directly with XLA-compatible runtimes

External links

Exercise

Take a trained model. Export to TF SavedModel via jax2tf. Reload it in plain TensorFlow and verify identical predictions. The export story decides whether JAX is research-only at your shop.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.