Implement Softmax + Cross-Entropy Stably
Building a numerically stable Softmax and Categorical Cross Entropy loss module using log sum exp stabilization.
The Numerical Overflow Problem
The Softmax Function converts a vector of real-valued logits into a probability distribution :
If a neural network outputs a raw logit :
Conversely, if , computing Categorical Cross-Entropy Loss produces !
Math Foundations for Numerical Stability
1. Logit Max Shift (Softmax Trick)
Notice that shifting all logits by an arbitrary constant leaves the Softmax probabilities completely unchanged:
Set . This guarantees that the largest logit becomes , so , preventing floating point overflow completely!
2. Combined Softmax + Cross-Entropy Gradient
Combining Softmax with Categorical Cross-Entropy Loss simplifies the loss derivative with respect to logits :
Where is the one-hot target label vector.
NumPy Implementation from Scratch
import numpy as np
class StableSoftmaxCrossEntropy:
def softmax(self, z):
# z shape: [batch_size, num_classes]
# Subtract max logit per row for numerical stability
z_max = np.max(z, axis=-1, keepdims=True)
exp_z = np.exp(z - z_max)
return exp_z / np.sum(exp_z, axis=-1, keepdims=True)
def loss(self, z, y_true_indices):
# z shape: [batch_size, num_classes]
# y_true_indices shape: [batch_size] (integer class labels)
batch_size = z.shape[0]
# Compute stable Softmax probabilities
probs = self.softmax(z)
# Pick probability of true class for each sample
correct_class_probs = probs[np.arange(batch_size), y_true_indices]
# Clip probabilities to prevent log(0)
correct_class_probs_clipped = np.clip(correct_class_probs, 1e-15, 1.0)
# Compute Cross Entropy Loss
loss = -np.mean(np.log(correct_class_probs_clipped))
return loss, probs
def gradient(self, probs, y_true_indices):
# dz = Softmax(z) - OneHot(y)
batch_size = probs.shape[0]
dz = probs.copy()
dz[np.arange(batch_size), y_true_indices] -= 1.0
return dz / batch_size
Say this out loud
Numerically stable Softmax subtracts the maximum logit value per sample before exponentiation, shifting the highest exponent to zero and preventing floating point overflow. Combining Softmax with Cross Entropy loss yields a clean loss gradient derivative dz = softmax(z) - y.
Followups to expect
- What is LogSumExp trick? Calculating as where , preventing numerical underflow and overflow when computing log probabilities.
- What is Label Smoothing? Replacing hard one-hot target vectors with smoothed targets to prevent Softmax logits from growing infinitely large during training.
Check yourself
What numeric stabilization trick prevents floating point overflow when computing Softmax over large input logits?