←Back to Science Coding Hub/Part A/A1
A1Easy★ Core Essential · Industrial BedrockPart A · Core Kernels & Activation Functions

Numerically Stable Softmax

Industrial-grade implementation subtracting max to prevent exponent overflow while preserving dimensional broadcasting.

⏱️ Time Complexity: O(N * C)
💾 Space Complexity: O(N * C)
💡

Core Mental Anchor / Mnemonic

Subtract max to guard against overflow; keepdims ensures broadcasting; denominator has 1 to avoid zero-division

📐 Mathematical Derivation & Core Formula

softmax(x)i=exi−max⁡jxj∑kexk−max⁡jxj\mathrm{softmax}(x)_i = \frac{e^{x_i - \max_j x_j}}{\sum_{k} e^{x_k - \max_j x_j}}
### Mathematical Principles & Derivation
Softmax maps real-valued logits x∈RCx \in \mathbb{R}^C to a valid probability distribution. Directly evaluating exie^{x_i} triggers floating-point overflow for xi>709x_i > 709 (FP64) or xi>88x_i > 88 (FP32), yielding NaN or inf.

Using the shift-invariance identity:
exi∑kexk=exi−c∑kexk−c\frac{e^{x_i}}{\sum_k e^{x_k}} = \frac{e^{x_i - c}}{\sum_k e^{x_k - c}}
Setting c=max⁡jxjc = \max_j x_j guarantees that xi−c≤0x_i - c \le 0, bounding all exponents into (0,1](0, 1]. The denominator is always ≥e0=1\ge e^0 = 1, eliminating division by zero.

🔄 Tensor Dimensions & Shape Flow

(B, S, C) -> np.max(keepdims=True) -> (B, S, 1) -> broadcast subtraction -> (B, S, C) -> exp -> (B, S, C) -> sum -> (B, S, 1) -> broadcast division -> (B, S, C)

🛡️ Industrial Numerical Stability & Pitfalls

  • Must subtract max(x, keepdims=True) prior to exp; never call np.exp(x) directly
  • Keep keepdims=True on both max and sum for correct multi-dimensional broadcasting
  • In backpropagation, use the closed-form dlogits = probs - labels instead of manual chain rule

💻 Industrial Code Implementation

import numpy as np

def softmax(x: np.ndarray, axis: int = -1) -> np.ndarray:
    """
    Numerically stable Softmax implementation.
    Subtracts the maximum value along the target axis before exponentiation to prevent overflow.
    """
    x_max = np.max(x, axis=axis, keepdims=True)
    exp_shifted = np.exp(x - x_max)
    return exp_shifted / np.sum(exp_shifted, axis=axis, keepdims=True)

🧪 Runnable Assertions & Validation

Copy and run directly in Python / Jupyter to verify correctness:

import numpy as np
x = np.array([[1000.0, 1001.0, 1002.0], [-1000.0, -1001.0, -1002.0]])
probs = softmax(x, axis=-1)
assert not np.isnan(probs).any(), "Contains NaN!"
assert np.allclose(probs.sum(axis=-1), [1.0, 1.0]), "Probabilities must sum to 1"
assert np.isclose(probs[0, 2], 0.66524096), "Numerical error exceeds tolerance"
print("✓ Softmax assertion passed")

🎯 Core Architecture Follow-up Q&A

Q1:What happens if all inputs are extremely negative (e.g. -10,000)?
The maximum term becomes 00 after subtraction, so e0=1e^0 = 1. The denominator is always at least 11. The distribution gracefully degrades into a Dirac delta without NaN or division by zero.
Q2:Why is LogSoftmax numerically superior to log(softmax(x))?
Evaluating log(softmax(x)) fails if softmax probabilities underflow to 0. LogSoftmax is computed as xi−logsumexp(x)x_i - \mathrm{logsumexp}(x), operating strictly in log-space.
All 69 KernelsNext: Numerically Stable Sigmoid & Log-Sigmoid→