Training can improve and then regress before its final epoch, which makes the last in-memory weights a poor substitute for the strongest validation result. Keras provides the ModelCheckpoint callback to save a model during model.fit() whenever a monitored value reaches a new best point.
The monitor name must appear in the training logs. A validation loss produced by validation_data is available as val_loss, and mode=“min” tells the callback that a lower value is better. With save_best_only=True and a fixed path, each improvement replaces the previous checkpoint.
A whole-model checkpoint uses the native .keras format and contains the model configuration, weights, compile information, and optimizer state. Reloading that file and evaluating the same validation data proves that the saved artifact represents the best recorded epoch rather than merely confirming that a file exists.
Steps to save the best Keras model checkpoint:
- Create checkpoint_best_model.py with the imports and deterministic training data.
- checkpoint_best_model.py
from pathlib import Path import keras import numpy as np keras.utils.set_random_seed(42) x_train = np.linspace(-1.0, 1.0, 160, dtype="float32").reshape(80, 2) y_train = (0.75 * x_train[:, :1]) - (0.25 * x_train[:, 1:2]) x_val = np.linspace(-0.8, 0.8, 40, dtype="float32").reshape(20, 2) y_val = (0.75 * x_val[:, :1]) - (0.25 * x_val[:, 1:2])
- Append the model definition below the validation data.
- checkpoint_best_model.py
model = keras.Sequential( [ keras.layers.Input(shape=(2,)), keras.layers.Dense(8, activation="relu"), keras.layers.Dense(1), ] )
- Add the compile settings below the model definition.
- checkpoint_best_model.py
model.compile( optimizer=keras.optimizers.Adam(learning_rate=0.05), loss="mse", metrics=["mae"], )
- Add the ModelCheckpoint callback below model.compile() to retain the lowest val_loss.
- checkpoint_best_model.py
checkpoint_path = Path("checkpoints/best.keras") checkpoint = keras.callbacks.ModelCheckpoint( filepath=checkpoint_path, monitor="val_loss", mode="min", save_best_only=True, verbose=1, )
A whole-model checkpoint requires a .keras suffix. A weights-only callback requires save_weights_only=True and a filename ending in .weights.h5.
- Append the training call below the callback so every epoch supplies val_loss for comparison.
- checkpoint_best_model.py
history = model.fit( x_train, y_train, validation_data=(x_val, y_val), epochs=4, batch_size=16, callbacks=[checkpoint], verbose=0, )
- Add the reload and comparison checks below model.fit().
- checkpoint_best_model.py
loaded = keras.models.load_model(checkpoint_path) loaded_loss, loaded_mae = loaded.evaluate(x_val, y_val, verbose=0) best_loss = min(history.history["val_loss"]) matches_best = np.isclose(loaded_loss, best_loss, rtol=1e-6, atol=1e-7) assert checkpoint_path.is_file() assert matches_best print(f"Saved checkpoint: {checkpoint_path}") print(f"Best val_loss: {best_loss:.6f}") print(f"Loaded val_loss: {loaded_loss:.6f}") print(f"Loaded val_mae: {loaded_mae:.6f}") print(f"Reload matches best epoch: {matches_best}")
- Verify the saved checkpoint by running the completed script in the Keras environment.
$ python checkpoint_best_model.py Epoch 1: val_loss improved from None to 0.05945, saving model to checkpoints/best.keras ##### snipped ##### Saved checkpoint: checkpoints/best.keras Best val_loss: 0.000601 Loaded val_loss: 0.000601 Loaded val_mae: 0.021829 Reload matches best epoch: True
The final assertion fails if the reloaded checkpoint does not reproduce the lowest validation loss recorded by the training history.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.