This guide explains how to use the JAX backend in Meridian.
Introduction to the JAX backend
Meridian uses JAX as its default numerical backend for core numerical operations and probabilistic Markov Chain Monte Carlo (MCMC) sampling (starting in Meridian 2.0). JAX encourages a functional programming style and utilizes XLA (Accelerated Linear Algebra) compilation to offer advanced performance optimizations and memory efficiency.
The legacy TensorFlow backend is deprecated and will be removed in a future release.
Tutorial: To see JAX in action, see the Getting started with JAX notebook.
Backend configuration
By default, Meridian runs on JAX. You don't need to configure any environment variables to use JAX.
Legacy TensorFlow backend (deprecated)
If you need to temporarily run with the legacy TensorFlow backend, set the
MERIDIAN_BACKEND environment variable to 'tensorflow' before importing
Meridian:
import os
# Select legacy TensorFlow backend (deprecated)
os.environ['MERIDIAN_BACKEND'] = 'tensorflow'
# Now it is safe to import Meridian modules
from meridian.model import model
from meridian.data import load
Precision configuration
By default, Meridian runs with 64-bit precision (float64) on JAX.
Users can choose to run with 32-bit precision (float32) instead, for example:
- Faster training runtime: 32-bit floating-point operations can run faster on hardware accelerators (such as GPUs or TPUs).
- Lower memory usage: 32-bit precision reduces memory consumption during MCMC sampling.
To use 32-bit precision, set the MERIDIAN_ENABLE_JAX_X64 environment
variable to 'False' (or '0') before importing Meridian:
import os
# Disable 64-bit precision (enable 32-bit precision)
os.environ['MERIDIAN_ENABLE_JAX_X64'] = 'False'
# Now it is safe to import Meridian modules
from meridian.model import model
If the MERIDIAN_ENABLE_JAX_X64 environment variable is unset or set to
'True' or '1', Meridian defaults to 64-bit precision.
Type consistency
Because Meridian operates in 64-bit precision by default, ensure that all user-provided values, custom arrays, and distribution parameters maintain type consistency:
- Float literals and arrays: Standard Python float literals (such as
0.2,0.9) default to 64-bit floats. When creating NumPy arrays for priors or inputs, usenp.float64ordtype=np.float64to match the default precision. - Construct custom prior distributions with matching precision: When
defining custom prior distributions in
PriorDistribution, ensure all distribution parameters (such asloc,scale,concentration0, andconcentration1) match the active precision (64-bit float by default).
API differences when using JAX versus TensorFlow
When using the JAX backend, there are key API differences to keep in mind:
Prior distributions
Meridian models use TensorFlow Probability on JAX
(tensorflow_probability.substrates.jax).
When configuring custom prior distributions under JAX, import
tensorflow_probability.substrates.jax as tfp_jax and construct distributions
using tfp_jax.distributions.
Ensure all custom distribution parameters use 64-bit precision (such as
np.float64 or 64-bit float arrays) to maintain type consistency with
Meridian's default precision settings.
JAX
import numpy as np
import tensorflow_probability.substrates.jax as tfp_jax
from meridian.model import constants
from meridian.model import prior_distribution
# Parameters use 64-bit precision
roi_mu = np.float64(0.2)
roi_sigma = np.float64(0.9)
prior = prior_distribution.PriorDistribution(
roi_m=tfp_jax.distributions.LogNormal(
roi_mu, roi_sigma, name=constants.ROI_M
)
)
TensorFlow (Deprecated)
import tensorflow_probability as tfp
from meridian.model import constants
from meridian.model import prior_distribution
roi_mu = 0.2
roi_sigma = 0.9
prior = prior_distribution.PriorDistribution(
roi_m=tfp.distributions.LogNormal(
roi_mu, roi_sigma, name=constants.ROI_M
)
)
Explicit seed requirement
When using the JAX backend, an explicit seed is required for stochastic
functions (for example, in sample_posterior()). While TensorFlow uses a global
random number generator that automatically picks a random seed, JAX makes this
seed explicit. We found no statistically significant differences in ROI
estimates or budget shifts across different seeds.
# Explicitly set a seed for MCMC sampling when using the JAX backend
mmm.sample_posterior(
n_chains=2,
n_adapt=1000,
n_burnin=500,
n_keep=1000,
seed=0,
)
For more information on JAX random numbers and seeds, refer to the JAX pseudorandom numbers documentation.
Numerical differences and reproducibility
Because TensorFlow and JAX compile their computational graphs differently, you may observe minor numerical differences in your posterior estimates when switching to JAX using the same data and random seeds.
While posterior distributions might not be identical across backends, the differences are generally small and not statistically significant for business metrics such as ROI and budget allocation. This ensures that switching to the JAX backend maintains the integrity of your model's insights.
Performance considerations
Internal testing found JAX supercharged initial model runs, cutting average runtime by ~40% and memory usage by ~70%, compared to TensorFlow when using GPUs. JAX also streamlined model iterations, enabling 2x faster runtimes, 4x less memory usage, and uninterrupted workflows by eliminating the need for kernel restarts.
Because of the increased memory efficiency, you have more headroom to adjust
computationally intensive parameters. For example, in
Meridian.sample_posterior(), you might increase the unrolled_leapfrog_steps
argument (e.g., from 1 to 5). This can accelerate convergence by increasing the
trajectory length of the No-U-Turn-Sampler (NUTS) without exceeding hardware
memory limits. You can also increase the n_adapt parameter to further aid
convergence during the adaptation phase.