Long-running Keras training can spend extra epochs after the validation metric has stopped improving. EarlyStopping watches a metric from model.fit() and ends training early, which helps when a run has a fixed epoch budget but the best validation state appears sooner.

The callback reads epoch-end values from the training logs. Monitoring val_loss requires validation data, and any custom metric named in monitor must be available from the compiled model so the callback can see it in the log dictionary.

The smoke test uses mode=“min” for validation loss, patience=2 for two stale epochs, and restore_best_weights=True so the in-memory model returns to the best monitored epoch. Add a checkpoint callback as well when the best model must survive the Python process ending.

Steps to stop Keras training early with EarlyStopping:

  1. Save the demonstration script as
    early_stopping_demo.py

    .

    early_stopping_demo.py
    import keras
    import numpy as np
     
     
    keras.utils.set_random_seed(42)
     
    x_train = np.linspace(-1.0, 1.0, 120, dtype="float32").reshape(-1, 1)
    y_train = (2.0 * x_train) + 0.5
    x_val = np.linspace(-0.8, 0.8, 40, dtype="float32").reshape(-1, 1)
    y_val = (2.0 * x_val) + 0.5
     
    model = keras.Sequential(
        [
            keras.layers.Input(shape=(1,)),
            keras.layers.Dense(8, activation="relu"),
            keras.layers.Dense(1),
        ]
    )
    model.compile(
        optimizer=keras.optimizers.SGD(learning_rate=0.35),
        loss="mse",
        metrics=["mae"],
    )
     
    early_stop = keras.callbacks.EarlyStopping(
        monitor="val_loss",
        mode="min",
        min_delta=0.0001,
        patience=2,
        restore_best_weights=True,
        verbose=1,
    )
     
    history = model.fit(
        x_train,
        y_train,
        validation_data=(x_val, y_val),
        epochs=20,
        batch_size=16,
        callbacks=[early_stop],
        verbose=0,
    )
     
    best_epoch = int(np.argmin(history.history["val_loss"])) + 1
    best_val_loss = min(history.history["val_loss"])
    restored_val_loss, restored_val_mae = model.evaluate(x_val, y_val, verbose=0)
     
    print("epochs requested: 20")
    print(f"epochs run: {len(history.history['loss'])}")
    print(f"best epoch: {best_epoch}")
    print(f"best val_loss: {best_val_loss:.4f}")
    print(f"restored val_loss: {restored_val_loss:.4f}")
    print(f"history keys: {sorted(history.history.keys())}")

    Set the Keras backend before Python imports keras when the project uses JAX or PyTorch instead of the default backend.
    Related: How to set the Keras backend

  2. Run the demonstration script.
    $ python3 early_stopping_demo.py
    Epoch 3: early stopping
    Restoring model weights from the end of the best epoch: 1.
    epochs requested: 20
    epochs run: 3
    best epoch: 1
    best val_loss: 0.0474
    restored val_loss: 0.0474
    history keys: ['loss', 'mae', 'val_loss', 'val_mae']

    The exact metric values can vary by backend and hardware. A run that stops before epoch 20 and prints the same best and restored validation loss confirms the callback stopped training and restored the best monitored weights.

  3. Copy the callback configuration into the project training call.
    early_stop = keras.callbacks.EarlyStopping(
        monitor="val_loss",
        mode="min",
        min_delta=0.0001,
        patience=2,
        restore_best_weights=True,
    )
     
    history = model.fit(
        train_features,
        train_labels,
        validation_data=(val_features, val_labels),
        epochs=100,
        callbacks=[early_stop],
    )

    Use mode=“max” for metrics that should increase, such as val_accuracy. Add start_from_epoch when the model needs warm-up epochs before monitoring begins.

  4. Check the project training history after the next run.
    print(f"epochs run: {len(history.history['loss'])}")
    print(f"best val_loss: {min(history.history['val_loss']):.4f}")

    history.history must include the monitored key. If val_loss is missing, pass validation data to model.fit() or choose a metric that is present in the training logs.

  5. Remove the demonstration script when it was only a smoke test.
    $ rm early_stopping_demo.py