Turning scores into probabilities
A model's last layer produces one unbounded real number per class, and not one of them is a probability.
- : the logit for class , any real number at all.
- : how many classes there are.
- : the probability this class ends up with.
Exponentiating makes every value positive, and dividing by the total makes them sum to one.
That is the entire definition: positive, then normalised.
Why exponentials rather than just dividing
Dividing raw scores by their sum breaks the moment one score is negative, because a negative probability is not a probability.
The exponential is also monotonic and smooth, so the largest logit always takes the largest share and the gradient exists everywhere.
It has one more consequence, and it is the useful one.
Only differences between logits matter. Adding the same constant to all of them changes nothing at all.
The shift that stops it overflowing
That last property is not a curiosity, it is what makes softmax computable at all.
Subtracting the largest logit leaves the answer identical and forces the biggest exponent to be exactly zero.
Without it a logit of 1000 overflows to infinity in float32, the denominator becomes infinity, and the whole row comes back as NaN.
Every library does this inside its own implementation, which is why you rarely see it written and why it bites the first time you write softmax yourself.
One pass, in order
- Take the vector of logits for one position.
- Find its maximum and subtract that from every element.
- Exponentiate each element.
- Add them up.
- Divide each exponential by that sum.
The derivative, which is why it pairs with cross-entropy
- : one when and are the same index, zero otherwise.
Every output depends on every logit, so this is a full matrix and not a single number.
Fed straight into cross-entropy it collapses to something far simpler, and that collapse is why the two are almost always implemented as one operation.