Explicit random keys

JAX · Practical · 10 min

JAX's RNG is reproducible and parallel-safe only if you handle keys correctly; reusing a key is a silent bug.

NumPy's np.random keeps hidden global state, which breaks the purity that jit and vmap rely on. JAX instead makes randomness explicit: every random function takes a key and is a pure function of it. The same key always produces the same numbers.

key = jax.random.key(42)
x = jax.random.normal(key, (3,))

The rule: never reuse a key for two different random draws you expect to be independent. Instead split it:

  • key, sub = jax.random.split(key) gives a fresh subkey to consume and a new key to carry forward.
  • jax.random.split(key, n) returns n keys at once, handy with vmap (e.g. one dropout key per example).
  • jax.random.fold_in(key, step) derives a key from an integer such as the step number or device index.

Inside a training loop you thread the key through the step function just like parameters: take it in, split, use one part, and return the other.

jax.random.key creates a typed key (dtype key<fry>), the recommended API. Older code uses jax.random.PRNGKey(seed), which returns a raw uint32 array of shape (2,); it still works, but typed keys catch misuse such as doing arithmetic on a key.

Random functions need static shapes: jax.random.uniform(key, (n,)) requires n to be a concrete Python int under jit.

Example

import jax

key = jax.random.key(0)
a = jax.random.normal(key, (3,))
b = jax.random.normal(key, (3,))
print((a == b).all())            # True: same key, same numbers (a bug if unintended)

key, k1, k2 = jax.random.split(key, 3)
print((jax.random.normal(k1, (3,)) == jax.random.normal(k2, (3,))).any())  # False

def dropout(key, x, rate=0.5):
    keep = jax.random.bernoulli(key, 1 - rate, x.shape)
    return jax.numpy.where(keep, x / (1 - rate), 0.0)

keys = jax.random.split(key, 4)  # one key per example
xs = jax.numpy.ones((4, 5))
print(jax.vmap(dropout)(keys, xs).shape)  # (4, 5)

step_key = jax.random.fold_in(key, 100)   # derive from a step number
print(step_key.dtype)                     # key<fry>

Remember

  • Random functions are pure functions of an explicit key
  • Split keys before every independent use; never reuse one
  • Prefer typed keys from jax.random.key over legacy PRNGKey

Watch out for

  • Using the same key for weight init in every layer, which makes the layers' weights identical or correlated.

Check yourself

  1. You call jax.random.uniform(key, (2,)) twice with the same key. What do you get?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.jax.dev/en/latest/101/random.html