Softmax: From Naive to Blocked Softmax
What is Softmax? The softmax function is a function that converts a vector of length into a probability distribution of length .
For a given an input vector
the softmax value for the -th element is defined as
where
- = softmax function
- = input vector; size of (1, K)
- = length of the vector row.
- = score of the -th class
- = probability of the -th element softmax
- = exponent of -th element
- = denominator that adds the entire row. This makes the sum of all to 1.
Computationally this happens in two passes:
- first pass: compute the denominator (need to sum all elements)
- second pass: divide each by the denominator
Table of contents
Safe Softmax#
While softmax can convert vector into probability, the normal softmax formula can be numerically unstable on computers.
To be more concise, some s in can be large (e.g. 1,000) which becomes . In computers, this is about
which is too large () to be represented in floating point operation. This it is important to cap the upper bound in order to safely calculate softmax.
Safe Softmax is a simple idea to subtract the maximum logit before applying exponential.
If we compare this with vanilla softmax above, we just subtracted the element that has the maximum value in vector . A minimal example would be to convert
[e^0, e^1, e^1000, e^5] -> [e^-1000, e^-999, e^0, e^-995]This does not change the final softmax result because subtracting the same constant from every logit does not change their relative differences. It only makes the computation numerically safe.
However unlike naive softmax, this requires an additional pass:
- first pass: find the maximum value
- second pass: compute the denominator (need to sum all elements)
- third pass: finally divide each by the denominator
Proof
Using the rule
we get
Since does not depend on , we can factor it out from the denominator:
Now appears in both the numerator and denominator, so it cancels out:
Therefore, we can guarantee subtracting does not change the softmax result.
Online Softmax#
Introduced in NVIDIA's paper Online Normalizer Calculation for Softmax, online softmax is a way that computes the same softmax result, but in a more efficient way.
Safe softmax required us to read and go through the same vector three times. Can we read fewer than this?
Online softmax improves this by computing the maximum value and the denominator target together (step 1 and 2 above) in one pass, which leads to two passes in total:
- first pass: compute the denominator and iteratively rescale the past sum based on the new maximum value (explained below)
- second pass: divide each by the denominator
In order to do this, we maintain two running values during the first pass:
where
- is the running maximum up to position
- is the running denominator up to position
What if a new maximum appears at -th index?
The previous denominator is rescaled by
Rescaling
We can represent the previous running denominator as
and by using the exponential rule,
we get
The goal we wanted to do in safe softmax was for all denominators to be
so we need to rescale the old denominator to match the new maximum value and then add the current element's exponent.
This can be written as
After scanning the whole vector, we get the global maximum value and global sum (by rescaling) in one pass!
and
The second pass works the same as third pass of safe softmax (divide each by the denominator):
Blocked Softmax#
Blocked softmax is the block-level version of online softmax. Instead of updating the running maximum and denominator one element at a time, we divide the vector into blocks and compute a local maximum and local denominator for each block.
Suppose the input vector is divided into blocks:
For each block , we compute the local maximum:
The local denominator normalized by that local maximum:
So each block produces a pair:
where
- = the largest value inside the block
- = the local exponential sum after subtracting that local maximum
Now we need to merge these block-level results into one global result. Let be the running global maximum and denominator after processing blocks through .
The running maximum is updated as:
The running denominator must be rescaled (same logic as online softmax) whenever the maximum changes:
After all blocks are merged, we get:
and
Then the final softmax value is computed as:
The full code from naive softmax to blocked softmax can be found here(~100 lines of easy python code). It will be very easy to read after you've read this section.