DS DevShelfHub Projects · AI tools
Cheatsheets / TensorFlow
Cheatsheet · AI frameworks

TensorFlow and Keras 3 Cheatsheet: Models, tf.data and Training

By DevShelfHub

Tensors, Keras 3, tf.data, GradientTape, distribution strategies, SavedModel, TFLite — the TF 2 + Keras 3 surface day-to-day.

121 items 9 min Keras 3 tf.data SavedModel

Start hereQuick start · 6 you’ll reach for daily

Tensortf.constant([1.,2.,3.])
Keras modelkeras.Sequential([...])
Compilemodel.compile(opt, loss, metrics)
Trainmodel.fit(ds, epochs=N, callbacks=[...])
Pipelinetf.data.Dataset.from_tensor_slices(...)
Savemodel.export("savedmodel")

Target versions · paceVersions

Targets: tensorflow ≥ 2.17 keras ≥ 3 python ≥ 3.10

TF 2.16+ ships Keras 3 as tf.keras — multi-backend, fully rewritten, breaks a few subclassed-model patterns from Keras 2. Model.save("...") writes the new .keras archive; model.export(...) writes a SavedModel for serving. tf.estimator is deprecated; the only path is Keras. macOS GPU goes through tensorflow-metal. This sheet pins to TF 2.17+ / Keras 3.

Install · deviceSetup

bash
# Install — one wheel covers CPU + GPU on Linux / WSL2 (TF 2.16+)
pip install "tensorflow>=2.17"               # Linux/WSL2 GPU is detected automatically
pip install "tensorflow-cpu>=2.17"           # CPU-only build (smaller)

# macOS — Apple Silicon needs the Metal plugin
pip install tensorflow tensorflow-metal      # MPS-backed accelerator

# Keras 3 as a standalone, multi-backend library (TF / JAX / PyTorch)
pip install "keras>=3"
export KERAS_BACKEND="tensorflow"            # or "jax" / "torch"

# Confirm the device the runtime sees
python -c "
import tensorflow as tf
print('tf', tf.__version__)
print('GPUs:', tf.config.list_physical_devices('GPU'))
print('Keras', tf.keras.__version__)
"

# Memory growth — almost always what you want on a shared GPU
# import tensorflow as tf
# for g in tf.config.list_physical_devices('GPU'):
#     tf.config.experimental.set_memory_growth(g, True)

Where things liveCommon imports

import tensorflow as tfCore — tensors, ops, datasets, distribution.
import keras / from keras import layers, models, optimizers, losses, metricsKeras 3 modules. Preferred over the old tf.keras.* deep imports.
from keras.callbacks import EarlyStopping, ModelCheckpoint, TensorBoard, ReduceLROnPlateauLifecycle hooks for fit.
from keras.preprocessing import image_dataset_from_directoryOne-call image dataset builder.
from keras import mixed_precisionMixed-precision policies (FP16 / BF16).
import tensorflow_datasets as tfdsCurated public datasets, ready-batched.
from tensorflow import data as tf_dataAlias for tf.data — the input pipeline API.

Create · reshape · opsTensors

tf.constant([1., 2.], dtype=tf.float32)Immutable tensor.
tf.Variable(initial_value, trainable=True)Mutable. Used for model weights.
tf.zeros((2,3)) / tf.ones(...) / tf.fill(shape, value)Constant-filled.
tf.range(0, 10) / tf.linspace(0., 1., 50)Range / even spacing.
tf.random.normal((B, D), seed=42)Standard Gaussian. Set seed for reproducibility.
tf.cast(x, tf.float32)Type cast.
tf.convert_to_tensor(np_arr)NumPy → tensor. Implicit in most ops.
x.shape / x.dtype / x.deviceInspect.
tf.reshape(x, (B, -1)) / tf.transpose(x, [1, 0])Reshape / permute.
tf.concat([a, b], axis=0) / tf.stack([a, b], axis=0)Join along existing / new axis.
a @ b / tf.matmul(a, b) / tf.einsum("bnd,bdm->bnm", a, b)Matrix multiply / general contraction.
tf.reduce_sum(x, axis=0) / reduce_mean / reduce_maxReductions. keepdims=True preserves rank.

Eager autodiffGradientTape

with tf.GradientTape() as tape: loss = ...Record ops on watched tensors.
grads = tape.gradient(loss, model.trainable_variables)Compute grads on the recorded graph.
optimizer.apply_gradients(zip(grads, model.trainable_variables))One optimizer step.
with tf.GradientTape(persistent=True): ...Reuse the tape for multiple gradient() calls. Don’t forget del tape.
tape.watch(x)Force tracking on a constant (Variables are auto-watched).
tape.jacobian / batch_jacobianHigher-order derivatives.
@tf.functionCompile a Python fn into a static graph — speed + serializability.
tf.function(jit_compile=True)XLA compilation — bigger speedup on GPU / TPU.
tf.stop_gradient(x)Cut the graph — useful for targets in distillation / RL.

Sequential · Functional · subclassKeras models

keras.Sequential([layers.Dense(...), ...])Quickest path — linear stack.
inputs = keras.Input((D,)); x = layers.Dense(...)(inputs); ...Functional API — branches + multi-input / output.
keras.Model(inputs, outputs, name="...")Wrap functional graph as a Model.
class M(keras.Model): def call(self, x, training=False): ...Subclassed — max flexibility.
model.summary() / keras.utils.plot_model(model, show_shapes=True)Inspect structure.
model.build(input_shape=(None, D))Force-build a subclassed model so summary works.
model.trainable_variables / non_trainable_variablesIterate params for custom training loops.
model.get_layer("name") / layer.get_weights() / set_weights()Reach into specific layers.
layer.trainable = FalseFreeze layers — transfer learning.
python
import tensorflow as tf
import keras
from keras import layers

# 1. Sequential — quickest, for plain feedforward stacks
seq = keras.Sequential([
    layers.Input((28, 28)),
    layers.Flatten(),
    layers.Dense(128, activation="gelu"),
    layers.Dropout(0.2),
    layers.Dense(10),                          # raw logits — let the loss apply softmax
])

# 2. Functional API — branches, residuals, multi-input
inputs  = keras.Input(shape=(28, 28))
x       = layers.Flatten()(inputs)
hidden  = layers.Dense(256, activation="gelu")(x)
skip    = layers.Dense(256, activation="gelu")(hidden) + hidden
outputs = layers.Dense(10)(skip)
func    = keras.Model(inputs, outputs, name="mlp_skip")

# 3. Subclassed Model — full Python control (research code)
class MLP(keras.Model):
    def __init__(self, hidden=128, n_classes=10):
        super().__init__()
        self.flat = layers.Flatten()
        self.fc1  = layers.Dense(hidden, activation="gelu")
        self.drop = layers.Dropout(0.2)
        self.fc2  = layers.Dense(n_classes)

    def call(self, x, training=False):
        x = self.flat(x); x = self.fc1(x)
        x = self.drop(x, training=training)
        return self.fc2(x)

sub = MLP()

# Inspect — works on all three styles after build
sub.build((None, 28, 28))
sub.summary()

Dense · Conv · normLayers & activations

layers.Dense(units, activation="gelu", kernel_initializer="he_normal")Fully connected.
layers.Conv2D(filters, kernel_size=3, padding="same", strides=1)2-D convolution.
layers.Conv2DTranspose(...) / MaxPooling2D / GlobalAveragePooling2DUpsample / pool.
layers.BatchNormalization() / LayerNormalization()Normalization.
layers.Dropout(0.2) / SpatialDropout2D(0.2)Element / channel dropout.
layers.Embedding(input_dim=vocab, output_dim=D, mask_zero=True)Token embeddings. mask_zero=True propagates padding masks.
layers.MultiHeadAttention(num_heads, key_dim)(q, k, v)Self / cross attention.
layers.LSTM / GRU(units, return_sequences=True)Recurrent.
layers.RandomFlip / RandomRotation / RandomCrop / RescalingBuilt-in image augmentation. Apply in the model so they run on GPU.
layers.Lambda(fn)Wrap an arbitrary function into a layer.
keras.activations.relu / gelu / silu / softmax(axis=-1)Functional activations.

Input pipelinestf.data pipeline

tf.data.Dataset.from_tensor_slices((X, y))Build from in-memory tensors.
tf.data.Dataset.list_files("data/*.tfrec")Glob files. shuffle=True randomizes shard order.
tf.data.TFRecordDataset(files, num_parallel_reads=AUTOTUNE)Read sharded TFRecords in parallel.
.map(fn, num_parallel_calls=AUTOTUNE)Parse / transform in parallel.
.batch(N, drop_remainder=True)Drop partial last batch — required for static shapes.
.shuffle(buffer_size, reshuffle_each_iteration=True)Buffer size should comfortably exceed one epoch’s patterns.
.cache() / .cache("/tmp/cache")Cache after expensive ops. Path version spills to disk.
.prefetch(tf.data.AUTOTUNE)Overlap CPU pipeline with GPU train. Always last step.
.repeat() / .take(N) / .skip(N)Lifecycle helpers.
.interleave(map_fn, cycle_length=4)Read multiple files in round-robin — faster than chained map.
tf.data.Options() / dataset.with_options(opts)Tweak threading / determinism.
python
import tensorflow as tf

# From in-memory tensors
ds = tf.data.Dataset.from_tensor_slices((X, y))

# From files — variable-length shards, parallel reads
files = tf.data.Dataset.list_files("data/shard-*.tfrec")
ds    = tf.data.TFRecordDataset(files, num_parallel_reads=tf.data.AUTOTUNE)

# Parse one TFRecord example
feature_spec = {
    "image": tf.io.FixedLenFeature([], tf.string),
    "label": tf.io.FixedLenFeature([], tf.int64),
}
def parse(record):
    ex = tf.io.parse_single_example(record, feature_spec)
    img = tf.io.decode_jpeg(ex["image"], channels=3)
    img = tf.image.resize(img, (224, 224)) / 255.0
    return img, ex["label"]

ds = (ds
    .shuffle(10_000, reshuffle_each_iteration=True)
    .map(parse, num_parallel_calls=tf.data.AUTOTUNE)
    .batch(64, drop_remainder=True)
    .prefetch(tf.data.AUTOTUNE)                # overlap CPU prep with GPU train
)

# Caching — for datasets that fit in memory or on disk
ds_cached = ds.cache().repeat()
# ds_cached = ds.cache("/tmp/cache").repeat()  # spill to disk

# Distributed-aware sharding
strategy = tf.distribute.MirroredStrategy()
ds_dist  = strategy.experimental_distribute_dataset(ds)

compile · fit · callbacksTraining

model.compile(optimizer, loss, metrics=[...])Wire optimizer + loss + metrics.
keras.optimizers.AdamW(learning_rate=3e-4, weight_decay=0.01)Modern default. Preferred over Adam + manual decay.
keras.optimizers.SGD(lr, momentum=0.9, nesterov=True)Classic.
keras.losses.SparseCategoricalCrossentropy(from_logits=True)Preferred — pass raw logits, no softmax in the last layer.
keras.losses.BinaryCrossentropy(from_logits=True)Binary / multi-label.
model.fit(train_ds, validation_data=val_ds, epochs=N, callbacks=[...])High-level training loop.
model.evaluate(test_ds) / model.predict(x)Score / inference.
EarlyStopping(monitor="val_loss", patience=3, restore_best_weights=True)Stop when val plateaus.
ModelCheckpoint("best.keras", save_best_only=True)Save best checkpoint by metric.
ReduceLROnPlateau(factor=0.5, patience=2)Drop LR when stuck.
TensorBoard(log_dir="./runs")Live metrics + graph in the TB UI.
LearningRateScheduler(fn) / CosineDecay(...)Custom / cosine schedules.

train_step · AMPCustom training & mixed precision

class M(keras.Model): def train_step(self, data): ...Override the per-batch step but keep fit() + callbacks.
@tf.function for d in ds: train_step(d)Pure custom loop. Compile with @tf.function for speed.
tape.gradient(loss, vars) → optimizer.apply_gradients(zip(...))Manual optimizer step.
keras.mixed_precision.set_global_policy("mixed_float16")Enable AMP. Compute in FP16, vars in FP32.
last layer: dtype="float32"Keep the head FP32 for numerically stable softmax / loss.
opt = mixed_precision.LossScaleOptimizer(opt)Auto loss-scaling. fit() wires this for you.
jit_compile=True in compile()XLA on the whole graph — bigger gains on TPU + modern GPUs.
tf.config.experimental.set_memory_growth(gpu, True)Don’t pre-allocate the whole GPU.

Multi-GPU / TPUDistribution strategies

strategy = tf.distribute.MirroredStrategy()Single-host multi-GPU.
strategy = tf.distribute.MultiWorkerMirroredStrategy()Multi-host. Set TF_CONFIG env per worker.
strategy = tf.distribute.TPUStrategy(resolver)TPU pods (Cloud / Colab).
with strategy.scope(): model = ... ; model.compile(...)Required — variables created in this scope are mirrored.
strategy.experimental_distribute_dataset(ds)Shard a dataset across replicas.
tf.distribute.ParameterServerStrategy(...)Async PS-worker setups — legacy.
tf.distribute.get_strategy()Current scope — returns default outside with blocks.
strategy.run(step_fn, args=(batch,))Per-replica step in custom loops.

.keras · SavedModel · TFLiteSave, load, serve

model.save("model.keras")Preferred single-file archive (Keras 3).
keras.models.load_model("model.keras")Reload — weights + architecture + optimizer.
model.save_weights("w.weights.h5") / model.load_weights(...)Weights only.
model.export("savedmodel")TensorFlow SavedModel — for TF Serving / TFLite / TF.js.
tf.saved_model.load("savedmodel")Load a SavedModel as a callable.
converter = tf.lite.TFLiteConverter.from_saved_model(...)Quantize + export for mobile.
converter.optimizations = [tf.lite.Optimize.DEFAULT]Default = post-training int8 quant.
tfjs.converters.save_keras_model(model, "tfjs_out")Browser deployment via @tensorflow/tfjs.
tensorflow_model_server --model_base_path=...REST / gRPC serving for SavedModels.

MNIST · Keras 3 · SavedModelEnd-to-end · MNIST classifier

tf.data pipeline, mixed-precision Keras 3 model, early stopping + checkpoint callbacks, SavedModel export.

python
# End-to-end MNIST — tf.data, Keras model, compile/fit, callbacks, SavedModel.
import tensorflow as tf
import keras
from keras import layers

# Mixed-precision: free speedup on Ampere+ / Apple Silicon
keras.mixed_precision.set_global_policy("mixed_float16")

(x_tr, y_tr), (x_te, y_te) = keras.datasets.mnist.load_data()
x_tr = (x_tr / 255.0).astype("float32"); x_te = (x_te / 255.0).astype("float32")

BATCH = 128
train_ds = (tf.data.Dataset.from_tensor_slices((x_tr, y_tr))
            .shuffle(60_000).batch(BATCH).prefetch(tf.data.AUTOTUNE))
val_ds   = (tf.data.Dataset.from_tensor_slices((x_te, y_te))
            .batch(BATCH).prefetch(tf.data.AUTOTUNE))

model = keras.Sequential([
    layers.Input((28, 28)),
    layers.Flatten(),
    layers.Dense(256, activation="gelu"),
    layers.Dropout(0.2),
    layers.Dense(10, dtype="float32"),         # last layer in fp32 — stable softmax
])

model.compile(
    optimizer=keras.optimizers.AdamW(learning_rate=3e-4, weight_decay=0.01),
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=["sparse_categorical_accuracy"],
)

cbs = [
    keras.callbacks.EarlyStopping(monitor="val_loss", patience=3, restore_best_weights=True),
    keras.callbacks.ModelCheckpoint("mnist.keras", save_best_only=True, monitor="val_loss"),
    keras.callbacks.TensorBoard(log_dir="./runs"),
]
model.fit(train_ds, validation_data=val_ds, epochs=10, callbacks=cbs)

# Save + reload
model.export("mnist_savedmodel")               # for TF Serving / TFLite
reloaded = keras.models.load_model("mnist.keras")

Best practiceGood to know

Always end the tf.data pipeline with .prefetch(tf.data.AUTOTUNE). It overlaps CPU input prep with GPU compute — usually frees 10–30% of wall time for one extra line of code.
Use from_logits=True, leave the final activation off. Softmax / sigmoid + cross-entropy as separate ops is numerically unstable. Logits + from_logits=True in the loss is faster and avoids underflow.
Mixed-precision policy on day one with modern GPUs. set_global_policy("mixed_float16") is a one-line speedup. Keep the output layer FP32 for stable softmax / loss.

Common trapsWatch out for

Re-tracing inside @tf.function kills performance. Python-side branching on tensor values or passing different shapes / dtypes triggers a re-trace. Stabilize input signatures with input_signature=[tf.TensorSpec(...)].
Keras 3 subclassed models silently load with wrong weights without explicit build(). Layers materialize on first call, so load_weights before that call fails or aligns wrong. Call model.build(input_shape) or run a dummy forward pass first.
GPU memory pre-allocation strangles other processes. Default TF grabs all GPU memory at startup. Set tf.config.experimental.set_memory_growth(gpu, True) — especially on shared dev boxes.

Go deeperSee also

TensorFlow FAQ

What is the difference between TensorFlow and Keras 3?

TensorFlow is Google's numerical computation and ML framework providing GPU-accelerated tensor operations, automatic differentiation, distributed training, and model deployment tools. Keras 3 is a high-level neural network API that runs on top of TensorFlow, JAX, or PyTorch as a backend. Keras simplifies model building and training with a clean sequential/functional API, while raw TensorFlow gives full control for custom ops and GradientTape-based training loops.

What is GradientTape in TensorFlow?

GradientTape records operations executed inside its context so they can be automatically differentiated. Use it for custom training loops: with tf.GradientTape() as tape, run a forward pass, then call tape.gradient(loss, model.trainable_variables) to get gradients and pass them to an optimizer. This is the lower-level alternative to model.fit() and gives full control over the training step.

How does tf.data work in TensorFlow?

tf.data provides a high-performance input pipeline API. Create a dataset with tf.data.Dataset.from_tensor_slices() or from_generator(), then chain transformations like .map(), .filter(), .batch(), .shuffle(), and .prefetch(). Prefetching with AUTOTUNE overlaps data loading with GPU training, preventing the data pipeline from becoming a bottleneck on large datasets.

How do I save and load a TensorFlow model?

Call model.save('path') to export in Keras format (.keras, recommended) or SavedModel format (a directory). Reload with tf.keras.models.load_model('path'). The SavedModel format preserves the computation graph, weights, and serving signatures, making it portable to TensorFlow Serving and TFLite. For weight-only checkpoints use model.save_weights() and model.load_weights().

How do I run TensorFlow training on multiple GPUs?

Wrap model creation and compilation inside a tf.distribute.MirroredStrategy().scope() to automatically replicate the model across all visible GPUs and sum gradients. For multi-machine training use MultiWorkerMirroredStrategy. Scale the batch size proportionally to the number of GPUs to maintain the effective learning rate. No other code changes are needed for standard Keras model.fit() training loops.