Skip to content
AI360XpertBeta

Cross-Entropy

The price you pay for being confidently wrong: it reads off the probability your model gave the correct answer and charges you the negative logarithm of it.

Cross-entropy reads off only one number from the prediction, the 0.7 given to the correct class, and charges minus log of it for a loss of 0.357
Cross-entropy reads off only one number from the prediction, the 0.7 given to the correct class, and charges minus log of it for a loss of 0.357

Why Does This Exist?

Classification needs a loss that punishes confident mistakes far harder than hesitant ones, and accuracy cannot do it. A model that says 51 percent for the right class and one that says 99 percent both count as correct and score identically. There is no gradient to follow, so there is nothing to train on.

Cross-entropy fixes exactly this. A prediction of 0.990.99 on the right answer costs −ln⁡(0.99)≈0.01-\ln(0.99) \approx 0.01. A prediction of 0.010.01 on it costs −ln⁡(0.01)≈4.6-\ln(0.01) \approx 4.6, over four hundred times as much. Being confidently wrong is expensive, being tentatively wrong is survivable, and the resulting slope points somewhere useful.

It is also the principled choice rather than a convenient one: minimising cross-entropy is mathematically identical to maximum likelihood estimation under a categorical assumption. It is not a heuristic that happens to work. It is what the probability theory tells you to do.

Think of It Like This

A bookmaker settling up

You are a bookmaker who posted odds on a three-horse race. Your posted probabilities are your prediction. One horse wins.

The settlement rule is simple: you pay the negative logarithm of the probability you gave the horse that actually won. Nothing else on your board matters.

Post 0.70.7 on the winner and you pay 0.360.36. Post 0.010.01 and you pay 4.64.6. Post 0.9990.999 and you pay almost nothing. Post a flat zero on the winner and you owe infinity, which is the mathematics telling you that declaring something impossible and watching it happen is unrecoverable, not small.

Two consequences follow. Your best strategy is to post odds matching your true beliefs, since exaggeration eventually costs more than it gains. And because your probabilities total one, generosity to one horse means stinginess elsewhere. There is no hedge.

How It Actually Works

Cross-entropy measures the average surprise of using distribution qq to describe outcomes actually drawn from pp:

H(p,q)=−∑ip(xi)log⁡q(xi)H(p, q) = -\sum_i p(x_i) \log q(x_i)

In plain words: weight the surprise of each outcome under your predicted distribution qq by how often it truly happens under pp. Compare entropy with H(p)=−∑plog⁡pH(p) = -\sum p \log p: the only change is that the logarithm is taken of qq rather than pp. Same average surprise, computed with the wrong probabilities.

Why the formula collapses to one term

For classification the true distribution is one-hot: the correct class has probability 11 and every other class has 00. Every term where p(xi)=0p(x_i) = 0 vanishes, and the single surviving term has p=1p = 1:

H(p,q)=−log⁡q(correct class)H(p, q) = -\log q(\text{correct class})

In plain words: the loss is the negative log of the probability assigned to the right answer, full stop. Wrong-class probabilities never appear in the formula. They matter only indirectly, because the softmax forces probabilities to total 11, so raising a wrong class lowers the right one.

Its relationship to entropy and KL divergence

Cross-entropy decomposes cleanly:

H(p,q)=H(p)+DKL(p∥q)H(p, q) = H(p) + D_{\mathrm{KL}}(p \parallel q)

In plain words: the cost of using qq splits into the uncertainty genuinely present plus the extra paid for qq being wrong. Since H(p)H(p) depends only on the data and not on the model, minimising cross-entropy and minimising KL divergence are the same optimisation.

This also explains why the loss rarely reaches zero on real data. Its floor is H(p)H(p), the irreducible uncertainty in the labels. If two identical inputs carry different labels, no model drives below that, and chasing zero means memorising noise.

The gradient is the reason it is used

Pair a softmax output with cross-entropy and the gradient with respect to the pre-softmax logits zz is startlingly simple:

∂L∂z=y^−y\frac{\partial \mathcal{L}}{\partial z} = \hat{y} - y

In plain words: predicted probabilities minus the one-hot target. That is the entire backward pass for the output layer. The gradient scales with the error, so badly wrong predictions produce large gradients and learning is fast where needed. No derivative of the exponential survives, which is why the two are always implemented as one fused operation.

Worked example

Three classes, the true class is the second one, so y=[0,1,0]y = [0, 1, 0]. The model predicts y^=[0.1,0.7,0.2]\hat{y} = [0.1, 0.7, 0.2].

Only the true class contributes:

L=−ln⁡(0.7)≈0.357\mathcal{L} = -\ln(0.7) \approx 0.357

Hand check: ln⁡(0.7)≈−0.3567\ln(0.7) \approx -0.3567, so negating gives 0.35670.3567, rounded to 0.3570.357. With a worse prediction y^=[0.2,0.2,0.6]\hat{y} = [0.2, 0.2, 0.6] on the same label:

L=−ln⁡(0.2)≈1.609\mathcal{L} = -\ln(0.2) \approx 1.609

Hand check: ln⁡(0.2)≈−1.6094\ln(0.2) \approx -1.6094, so the loss is 1.60941.6094. Four and a half times the first loss from the same label. The gradient in the first case is y^−y=[0.1,−0.3,0.2]\hat{y} - y = [0.1, -0.3, 0.2]: push the true class up by 0.30.3 and push the wrong classes down. A useful reference: a uniform guess over 33 classes gives −ln⁡(1/3)≈1.099-\ln(1/3) \approx 1.099. Any loss above that means the model does worse than knowing nothing.

Code

The snippet below uses only the standard library. It recomputes both hand losses and shows the stable logit path that survives large magnitudes.

import math

def cross_entropy_from_logits(logits: list[float], target: int) -> float:    # Log-sum-exp: subtract the max first so exp() can never overflow.    m = max(logits)  # softmax is unchanged by shifting every logit    shifted = [v - m for v in logits]    log_sum = math.log(sum(math.exp(v) for v in shifted))    log_probs = [v - log_sum for v in shifted]  # log softmax, stable    return -log_probs[target]

print(round(-math.log(0.7), 4))  # -> 0.3567  the true classprint(round(-math.log(0.2), 4))  # -> 1.6094  confidently wrongprint(round(-math.log(1.0 / 3.0), 4))  # -> 1.0986  uniform over 3
logits = [800.0, 802.0, 801.0]  # naive math.exp(800) overflows hereprint(round(cross_entropy_from_logits(logits, 1), 4))  # -> 0.4076try:    naive = [-math.log(math.exp(v) / sum(math.exp(u) for u in logits)) for v in logits]    print(naive[1])except OverflowError as e:    print(type(e).__name__)  # -> OverflowError

The first three prints match the hand arithmetic: 0.3570.357, 1.6091.609, and 1.0991.099 for uniform. The last pair is the point: identical mathematics, and only the shifted version survives realistic logit magnitudes because the largest exponent becomes e0=1e^0 = 1.

Watch Out For

Computing softmax and log separately

log(softmax(x)) and log_softmax(x) are the same function on paper and not in floating point.

Softmax exponentiates. A logit of 800800 gives e800e^{800}, which overflows every float type, while a small probability underflows to exactly 00 and log⁡(0)\log(0) is negative infinity. The contaminating inf or nan then spreads through every gradient.

The fix is the log-sum-exp trick used above: subtract the maximum logit before exponentiating, which is exact rather than approximate. In practice always use the fused API on raw logits, never nll(log(softmax(x))) assembled by hand. If the final layer already applies softmax and the loss expects logits, it gets applied twice, and the symptom is a loss stuck near the uniform value.

Chasing a loss of zero

The floor on real data is H(p)H(p), the entropy of the labels, not zero. Genuine label noise or ambiguity puts a hard bound on the loss, and a training loss heading to zero means memorisation.

The mechanism is a stability problem too. Driving a probability to 11 needs the winning logit to run toward infinity, so weights grow without bound and calibration collapses: the model reports 99.999.9 percent confidence on inputs it gets wrong. Label smoothing, such as 0.90.9 on the true class with the rest spread across the others, gives a finite minimum at finite logits. It costs a little raw accuracy and buys calibration, which is usually the right trade when probabilities are used downstream.

The Quick Version

  • Cross-entropy is the average surprise of describing reality pp with prediction qq: −∑plog⁡q-\sum p \log q.
  • With one-hot targets it collapses to −log⁡q(correct class)-\log q(\text{correct class}). Wrong-class probabilities never appear directly.
  • Confidently wrong is punished enormously: 0.70.7 costs 0.3570.357 while 0.20.2 costs 1.6091.609, and zero on the truth costs infinity.
  • It decomposes as H(p)+DKL(p∥q)H(p) + D_{\mathrm{KL}}(p \parallel q), so minimising it is minimising KL divergence.
  • Its floor is H(p)H(p), not zero. A uniform guess over 3 classes gives 1.0991.099.
  • Paired with softmax, the gradient on the logits is just y^−y\hat{y} - y, which is why they are fused.
  • Minimising cross-entropy is maximum likelihood under a categorical assumption.
  • Always compute from logits with log-sum-exp. Separate softmax and log overflows on ordinary magnitudes.