Skip to content
Work Practice Products Coding interviews Machine learning coding Quant developer HFT course Blog Method Why me About Enquire Book a call

ML coding · Numerical stability

Binary cross-entropy from logits

Medium · Target 15 minutes · Python and NumPy

Given logits z and labels y (each 0 or 1) of the same length, return the mean binary cross-entropy

L = −(1/n) Σ [ yi log σ(zi) + (1 − yi) log(1 − σ(zi)) ],   σ(t) = 1 / (1 + e−t)

as a float. It must be accurate for |z| up to 10⁴, where σ(z) rounds to exactly 0 or 1.

Examples

Input z = [0], y = [1]

Output 0.6931

σ(0) = ½ and −log ½ = log 2.

Input z = [2, -1], y = [1, 0]

Output 0.2201

Both predictions lean the right way.

Input z = [-800], y = [1]

Output 800

σ(−800) underflows to 0. The loss is still exactly 800.

Constraints

  • 1 ≤ n ≤ 10⁵
  • |z[i]| ≤ 10⁴
  • y[i] ∈ {0, 1}
  • Checked to a relative tolerance of 10⁻⁹

Hints

Hint 1

Write log σ(z) and log(1 − σ(z)) in terms of log(1 + e^(−z)) and log(1 + e^(z)). Then combine the two terms of the loss into one expression.

Hint 2

You should reach ℓ = log(1 + eᶻ) − y·z. That is still unsafe for large z. How does log(1 + eᶻ) behave when z is large?

Hint 3

log(1 + eᶻ) = max(z, 0) + log(1 + e^(−|z|)). Every exponent is now ≤ 0. Use np.log1p for the last term.

Where candidates go wrong

  • Computing σ(z) first and then the log: σ(−800) is exactly 0 and log 0 = −inf.
  • Clipping probabilities to [1e−7, 1 − 1e−7]. The loss of a badly wrong prediction is then capped at about 16, and the gradient stops pushing.
  • Using np.log(1 + np.exp(-abs(z))) instead of np.log1p: for |z| above about 37, 1 + e^(−|z|) rounds to 1 and the term is lost.
Console⌘/Ctrl + Enter runs

Python runs in your browser with NumPy. The first run downloads the runtime, which takes a few seconds.


Why interviewers ask it

Logistic regression, every binary classifier and every GAN discriminator is trained on this loss. Writing it as sigmoid followed by log is the obvious answer and it is wrong in floating point. The interviewer wants to see the algebra that removes the problem, not a clip.

The theory behind it is in the lesson Numerically stable softmax and log-sum-exp.


Keep going