Keras
learning curve
machine learning
data visualization
Python

How to plot a learning curve for a keras experiment?

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

A learning curve shows how your model behaves over training epochs, usually by plotting loss and one or more metrics for both training and validation data. In Keras, the data you need is already returned by model.fit, so plotting a useful curve is mostly about saving the History object and charting the right keys.

Training a Model and Capturing History

model.fit returns a History instance whose history attribute is a dictionary. Each key stores the per-epoch values for a metric such as loss, val_loss, accuracy, or val_accuracy.

The example below trains a small binary classifier on synthetic data and records the training history:

python
1import numpy as np
2import tensorflow as tf
3
4np.random.seed(0)
5tf.random.set_seed(0)
6
7x = np.random.randn(2000, 20).astype("float32")
8y = (x[:, 0] + 0.5 * x[:, 1] - 0.3 * x[:, 2] > 0).astype("int32")
9
10x_train, x_val = x[:1600], x[1600:]
11y_train, y_val = y[:1600], y[1600:]
12
13model = tf.keras.Sequential(
14    [
15        tf.keras.layers.Input(shape=(20,)),
16        tf.keras.layers.Dense(32, activation="relu"),
17        tf.keras.layers.Dense(1, activation="sigmoid"),
18    ]
19)
20
21model.compile(
22    optimizer="adam",
23    loss="binary_crossentropy",
24    metrics=["accuracy"],
25)
26
27history = model.fit(
28    x_train,
29    y_train,
30    validation_data=(x_val, y_val),
31    epochs=20,
32    batch_size=32,
33    verbose=0,
34)
35
36print(history.history.keys())

That final print is useful because it tells you the exact metric names available for plotting.

Plotting Loss and Accuracy

Once you have the history object, use Matplotlib to draw the curves:

python
1import matplotlib.pyplot as plt
2
3epochs = range(1, len(history.history["loss"]) + 1)
4
5plt.figure(figsize=(10, 4))
6
7plt.subplot(1, 2, 1)
8plt.plot(epochs, history.history["loss"], label="train loss")
9plt.plot(epochs, history.history["val_loss"], label="val loss")
10plt.xlabel("Epoch")
11plt.ylabel("Loss")
12plt.title("Loss Curve")
13plt.legend()
14
15plt.subplot(1, 2, 2)
16plt.plot(epochs, history.history["accuracy"], label="train accuracy")
17plt.plot(epochs, history.history["val_accuracy"], label="val accuracy")
18plt.xlabel("Epoch")
19plt.ylabel("Accuracy")
20plt.title("Accuracy Curve")
21plt.legend()
22
23plt.tight_layout()
24plt.show()

This gives you the standard learning-curve view most teams use during experimentation.

Reading the Curve Correctly

A plot is only useful if you interpret it well:

  • If training loss keeps dropping but validation loss starts rising, the model is likely overfitting.
  • If both training and validation metrics stay poor, the model may be underfitting.
  • If both curves improve and remain relatively close, the model is usually learning something that generalizes.

Learning curves also help you spot unstable training. Large oscillations can mean the learning rate is too high, the batch size is poorly chosen, or the data pipeline is noisy.

Making the Plot More Useful

For real experiments, you often want more than a raw plot. Early stopping is a common addition because it marks the best validation point:

python
1callback = tf.keras.callbacks.EarlyStopping(
2    monitor="val_loss",
3    patience=3,
4    restore_best_weights=True,
5)
6
7history = model.fit(
8    x_train,
9    y_train,
10    validation_data=(x_val, y_val),
11    epochs=50,
12    callbacks=[callback],
13    verbose=0,
14)

If you log custom metrics, they will appear in history.history too. For example, regression experiments may include mae, while multiclass models may report sparse categorical accuracy.

Common Pitfalls

  • Forgetting to pass validation_data means you can only plot training curves, which hides overfitting.
  • Hard-coding metric names can fail because some tasks use sparse_categorical_accuracy, mae, or other names instead of plain accuracy.
  • Comparing curves across experiments without matching batch size, learning rate, and data split can be misleading.
  • Judging a model from a single noisy run often leads to the wrong conclusion about convergence.
  • Treating a nice-looking training curve as proof of real-world performance ignores the need for a separate test set.

Summary

  • Keras already records per-epoch loss and metric values in the History object returned by model.fit.
  • Plot both training and validation curves to understand generalization, not just optimization.
  • Use the exact keys in history.history when building the chart.
  • Interpret divergence between training and validation curves as a signal about overfitting or underfitting.
  • Add callbacks such as early stopping when you want the plot to reflect real experiment control decisions.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.