Unit 1.2 · about 5 h
171 / 324Stable softmax, cross-entropy and loss reductions
Classification and contrastive objectives call softmax and cross-entropy on raw logits. The naive form overflows on ordinary inputs, and the wrong reduction silently rescales every gradient.
- derive
- implement
You can now
- Derive the per-example cross-entropy with logits as \(-f_y + \log \sum_j e^{f_j}\), and its gradient \(\operatorname{softmax}(z)\) minus one-hot(\(y\)).
- Implement max-subtracted softmax and log-softmax that never overflow, and binary cross-entropy with logits that never takes log of a rounded sigmoid.
- State sum, mean and masked-mean reductions exactly: divisor used, and which positions get zero gradient.
Covers
- Stable softmax and log-softmax
- Cross-entropy and binary cross-entropy with logits
- Regression, reconstruction and masked loss reductions
Study, in this order
4 steps
- Losses from maximum likelihoodUnderstanding Deep Learning - Chapters 3 to 7
Chapter 5, Sections 5.1 to 5.2 (Eq 5.1 to 5.6, printed pp. 56 to 60) for likelihood to negative log-likelihood; Section 5.3 (Eq 5.7 to 5.15, pp. 61 to 64) for least squares; Section 5.4 (Eq 5.16 to 5.20, pp. 64 to 66) for sigmoid plus binary cross-entropy; Section 5.5 (Eq 5.21 to 5.24, pp. 67 to 69) and Section 5.7 (Eq 5.27 to 5.31, pp. 71 to 72) for softmax plus multiclass cross-entropy
- Naive softmax and the spelled-out cross-entropyD2L - tensors, autograd, MLP and backpropagation
The Softmax: the three-step naive code plus the warning that it is not robust and not for serious use; The Cross-Entropy Loss:
y_hat[[0, 1], y]indexing and thereduce_meanof negative logsDepth · Do not copy the naive softmax or the log-of-probability loss into your own code. Use them only to see what the stable versions below replace.
- The max-subtraction trickCS231n Linear Classification - Softmax classifier and numeric stability
Softmax classifier: \(L_i = -f_{y_i} + \log \sum_j e^{f_j}\); Practical issues: Numeric stability: the
f -= np.max(f)code turning[123, 456, 789]into[-666, -333, 0] - Masked reductions in `cross_entropy``torch.nn.functional.cross_entropy` - `reduction` and `ignore_index`
ignore_indexandreductionparameters: ignored targets contribute no gradient, and mean averages over the non-ignored targets only
Focus on
- Max subtraction is exact, not approximate: shifting every logit by \(c\) multiplies numerator and denominator by \(e^c\), which cancels. The largest shifted logit is 0, so every \(\exp\) input is at most 0 and no term overflows.
- The fused per-example loss is \(L_i = -f_{y_i} + \log \sum_j \exp(f_j)\), and its logit gradient is \(\operatorname{softmax}(z)\) minus one-hot(\(y\)). This matches centered differences to about 1e-9 in
float64. - Binary cross-entropy with logits must use the log-sigmoid form, never sigmoid followed by log: a confident sigmoid rounds to exactly 0 or 1 and the log is then infinite. Its logit gradient is \(\operatorname{sigmoid}(z) - y\).
- Mean divides every gradient by \(N\); a masked mean divides by the valid count and gives masked positions exactly zero gradient. D2L
cross_entropyusesreduce_mean; UDL least squares (Eq 5.11) sums.
Reproduce closed-book
log_softmax with max subtraction and a cross-entropy returning per-example losses plus their mean. Derive \(dL/dz = \operatorname{softmax}(z)\) minus one-hot(\(y\)). Show softmax([1000, 1001, 1002]) = [0.0900, 0.2447, 0.6652], and explain why the naive form returns [nan, nan, nan].Traps
- Taking \(\log(\operatorname{softmax}(z))\) in two steps. Even with max subtraction, a very small probability can underflow to 0 and its log becomes
-inf.log_softmax(z)= \((z - m) - \log \sum \exp(z - m)\), with \(m\) the max, never forms the probability. - Clipping probabilities after a naive softmax to fix overflow.
exp(1000)already overflowed toinfand the division already producednan; clipping cannot recover the true distribution. - Expecting the stable answer on
[1000, 1001, 1002]to be[1, 0, 0]. Only logit gaps matter, so the result equalssoftmax([0, 1, 2]): about[0.09, 0.245, 0.665].
Practice
Deep-ML 1227: Numerically Stable Softmax
Implement softmax(t, dim) by subtracting the max along dim. The two platform tests use a small \((2 \times 3)\) tensor along rows and along columns, so they do not exercise overflow.
After it passes, add your own large-logit test: [1000, 1001, 1002] must give [0.0900, 0.2447, 0.6652], and your log-softmax plus mean cross-entropy must stay finite.
TensorTonic: Calculate Squared-Error Loss Gradients (autograd-l08-squared-error-loss-gradients)
Losses, Training, and Zeroing Gradients section: Calculate Squared-Error Loss Gradients
State whether your derivation sums or means over examples, and how that choice scales the gradient.
Self-test
-
Why does subtracting the maximum logit prevent overflow without changing softmax?
Softmax depends only on logit differences: shifting all logits by \(c\) scales numerator and denominator by the same \(e^c\). After subtracting the max, the largest \(\exp\) input is 0, so every term is at most 1. -
What is the gradient of the per-example cross-entropy with respect to the logits?
\(\operatorname{softmax}(z)\) minus one-hot(\(y\)): \(p_k - 1\) at the true class, \(p_j\) elsewhere. It matches centered finite differences to about 1e-9 infloat64. -
A batch of 5 has 2 masked positions under a masked mean. What is the divisor, and what gradient do masked positions get?
The divisor is the valid count, 3. Masked positions get exactly zero gradient, matchingignore_indexsemantics. -
Why must binary cross-entropy with logits avoid sigmoid-then-log?
A confident logit makes sigmoid round to exactly 0 or 1 in floating point, and \(\log(0)\) is infinite. The fused log-sigmoid form stays finite, with gradient \(\operatorname{sigmoid}(z) - y\).
Completion criterion
Stop when
Your fused loss matches centered differences, survives logits of magnitude 1000, and you can state the divisor and the masked-position gradient for sum, mean and masked mean without notes.
Mark passed, then open 1.3 Only mark this when you can do it without notes.Leads to
- 1.3 Computation graphs and reverse accumulation Push the loss gradient back through the network.
- 1.5 Gradient checks with finite differences Check the fused loss gradient against finite differences.
Comes after
- 2.4 Gradients of logistic, softmax cross-entropy and regularized lossesThat unit fuses
log_softmaxwith max subtraction and uses softmax minus one-hot; this unit derived both. - 5.7 Stable log-sum-exp, online reductions and mixed precisionApply the log-partition identity to stable probability losses.
- 1.1 Affine maps, nonlinearities and logitsFeed logits into a stable softmax and cross-entropy.
- 3.7 Objective checks, tiny-set overfit and shuffled-label controlsGround the loss in stable softmax and reduction semantics.
- 5.1 Embedding geometry and contrastive logitsImplement the log-sum-exp and reduction the denominator needs.
Resources for this unit
- Understanding Deep Learning - Chapters 3 to 7 Read Chapters 3-7, emphasizing Chapter 5 for likelihood-derived losses and Chapter 7 for backpropagation and initialization. Use notebooks 3.1, 4.1, 5.1, 6.1 and 7.1 only as equation and plot companions.
- D2L - tensors, autograd, MLP and backpropagation Use chapter_preliminaries ndarray and autograd, then MLP, backpropagation, and numerical stability and initialization. Recreate scalar operations, affine maps, activation, reduction, stable cross-entropy, topological backward and unbroadcast in a blank file.
- CS231n Linear Classification - Softmax classifier and numeric stability Read the Softmax classifier section for the cross-entropy form \(L_i = -f_{y_i} + \log \sum_j e^{f_j}\), then the Practical issues: Numeric stability part for the max-subtraction code
f -= np.max(f). - `torch.nn.functional.cross_entropy` - `reduction` and `ignore_index` Read the
ignore_indexandreductionparameters: ignored targets contribute no gradient, and mean reduction averages over the non-ignored targets only.
Also in this section
- TensorTonic - Build Micrograd from Scratch Use the 25-problem Micrograd study plan in order for derivatives, gradient checks, value graphs, reverse mode, accumulation, operators, modules, losses and training. Preserve local tests as the evidence instead of a platform score.
- Deep-ML candidate - autograd and loss problems After the five-sample platform check passes, use at most one of 26 Basic Autograd Operations, 997 Manual Cross-Entropy Backprop or 1227 Stable Softmax as a changed-prompt drill.
- CS231n Neural Networks 3 - Gradient checks Read the Gradient Checks section only: centered formula with \(O(h^2)\) error, relative error with thresholds, double precision, step size \(h\) near 1e-5, kinks, few datapoints, burn-in, regularization and dropout discipline, and checking a few dimensions per parameter.
- PyTorch Broadcasting semantics Read General semantics for the trailing-dimension rules, prepend-1 alignment and max-size result, with the
(5, 1, 4, 1)plus(3, 1, 1)examples; read In-place semantics for why an in-place tensor may not change shape. - `torch.autograd.gradcheck` API and tolerances Read the
gradchecksignature defaults (eps1e-6,atol1e-5,rtol1e-3) and the note that inputs with overlapping memory, such asexpandoutputs, will likely fail the check. - PyTorch Tensor Views Read the view and storage sharing example (
data_ptr, mutating the view changes the base), the transpose non-contiguous example, the full view-ops list, and the special notes:reshapemay copy,contiguouscopies only when needed, basic indexing returns views while advanced indexing copies. - `torch.Tensor.stride` - stride and storage offset Read the stride definition: strides are per-dimension element steps, and with
storage_offsetthey locate every element. Combine with the Tensor Views page for transposed and expanded layouts. - PyTorch Autograd mechanics - in-place operations Read In-place operations with autograd (why in-place rewrites the graph and is discouraged) and In-place correctness checks (the version counter and the error on accessing a modified saved tensor).
- `torch.nn.Module` - parameters, buffers and state Read the parameters and buffers documentation: modules collect parameters recursively for the optimizer, while
register_bufferholds persistent non-parameter tensors saved alongside them.