Keras’s high-level training loop (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ageron/handson-ml3/llms.txt
Use this file to discover all available pages before exploring further.
model.fit()) is convenient but sometimes you need finer control — perhaps an unusual loss term, a layer with non-standard state, or a training step that doesn’t fit the standard supervised learning mould. Chapter 12 teaches you to drop down from the Keras abstraction to TensorFlow primitives: tensors, variables, automatic differentiation with tf.GradientTape, and graph compilation with tf.function. You’ll build custom loss functions, custom layers with trainable weights, and complete custom training loops from scratch.
What you’ll learn
- TensorFlow tensors and the basics of operating on them like NumPy
- Mutable state with
tf.Variable - Custom loss functions and metrics that plug into
model.compile() - Custom layers by subclassing
tf.keras.layers.Layer - Custom models by subclassing
tf.keras.Model - Automatic differentiation with
tf.GradientTape - Writing a full custom training loop without
model.fit() - Accelerating Python functions with
tf.function(graph mode compilation) - TensorFlow’s additional data types: strings, ragged tensors, sparse tensors
Key concepts
Tensors and variables
Atf.Tensor is an immutable, multi-dimensional array analogous to a NumPy ndarray but capable of running on GPUs and TPUs. TensorFlow operations mirror NumPy closely: indexing, slicing, broadcasting, and most math ops work as expected. A tf.Variable wraps a mutable tensor and is what neural network weights are stored in; it supports in-place operations like assign(), assign_add(), and scatter_nd_update().
Custom loss functions and metrics
The simplest customisation is a Python function that acceptsy_true and y_pred tensors and returns a scalar loss. For more complex needs — losses with hyperparameters, or losses that need to be serialised — you subclass tf.keras.losses.Loss. The same pattern applies to metrics via tf.keras.metrics.Metric, where you override update_state() and result().
Custom layers
Subclassingtf.keras.layers.Layer lets you create layers with arbitrary learnable parameters and non-standard forward passes. The __init__ method is called once; build() is called lazily on the first call with the actual input shape, which is when you create your weight matrices; call() runs the forward pass. Marking weights with self.add_weight() ensures they are tracked by Keras and included in model.trainable_variables.
tf.GradientTape and custom training loops
tf.GradientTape records TensorFlow operations executed inside its context so that automatic differentiation (reverse-mode autodiff) can compute gradients. A minimal custom training step:
- Run the forward pass inside a
GradientTapeblock to record the computation. - Compute the loss.
- Call
tape.gradient(loss, trainable_variables)to get gradients. - Apply gradients with
optimizer.apply_gradients().
tf.function
Decorating a Python function with@tf.function traces it the first time it is called and compiles it into a TensorFlow graph. Subsequent calls use the compiled graph, which can be significantly faster than eager execution, especially for training loops.
Code examples
TensorFlow tensors and variables
Custom loss function
Custom layer with trainable weights
Custom training loop with tf.GradientTape
Running this notebook
Open in Colab
Note on Keras version
This chapter uses standard Keras 3 (bundled with TensorFlow ≥ 2.16). No special environment variable is required.
Exercises
Exercises ask you to implement a custom layer that normalises its inputs, write a custom training loop that also logs metrics to TensorBoard, and replicate Keras’s built-inHuber loss class from scratch. Solutions are at the end of the notebook.