Loss functions: MSE and cross-entropy

Loss functions: MSE and cross-entropy

30 min read

Measuring isn't answering, and answering is all the network knows how to do. The previous lesson, on the forward pass in matrix form, closed off one half of the route from X\mathbf{X} to Y^\hat{\mathbf{Y}}: a chain of products that crosses every layer without ever once asking whether what it hands back looks anything like what it should. The other half starts here, by fixing a rule that compares Y^\hat{\mathbf{Y}} with Y\mathbf{Y} and sums up, in a single number, how badly the batch is doing. That rule is called a loss function, and it has to be chosen carefully.

Plenty of rules would technically work, and the choice among them carries real weight. Take fun and i recommend it, which speaks well of the movie: its label is y=1y = 1, and the network answers with a number between 00 and 11 that grows the more it leans towards that class. Suppose it hands back 0.0020.002. It's wrong, since it should have landed right up against 11, and it's wrong with total confidence, since it couldn't have sat any closer to 00. This lesson compares two candidates, mean squared error and cross-entropy, and both agree the loss is high here; that's not where they part ways. What separates them is how much that loss changes when a weight moves, and on an example like this one, the first candidate, the one most people would reach for instinctively, barely reacts at all.

Before writing down a single formula, look at the shape of that difference. What's drawn below isn't the two losses themselves but the correction each one hands back: its derivative with respect to the pre-activation zz of the output neuron, which is exactly where the weights pull from (how much the loss shifts for every unit that zz moves). In green, cross-entropy; in grey, mean squared error (MSE). With the label at 11, the network gets it right towards the right of the drawing and gets it wrong towards the left.

A graph with the pre-activation z from minus 6 to 6 on the horizontal axis and the correction, from 0 to 1, on the vertical one. The cross-entropy curve, in green, enters through the top-left corner right up against 1, descends crossing the centre at 0.5 and ends right up against 0 on the right: it decreases without stopping. The MSE curve, in grey, enters through the bottom-left corner right up against 0, rises to a maximum of 0.30 shortly before the centre and falls back to 0 on the right: it is a hump with both tails on the floor.
The two tails of the MSE share the same value and mean opposite things: the one on the right is a network that gets it right and needs no correction, and the one on the left is a network that fails with confidence and still gets none. Cross-entropy only goes quiet at one end.

From predictions to a single number

The network hands over Y^∈RB×dL\hat{\mathbf{Y}} \in \mathbb{R}^{B \times d_L}, and the examples bring their labels in an array of the same shape, Y∈RB×dL\mathbf{Y} \in \mathbb{R}^{B \times d_L}, row for row. A loss sums that pair up into one number, and it's built on two levels: a function ℓ\ell that judges one example by comparing its prediction with its label, and the mean of the BB results that come out of it,

L=1B∑i=1Bℓ(y^i,yi),\mathcal{L} = \frac{1}{B} \sum_{i=1}^{B} \ell\left(\hat{\mathbf{y}}_i, \mathbf{y}_i\right),

where y^i,yi∈RdL\hat{\mathbf{y}}_i, \mathbf{y}_i \in \mathbb{R}^{d_L} are the prediction and the label of example ii: rows ii of Y^\hat{\mathbf{Y}} and of Y\mathbf{Y}, written as columns. It's the mean rather than the sum precisely so L\mathcal{L} doesn't grow with BB. The two symbols share a name throughout the lesson (MSE, cross-entropy): ℓ\ell and L\mathcal{L} are the same criterion, one per example and the other averaged over the whole batch.

Three demands fall on ℓ\ell, and they don't carry equal weight: it has to return a number, it has to fall as the prediction approaches the label, and it has to have a derivative with respect to every parameter, because the method that corrects the weights runs entirely on those derivatives (the next lesson, on gradient descent). Almost anything satisfies the first two; the third is the one that actually rules candidates out, and it's the same criterion the lesson on activation functions and non-linearity used to choose φ\varphi: there, the step got ruled out for having a null derivative, not for measuring badly.

The first candidate measures the squared distance. Mean squared error is

LMSE=1B∑i=1B∥y^i−yi∥2,ℓMSE(y^,y)=(y^−y)2,\mathcal{L}_{\text{MSE}} = \frac{1}{B} \sum_{i=1}^{B} \left\lVert \hat{\mathbf{y}}_i - \mathbf{y}_i \right\rVert^{2}, \qquad \ell_{\text{MSE}}(\hat{y}, y) = (\hat{y} - y)^{2},

with the subscript naming which candidate it is, and the second form written for dL=1d_L = 1, the review network, where y^\hat{y} and yy are scalars: they've dropped the bold of y^i\hat{\mathbf{y}}_i and yi\mathbf{y}_i, since with a single output the vector shrinks down to a single number. The square does two things at once: it strips away the sign, so overshooting and falling short cost the same, and it punishes large errors more than proportionally, since missing by 0.80.8 costs sixteen times what missing by 0.20.2 does, not four.

That's exactly the right criterion when the network is predicting a quantity: if the job is to guess how many stars its author gave the movie, the difference y^−y\hat{y} - y is stars, and its square measures precisely what you want. Differentiating gives ∂ℓMSE/∂y^=2(y^−y)\partial \ell_{\text{MSE}} / \partial \hat{y} = 2(\hat{y} - y), the first good property of this loss: the correction stays proportional to the error. About that factor of 22, there's a convention I don't follow (many people put a 12\tfrac{1}{2} in front so it cancels on differentiating), and it changes nothing measurable: multiplying every correction by a constant gets absorbed into the size of the step (how far the weights move on each update), which is what the next lesson adjusts.

Why it switches off exactly when it's most needed

Classifying isn't predicting a quantity, and there the arithmetic changes. The label is 00 or 11, the output comes out of a sigmoid, y^=σ(z)\hat{y} = \sigma(z), and the derivative you actually need isn't the one with respect to y^\hat{y} but the one with respect to zz, which is what the weights control. Chain the two, using σ′(z)=σ(z)(1−σ(z))\sigma'(z) = \sigma(z)\left(1 - \sigma(z)\right) from the lesson on activation functions:

∂ℓMSE∂z=∂ℓMSE∂y^⋅dy^dz=2(y^−y)y^(1−y^).\frac{\partial \ell_{\text{MSE}}}{\partial z} = \frac{\partial \ell_{\text{MSE}}}{\partial \hat{y}} \cdot \frac{d\hat{y}}{dz} = 2\left(\hat{y} - y\right)\hat{y}\left(1 - \hat{y}\right).

Look at the product of the last two factors: it's the derivative of the sigmoid, and the lesson on activation functions already told you where that switches off: when y^\hat{y} approaches 00 or 11, that is, exactly when the network is sure of itself. The saturation that was a defect of the activation there resurfaces here, multiplying straight into the correction. With y=1y = 1 and z=−6z = -6 (the network says no, confidently, and is wrong), the first factor is almost −2-2, but y^(1−y^)\hat{y}\left(1 - \hat{y}\right) is 0.00250.0025, and the product stalls at 0.00490.0049. The worst error in the whole batch gets the smallest correction in the whole batch.

This isn't some rare case picked by hand: it's the entire left half of the grey curve above. And no amount of adjusting the step size fixes a loss that confuses its own two tails, since it scales both of them the same way.

Cross-entropy, reading the output as a probability

The fix isn't to patch the formula, but to change what the network is supposed to be saying in the first place. Up to now y^\hat{y} was a number between 00 and 11; read it instead as the probability the network assigns to the positive class, y^=P(y=1∣x)\hat{y} = P(y = 1 \mid \mathbf{x}). With that reading, the probability it assigns to whichever label the example actually came with is written once, covering both cases,

P(y∣x)=y^ y(1−y^)1−y,P(y \mid \mathbf{x}) = \hat{y}^{\,y}\left(1 - \hat{y}\right)^{1-y},

because with y=1y = 1 the second factor becomes 11 and what's left is y^\hat{y}, while with y=0y = 0 it's the first factor that disappears. Assuming the BB examples are independent, the probability of observing the whole set is the product of all BB of them, and that quantity, viewed as a function of the parameters, is called the likelihood. It has to be made as large as possible. Multiplying BB numbers smaller than one drives the product towards zero very fast, so take logarithms instead (which preserve the order, so the maximum stays put), flip the sign so there's something to minimise, and divide by BB. Out comes cross-entropy:

LCE=−1B∑i=1B[ yilog⁡y^i+(1−yi)log⁡(1−y^i)].\mathcal{L}_{\text{CE}} = -\frac{1}{B} \sum_{i=1}^{B} \left[\, y_i \log \hat{y}_i + \left(1 - y_i\right)\log\left(1 - \hat{y}_i\right) \right].

Read it example by example. One with yi=1y_i = 1 contributes −log⁡y^i-\log \hat{y}_i: it's 00 if the network gives it probability 11, and it grows without a ceiling as that probability falls towards zero; one with yi=0y_i = 0 looks at 1−y^i1 - \hat{y}_i instead. That missing ceiling is the underlying difference from MSE, which never climbed above 11.

Now for the derivative, which is what actually decides things. Start from the loss of a single example,

ℓCE=−ylog⁡y^−(1−y)log⁡(1−y^),\ell_{\text{CE}} = -y\log\hat{y} - (1-y)\log(1-\hat{y}),

differentiate it with respect to y^\hat{y}, put the two terms over a common denominator, and chain with the sigmoid:

∂ℓCE∂y^=−yy^+1−y1−y^=−y(1−y^)+y^(1−y)y^(1−y^)=y^−yy^(1−y^),∂ℓCE∂z=∂ℓCE∂y^⋅dy^dz=y^−yy^(1−y^)⋅y^(1−y^)=y^−y.\begin{aligned} \frac{\partial \ell_{\text{CE}}}{\partial \hat{y}} &= -\frac{y}{\hat{y}} + \frac{1 - y}{1 - \hat{y}} = \frac{-y\left(1 - \hat{y}\right) + \hat{y}\left(1 - y\right)}{\hat{y}\left(1 - \hat{y}\right)} = \frac{\hat{y} - y}{\hat{y}\left(1 - \hat{y}\right)}, \\[4pt] \frac{\partial \ell_{\text{CE}}}{\partial z} &= \frac{\partial \ell_{\text{CE}}}{\partial \hat{y}} \cdot \frac{d\hat{y}}{dz} = \frac{\hat{y} - y}{\hat{y}\left(1 - \hat{y}\right)} \cdot \hat{y}\left(1 - \hat{y}\right) = \hat{y} - y. \end{aligned}

On the first line, the numerator simplifies because the two yy^y\hat{y} terms cancel out. On the second, the denominator turns out to be exactly the y^(1−y^)\hat{y}(1 - \hat{y}) the sigmoid contributes when you chain, and the two cancel each other away.

The correction turns out to be nothing but the plain difference between what the network said and what it should have said. On the example from before, that's −0.9975-0.9975 instead of MSE's 0.00490.0049. The saturation hasn't gone anywhere, the sigmoid still has it; what happens is that the loss's derivative carries the exact inverse factor that undoes it, which is why the final formula never even mentions φ\varphi.

That's why it's called a loss and not an error: mean squared error genuinely measures something in the units of the label, while cross-entropy measures nothing of the kind (it's a logarithm of a probability), and it's still the better of the two. What decides isn't what they measure, but what they hand back once you differentiate them.

More than two classes: softmax

With three answers instead of two (positive, negative and neutral), the output layer has dL=3d_L = 3 neurons, and the sigmoid stops being any use: it squashes each coordinate on its own, and three numbers in (0,1)(0,1) don't automatically form a distribution. The function that does is softmax, the same one from the lesson on Word2Vec, which shared probability out among the entries of the vocabulary:

y^k=ezk∑m=1dLezm,k=1,…,dL,\hat{y}_k = \frac{e^{z_k}}{\sum_{m=1}^{d_L} e^{z_m}}, \qquad k = 1, \dots, d_L,

positive because it's an exponential, and summing to 11 thanks to the denominator. The label is written one-hot, y∈{0,1}dL\mathbf{y} \in \{0,1\}^{d_L} with a single 11 at the position of the correct class cc, exactly the way the lesson on one-hot encoding encoded a vocabulary entry. The loss is the same one as before, written out for dLd_L classes:

ℓ=−∑k=1dLyklog⁡y^k=−log⁡y^c,\ell = -\sum_{k=1}^{d_L} y_k \log \hat{y}_k = -\log \hat{y}_c,

where the second equality comes from the one-hot encoding cancelling every term but one. Only the probability the network gave the true class actually counts, though what it shares out among the others still matters, since the denominator ties all of them together.

The derivative, with the two kinds of index that have to be told apart

Differentiate ℓ=−log⁡y^c\ell = -\log \hat{y}_c with respect to each zjz_j. The softmax mixes every coordinate into each one (moving zjz_j moves the denominator, and with it all dLd_L probabilities), so you need two separate computations, depending on whether jj is the correct class or not. Call S=∑mezmS = \sum_{m} e^{z_m}, with ∂S/∂zj=ezj\partial S / \partial z_j = e^{z_j}.

When j=cj = c, the numerator depends on zjz_j too, and it's the derivative of a quotient:

∂y^c∂zc=ezcS−ezcezcS2=ezcS(1−ezcS)=y^c(1−y^c).\frac{\partial \hat{y}_c}{\partial z_c} = \frac{e^{z_c} S - e^{z_c} e^{z_c}}{S^{2}} = \frac{e^{z_c}}{S}\left(1 - \frac{e^{z_c}}{S}\right) = \hat{y}_c\left(1 - \hat{y}_c\right).

When j≠cj \neq c, the numerator ezce^{z_c} doesn't depend on zjz_j at all, and only the denominator moves:

∂y^c∂zj=ezc⋅(−1S2)ezj=−ezcS⋅ezjS=−y^c y^j.\frac{\partial \hat{y}_c}{\partial z_j} = e^{z_c} \cdot \left(-\frac{1}{S^{2}}\right) e^{z_j} = -\frac{e^{z_c}}{S}\cdot\frac{e^{z_j}}{S} = -\hat{y}_c\,\hat{y}_j.

With ∂ℓ/∂y^c=−1/y^c\partial \ell / \partial \hat{y}_c = -1/\hat{y}_c, the chain rule gives both cases:

∂ℓ∂zc=−1y^c y^c(1−y^c)=y^c−1,∂ℓ∂zj=−1y^c(−y^c y^j)=y^j(j≠c),\frac{\partial \ell}{\partial z_c} = -\frac{1}{\hat{y}_c}\,\hat{y}_c\left(1 - \hat{y}_c\right) = \hat{y}_c - 1, \qquad \frac{\partial \ell}{\partial z_j} = -\frac{1}{\hat{y}_c}\left(-\hat{y}_c\,\hat{y}_j\right) = \hat{y}_j \quad (j \neq c),

and the two turn out to be the same expression, since yc=1y_c = 1 and yj=0y_j = 0. Coordinate by coordinate:

∇z ℓ=y^−y.\nabla_{\mathbf{z}}\,\ell = \hat{\mathbf{y}} - \mathbf{y}.

With dL=2d_L = 2 this reproduces the binary case exactly: the short formula from the previous section was never an accident of having a single output.

Both derivations land in the same place, and that's exactly why they're worth doing once: from here through the lesson on backpropagation, the output layer never gets differentiated again, it goes in as y^−y\hat{\mathbf{y}} - \mathbf{y} and that's the end of it. Softmax matters here because the rest of the course predicts over entire vocabularies, where the classes number in the tens of thousands.

The two losses over the ten reviews

The cell evaluates both candidates over the usual ten reviews, using the pre-activations of the neuron with hand-set weights from the lesson on the artificial neuron. Nothing here is random. Run it and look at three things: what order each column puts the networks in, where each correction switches off, and what happens with a logarithm of zero.

import numpy as np

# --- The usual ten reviews, with their labels and their pre-activations.
y = np.array([1., 0., 1., 0., 1., 0., 0., 1., 0., 1.])
z = np.array([2., -2., 2., -2., 2., -1., -2., 1., 0., 2.])
sigmoid = lambda z: 1.0 / (1.0 + np.exp(-z))


def mse(y_hat, y):
return float(np.mean((y_hat - y) ** 2))


def cross_entropy(y_hat, y):
p = np.clip(y_hat, 1e-12, 1.0 - 1e-12) # without this, one 0 takes the whole mean with it
return float(-np.mean(y * np.log(p) + (1.0 - y) * np.log(1.0 - p)))


# 1. Three networks: the hand-set-weight one, the same without conviction, and one that answers 0.5 to everything.
confident = sigmoid(z)
lukewarm = 0.5 + (confident - 0.5) * 0.1

print("%-25s %8s %8s %8s" % ("", "correct", "MSE", "CE"))
for name, p in [("hand-set-weight neuron", confident),
("the same, no conviction", lukewarm),
("answers 0.5 to everything", np.full(10, 0.5))]:
correct = int(((p >= 0.5) == (y == 1.0)).sum())
print("%-25s %8d %8.4f %8.4f" % (name, correct, mse(p, y), cross_entropy(p, y)))
print()

# 2. The correction of each loss, for an example whose label is 1.
print("%6s %6s %11s %11s %9s" % ("z", "ŷ", "|dMSE/dz|", "|dCE/dz|", "ratio"))
for zz in [-6.0, -3.0, 0.0, 3.0, 6.0]:
s = sigmoid(zz)
d_mse = abs(2.0 * (s - 1.0) * s * (1.0 - s))
d_ce = abs(s - 1.0)
print("%6.1f %6.3f %11.6f %11.6f %9.1f" % (zz, s, d_mse, d_ce, d_ce / d_mse))
print()

# 3. The logarithm of zero, and what covering it up costs.
p = np.array([0.9, 0.0, 0.5])
clipped = np.clip(p, 1e-12, 1.0)
with np.errstate(divide="ignore"):
print("without clipping:", -np.log(p), " mean:", float(-np.mean(np.log(p))))
print("clipping: ", np.round(-np.log(clipped), 2),
" mean:", round(float(-np.mean(np.log(clipped))), 2))
numpy

The first run downloads the Python interpreter (~15 MB). After that it stays in the browser cache and is reused across every lesson.

The first table makes the case for the whole lesson. The count of correct answers ties the first two networks at nine out of ten: it can't tell apart saying 0.8810.881 from saying 0.5380.538, because both land on the same side. The two losses separate them cleanly (0.0490.049 against 0.2200.220 for the confident network, 0.2200.220 against 0.6330.633 for the lukewarm one), and both put the one that answers 0.50.5 to everything in last place, at 0.2500.250 and 0.6930.693, which is log⁡2\log 2. They rank the networks the same way. What they don't do the same way is correct them.

That's the second table. At z=−6z = -6, with the label at 11, cross-entropy hands back 0.99750.9975 and MSE hands back 0.00490.0049: a ratio of 202.7202.7. At z=0z = 0, with the network holding no opinion either way, the ratio drops to 22. The advantage grows towards the extremes, and one of those extremes is exactly where the network is wrong. The last row repeats that same 202.7202.7 with the network getting it right, and there both corrections are tiny, which is exactly as it should be.

The third experiment shows the detail that breaks a real implementation. −log⁡0-\log 0 is infinite, and one example the network assigns probability zero to is enough to send the mean of the entire batch to infinity and stop it from saying anything useful at all. Clipping at 10−1210^{-12} avoids that, at a cost worth naming out loud: nobody ever actually measured that 27.6327.63.

Test your intuition

Four questions (a cross-entropy worked out by hand, MSE when classifying, the clean correction, and the maximum of the softmax), plus a challenge on the output layer.

A network with a sigmoid on its output assigns y^=0.2\hat{y} = 0.2 to a review whose label is y=1y = 1. What is the cross-entropy of that example?

A margin of ±0.01 is accepted.

What happens to mean squared error when the output of the network is a sigmoid and the label is 00 or 11?

Tick everything that is true about this lesson's result ∂ℓ∂z=y^−y\dfrac{\partial \ell}{\partial z} = \hat{y} - y.

Select every correct option. This is graded all-or-nothing: there is no partial credit.

softmax(z) and softmax(z + 100) return exactly the same thing. Why, and what is gained by subtracting the maximum of each row before exponentiating?

Write the output layer of a network with several classes, over a whole batch and without Python loops:

  • softmax(Z) receives Z of shape (B, dL)(B,\, d_L) and returns the probabilities row by row. Each row has to sum to 11, and the function has to survive large pre-activations: np.exp(1000) overflows.
  • cross_entropy(Y_hat, Y) returns the mean over the batch of −∑kyklog⁡y^k-\sum_k y_k \log \hat{y}_k, with Y encoded one-hot. A y^k\hat{y}_k equal to zero cannot return inf.

Neither function may write into the arrays it receives.

The first run downloads the Python interpreter (~15 MB); after that it stays in the browser cache. This challenge is much easier to solve on a physical keyboard: on a phone, read it and come back later.


There's now a number that says how badly the network is doing, and across this entire lesson, not a single weight has moved. All three networks arrived with their numbers already fixed, the loss has ranked them, which is all it was ever asked to do, and none of them has improved one bit for having been measured.

Training means making that number small, and the only thing you can actually touch to get there is the weights: changing them changes Y^\hat{\mathbf{Y}}, and through it, L\mathcal{L}. Of that movement, the derivative gives you the direction (if y^−y\hat{y} - y is positive, raising zz raises the loss, so it needs to come down instead), but it doesn't give you the distance: it describes the slope right where you're standing and stops holding the moment you move. So the weights don't get corrected in one single leap, but in a series of steps, and the size of each step is itself a decision: too short and you never arrive, too long and you overshoot. How that size gets chosen, what goes wrong when it's chosen badly, and why repeating the process ends up finding the minimum without ever actually seeing it, is the next lesson, on gradient descent.

Was this helpful?
Further reading1 source · 1 book

Where this lesson comes from, and where to go next. None of it is needed to carry on with the course.

  • Deep Learning, ch. 6: Deep Feedforward Networks
    bookGoodfellow, Bengio and Courville, 2016deeplearningbook.orgEN

    Its §6.2 derives the loss from the likelihood, arriving at cross-entropy, and explains why a saturating output clashes with mean squared error and how the logarithm fixes it. This lesson's argument, with less algebra.