AIInterviewTraining logoAIInterview/Training
Coding & DSA / 03
medium★ EssentialNVIDIAGoogleMeta

Implement a numerically stable softmax and cross-entropy loss from scratch.

A deceptively easy ML-coding ask. Writing exp/sum is trivial; the signal is the max-subtraction trick and the log-sum-exp form that stop it overflowing. Here is the stable implementation and the reason the naive version breaks.

Updated Sep 2026 · Grounded in real GenAI, LLM, and AI/ML engineering interview loops and written to a senior-engineer editorial bar.

TL;DR: Naive softmax overflows since exp(large logit) becomes inf. Subtract the row max before you exponentiate: mathematically the same, but numerically safe. For cross-entropy, avoid taking the log of softmax on its own; use the log-sum-exp form so log(0) never happens. The shift cancels exactly because softmax is unchanged when you add a constant to every logit.

TOP-K vs TOP-P (the sampling pool)
t1
t2
t3
t4
t5
t6
t7
t8
Top-p keeps the smallest set whose probability adds up to p, so the pool adapts: it shrinks when the model is confident and widens when it is unsure. Right now it samples from 3 tokens holding 82% of the mass.

How to approach it. Name how the naive version fails (overflow/underflow) and the fix (subtract the max), then show it is exact rather than an approximation. After that, write both functions vectorized across a batch, with cross-entropy fused through log-sum-exp.

A strong answer. Softmax is invariant to adding a constant c to every logit: exp(x_i - c) / Σ exp(x_j - c) = exp(x_i)/Σ exp(x_j). Choosing c = max(x) makes the largest exponent exp(0)=1, so nothing overflows and the dominant term never underflows to zero.

import numpy as np

def softmax(x):                       # x: (batch, classes)
    x = x - x.max(axis=-1, keepdims=True)     # shift: exact, prevents overflow
    e = np.exp(x)
    return e / e.sum(axis=-1, keepdims=True)

def cross_entropy(logits, y):         # logits: (B, C), y: (B,) int labels
    z = logits - logits.max(axis=-1, keepdims=True)
    logsumexp = np.log(np.exp(z).sum(axis=-1))   # stable normalizer
    # log_softmax = z - logsumexp ; pick the true-class log-prob
    log_probs = z[np.arange(len(y)), y] - logsumexp
    return -log_probs.mean()

Two things signal experience: computing cross-entropy via log-softmax (z - logsumexp) rather than log(softmax(...)) avoids ever evaluating log of a number that underflowed to 0, which would give -inf; and the max-subtraction is applied in both functions. This is exactly why frameworks expose a fused log_softmax and a cross_entropy that takes raw logits, not probabilities.

Key takeaways

  • Subtract the row max before exp: exact (softmax is shift-invariant) and it caps the largest exponent at 1.
  • Compute cross-entropy as z - logsumexp to dodge log(0) = -inf, never log(softmax(...)).
  • Pass raw logits to a fused loss; the softmax-CE gradient collapses to softmax(logits) - one_hot(y).
  • The overflow risk grows in fp16, so accumulate the loss in fp32 even when activations are half precision.

What interviewers probe next.

  • "Why subtract the max specifically, not any constant?" Any constant keeps it exact, but the max guarantees the largest exponent is 1, bounding everything in (0,1] so neither overflow nor catastrophic underflow occurs.
  • "Gradient of softmax-cross-entropy?" It simplifies beautifully to softmax(logits) - one_hot(y), which is why the two are fused in practice.
  • "fp16 implications?" The overflow risk is worse in low precision; the shift plus computing the loss in fp32 is standard.
  • "Temperature?" Divide logits by T before softmax; higher T flattens the distribution, lower sharpens it.

Common mistakes.

  • Naive exp(x)/sum(exp(x)) that overflows on large logits.
  • Computing log(softmax(x)) and hitting log(0) = -inf instead of using log-sum-exp.
  • Forgetting keepdims=True, so broadcasting silently does the wrong thing.
  • Taking softmax then feeding probabilities into a separate log step in training, losing precision and speed versus a fused loss on logits.
That answer was free, and so are 10 per topic without an account. A free account doubles that to 20, remembers what you have answered, and tracks which topics you are weakest in.no card · Google sign-in · nothing to cancel
HOW DID IT GO?
0
UP NEXT ON YOUR JOURNEY
DISCUSSION · 0

No comments yet — be the first to share your approach.