If the image is a 3
The target is 1. We want `p` close to 1. Chapter 4 uses the distance from 1:
Start with the loss you already know
Chapter 4 gives a sensible loss for separating 3s from 7s. Binary cross-entropy asks the same basic question—how much probability did the model give the correct answer?—but prices confident mistakes much more heavily.
Let `p` mean “the model’s probability that this image is a 3.” The target is 1 for a 3 and 0 for a 7.
The target is 1. We want `p` close to 1. Chapter 4 uses the distance from 1:
The target is 0. We want `p` close to 0. Chapter 4 uses the distance from 0:
def mnist_loss(predictions, targets):
probabilities = predictions.sigmoid()
return torch.where(
targets == 1,
1 - probabilities, # image is a 3
probabilities # image is a 7
).mean()
A compact way to say the same thing is: Chapter 4 loss = 1 minus the probability given to the correct class.
Binary cross-entropy first finds the probability assigned to the correct answer. Then it applies `-log`.
Why is it called binary cross-entropy? For one outcome, -log(p) is its information or “surprise.” Entropy is not merely one `-log`; it is an average of surprise under a probability distribution. Cross-entropy averages the surprise produced by one distribution’s predictions when the true outcomes come from another distribution. In our binary 3-versus-7 case, that becomes the average BCE across examples.
-log(probability) surprise for what actually happened.For probabilities between 0 and 1, log(p) is zero or negative. Multiplying by -1 flips the curve upward, giving a nonnegative loss. Near probability 1, the loss is near 0. Near probability 0, the loss becomes very large.
| Probability given to the correct answer | Chapter 4 loss 1 − correct probability | Binary cross-entropy −log(correct probability) |
|---|---|---|
| 0.99 | 0.01 | 0.01 |
| 0.90 | 0.10 | 0.11 |
| 0.60 | 0.40 | 0.51 |
| 0.50 | 0.50 | 0.69 |
| 0.10 | 0.90 | 2.30 |
| 0.01 | 0.99 | 4.61 |
The two losses agree about which direction is better. They disagree about how serious a confident mistake should be.
Choose the true label, then move the model’s probability that the image is a 3. The vertical marker shows the current prediction.
The long form shows both cases. The PyTorch helper is the safer implementation.
def binary_cross_entropy_from_probabilities(probabilities, targets):
losses = -(
targets * torch.log(probabilities)
+ (1 - targets) * torch.log(1 - probabilities)
)
return losses.mean()
Chapter 4’s `linear1(xb)` returns raw scores before sigmoid. PyTorch calls these logits. Its combined BCE-with-logits function accepts those raw scores, applies the sigmoid idea internally, and calculates BCE in a numerically stable way:
import torch.nn.functional as F
def bce_loss(logits, targets):
return F.binary_cross_entropy_with_logits(
logits, targets.float()
)
predictions = linear1(xb) # raw scores: do not call sigmoid first
loss = bce_loss(predictions, yb)
Both losses are smooth after sigmoid, both reward probability on the correct answer, and both can drive SGD. They create different gradient signals.
| Question | Chapter 4 `mnist_loss` | Binary cross-entropy |
|---|---|---|
| Smallest possible item loss | 0 | 0 |
| Largest possible item loss | Approaches 1 | Grows without a fixed upper limit |
| Confident wrong prediction | Costs almost 1 | Costs much more than an uncertain mistake |
| Main classroom advantage | Its two cases are immediately readable | It strongly distinguishes “slightly wrong” from “confidently wrong” |
| PyTorch input | Custom function shown in the chapter | Raw logits with `binary_cross_entropy_with_logits` |
Use the same 3-versus-7 data, model, split, learning rate, and number of epochs. Compare Chapter 4’s `mnist_loss` with binary cross-entropy from logits.