Skip to content

Cost Functions - Mean Squared Error (MSE)

  • the difference between a cost function, a loss function and a metric
  • the definition of MSE, and why we train on it but report RMSE
  • how squaring changes what the model considers important
  • MAE and Huber — what they optimise instead, and when each wins
  • why the MSE surface is a convex bowl, and what that guarantees
  • the statistical reason MSE is the default: it is maximum likelihood under Gaussian noise

A model needs a single number that says how wrong it currently is — one number, because optimisation can only push one thing downhill. That number is the cost.

Choosing the cost is not a technicality. It is a statement of values. Squaring the errors declares that one miss of 10 is worse than ten misses of 1 (100 against 10), so the model will contort itself to avoid big misses even at the cost of many small ones. Taking absolute values declares they are equally bad. Both are defensible; they simply produce different models from identical data.

diagram Diagram mermaid
TermScopeExample
LossOne instance(y^iyi)2(\hat{y}_i - y_i)^2
CostThe whole training set — what the optimiser minimises1m(y^iyi)2\frac{1}{m}\sum(\hat{y}_i - y_i)^2
MetricWhat you report to humans; need not be differentiableRMSE, MAE, R2R^2, “percentage within ±5%”

The cost and the metric are allowed to differ, and often should. You train on MSE because it has clean gradients; you report RMSE because it is in the units of the target.

MSE(θ)=1mi=1m(y^(i)y(i))2=1mXθy22\text{MSE}(\boldsymbol{\theta}) = \frac{1}{m}\sum_{i=1}^{m}\left(\hat{y}^{(i)} - y^{(i)}\right)^2 = \frac{1}{m}\lVert \mathbf{X}\boldsymbol{\theta} - \mathbf{y}\rVert_2^2

Units are the square of the target’s units — squared dollars, squared degrees. Nobody has intuition for squared dollars, which is why we take the root:

RMSE=MSE\text{RMSE} = \sqrt{\text{MSE}}

The square root is monotonic, so whichever θ\boldsymbol{\theta} minimises MSE also minimises RMSE. The minimiser is identical. But the gradient is not:

θjMSE=2mi(y^(i)y(i))xj(i)\frac{\partial}{\partial \theta_j}\text{MSE} = \frac{2}{m}\sum_i (\hat{y}^{(i)} - y^{(i)})x_j^{(i)} θjRMSE=12MSEMSEθj\frac{\partial}{\partial \theta_j}\text{RMSE} = \frac{1}{2\sqrt{\text{MSE}}}\cdot\frac{\partial \text{MSE}}{\partial \theta_j}

The RMSE gradient carries a 1/MSE1/\sqrt{\text{MSE}} factor that blows up as the cost approaches zero. MSE’s gradient is clean and linear in the residual. Same answer, better-behaved arithmetic — so train on MSE, report RMSE.

MSE(θ)\text{MSE}(\boldsymbol{\theta}) is a quadratic form in θ\boldsymbol{\theta} with Hessian

H=2mXX\mathbf{H} = \frac{2}{m}\mathbf{X}^\top\mathbf{X}

XX\mathbf{X}^\top\mathbf{X} is positive semi-definite for any X\mathbf{X} — for any vector v\mathbf{v}, vXXv=Xv20\mathbf{v}^\top\mathbf{X}^\top\mathbf{X}\mathbf{v} = \lVert\mathbf{X}\mathbf{v}\rVert^2 \geq 0. So the cost is convex: no local minima, no saddle points, no bad starting position. Whatever optimiser you point at it will find the global minimum, and if the columns are linearly independent that minimum is unique.

figure MSE over the two parameters of a simple linear model matplotlib
Contour plot of MSE over slope and intercept for the five-point dataset, showing nested elliptical contours around a single starred minimum at slope 0.6, intercept 2.2. Contour plot of MSE over slope and intercept for the five-point dataset, showing nested elliptical contours around a single starred minimum at slope 0.6, intercept 2.2.
Nested ellipses closing on one point. Every direction leads downhill to the same place — this is what convexity looks like, and it is why linear regression never needs a restart.

MSE is not an arbitrary choice. Assume the target is a linear function plus Gaussian noise:

y(i)=θx(i)+ε(i),ε(i)N(0,σ2)y^{(i)} = \boldsymbol{\theta}^\top\mathbf{x}^{(i)} + \varepsilon^{(i)}, \qquad \varepsilon^{(i)} \sim \mathcal{N}(0, \sigma^2)

The log-likelihood of the data is then

logL(θ)=m2log(2πσ2)12σ2i=1m(y(i)θx(i))2\log L(\boldsymbol{\theta}) = -\frac{m}{2}\log(2\pi\sigma^2) - \frac{1}{2\sigma^2}\sum_{i=1}^{m}\left(y^{(i)} - \boldsymbol{\theta}^\top\mathbf{x}^{(i)}\right)^2

Only the last term depends on θ\boldsymbol{\theta}, and it is 12σ2-\frac{1}{2\sigma^2} times the sum of squared errors. Maximising the likelihood is exactly minimising MSE. Choosing squared error is the same act as assuming your noise is Gaussian — which also explains why MSE handles outliers badly: Gaussians have thin tails and treat a large deviation as nearly impossible.

The Phase 3 five-point set with the fitted line y^=0.6x+2.2\hat{y} = 0.6x + 2.2:

xix_iyiy_iy^i\hat{y}_ieie_iei2e_i^2ei\lvert e_i \rvertHuber (δ=1\delta=1)
122.8−0.80.640.80.32
243.4+0.60.360.60.18
354.0+1.01.001.00.50
444.6−0.60.360.60.18
555.2−0.20.040.20.02
2.403.201.20
MSE=2.405=0.48,RMSE=0.480.693,MAE=3.205=0.64\text{MSE} = \frac{2.40}{5} = 0.48, \qquad \text{RMSE} = \sqrt{0.48} \approx 0.693, \qquad \text{MAE} = \frac{3.20}{5} = 0.64

Note that RMSE (0.693) exceeds MAE (0.64). That is not a coincidence: RMSE is always at least as large as MAE, and the gap widens as the errors become less uniform. A big RMSE-to-MAE ratio is a quick signal that a few large errors dominate.

Change the last observation from 5 to 25 and refit. The least-squares line becomes y^=4.6x5.8\hat{y} = 4.6x - 5.8:

xix_iyiy_iy^i\hat{y}_ieie_i
12−1.2+3.2
243.4+0.6
358.0−3.0
4412.6−8.6
52517.2+7.8

MSE has jumped from 0.48 to 30.88, and — far worse — every single point is now badly predicted. The line abandoned four good observations to chase one bad one. The MAE-optimal line for the same data is y^1.51x+0.49\hat{y} \approx 1.51x + 0.49, which stays close to the original four points and simply accepts a large error on the outlier.

MSE=1mei2MAE=1meiHuberδ={12ei2eiδδ(ei12δ)otherwise\text{MSE} = \frac{1}{m}\sum e_i^2 \qquad \text{MAE} = \frac{1}{m}\sum \lvert e_i \rvert \qquad \text{Huber}_\delta = \begin{cases} \tfrac{1}{2}e_i^2 & \lvert e_i\rvert \le \delta \\[4pt] \delta\left(\lvert e_i\rvert - \tfrac{1}{2}\delta\right) & \text{otherwise} \end{cases}

Huber is quadratic near zero (so it has a smooth gradient where it matters) and linear in the tails (so one wild point cannot dominate). It is the pragmatic default when you suspect outliers but still want gradient-friendly behaviour.

figure Penalty as a function of a single residual matplotlib
Three loss curves plotted against residual size: a steep parabola for squared error, a V shape for absolute error, and a Huber curve that is parabolic near zero and straight further out. Three loss curves plotted against residual size: a steep parabola for squared error, a V shape for absolute error, and a Huber curve that is parabolic near zero and straight further out.
At a residual of 3 the squared loss charges 9 while absolute error charges 3. Huber tracks the parabola until the residual passes delta, then goes straight.
figure Dragging one point upward, and watching each loss react matplotlib
Line chart showing the fitted slope as one data point is dragged upward. The MSE slope climbs steadily away from the true value of 2; the MAE slope stays flat. Line chart showing the fitted slope as one data point is dragged upward. The MSE slope climbs steadily away from the true value of 2; the MAE slope stays flat.
The MSE fit follows the outlier almost linearly. The MAE fit does not move at all — the median is immune to how far away the far point is.
  • The MAE line is flat. Not merely flatter — flat. Absolute-error fitting depends on the rank of the residuals, not their size, so once a point is above the line it does not matter whether it is above by 5 or by 500.
  • The MSE line rises without limit. Each unit of extra distance adds a proportional pull on the slope. There is no point at which MSE stops caring.
  • Huber sits between them, by construction. Below δ\delta it behaves like MSE, above it like MAE, and δ\delta is the dial.
MSEMAEHuber
Optimises toward themeanmedianmean, robustified
Outlier sensitivityHighLowTunable via δ\delta
Differentiable everywhereYesNo (corner at 0)Yes
Closed-form solutionYesNoNo
scikit-learnLinearRegression, RidgeQuantileRegressorHuberRegressor
Reach for it whenNoise is roughly GaussianOutliers are real dataYou are unsure

Each pink square’s area is one point’s squared error. Watch the total area collapse as the line improves — that shrinking area is literally the cost.

sketch MSE as shrinking squares p5.js
Each square's area equals one point's squared error; as the fitted line improves, the squares shrink and MSE drops. Click for a fresh dataset.

All three losses, plus the invariant that RMSE never falls below MAE:

losses.py
import numpy as np
 
 
def mse(y, pred):
    return float(((pred - y) ** 2).mean())
 
 
def rmse(y, pred):
    return mse(y, pred) ** 0.5
 
 
def mae(y, pred):
    return float(np.abs(pred - y).mean())
 
 
def huber(y, pred, delta=1.0):
    e = np.abs(pred - y)
    quadratic = 0.5 * e**2
    linear = delta * (e - 0.5 * delta)
    return float(np.where(e <= delta, quadratic, linear).mean())
 
 
x = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
y = np.array([2.0, 4.0, 5.0, 4.0, 5.0])
pred = 0.6 * x + 2.2
 
print(f"MSE   = {mse(y, pred):.4f}")     # MSE   = 0.4800
print(f"RMSE  = {rmse(y, pred):.4f}")    # RMSE  = 0.6928
print(f"MAE   = {mae(y, pred):.4f}")     # MAE   = 0.6400
print(f"Huber = {huber(y, pred):.4f}")   # Huber = 0.2400
 
print(rmse(y, pred) >= mae(y, pred))     # True — always
robust_vs_ols.py
import numpy as np
from sklearn.linear_model import LinearRegression, HuberRegressor
from sklearn.metrics import mean_squared_error, mean_absolute_error
 
X = np.array([1.0, 2.0, 3.0, 4.0, 5.0]).reshape(-1, 1)
clean = np.array([2.0, 4.0, 5.0, 4.0, 5.0])
dirty = np.array([2.0, 4.0, 5.0, 4.0, 25.0])   # last point corrupted
 
ols_clean = LinearRegression().fit(X, clean)
ols_dirty = LinearRegression().fit(X, dirty)
huber_dirty = HuberRegressor(epsilon=1.35).fit(X, dirty)
 
print(f"OLS on clean data : slope {ols_clean.coef_[0]:.2f}")    # slope 0.60
print(f"OLS on dirty data : slope {ols_dirty.coef_[0]:.2f}")    # slope 4.60
print(f"Huber on dirty    : slope {huber_dirty.coef_[0]:.2f}")  # slope 1.91
 
# Same predictions, two different metrics
pred = ols_dirty.predict(X)
print(f"MSE = {mean_squared_error(dirty, pred):.2f}")           # MSE = 30.88
print(f"MAE = {mean_absolute_error(dirty, pred):.2f}")          # MAE = 4.64

One corrupted observation multiplies the OLS slope by more than seven, from 0.60 to 4.60. Huber still drifts — 1.91 — because five points is a brutal test and one of them is now an extreme value, but it absorbs less than half the damage OLS does.

pch.quizTag pch.quizDefaultTitle
  1. Why is MSE minimised during training while RMSE is reported afterwards?

    pch.quizShowAnswer

    B — They share the same minimiser, but MSE has a cleaner gradient while RMSE is in the target's units — The square root is monotonic, so the optimal parameters are identical. MSE's gradient is linear in the residual; RMSE's carries a 1 over root-MSE factor that explodes near zero.

  2. Which constant value minimises the sum of absolute errors?

    pch.quizShowAnswer

    B — The median — The median minimises absolute error; the mean minimises squared error. That single fact explains most of the behavioural difference between MAE and MSE.

  3. Why is the MSE cost surface guaranteed to have no local minima?

    pch.quizShowAnswer

    B — Because its Hessian, 2/m times X transpose X, is positive semi-definite, making the cost convex — X transpose X is positive semi-definite for any X, so the quadratic cost is convex — a single global minimum with no traps along the way.

  4. Your model reports RMSE 40 and MAE 12. What does that gap suggest?

    pch.quizShowAnswer

    B — A small number of very large errors are dominating the squared average — RMSE is always at least MAE, and the gap grows with the spread of the errors. A ratio above three points at a few extreme residuals rather than uniform mediocrity.

Exercise 4 – Mean minimises MSE, median minimises MAE

Section titled “Exercise 4 – Mean minimises MSE, median minimises MAE”

Exercise 5 – Huber resists what OLS cannot

Section titled “Exercise 5 – Huber resists what OLS cannot”
  • Loss is per instance, cost is over the training set, and the metric is what you report.
  • MSE=1mei2\text{MSE} = \frac{1}{m}\sum e_i^2; train on it, report RMSE because it is in target units.
  • The cost surface is convex because XX\mathbf{X}^\top\mathbf{X} is positive semi-definite — one global minimum, no traps.
  • Minimising MSE is maximum likelihood under Gaussian noise, which is also why it fears outliers.
  • MSE targets the mean, MAE the median, Huber the mean with a cap on how much any one point can pull.
  • On the five-point set: MSE 0.48, RMSE 0.693, MAE 0.64, Huber 0.24. Corrupt one point and MSE jumps to 30.88 while the Huber slope barely moves.

Exercise 6 – Which statistic is each loss estimating?

Section titled “Exercise 6 – Which statistic is each loss estimating?”

Continue to Gradient Descent Explained — how to actually walk down the bowl when the closed-form solution is too expensive to compute.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading