Long-running Keras training can keep updating weights after validation performance has stopped improving. EarlyStopping watches an epoch-level metric from model.fit() and ends the run once the configured wait for improvement is exhausted.

The callback can monitor val_loss when model.fit() receives validation data. mode=“min” treats a lower value as better, while min_delta sets the smallest decrease that resets the patience counter.

The demonstration uses patience=2 and restore_best_weights=True. Training should stop before the requested 20 epochs, and evaluating the restored model should reproduce the best validation loss recorded in the training history.

Steps to stop Keras training early with EarlyStopping:

  1. Create early_stopping_demo.py with the imports and regression data.
    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
  2. Append the model definition and compilation settings to early_stopping_demo.py.
    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"],
    )
  3. Append the validation-loss callback configuration to early_stopping_demo.py.
    early_stop = keras.callbacks.EarlyStopping(
        monitor="val_loss",
        mode="min",
        min_delta=0.0001,
        patience=2,
        restore_best_weights=True,
        verbose=1,
    )

    mode=“max” suits a monitored metric that should increase, such as val_accuracy. start_from_epoch delays monitoring while the model completes warm-up epochs.

  4. Append the training call to early_stopping_demo.py.
    history = model.fit(
        x_train,
        y_train,
        validation_data=(x_val, y_val),
        epochs=20,
        batch_size=16,
        callbacks=[early_stop],
        verbose=0,
    )

    The callbacks argument accepts a list, so EarlyStopping can run beside logging or checkpoint callbacks.

  5. Append the restored-weight checks to early_stopping_demo.py.
    best_epoch = int(np.argmin(history.history["val_loss"])) + 1
    best_val_loss = min(history.history["val_loss"])
    restored_val_loss, _ = model.evaluate(x_val, y_val, verbose=0)
    epochs_run = len(history.history["loss"])
     
    if epochs_run >= 20:
        raise RuntimeError("EarlyStopping did not stop training before epoch 20")
    if not np.isclose(restored_val_loss, best_val_loss, rtol=1e-5, atol=1e-7):
        raise RuntimeError("restored weights do not match the best validation loss")
     
    print("epochs requested: 20")
    print(f"epochs run: {epochs_run}")
    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())}")

    history.history must contain the monitored key. If val_loss is absent, pass validation data to model.fit() or monitor an epoch metric already present in the training logs.

  6. Run the completed 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 loss values can vary by backend and hardware.

  7. Confirm epochs run is below 20 in the script output.

    This observation shows the callback ended training after the patience window instead of consuming the full epoch budget.

  8. Confirm restored val_loss matches best val_loss in the script output.

    This observation shows restore_best_weights=True returned the model to the best monitored epoch.

  9. Rerun the retained early_stopping_demo.py program to verify its EarlyStopping checks still pass.
    $ 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']