MediumActivation Functions

Implement Softmax Function

Activation Functions

Medium

Problem

Convert logits into probabilities. For a one-dimensional input, normalize the full vector. For a two-dimensional input, normalize each row independently.

p_i = \frac{e^{x_i - m}}{\sum_j e^{x_j - m}}

Here, x_i is logit i, m is the maximum logit in the same vector or row, and p_i is the resulting probability. Subtracting m prevents overflow without changing the probabilities. Return a NumPy array with the same shape as the input.

Theory

The final layer of a classification network outputs a vector of raw numbers, one per class. These are called logits. For a 3-class problem, you might get:

z = [2.0, 1.0, 0.5]

These logits tell you that class 0 is the most confident prediction and class 2 is the least. But they are not probabilities:

Softmax converts these raw scores into a proper probability distribution: all values between 0 and 1, summing to exactly 1.


The Softmax Formula

For a vector z = [z_1, z_2, \ldots, z_n]:

\text{softmax}(z_i) = \frac{e^{z_i}}{\sum_{j=1}^{n} e^{z_j}}

Each element is exponentiated, then divided by the sum of all exponentiated elements.

Example: z = [2.0, 1.0, 0.5]

  1. Exponentiate each element:

    • e^{2.0} \approx 7.389
    • e^{1.0} \approx 2.718
    • e^{0.5} \approx 1.649
  2. Sum: 7.389 + 2.718 + 1.649 = 11.756

  3. Divide each by the sum:

    • \frac{7.389}{11.756} \approx 0.629
    • \frac{2.718}{11.756} \approx 0.231
    • \frac{1.649}{11.756} \approx 0.140

Result: [0.629, 0.231, 0.140]. These sum to 1.0 and can be interpreted as: "63% chance of class 0, 23% chance of class 1, 14% chance of class 2."


Why Exponentiation?

The exponential function e^x does several things at once:

This amplification effect means softmax is "opinionated." It concentrates probability mass on the largest logits. The more confident the network is (larger gap between top logit and the rest), the closer the output is to a one-hot vector.


The Numerical Overflow Problem

There is a practical issue. If z_i is large (say, 1000), then $$ overflows to infinity in floating-point arithmetic. Even moderately large values like e^{100} \approx 2.7 \times 10^{43} can cause problems.

The fix is to subtract the maximum value before exponentiating:

\text{softmax}(z_i) = \frac{e^{z_i - \max(z)}}{\sum_{j} e^{z_j - \max(z)}}

This works because:

\frac{e^{z_i - c}}{\sum_j e^{z_j - c}} = \frac{e^{z_i} / e^c}{\sum_j e^{z_j} / e^c} = \frac{e^{z_i}}{\sum_j e^{z_j}}

Subtracting any constant c from all elements does not change the result. Choosing c = \max(z) ensures the largest exponent is e^0 = 1, and all others are e^{\text{negative}} < 1. No overflow possible.

Example: z = [1000, 999, 998]

Without the trick: e^{1000} = \text{Inf}. Computation fails.

With the trick: subtract 1000 to get [0, -1, -2], then:


Softmax Temperature

A common modification is to divide the logits by a temperature parameter T before applying softmax:

\text{softmax}(z_i; T) = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}}

Temperature controls how "sharp" or "soft" the distribution is:

Temperature is used in:


Softmax on 2D Arrays

When the input is a matrix (batch of logit vectors), softmax is applied row-wise. Each row is a separate data point with its own probability distribution:

\text{For each row } i: \quad \text{softmax}(z_{i,j}) = \frac{e^{z_{i,j}}}{\sum_k e^{z_{i,k}}}

The max subtraction and normalization happen independently per row.


Where Softmax Shows Up

Examples

Example 1

Input
x = [1, 2, 3]
Output
[0.090031, 0.244728, 0.665241]
Explanation
Subtracting 3 gives [-2, -1, 0]; exponentiating and dividing by the sum produces the probability vector.

Example 2

Input
x = [[1, 2, 3], [0, 0, 0]]
Output
[[0.090031, 0.244728, 0.665241], [0.333333, 0.333333, 0.333333]]

Hints

  1. Use np.max(x) for a vector and np.max(x, axis=1, keepdims=True) for a matrix.
  2. Compute exp_values / exp_values.sum(...) with the same axis used for the maximum.

Requirements

Constraints

Starter Code

import numpy as np

def softmax(x: list) -> np.ndarray:
    """
    Returns stable softmax probabilities as a NumPy array matching the shape of x.
    """
    # Write code here
    pass

Test Cases

CaseMatches
1D arrayExample 1public
2D arrayExample 2public