Quick Reference · deep learning in Python · Keras 3.15 (Jun 2026)

keras + keras_preprocessing · tensorflow · torch

Keras is an API, not a runtime. You describe layers and models; a backend — JAX, TensorFlow, PyTorch, or OpenVINO for inference — actually executes them. You choose it with one environment variable before the import, and a model built only from built-in layers runs unchanged on any of them. Everything below follows from that one separation.
setup & backend keras.ops & tensors layers & models training data & preprocessing save, export & tune gotcha / deprecated most common
Cross-checked against: keras.io (API docs, developer guides, Keras 3 announcement) · github.com/keras-team (keras releases, keras-preprocessing archive) · tensorflow.org · pytorch.org · pypi.org (keras 3.15.0, Jun 2026)
One API, four runtimes
A · THE SEPARATION THAT DEFINES KERAS 3 your code — keras.layers · keras.Model · compile() · fit() keras.ops — one NumPy-shaped surface over every backend dispatch jax fastest, TPU-native tensorflow tf.data · SavedModel torch DataLoader · nn.Module openvino inference only the switch os.environ["KERAS_BACKEND"] = "torch" import keras must be set BEFORE the import or edit ~/.keras/keras.json B · THE SAME LOOP WHATEVER IS UNDERNEATH any data source NumPy · tf.data.Dataset torch DataLoader · PyDataset keras.Model Sequential · Functional Subclass .compile().fit() or your own train_step or a native backend loop three different outputs model.save("m.keras") ← resume training model.save_weights("m.weights.h5") ← weights only model.export(...) ← SavedModel / LiteRT C · WHERE keras-preprocessing WENT keras-preprocessing final release 1.1.2 · May 2020 repo archived Sep 2024 Python 2.7–3.6 only keras._legacy.preprocessing keras.utils.image_dataset_from_directory keras.layers.Random* · Rescaling · Resizing keras.layers.TextVectorization why it matters Augmentation now runs as layers — on the GPU, inside the model, and travelling with it when exported. The old generators were CPU-bound NumPy loops that had to be reimplemented at serving time. One genuine gap: shear has no built-in replacement layer.
import os
os.environ["KERAS_BACKEND"] = "jax"   # or "tensorflow" | "torch" — BEFORE the import
import keras

model = keras.Sequential([
    keras.layers.Rescaling(1./255),        # preprocessing IS a layer now
    keras.layers.Dense(128, activation="relu"),
    keras.layers.Dense(10),                  # logits, no softmax
])
model.compile(optimizer="adam",
              loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              metrics=["accuracy"])
model.fit(train_data, epochs=5, validation_data=val_data)
model.save("m.keras")                       # extension is mandatory

# train_data may be NumPy, a tf.data.Dataset, a torch DataLoader,
# or a keras.utils.PyDataset — on ANY backend.
Change one string on line 2 and the same model trains on a different framework.
PART I

keras  ·  the core

Keras 3 is a full rewrite: the same API you know, reimplemented over four backends. Anything built only from built-in layers is portable today — existing tf.keras models included. Custom code is portable only if you write it with keras.ops instead of tf.* or torch.*.
01Install & the backend switchthe first decision
02The API surfacewhat lives where
03keras.opswrite once, run anywhere
04Tensors, variables & RNGstate, portably
05Three ways to buildpick the simplest that fits
06Layersthe catalogue
07Write your own layerthe extension point
08Losses, metrics, optimizersthe training contract
09compile() & fit()the loop, for free
10Callbackshooks into fit()
11Custom trainingfrom train_step to raw loops
12Save, load & serializethree different files
13Export & deployout of Python
14Quantization & LoRAmaking big models fit
15Feeding the modeldata, portably
16Preprocessing layersthe modern way
PART II

keras-preprocessing  ·  the archived module

Read this before copying any tutorial. The standalone package's final release was 1.1.2 in May 2020 — declaring support for Python 2.7–3.6 — and the repository was archived in September 2024. Its symbols moved into core Keras, were deprecated there, and in Keras 3 survive only under the private keras._legacy.preprocessing namespace. This part covers what each symbol became.
K1Where it standsthe honest status
K2The image APIImageDataGenerator → layers
K3The text APITokenizer → TextVectorization
K4The sequence APIgenerators → functions
K5Migration mapold call → new call
PART III

keras + tensorflow  ·  the default pairing

Since TF 2.16, pip install tensorflow brings Keras 3 and tf.keras is Keras 3. This is the backend with the most mature serving story — tf.data, SavedModel, TF Serving, tf.distribute — and the one where reaching for tf.* inside a custom layer costs you portability without any warning.
T1Setup on TensorFlowgains and trade-offs
T2tf.data with Kerasthe input pipeline
T3TF-native trainingwhen you drop down
T4Export & servingthe TF advantage
T5The Keras 2 escape hatchwhen you must go back
PART IV

keras + pytorch  ·  two-way interop

The most surprising part of Keras 3: on the torch backend a Keras layer is a torch.nn.Module. Not wrapped, not adapted — it subclasses it. So Keras layers drop into PyTorch models and register their parameters, PyTorch modules drop into Keras models as layers, and fit() accepts a DataLoader. You can adopt as much or as little of either as you like.
P1Setup on PyTorchgains and trade-offs
P2Data with DataLoadertorch-native input
P3Layers both waysthe interop
P4A native torch loopfull manual control
P5Multi-GPU with DDPscaling out

Four pictures worth the whole reference

What each backend gives you, where preprocessing went, how the PyTorch interop works, and which file to save.

1 · choosing a backend

The same model runs on all four. What differs is the ecosystem you inherit — and how much of your custom code stays portable.

jax fastest, TPU-native stateless / functional sharding built in tensorflow tf.data pipelines SavedModel, Serving most existing code torch DataLoader works layers ARE nn.Modules DDP for multi-GPU openvino CPU inference no training load and predict built-in layers only → portable across all four, unchanged custom layers using keras.ops → still portable custom code using tf.* or torch.* → pinned to that one backend

2 · where preprocessing went

Every symbol from the archived module has a home — usually a layer, so the transformation lives inside the model and is exported with it.

keras-preprocessing (2020) Keras 3 ImageDataGenerator image_dataset_from_directory rotation/zoom/flip_range layers.Random* rescale=1./255 layers.Rescaling Tokenizer layers.TextVectorization TimeseriesGenerator utils.timeseries_dataset_from_array Sequence utils.PyDataset pad_sequences utils.pad_sequences — unchanged shear_range nothing built in — write a layer

3 · the PyTorch interop

On the torch backend the inheritance is real: keras.layers.Layer subclasses torch.nn.Module. That single fact makes the interop work in both directions with no adapter.

torch.nn.Module keras.layers.Dense(64) appears in net.parameters() keras.Sequential MyTorchModule() used as an ordinary layer class Layer(torch.nn.Module): — on the torch backend, literally So a torch optimizer trains Keras layers, DDP wraps a Keras model, and state_dict() / .to(device) all behave exactly as you expect. None of this holds on the JAX or TensorFlow backend — it is torch-specific.

4 · which file do you want

Three calls, three artifacts, three purposes. Choosing the wrong one is the most common Keras 3 stumble, because save() stopped producing a SavedModel.

model.save("m.keras") architecture + weights + optimizer state → keras.saving.load_model() resume training model.save_weights("m.weights.h5") weights only → load_weights() into an identical architecture transfer / partial model.export("dir/") SavedModel / LiteRT / ONNX → serving runtimes, not load_model() deploy A bare path in save() raises; load_model() cannot read an exported SavedModel.

Worth memorizing

backend before importKERAS_BACKEND must be set before import keras; it cannot change after
keras.ops, not tf.* or torch.*the one swap that keeps a custom layer portable across backends
built-ins are portable alreadya model of only built-in layers runs on all four backends unchanged
weights go in build()and without get_config() the layer cannot be reloaded
keras.random, not Python randomstateless RNG is what makes custom layers work under JAX tracing
.keras or .h5, never baresave("path") raises; export() is what makes a SavedModel
load_model can't read SavedModeluse keras.layers.TFSMLayer for that
from_logits must matcha softmax layer plus from_logits=True trains on nonsense, silently
loss_fn(y_true, y_pred)true label first
clipnorm is on the optimizernot in the training loop
metrics are statefulreset_state() each epoch or you average over all of training
adapt() on features onlypass ds.map(lambda x, y: x), not the whole dataset
augmentation layers self-disableactive in training, bypassed at inference — no flag needed
keras-preprocessing is archivedfinal release 1.1.2 (2020); in Keras 3 it survives only under _legacy
Tokenizer → TextVectorizationand it belongs inside the model, so the vocabulary can't drift
Sequence → PyDatasetsame two methods, new name, better multiprocessing
on torch, a layer IS an nn.Moduleso torch optimizers, DDP and state_dict() just work
torch backend needs zero_grad()gradients accumulate; forgetting it is the classic bug there
training=True, not model.train()the Keras kwarg is what switches Dropout and BatchNorm
a raw loop is never portableoverride compute_loss or train_step if you want to keep backends open
quantize, then enable_lorathat order is QLoRA; adapters stay full precision
don't load untrusted .h5Keras 3.12.3 was a security release hardening exactly that path