A learning curve plots training and validation scores as data increases. The gap between them tells you everything: large gap = overfitting, both low = underfitting, converging = sweet spot. It also tells you if more data would help.
- Plot learning curves early in your project — before spending time on feature engineering
- Large gap (overfit) → try regularization, dropout, less complex model, or more data
- Both scores low (underfit) → try more features, more complex model, or feature engineering
- Curves converging but slowly → more data will help — go collect it
- Curves flat and apart → more data won't help — simplify the model instead
→ Balanced setup — moderate gap between train and val scores
A degree-7 polynomial fitted to a noisy sine wave. With 20 points the training error is 0.050 and the validation error 0.155; with 80, 0.079 and 0.102; with 320, 0.094 and 0.095. Training error rises and validation error falls until they meet near the noise level (0.09). A gap that is still closing says more data will help.
- Curves from a single split are noisy; average several before reading a trend into them.
- Learning curves for different models cross: the best model for small data is often not the best for large (Perlich, Provost & Simonoff 2003).
- A flat validation curve can mean the model is limited, or that the labels are too noisy to learn more from.
rng = np.random.default_rng(3)
f = lambda x: np.sin(3 * x)
x_val = rng.uniform(-1, 1, 2_000); y_val = f(x_val) + rng.normal(0, 0.3, 2_000)
gaps = {}
for n in (20, 80, 320):
x = rng.uniform(-1, 1, n); y = f(x) + rng.normal(0, 0.3, n)
c = np.polyfit(x, y, 7)
train = ((np.polyval(c, x) - y) ** 2).mean(); val = ((np.polyval(c, x_val) - y_val) ** 2).mean()
gaps[n] = (round(train, 3), round(val, 3))Assumes import numpy as np and import pandas as pd (and SciPy where imported). Each snippet simulates or makes up its own data, as the comments say.
The same idea with a library
# Python — learning curves from sklearn.model_selection import learning_curve import matplotlib.pyplot as plt sizes, train_scores, val_scores = learning_curve( model, X, y, cv=5, n_jobs=-1, train_sizes=np.linspace(0.1, 1.0, 10), scoring='accuracy' ) plt.plot(sizes, train_scores.mean(axis=1), label='Train') plt.plot(sizes, val_scores.mean(axis=1), label='Val') plt.legend(); plt.show()
- C. Perlich, F. Provost & J. S. Simonoff, “Tree Induction vs. Logistic Regression: A Learning-Curve Analysis”, Journal of Machine Learning Research 4, 2003
- P. Domingos, “A Few Useful Things to Know about Machine Learning”, Communications of the ACM 55(10), 2012
Last reviewed October 2026 · How this site checks its content · Report a mistake
- Pattern EssaysSignal in the NoiseFinding patterns is easy. The hard part is resisting them where there are none
- ML MathGradient DescentRolling downhill on the loss landscape to find optimal weights
- MLOps & Production MLModel Monitoring DashboardsPrediction health, feature drift, and system metrics in one view
- The Toolkitscikit-learn Evaluation SuiteThe Swiss army knife — classification_report, cross_val_score, learning_curve, and the metrics module