Skip to content

← JAX Numerical Computing step 23 of 25

Medium Primitives

Stable log_softmax

Jump to the task

Log-softmax returns the natural logarithms of softmax probabilities. This is useful when a loss needs log probabilities rather than probabilities:

import jax
import jax.numpy as jnp

x = jnp.array([1.0, 2.0, 3.0])
log_probs = jax.nn.log_softmax(x)
print(jnp.round(log_probs, 4))  # approximately [-2.4076, -1.4076, -0.4076]
print(jnp.isclose(jnp.exp(log_probs).sum(), 1.0))  # True

The log probabilities do not themselves sum to zero. Their exponentials sum to approximately one. For four equal scores, each result is -log(4), about -1.386294.

Why log after softmax can fail

A small probability can underflow to zero before you take its logarithm. Then log returns negative infinity, even when the correct log probability is finite:

x = jnp.array([-1000.0, 0.0], dtype=jnp.float32)
print(jnp.log(jax.nn.softmax(x)))  # [-inf, 0.0]
print(jax.nn.log_softmax(x))       # [-1000.0, 0.0]

This is a numerical difference, not just a speed preference. Runtime depends on the backend and compilation, so the lesson makes no fixed speedup claim.

Keep the calculation in shifted coordinates

Mathematically, log_softmax(x) equals x - logsumexp(x). But computing those two large quantities separately can lose small differences through rounding. For a manual implementation, first let shifted = x - max(x), then compute shifted - log(sum(exp(shifted))). That avoids both the probability underflow above and subtracting a large rounded normalization constant.

Your task

Implement stable_log_softmax(x) using jax.nn.log_softmax. The input is a nonempty one-dimensional array of finite float32 logits. Return an array of the same shape containing natural log probabilities. This exercise allows and expects the dedicated JAX operation.

The visualization below displays related softmax probabilities. Your function returns their logarithms and has no temperature argument.

Loading visualization…