Bug in TensorFlow reduce_max for negative infinity?
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.
Introduction
tf.reduce_max returns unexpected results when applied to tensors containing negative infinity (-inf) or to empty tensors. For an empty tensor, tf.reduce_max returns -inf (the identity element for max), which is mathematically correct but often surprises developers who expect an error. For tensors where all values are -inf, the result is -inf as expected, but when mixed with NaN values, NaN propagates and the max is NaN instead of -inf. Understanding these edge cases is critical for masked attention in transformers, log-probability computations, and any model that uses -inf for padding or masking.
The Expected Behavior
The Surprising Edge Cases
The primary "bug" is actually correct IEEE 754 behavior — NaN propagates through comparisons and the max of an empty set is conventionally -inf. But these results can cause numerical instability in softmax, log-sum-exp, and attention mechanisms.
Why This Matters: Softmax with Masking
Workaround 1: Replace -inf Before reduce_max
Workaround 2: Check for All-Masked Rows
Workaround 3: Use tf.math.reduce_max with Initial Value
Log-Sum-Exp Stability
Common Pitfalls
- Subtracting
-inffrom-inf: The expression-inf - (-inf)producesNaN, not0. This breaks the standard numerical stability trick ofsoftmax(x) = softmax(x - max(x))when all elements are-inf. Always check for all-masked rows before applying the max subtraction. - Using
-infinstead of a large negative number for masking: While-infis mathematically correct for masking, using-1e9avoidsNaNpropagation in edge cases. Most models use-1e9orfloat('-inf')with explicit guards. - Empty tensor reduce_max returning
-inf:tf.reduce_max(tf.constant([]))returns-inf, not an error. If you expect the tensor to always have elements, add an assertion:tf.debugging.assert_greater(tf.size(tensor), 0). - NaN silently propagating through max:
tf.reduce_max([nan, 1.0, 2.0])returnsNaN, not2.0. NaN poisons all comparisons. Usetf.where(tf.math.is_nan(tensor), -inf, tensor)to replace NaN before computing max. - GPU vs CPU behavior differences: Some GPU kernels handle
-infandNaNdifferently than CPU. A model that works on CPU may produce different results on GPU for tensors containing special float values. Always test edge cases on the target hardware.
Summary
tf.reduce_maxreturns-inffor empty tensors and all--inftensors (correct IEEE 754 behavior)-inf - (-inf)producesNaN, breaking softmax numerical stability tricks- NaN propagates through
reduce_max— always sanitize inputs first - Use
-1e9instead of-inffor masking to avoid NaN in edge cases - For masked attention, check for fully masked rows and zero out their softmax output
tf.math.reduce_logsumexphandles-infcorrectly in TensorFlow 2.x
Related reading
- Build a graph that works with variable batch size using Tensorflow
- build tensorflow lite on other platform such as Linux
- Building a mutlivariate, multi-task LSTM with Keras
- Building a mutlivariate, multi-task LSTM with Keras
- Build an approximately uniform grid from random sample python
- Building an SVM with Tensorflow
- Building an SVM with Tensorflow
- Building Kivy Android app with Tensorflow
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.