NeuronCanvas
Neural Networks
Output Layers

Softmax for Multi-Class Output

Softmax turns raw scores into a probability distribution for multi-class classification, and its clean gradient with cross-entropy.

Step 01 - The break

The obvious way to turn three scores into three probabilities

Divide each score by the sum of the scores. It costs nothing and it obviously adds to 1.

scores ( 2.0, 1.0, 0.1) sum = 3.1
divide (0.645161, 0.322581, 0.032258) sum = 1.000000 looks fine
now scores a real network will absolutely produce, because logits
are unconstrained and about half of them come out negative
scores ( 2.0, -1.0, 0.1) sum = 1.1
divide (1.818182, -0.909091, 0.090909) sum = 1.000000
a 181.8% chance, and a -90.9% chance.
and one more
scores ( 1.0, -1.0, 0.0) sum = 0.0
divide by zero

All three of those sum to exactly 1, which was the only property the method was built for, and none of the last two are probabilities.

Step 02 - Before this page
Step 03 - The stage

Three sliders, three bars. Push one up and watch the other two fall without your touching them, which is the property this whole page is about.

The sliders move in steps of 0.01, so nudging one after loading a preset will round it. Reload the preset to get the exact logits back.

Preview frame from the "Softmax for Multi-Class Output" animation0:13

What this animation shows

Three bars, labeled class A, B, and C, start at heights 0.66, 0.24, and 0.10 with those values captioned above each bar, and "sums to 1.00" beneath. The bars then redistribute, with class B growing to become the dominant bar while the others shrink, and the values update and still sum to 1.00. This is softmax in action: turning a set of raw scores into a genuine probability distribution across classes.

Cat
65.9%
Dog
24.2%
Bird
9.9%

Bars always sum to 100% - raising one steals mass from the others.

loss = −log(p_cat) = 0.4170
gradient (p − y): [-0.341, 0.242, 0.099]
Correct class

The second button loads the three logits the rain network produced for dry, drizzle and downpour. The widget's own labels say cat, dog and bird, and they are not being renamed: read the first slider as dry, the second as drizzle and the third as downpour while that preset is loaded.

Step 04 - One question first

Set all three logits to the same number. Any number you like. Before you look at the bars: what are the three probabilities? Then add 1.00 to all three and look again.

Commit to a guess, then open this

Exactly 33.3% each, whatever number you chose. And after adding 1.00 to all three, still exactly 33.3% each.

That is not a rounding coincidence. Adding the same constant to every logit multiplies every exponential by the same e^c, in the numerator and in the denominator, where it cancels exactly. Only the differences between logits ever matter. The absolute level carries no information at all.

That fact looks like trivia and is load-bearing twice over. It is why one of the three output biases is redundant and could be pinned at zero forever. And it is what makes the single most important line in every real softmax implementation legal, which rung 5 gets to.

Step 05 - Plain explanation

Softmax turns unbounded scores into competing shares. It does it in two steps, and the first one is the interesting one: run every score through e^x, which makes every number positive without disturbing which is largest, then divide each result by their total, which makes them add to exactly 1. The consequence is that these outputs are not independent. Raising one score lowers every other probability automatically, because there is one hundred percent to distribute and it is always fully distributed.

Election night

Raw vote counts become percentages by dividing by the total, which is exactly the method that failed at the top of this page, and it works there for one reason: you cannot cast a negative vote. Scores out of a network can be negative, so softmax does one thing first: it runs every score through e^x, which turns any number, however negative, into a positive one without changing who is ahead of whom. Then it divides. And it has a consequence a returning officer would recognise: nobody's share can go up without somebody else's going down, because there is exactly one hundred percent to hand out.

Where it breaks downvotes are counted once and stop moving. A network's logits keep changing during training, so the shares are being redistributed at every step.

Step 06 - The depth ladder
WordsWhat we're asking for, and why the obvious idea failsThree requirements stated before any formula, then the divide-by-the-total idea taken apart.Rung 01

Sigmoid answers a yes/no question. "Cat, dog, or bird?" needs a probability distribution spread across several options at once, not an independent yes/no per option. Softmaxdoes exactly that: e to the power of one class's score, divided by the sum of e to the power of every class's score.

Before the formula, the requirements. You have three raw scores out of the last layer, one per class, and you want three probabilities. Whatever turns one into the other has to satisfy three things:

  1. Every output is positive. There is no such thing as a -12% chance.
  2. The outputs add up to exactly 1. If exactly one of the three classes is true, the confidence has to be fully distributed among them with nothing left over.
  3. A bigger score gives a bigger probability. If the network scored "cat" above "dog", the cat probability must come out above the dog probability. The ordering must survive.

Why not just divide by the total?

The block at the top of this page, taken apart. Divide each score by the sum of all the scores. It costs nothing and it obviously sums to 1. On (2.0, 1.0, 0.1) it gives (0.645161, 0.322581, 0.032258), which looks fine. On (2.0, -1.0, 0.1) it gives (1.818182, -0.909091, 0.090909): still sums to 1, and complete nonsense, a probability of 181.8% and a probability of -90.9%. Requirement 1 is broken. And on (1.0, -1.0, 0.0) the sum is exactly 0 and the method divides by zero.

Both failures come from the same source: raw scores can be negative, and the fix has to be something that turns any number, however negative, into a positive one, without breaking the ordering. e^x does exactly that. It is positive for every input without exception (e^-1000 is a very small positive number, never zero and never negative), and it is strictly increasing, so a bigger score always produces a bigger e^score. Requirements 1 and 3 are satisfied by the exponential itself; requirement 2 is satisfied by dividing by the sum afterwards.

Watch it fix the broken case:

scores (2.0, -1.0, 0.1)
exp (7.389056, 0.367879, 1.105171), sum = 8.862106
divide (0.833781, 0.041512, 0.124707), sum = 1.000000

Three genuine probabilities, ordering preserved, no division by zero possible because a sum of positive numbers is always positive.

There is a cost, and it is worth naming. Exponentials grow fast, so softmax exaggerates differences. A logit gap of 1.0 becomes a probability ratio of e^1 = 2.718, so "cat scored one point higher than dog" becomes "cat is 2.7 times more likely than dog". That amplification is deliberate and it is where the "soft max" name comes from: the function is a smoothed version of "pick the largest and give it everything".

NumbersFrom two inputs to three probabilities, with no gapsThe whole chain, input to loss to gradient, plus the shift-invariance check.Rung 02

Worked example - logits z=[2.0, 1.0, 0.1] for cat/dog/bird: e²=7.389056, e¹=2.718282, e^0.1=1.105171, summing to 11.212509. Dividing each by that sum gives p=(0.659001, 0.242433, 0.098566), which sums to 1.000000, a genuine probability distribution.

Where the three logits come from

This page has been handing you three numbers. Here is a network producing them, so the chain from input to probability is unbroken. Same small rain network as the rest of this module: two inputs, two tanh hidden neurons, now with three output neurons instead of one, because there are three classes.

WORKED EXAMPLE - input to probabilities, no gaps
INPUT
x1 = 0.800000 how grey the sky looks
x2 = 0.600000 forecast chance of rain
HIDDEN LAYER (tanh)
z1 = 1.50 * 0.800000 + 2.00 * 0.600000 - 1.00 = 1.400000
z2 = -1.00 * 0.800000 + 0.50 * 0.600000 - 0.20 = -0.700000
a1 = tanh( 1.400000) = 0.885352
a2 = tanh(-0.700000) = -0.604368
OUTPUT LAYER, one neuron per class, no activation yet
dry: -1.50 * 0.885352 + 1.00 * (-0.604368) + 0.20 = -1.732395
drizzle: 0.50 * 0.885352 + 0.50 * (-0.604368) + 0.00 = 0.140492
downpour: 2.00 * 0.885352 - 1.00 * (-0.604368) - 0.50 = 1.875071
those three numbers are the logits.
SOFTMAX, step 1: exponentiate
e^-1.732395 = 0.176860
e^ 0.140492 = 1.150840
e^ 1.875071 = 6.521283
sum = 7.848983
SOFTMAX, step 2: divide by the sum
p_dry = 0.176860 / 7.848983 = 0.022533
p_drizzle = 1.150840 / 7.848983 = 0.146623
p_downpour = 6.521283 / 7.848983 = 0.830844
--------
1.000000
LOSS, if it really did pour
the label is one-hot: y = (0, 0, 1)
L = -log(p_downpour) = -log(0.830844) = 0.185313
GRADIENT at the logits
dL/dz = p - y = (0.022533 - 0, 0.146623 - 0, 0.830844 - 1)
= (0.022533, 0.146623, -0.169156)

Read the gradient signs. The two wrong classes get positivegradients, meaning "push these logits down". The correct class gets a negativegradient, meaning "push this logit up". And the sizes are exactly how wrong each one was. dry was already at 2.3%, so its correction is tiny at 0.022533. drizzle at 14.7% gets a firmer shove at 0.146623. Softmax with cross-entropy gives you a correction proportional to the error and nothing else, which is the whole reason this pairing is standard.

One arithmetic check on that gradient: the three components sum to 0.022533 + 0.146623 - 0.169156, which is 0.000000. That is not a coincidence and it happens every time. The probabilities sum to 1, the one-hot label sums to 1, so p - y always sums to 0. Softmax cannot raise one logit without lowering the others; the gradient enforces the same coupling the forward pass has. (The printed six-decimal values happen to cancel perfectly here. They will not always, and if you run this check on other numbers and land on 0.000001 rather than 0.000000 that is the rounding, not a broken gradient. The reason just given is the proof; the addition is only a spot check.)

Shifting every logit changes nothing

Add the same number to all the logits and the probabilities do not move. Not approximately, exactly.

WORKED EXAMPLE - shift invariance
original z = ( 2.0, 1.0, 0.1)
exp (7.389056, 2.718282, 1.105171), sum = 11.212509
probs (0.659001, 0.242433, 0.098566)
subtract 2.0 from every logit
shifted z = ( 0.0, -1.0, -1.9)
exp (1.000000, 0.367879, 0.149569), sum = 1.517448
probs (0.659001, 0.242433, 0.098566) identical

The reason is one line of algebra. Multiplying every exponential by the same constant e^-c multiplies the numerator and the denominator by e^-c, and it cancels. Only the differences between logits ever matter. The absolute level is meaningless, which is another way of saying one of the three output biases is redundant: you could pin it at zero forever and lose nothing at all.

PictureWhat the exponential actually does to three numbersThe failing case from the top of the page, drawn before and after e^x.Rung 03

The bars on the stage figure are the probabilities and they always fill the track exactly. That is not a drawing convention, it is the constraint: raising one bar takes width from the others, because there is one track and it is full.

Drag the third slider up on its own and watch the first two shrink without your touching them. Nothing about the first two classes changed. Their scores are identical. Their shares fell because somebody else's share rose.

Requirements 1 and 3 are satisfied by the exponential itself. Requirement 2 is satisfied by dividing afterward, which is the easy part.

The top panel is the three raw scores from the break at the top of this page, one of which hangs below the axis. Dividing by the total at that point produces a negative share, and if the three happened to sum to zero it produces nothing at all. The bottom panel is the same three numbers after e^x: every bar is now above the axis, and the ranking strip under each panel is identical, first, third, second, before and after. That is requirement 1 fixed with requirement 3 left untouched, which is exactly what the exponential was chosen for.

EquationThe formula and its gradientThe formula, an honest note about why it is not derived line by line, and a finite-nudge check of the result.Rung 04
softmax(z)_i = e^(z_i) / sum_j e^(z_j)
dL/dz_i = p_i - y_i

This next result is one of the least obvious "nice" identities in this subject. Unlike the sigmoid case on the output-activations page, walking through every step here requires tracking how changing one logit shifts every output probability at once, since softmax ties all the outputs together. We're going to state the result and confirm it numerically, rather than derive it line by line.

Here's a genuine independent check, not a restatement of the formula: nudge just the "cat" logit by 0.001 (z=[2.001, 1.0, 0.1]), recompute softmax and the cross-entropy loss for the true class cat, and measure the change. The measured slope comes out to ≈−0.3409; the predictedgradient, p_cat−1 = 0.659−1 = −0.3410, matches to three decimal places, the same small gap you'd expect from the earlier finite-nudge check, not a coincidence. The general rule, holding for every class at once, is ∂L/∂zᵢ = pᵢ−yᵢ - for this example, the full gradient is [−0.341, 0.242, 0.099].

General caseSoftmax as it is actually implemented and usedThe max-subtraction trick, softmax over two classes, temperature, and when you can skip softmax entirely.Rung 05

Why every real implementation subtracts the maximum

Shift invariance is not a curiosity. It is what makes softmax computable.

e^x overflows fast. In standard 32-bit floating point the largest representable number is about 3.4 * 10^38, and e^88 is already 1.65 * 10^38, so e^89 overflows to infinity. Logits in the tens are routine, and it does not take much beyond that to cross the line, so the naive formula produces inf / inf, which a computer reports as NaN(short for "Not a Number", the value it returns when it has been asked for something undefined). NaN then propagates through the rest of the model, poisoning every number it touches, and destroys the run.

WORKED EXAMPLE - the stability trick
logits z = (1000, 999, 998)
naive
e^1000 overflows to infinity
e^999 overflows to infinity
e^998 overflows to infinity
infinity / infinity = NaN the whole computation is destroyed
subtract the largest logit, 1000, from all three
shifted z = (0, -1, -2)
e^ 0 = 1.000000
e^-1 = 0.367879
e^-2 = 0.135335
sum = 1.503214
p = (0.665241, 0.244728, 0.090031) correct, and nothing overflowed

Subtracting the maximum makes the largest shifted logit exactly 0, so the largest exponential is exactly e^0 = 1 and nothing can ever overflow. The smallest ones may underflow to 0, which is harmless: they were negligible anyway, and the sum still contains that guaranteed 1, so the denominator can never be zero.

Confirm on the page's own numbers that the trick changes nothing:

our logits (-1.732395, 0.140492, 1.875071)
minus the max (-3.607466, -1.734579, 0.000000)
exp ( 0.027120, 0.176474, 1.000000), sum = 1.203595
probs ( 0.022533, 0.146623, 0.830844) identical to before

This is also the concrete reason the previous page's gotcha exists. When PyTorch's CrossEntropyLoss takes raw logits rather than probabilities, it can do this subtraction and combine the log with the exp internally, cancelling them algebraically instead of computing both. Hand it probabilities and you have already thrown away the numbers it needed.

Softmax over two classes is sigmoid

The last two pages have presented sigmoid and softmax as two different tools. They are the same tool.

WORKED EXAMPLE - two-class softmax
binary problem, one logit z = 1.845916
sigmoid(1.845916) = 0.863647
same problem as two classes, logits (1.845916, 0), meaning "rain" and "no rain"
e^1.845916 = 6.333899
e^0 = 1.000000
sum = 7.333899
p = (0.863647, 0.136353)
identical, to every printed digit.

Since only logit differences matter, a two-class softmax has one meaningful number in it, the gap between the two logits, and softmax of (z, 0) reduces algebraically to 1 / (1 + e^-z), which is sigmoid. Verify it on any pair with the same gap:

softmax(2.0, 1.0) = (0.731059, 0.268941)
softmax(5.0, 4.0) = (0.731059, 0.268941)
softmax(-3.0, -4.0) = (0.731059, 0.268941)
sigmoid(1.0) = 0.731059

So binary classification with a sigmoid is not a special case that happens to work. It is softmax with the redundant second logit removed, and using one output instead of two saves a parameter that could never have meant anything.

Temperature

One knob, worth knowing because you have already used it. Divide every logit by a number T before applying softmax.

WORKED EXAMPLE - temperature on the same logits (2.0, 1.0, 0.1)
T = 0.5 p = (0.863777, 0.116900, 0.019323) sharper, more decisive
T = 1.0 p = (0.659001, 0.242433, 0.098566) the ordinary softmax
T = 2.0 p = (0.501688, 0.304289, 0.194023) flatter, more hedged
T = 10.0 p = (0.366059, 0.331224, 0.302716) nearly uniform

Small T divides the logits by a small number, which spreads them further apart, which the exponential amplifies further still: the top class approaches 100% and the rest approach 0. Large T squeezes the logits together and the distribution flattens toward equal probabilities for everything. In the limits, Tapproaching 0 gives a hard "always pick the winner" and T approaching infinity gives a coin flip among all classes.

The ordering never changes at any temperature, because dividing by a positive number preserves order. Only the confidence changes. This is the same "temperature" setting on every text-generation interface you have used: low temperature makes a model pick its favourite word almost every time, high temperature lets less likely words through.

At inference, you often don't need softmax at all

During training you need the probabilities, because cross-entropy is defined on them. At prediction time, if all you want is "which class", you can skip softmax entirely.

The predicted class is argmax, meaning "the position of the largest number". And since e^x is strictly increasing and dividing by the same positive sum preserves order, the largest logit is always the largest probability. In our example the largest logit is downpour at 1.875071 and the largest probability is downpour at 0.830844. This holds always, not usually.

logits (-1.732395, 0.140492, 1.875071) -> argmax = index 2, downpour
probs ( 0.022533, 0.146623, 0.830844) -> argmax = index 2, downpour

So a deployed classifier that only reports a label can read the answer straight off the logits. You need softmax when you need to report a confidence, when you need to thresholdon one ("only act if above 90%"), or when you need to compare probabilities across examples. If you only need the label, the extra step buys nothing.

Step 07 - Why this and not that

Why this and not that

Why exponentiate rather than just divide by the total?

Because logits can be negative, and dividing by the total then produces negative "probabilities" and can divide by zero. e^x is the cheapest function that is positive for every input and strictly increasing, so it fixes the sign without disturbing the ranking. Step 1 and rung 1 show both failures.

Why not subtract the minimum first, then divide?

It does fix the negatives, and it breaks two other things. It changes the ratios between scores in a way that depends on the smallest score, so an irrelevant fourth class with a very low score would silently change the relative probabilities of the other three. And when all the scores are equal it makes them all zero and divides by zero anyway.

Why subtract the maximum rather than the mean?

Because the maximum is the only choice that guarantees the largest shifted logit is exactly 0, so the largest exponential is exactly 1 and overflow is impossible. Subtracting the mean shrinks the numbers, usually enough, and offers no guarantee.

Why is one of the biases redundant?

Because only differences matter. Adding a constant to all three logits changes nothing, so one of the three output biases can be pinned at zero forever with no loss of expressiveness at all. This is not a trick worth doing in practice; it is a fact worth knowing when a parameter count does not add up.

Why not use softmax for a multi-label problem?

Because the sum-to-one constraint is a claim that exactly one option is true. On a photo that is outdoor and also contains people, that claim is false, and softmax will make the two labels compete for confidence they should each hold independently. The previous page has the arithmetic.

If argmax is all I need, why compute softmax at all?

Only when you need a confidence to report, a threshold to compare against, or probabilities comparable across examples. If you need a label, the largest logit already is the answer, always, not usually.

Name origins
softmax
"soft" means a smoothed version of a hard operation, the same prefix as softplus on the modern-activations page. "Max" is the hard operation being smoothed. And the name is arguably wrong: what it smooths is not max, which returns the largest value, but argmax, which returns which one was largest, and softmax's output is a smoothed indicator of which. Some papers call it softargmax for this reason. The wrong name won.
temperature
borrowed directly from statistical physics, where the Boltzmann distribution gives the probability of a state with energy E as proportional to e^(-E / kT), and T there is literally a temperature. Softmax with a temperature is the same expression with logits in place of negative energies. High temperature means a system flitting between states; low temperature means it settles into the lowest one. The metaphor is exact, not decorative.
one-hot
from digital electronics, where a one-hot bus is one on which exactly one line is energised at a time.
argmax
"the argument at which the maximum occurs", meaning the input position rather than the output value.
logit
defined on the previous page and unchanged: the log-odds, the inverse of sigmoid, the number before the squash.
NaN
"Not a Number", the value floating-point arithmetic returns when asked for something undefined, such as infinity divided by infinity. Its defining property is that it contaminates: anything computed from a NaN is a NaN.
categorical
in categorical cross-entropy: because the label is one of k categories rather than a yes or no.
Step 08 - Where people go wrong
Step 09 - Practice
  1. 01
    Make all three classes exactly equally likely, then find a second, completely different way to do it.
    Hint
    The Socratic question above is the whole answer.
    Answer

    Any three equal logits: (0, 0, 0) gives 33.3% each, and so does (5, 5, 5), and so does (-3, -3, -3). The absolute level never mattered.

  2. 02
    Make cat exactly twice as likely as dog.
    Hint
    The ratio of two probabilities is e^(difference of their logits), so you want a difference whose exponential is 2.
    Answer

    A gap of ln(2) = 0.693147, for example cat at 2.00 and dog at 1.31, which gives a ratio of e^0.69 = 1.993716, close enough to see on the bars. Bird's logit does not matter at all: it changes both probabilities by the same factor, so it cannot change their ratio.

  3. 03
    Find two logit triples that are visibly different on the sliders and produce identical bars.
    Hint
    Shift, don't tilt.
    Answer

    Any pair differing by a constant:

    z = ( 2.0, 1.0, 0.1) -> p = (0.659001, 0.242433, 0.098566)
    z = ( 0.0, -1.0, -1.9) -> p = (0.659001, 0.242433, 0.098566)

    This is shift invariance, and it is what makes the max-subtraction trick in rung 5 legal rather than merely convenient.

  4. 04
    With cat as the correct class and dog and bird left at 1.0 and 0.1, find the smallest cat logit that keeps the loss under 0.50.
    Hint
    A loss below 0.50 means p_cat above e^-0.5.
    Answer

    About 1.78. e^-0.5 = 0.606531, so you need p_cat above that, and the crossing is at a cat logit of 1.7740, giving a loss of exactly 0.50. At 1.78 the probability is 0.607984 and the loss is 0.497607.

  5. 05
    Hardest. Show that raising the correct class's logit by 1.0 and lowering one wrong class's logit by 1.0 are not the same operation, even though both increase the gap by 1.
    Hint
    There are three logits, and only two of them are involved in either move.
    Answer
    start z = (2.0, 1.0, 0.1)
    raise cat z = (3.0, 1.0, 0.1) -> p = (0.840083, 0.113693, 0.046224)
    lower dog z = (2.0, 0.0, 0.1) -> p = (0.778268, 0.105327, 0.116405)

    Different, and notice bird: it went down in the first case and up in the second, without being touched in either. Only the full pattern of differences decides anything, and a move that changes one gap always changes the others too. This is the coupling from mistake 4, stated as an exercise.

Step 10 - Seen in the wild
  • The temperature sliderThe one in every chat interface you have used is the T on this page, dividing the model's logits before softmax. Turn it down and the model picks its top token nearly every time; turn it up and less likely tokens get through.
  • Your photo app's taggerThe other place you meet softmax daily is the last layer of every object and face tagger, where the 97% printed under the label is one number out of a set that was forced to add up to a hundred.
Step 11 - Memory anchor

Softmax is one cake, cut by exponentials.

Nobody gets a negative slice, the slices always finish the cake, and a one-point lead in score is a 2.7-times bigger piece. Only the gaps between the scores decide anything: add a hundred to everybody and not one crumb moves.

Step 12 - The next break

Everything in this module has been one network, hand-set, on one example, with you doing the arithmetic. Nothing on any of these pages has learned anything: every weight you have seen was chosen by a person so the numbers would come out readable. The playground is where those nine numbers stop being yours.

And the honest limit of the whole module. A fully connected network of the kind you have been reading treats a 28-by-28 image as 784 unrelated numbers, with no idea at all that two neighbouring pixels have anything to do with each other. Everything you have learned still holds exactly. It is simply not enough for a picture, and the fix has its own module.

Playground