Posts
A note on log-sum-exp
Subtracting the maximum turns an overflow into an exact identity. The same shift makes softmax safe.
Log-likelihoods, mixture models, and softmax layers all need the same quantity,
and the direct formula fails at the first large argument. In double precision, overflows to infinity once exceeds about 709.78, and underflows to zero once falls below about . Log-likelihoods of a few thousand observations easily reach either range.
The fix is one line of algebra. For any constant ,
and choosing makes the largest exponent exactly zero. Every term is then at most one, so nothing overflows, and the largest term is exactly one, so the sum is at least one and its logarithm is finite.
import math
def logsumexp(xs):
m = max(xs)
if m == -math.inf:
return -math.inf
return m + math.log(sum(math.exp(x - m) for x in xs))For , the direct formula returns inf, while the shifted one returns 1002.4076059644444. For , every exponential underflows to zero and the direct formula returns -inf; the shifted one returns -999.5923940355556. Both answers differ by exactly 2002, as the symmetry of the inputs requires.
The guard for matters when every term has zero probability: without it, is , which is not a number.
Libraries already do this: SciPy provides scipy.special.logsumexp, and deep-learning frameworks implement softmax and cross-entropy with the shift built in. The reason to know the trick is the code they do not cover, such as a hand-written likelihood, a log-space dynamic program, or a reduction over a custom axis.