What is Softmax? The softmax function is a function that converts a vector of length KK into a probability distribution of length KK.

For a given an input vector

z=[z1,z2,…,zK]\mathbf{z} = [z_1, z_2, \dots, z_K]

the softmax value for the ii-th element is defined as

σ(z)i=ezi∑j=1Kezj\sigma(\mathbf{z})_i = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}}

where

  • σ\sigma = softmax function
  • z\mathbf{z} = input vector; size of (1, K)
  • KK = length of the vector row.
  • ziz_i = score of the ii-th class
  • σ(z)i\sigma(\mathbf{z})_i = probability of the ii-th element softmax
  • ezie^{z_i} = exponent of ii-th element ziz_i
  • dK=∑j=1Kd_K = \sum_{j=1}^{K} = denominator that adds the entire row. This makes the sum of all σ(z)i\sigma(z)_i to 1.

Computationally this happens in two passes:

  1. first pass: compute the denominator dK=∑jKezjd_K = \sum_j^K e^{z_j} (need to sum all elements)
  2. second pass: divide each ezie^{z_i} by the denominator dKd_K

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 ziz_is in zz can be large (e.g. 1,000) which becomes e1000e^{1000}. In computers, this is about

e1000≈10434.294≈1.97×10434e^{1000} \approx 10^{434.294} \approx 1.97 \times 10^{434}

which is too large (≈∞\approx \infty) 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.

σ(z)i=ezi−max⁡(z)∑j=1Kezj−max⁡(z)\sigma(\mathbf{z})_i = \frac{e^{z_i - \max(\mathbf{z})}}{\sum_{j=1}^{K} e^{z_j - \max(\mathbf{z})}}

If we compare this with vanilla softmax above, we just subtracted the element that has the maximum value in vector zz. 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:

  1. first pass: find the maximum value mm
  2. second pass: compute the denominator ∑jKezj−m\sum_j^K e^{z_j - m} (need to sum all elements)
  3. third pass: finally divide each ezi−me^{z_i - m} by the denominator

Proof

Using the rule

ea−b=eae−be^{a-b} = e^a e^{-b}

we get

σ(z)i=ezie−m∑j=1Kezje−m\sigma(\mathbf{z})_i = \frac{e^{z_i} e^{-m}}{\sum_{j=1}^{K} e^{z_j} e^{-m}}

Since e−me^{-m} does not depend on jj, we can factor it out from the denominator:

σ(z)i=ezie−me−m∑j=1Kezj\sigma(\mathbf{z})_i = \frac{e^{z_i} e^{-m}}{e^{-m} \sum_{j=1}^{K} e^{z_j}}

Now e−me^{-m} appears in both the numerator and denominator, so it cancels out:

σ(z)i=ezie−me−m∑j=1Kezj\sigma(\mathbf{z})_i = \frac{e^{z_i} \cancel{e^{-m}}}{\cancel{e^{-m}} \sum_{j=1}^{K} e^{z_j}}
σ(z)i=ezi∑j=1Kezj\sigma(\mathbf{z})_i = \frac{e^{z_i}}{\sum_{j=1}^{K} e^{z_j}}

Therefore, we can guarantee subtracting max⁡(z)\max(\mathbf{z}) 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:

  1. first pass: compute the denominator di=∑jKezj−mid_i = \sum_j^K e^{z_j - m_i} and iteratively rescale the past sum based on the new maximum value (explained below)
  2. second pass: divide each ezi−mKe^{z_i - m_K} by the denominator

In order to do this, we maintain two running values during the first pass:

mi=max⁡(mi−1,zi)m_i = \max(m_{i-1}, z_i)
di=di−1emi−1−mi+ezi−mid_i = d_{i-1} e^{m_{i-1} - m_i} + e^{z_i - m_i}

where

  • mim_i is the running maximum up to position ii
  • did_i is the running denominator up to position ii

What if a new maximum appears at ii-th index?

The previous denominator di−1d_{i-1} is rescaled by emi−1−mie^{m_{i-1} - m_i}

Rescaling

We can represent the previous running denominator as

di−1=∑j=1i−1ezj−mi−1d_{i-1} = \sum_{j=1}^{i-1} e^{z_j - m_{i-1}}

and by using the exponential rule,

ezj−mi−1=ezje−mi−1e^{z_j - m_{i-1}} = e^{z_j} e^{-m_{i-1}}

we get

di−1=e−mi−1∑j=1i−1ezjd_{i-1} = e^{-m_{i-1}} \sum_{j=1}^{i-1} e^{z_j}

The goal we wanted to do in safe softmax was for all denominators to be

di=∑j=1i−1ezje−mid_{i} = \sum_{j=1}^{i-1} e^{z_j} e^{-m_{i}}

so we need to rescale the old denominator di−1d_{i-1} to match the new maximum value mim_i and then add the current element's exponent.

This can be written as

di=di−1emi−1−mi+ezi−mid_i = d_{i-1} e^{m_{i-1} - m_i} + e^{z_i - m_i}

After scanning the whole vector, we get the global maximum value and global sum (by rescaling) in one pass!

mK=max⁡(z)m_K = \max(\mathbf{z})

and

dK=∑j=1Kezj−mKd_K = \sum_{j=1}^{K} e^{z_j - m_K}

The second pass works the same as third pass of safe softmax (divide each ezi−mKe^{z_i - m_K} by the denominator):

σ(z)i=ezi−mKdK\sigma(\mathbf{z})_i = \frac{e^{z_i - m_K}}{d_K}

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 z\mathbf{z} is divided into BB blocks:

z=[block1,block2,…,blockB]\mathbf{z} = [\text{block}_1, \text{block}_2, \dots, \text{block}_B]

For each block bb, we compute the local maximum:

mb=max⁡j∈blockbzjm_b = \max_{j \in \text{block}_b} z_j

The local denominator normalized by that local maximum:

db=∑j=0block size−1ezj−mbd_b = \sum_{j = 0}^{\text{block size} - 1} e^{z_j - m_b}

So each block produces a pair:

(mb,db)(m_b, d_b)

where

  • mbm_b = the largest value inside the block
  • dbd_b = the local exponential sum after subtracting that local maximum

Now we need to merge these block-level results into one global result. Let (Mb,Db)(M_b, D_b) be the running global maximum and denominator after processing blocks 11 through bb.

The running maximum is updated as:

Mb=max⁡(Mb−1,mb)M_b = \max(M_{b-1}, m_b)

The running denominator must be rescaled (same logic as online softmax) whenever the maximum changes:

Db−1eMb−1−Mb+dbemb−MbD_{b-1} e^{M_{b-1} - M_b} + d_b e^{m_b - M_b}

After all blocks are merged, we get:

MB=max⁡(z)M_B = \max(\mathbf{z})

and

DB=∑j=1Kezj−MBD_B = \sum_{j=1}^{K} e^{z_j - M_B}

Then the final softmax value is computed as:

ezi−MBDB\frac{e^{z_i - M_B}}{D_B}

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.