keras.ops: backend-agnostic math

Keras · Practical · 10 min

Custom layers, losses and metrics stay portable across JAX, TensorFlow and PyTorch only if they use keras.ops.

`keras.ops` is Keras 3's portable tensor API. It implements most of the NumPy API (ops.sum, ops.matmul, ops.reshape, ops.where, ops.einsum...) plus neural-network functions (ops.softmax, ops.relu, ops.conv, ops.one_hot, ops.logsumexp) and core control flow (ops.cond, ops.while_loop, ops.scan). Each call dispatches to the active backend, so code written with it runs on JAX, TensorFlow and PyTorch alike.

Practical rules:

  • Inside custom layers, losses and metrics, use keras.ops instead of tf.*, jnp.* or torch.*, unless you deliberately target one backend.
  • ops.convert_to_tensor(x) makes a backend tensor; ops.convert_to_numpy(t) brings it back.
  • Dtypes are strings: "float32", "int32", "bfloat16".

Randomness goes through `keras.random`: normal, uniform, dropout, shuffle. Pass a seed or, better, a keras.random.SeedGenerator, which holds state as a variable so random ops behave correctly inside compiled and stateless (JAX) functions.

Because results are backend-native, printing them shows backend types; convert to NumPy for portable output.

Example

import keras
from keras import ops

x = ops.arange(6, dtype="float32")
m = ops.reshape(x, (2, 3))
print(ops.convert_to_numpy(ops.sum(m, axis=0)))                   # [3. 5. 7.]
print(ops.convert_to_numpy(ops.matmul(m, ops.transpose(m))))      # [[ 5. 14.] [14. 50.]]
print(ops.convert_to_numpy(ops.softmax(ops.array([0.0, 0.0]))))   # [0.5 0.5]
print(ops.convert_to_numpy(ops.where(m > 2, m, 0.0)))             # [[0. 0. 0.] [3. 4. 5.]]
print(ops.convert_to_numpy(ops.one_hot(ops.array([2, 0]), 3)))    # [[0. 0. 1.] [1. 0. 0.]]

seed = keras.random.SeedGenerator(42)
noise = keras.random.normal((2, 3), seed=seed)   # reproducible on every backend
print(tuple(noise.shape))                        # (2, 3)

Remember

  • keras.ops = NumPy API + NN ops, on every backend
  • Use it in custom layers, losses and metrics for portability
  • Use keras.random with a SeedGenerator for randomness

Watch out for

  • Calling tf.* functions in a custom layer, which breaks as soon as the backend is JAX or PyTorch.

Check yourself

  1. Why use keras.random.SeedGenerator instead of an integer seed inside a layer?

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

Official docs: keras.io/api/ops/