The lesson in one minute
What you'll be able to explain
- Cross-entropy is −ln(probability on the right answer): small when confident and right, huge when confident and wrong.
- Perplexity = e^(average cross-entropy): the effective number of choices per token.
- Compute it from logits with log-sum-exp; its gradient is softmax − onehot.
- MSE punishes big misses (mean); MAE is robust to outliers (median).
- Contrastive (InfoNCE) loss is cross-entropy over "which passage in this batch is mine?"; hard negatives drive retrieval quality.
Level 1
The practitioner's guide
In one sentence
A loss function turns "how wrong was this prediction?" into one number that training pushes down, and because the model learns whatever that number rewards, choosing the loss is choosing the behaviour.
When you need it
Every time you train or fine-tune anything, the first
line of the job is the loss, and every time you read a training curve, a
model card's perplexity or a fine-tuning API's "training loss" you are
reading one. The tell that you have the wrong loss, or are reading one
wrongly: the loss goes down and the thing you care about does not, or one
example is most of the number. This lesson's regression example shows how
easily that happens: four predictions off by 1 and one off by 10 give a mean
squared error of 20.8, of which the single outlier is 96%; the mean absolute
error is 2.8 and the outlier is 71% of it. In language modelling, three
tokens at 0.9 and one at 0.001 give a perplexity of 6.09, when the three
good tokens alone would give about 1.1 (this lesson's perplexity). You
don't need this lesson while you only prompt a hosted model: the vendor
chose the loss years ago. You need it the day you fine-tune, train an
embedding model, or have to explain why a loss of 0.69 is a coin flip.
Your options
The losses practice reaches for, by what the model predicts:
| Option | For predicting | What it pushes the model toward | What its number hides | Where it lives |
|---|---|---|---|---|
| Cross-entropy | a class, or the next token | Putting probability on the right answer; a confident miss (1%) costs 44 times a mostly-right one (90%) | An average over tokens, so one catastrophic token dominates the mean | Every classifier, and the pretraining and fine-tuning of every language model |
| Perplexity (a reading of cross-entropy, not a loss) | the same | Nothing new: it is e raised to the mean loss, "how many doors is the model choosing between" | Whether answers are correct or helpful; comparable only across the same tokenizer and text | Model cards, training dashboards |
| Mean squared error (MSE) | a number | The mean of the targets; big misses are squared, so the model chases them | A few wild values (96% of the number above) | Regression heads, forecasting, any "predict a quantity" model |
| Mean absolute error (MAE) | a number | The median; every unit of miss costs the same | How large the largest misses are | The same jobs, when the targets have noise you do not want chased |
| Huber loss | a number | MSE near zero, MAE far out: a blend | One more knob, the crossover point | Frameworks' regression losses |
| Contrastive (InfoNCE) | which items belong together | Picking each query's partner out of the batch, every other item a free decoy | Easy rows contribute almost nothing; a hard negative can be most of the loss | Embedding models for search and RAG, CLIP |
| Label smoothing (a modifier on cross-entropy) | classes or tokens | Never saying 100%: with ε = 0.1 over 4 classes the target is 92.5% on the right answer | The loss can no longer reach zero, so the floor moves | Classifiers, the original transformer |
How to choose
Start from what the model outputs, then ask what a wrong answer costs you.
- A class or the next token: cross-entropy, computed from the raw scores (logits) in one fused operation. Never take the softmax and then the log yourself; that is the classic source of a loss that reads NaN.
- A number: decide whether an outlier is signal or noise. If the wild values are real and expensive, MSE chases them for you; if they are bad labels or rare accidents, MAE shrugs them off; when you cannot tell, Huber.
- Pairs that belong together (a question and the passage that answers it, an image and its caption): InfoNCE with the largest batch you can afford, then mine hard negatives, because that is where the gradient is.
- A classifier that is confidently wrong too often, or a generator you will decode with beam search: add label smoothing with ε around 0.1.
- Whatever you pick, the loss is what the model optimises and the metric
(
primer.ml.metrics) is what you care about. Keep both on the same dashboard, and when they disagree, believe the metric.
What it costs
Compute is not the cost; a loss is a few operations next to a forward pass, and the safe cross-entropy is a max, a sum and one logarithm. The real costs are elsewhere. InfoNCE scores every query against every passage in the batch, so a batch of N costs an N-by-N grid and the number of free negatives is the batch size: memory buys signal, and SimCLR reports that contrastive learning wants larger batches and more steps than supervised training does. Label smoothing costs nothing at inference and raises the loss you will see in training: in this lesson a model that puts logit 50 on the right class scores 0 under plain cross-entropy and 3.75 under smoothing, which is the point. Perplexity is free, being arithmetic on a loss you already have. The expensive mistake is choosing a loss that optimises the wrong thing and only finding out at evaluation, after the GPU bill: MSE on data with a few wild labels moves the model's best constant guess from the median 3 to the mean 5 in this lesson's figure.
What breaks
- NaN or infinite loss. Softmax then log overflows on large logits and
underflows on tiny probabilities. Use the fused version that subtracts the
largest logit first (
primer.ml.lossesbuilds it aslog_sum_exp). - Loss falls, quality doesn't. Cross-entropy pays for probability on the reference text, not for a correct or useful answer. A model can lower its perplexity by matching style while getting facts wrong. Evaluate with a metric that reads the answer.
- One example is the whole number. Look at the distribution of per-example losses, not only the mean. A 96% outlier share in MSE means the gradient is almost entirely one row.
- Perplexity across models. A number from a different tokenizer, or on different text, is not comparable. Compare within one setup.
- A contrastive loss that stalls. In this lesson's 4-row batch three rows contribute 0.087, 0.048 and 0.004 while the row with a mined hard negative contributes 2.694. If your batch has no hard rows, the model has nothing left to learn from it; mine harder negatives or grow the batch.
- The wrong temperature. The same two perfectly matched vectors give an InfoNCE loss of 0.3133 at temperature 1 and 0.000045 at 0.1. Too low and the loss saturates on easy pairs; the temperature (or "scale") is a real hyperparameter, not a default to inherit.
- Smoothing a teacher. Müller, Kornblith and Hinton (2019) report that label smoothing improves calibration but makes a smoothed model a much worse teacher for distillation. Skip it on a model you will distil from.
In the wild
PyTorch's torch.nn.functional.cross_entropy takes
"predicted unnormalized logits" and has a label_smoothing argument, so
both the fused computation and the modifier are one call. A hosted fine-tuning API's
"training loss" is this cross-entropy, graded on the reply tokens only
(primer.ml.training_stages). Perplexity is the number on
language-model training dashboards and, still, on many model cards. In
retrieval, sentence-transformers' MultipleNegativesRankingLoss is InfoNCE
with in-batch negatives and optional mined hard negatives per query, and its
documentation describes the scale (the inverse temperature) as a parameter.
CLIP trained its image and text encoders with a symmetric InfoNCE loss on
400 million pairs, and SimCLR measured how much the batch size and the
temperature matter. Label smoothing was used to train the original
transformer.
Go deeper
Level 2 builds each of these from nothing: the −ln p curve and why a 1% miss costs 44 times a 90% hit, log-sum-exp and the one-line gradient "softmax minus one-hot", perplexity as a count of doors, the valleys of MSE and MAE on a toy dataset, the InfoNCE score grid with a gradient check, and label smoothing's soft target, every number rerunnable. If you only needed to choose a loss and read its number, you are done.
Level 2
How it works, from scratch
A loss is a scorecard with one number on it. Imagine a coach who, after every
practice, must summarise the whole team's performance in a single score so
the players know whether they're improving. Training a model does exactly
this: it computes the loss, then adjusts the weights to make that number
smaller (see primer.ml.neural_net for the loop).
What the scorecard rewards is what the model learns. Language models are scored on how much probability they gave the real next word; price predictors on how far off their numbers were; search models on whether they ranked the right passage above the wrong ones.
Figure 1 · Diagram
flowchart LR
P[Prediction] --> LF{Loss function}
T[Right answer] --> LF
LF -->|classes or tokens| CE[Cross-entropy]
LF -->|numbers| REG[MSE or MAE]
LF -->|matching pairs| CON[Contrastive / InfoNCE]
CE & REG & CON --> N[One number to minimize]
Chapter 1
Cross-entropy: how surprised were we by the truth?
Picture a weather forecaster scored every evening. If they said "90% chance of rain" and it rained, they lose a point or two. If they said "1% chance of rain" and it poured, they are humiliated. Cross-entropy is that humiliation meter: it charges according to how little probability you gave to what actually happened.
Worked example: the model assigned these probabilities to the correct next token.
| Probability on the correct token | Loss (−ln p) |
|---|---|
| 0.9 | 0.11 |
| 0.5 | 0.69 |
| 0.1 | 2.30 |
| 0.01 | 4.61 |
Being confidently wrong (1%) costs about 44× more than being mostly right (90%): 4.61 / 0.105 ≈ 44.
Figure 2 · Drawn from the lesson's code
The loss −ln p is near zero and flat above 90% but climbs steeply to 4.61 at p = 0.01
The natural logarithm ln(p) answers "e to what power gives p?" For a
probability between 0 and 1 the answer is negative (ln 0.5 = −0.69,
because e^−0.69 = 0.5), so we flip the sign to get a positive cost. (Every symbol used in these
lessons is built from zero in primer.notation.)
Level 3: the formula and its symbols
Symbols
| Symbol | Meaning here | In the example |
|---|---|---|
| the loss for one prediction | 0.69 | |
| probability the model gave to the right answer, between 0 and 1 | 0.5 | |
| natural logarithm: the power you'd raise to, to get the input | ||
| flips the sign so the loss is positive |
In words: "the loss is minus the natural log of the probability the model gave the right answer."
With the numbers: ; .
Level 3: in Python
import math
p_correct = 0.5
# −ln p_correct
round(-math.log(p_correct), 2) # → 0.69
# confidently wrong costs far more
round(-math.log(0.01), 2) # → 4.61
cross_entropy_from_prob is this one line.
Why it matters a language model predicts the next token by choosing among its whole vocabulary, so this is the pretraining loss of every LLM. The steep penalty for confident mistakes is what pushes models toward calibrated probabilities.
Chapter 2
From logits: the numerically safe way
A model doesn't output probabilities directly. It outputs raw scores called
logits, which softmax turns into probabilities (raise e to each score,
divide by the total; see primer.ml.attention). Computing that literally is
like measuring everyone's height in millimetres from the centre of the
Earth: the numbers get astronomically large and your calculator overflows.
Measure relative to the tallest person instead, and every number stays
small. That trick is log-sum-exp.
Worked example: logits (2, 1, 0.1), correct class 0. e² + e¹ + e^0.1 = 7.389 + 2.718 + 1.105 = 11.212, ln 11.212 = 2.417, so the loss is 2.417 − 2 = 0.417. And with logits (1000, 0) and class 1 correct, e^1000 overflows any computer, but log-sum-exp gives exactly 1000 − 0 = 1000.
Figure 3 · Diagram
flowchart LR Z[Logits z<br/>one score per class] --> M[Subtract max m] M --> LSE[log-sum-exp<br/>m + ln sum e^z-m] LSE --> L[Loss = LSE minus z_correct] Z --> L L -.backward.-> G[Gradient = softmax z<br/>minus one-hot target] G -.-> U[Raise the right logit,<br/>lower the others by their probability]
Level 3: the formula and its symbols
Symbols
| Symbol | Meaning here | In the example |
|---|---|---|
| the logits, one raw score per class | (2, 1, 0.1) | |
| the score of class | ||
| the index of the correct class | 0 | |
| the probability softmax gives the correct class | 7.389 / 11.212 = 0.659 | |
| "add up over every class " | three terms | |
| e ≈ 2.718 raised to the score | ||
| the largest logit, subtracted for safety | 2 | |
| "the biggest value over all " | 2 |
In words: "the loss is the log of the sum of e-to-every-score, minus the correct class's score; to compute that log safely, pull the biggest score out front first."
With the numbers: ; $\sum_j e^{z_j - 2} = e^0 + e^{-1} + e^{-1.9} = 1 + 0.368 + 0.150 = 1.518\ln 1.518 = 0.417= 2.417$ and the loss is (and too).
Level 3: in Python
import math
def log_sum_exp(z):
# m = max_j z_j
m = max(z)
# m + ln Σ_j e^(z_j − m)
return m + math.log(sum(math.exp(z_j - m) for z_j in z))
z, y = [2.0, 1.0, 0.1], 0
round(log_sum_exp(z), 3) # → 2.417
# ln Σ_j e^(z_j) − z_y
round(log_sum_exp(z) - z[y], 3) # → 0.417
z, y = [1000.0, 0.0], 1
# e^1000 is never computed: no overflow
log_sum_exp(z) - z[y] # → 1000.0
The gradient, which is how each logit should move:
Level 3: the formula and its symbols
Symbols
| Symbol | Meaning here | In the example |
|---|---|---|
| the gradient: one slope per logit | (0.25, −0.25) | |
| the predicted probabilities | (0.25, 0.75) for logits (0, ln 3) | |
| 1 at the correct class, 0 elsewhere | (0, 1) |
In words: "each logit's slope is its predicted probability, minus one if it's the right answer."
With the numbers: logits (0, ln 3) give softmax (1/4, 3/4); with class 1 correct, the gradient is (0.25 − 0, 0.75 − 1) = (0.25, −0.25).
Level 3: in Python
import math
z, y = [0.0, math.log(3)], 1
exps = [math.exp(z_j) for z_j in z]
# (1/4, 3/4)
softmax = [e / sum(exps) for e in exps]
# (0, 1)
onehot = [1 if j == y else 0 for j in range(len(z))]
# softmax(z) − onehot(y)
[round(s_j - o_j, 2) for s_j, o_j in zip(softmax, onehot)] # → [0.25, -0.25]
In code: log_sum_exp pulls the largest logit out front, log_softmax
subtracts that total from every logit, and softmax_cross_entropy returns
the batch's mean loss together with its softmax − onehot gradient.
Why it matters this is why every framework fuses softmax and
cross-entropy into one operation that takes logits
(torch.nn.functional.cross_entropy). Computing softmax first and then the
log is the classic source of NaN losses.
Chapter 3
Perplexity: how many options is the model torn between?
Imagine a game show with doors, one hiding the prize. If the model is as unsure as someone picking between two doors at random, its perplexity is 2; between ten doors, 10. Perplexity converts an average loss back into that "number of doors".
Worked example: a model that gives the right token 50% every time has loss 0.69 per token and perplexity e^0.69 = 2. One that gives 10% every time has perplexity 10. Three confident tokens (0.9) and one disaster (0.001) give perplexity about 6: one bad guess drags the whole average.
Figure 4 · Diagram
flowchart LR P["Per-token probabilities<br/>0.5, 0.5, 0.5"] --> NL["−ln each<br/>0.69, 0.69, 0.69"] NL --> AV["Average<br/>0.69"] AV --> EX["e to that power<br/>e^0.69 = 2"] EX --> D["≈ choosing between<br/>2 equally likely doors"]
Level 3: the formula and its symbols
Symbols
| Symbol | Meaning here | In the example |
|---|---|---|
| perplexity | 2 | |
| number of tokens | 3 | |
| a counter over the tokens | 1, 2, 3 | |
| probability given to the correct token at position | 0.5 each | |
| "the average over all tokens" | ||
| another way to write |
In words: "perplexity is e raised to the average cross-entropy per token."
With the numbers: .
Level 3: in Python
import math
p = [0.5, 0.5, 0.5]
N = len(p)
# (1/N) Σ −ln p_i
average = sum(-math.log(p_i) for p_i in p) / N
round(average, 2) # → 0.69
# exp(...)
round(math.exp(average), 1) # → 2.0
In code: perplexity averages cross_entropy_from_prob over the tokens
and raises e to the result.
Why it matters perplexity is the standard training metric for language models. It's comparable only between models that use the same tokenizer on the same text, and it says nothing direct about whether answers are helpful or correct.
Chapter 4
Regression losses: fines for being off by an amount
When the prediction is a number (a price, a temperature), think of fines for arriving late. MAE (mean absolute error) is a flat rate: every minute late costs the same. MSE (mean squared error) squares the minutes: 10 minutes late costs 100, not 10, so one very late arrival outweighs many slightly late ones.
Worked example: four predictions miss by 1 and one misses by 10. MSE = (1 + 1 + 1 + 1 + 100) / 5 = 20.8, and the outlier is 100/104 = 96% of it. MAE = (1 + 1 + 1 + 1 + 10) / 5 = 2.8, and the outlier is 10/14 = 71%.
Figure 5 · Drawn from the lesson's code
The squared penalty pulls away past an error of 1, and one outlier drags MSE's best constant to the mean 5.0 while MAE's stays at the median 3
Level 3: the formula and its symbols
Symbols
| Symbol | Meaning here | In the example |
|---|---|---|
| number of predictions | 5 | |
| the true value for example | 0 for all five | |
| "y-hat", the predicted value | 1, −1, 1, −1, 10 | |
| the error (residual) | −1, 1, −1, 1, −10 | |
| square: multiply by itself (always positive) | ||
| absolute value: drop the sign |
In words: "MSE is the average of the squared errors; MAE is the average of the errors ignoring their sign."
With the numbers: MSE ; MAE .
Level 3: in Python
y = [0, 0, 0, 0, 0]
y_hat = [1, -1, 1, -1, 10]
N = len(y)
# MSE
sum((y_i - y_hat_i) ** 2 for y_i, y_hat_i in zip(y, y_hat)) / N # → 20.8
# MAE
sum(abs(y_i - y_hat_i) for y_i, y_hat_i in zip(y, y_hat)) / N # → 2.8
In code: mse and mae are the two averages, and outlier_share
measures how much of each total the single largest error contributes (the
96% and 71% above).
Why it matters pick the loss whose valley is where you want your predictions. MSE chases outliers (its best constant is the mean); MAE shrugs them off (its best constant is the median). For noisy data with occasional wild values, MAE or a blend (Huber loss) is safer.
Chapter 5
Contrastive loss (InfoNCE): find your partner in a crowd
Picture a party game. Everyone arrives in pairs, gets separated, and must pick their partner out of the whole room. Everyone else in the room is a decoy. A contrastive loss scores how confidently each person picks their own partner over every decoy. The hardest decoy is your partner's lookalike twin: same topic, wrong person. That's a hard negative.
Embedding models (and CLIP) learn this way: a query and the passage that answers it are "partners"; the other passages in the same batch are free decoys (in-batch negatives).
Worked example: two queries and two passages, as vectors, scored with the dot product (multiply matching entries, add them up). Query 1 = (1, 0), query 2 = (0, 1), passage 1 = (1, 0), passage 2 = (0, 1), temperature 1. Query 1 scores 1 against its partner and 0 against the decoy. Its loss is −ln(e¹ / (e¹ + e⁰)) = ln(1 + e^−1) = 0.3133. Swap the passages and each query now prefers the decoy: the loss rises to ln(1 + e¹) = 1.3133.
Figure 7 · Diagram
flowchart LR Q[Batch of queries] --> S[Score matrix<br/>every query vs every passage] D[Their matching passages] --> S S --> CE[Cross-entropy per row<br/>right answer = the diagonal] CE --> L[Pull pairs together<br/>push the rest apart]
Figure 6 · Drawn from the lesson's code
Rows 0, 1 and 3 put over 0.9 on their own passage; row 2 puts 0.91 on the hard negative and only 0.07 on its own
Level 3: the formula and its symbols
Symbols
| Symbol | Meaning here | In the example |
|---|---|---|
| the loss for query | 0.3133 | |
| query 's vector | ||
| query 's own passage (its positive) | ||
| passage : every passage in the batch, the positive included | ||
| dot product: how aligned query and passage are | , | |
| "tau", the temperature: divides scores; small sharpens, large softens | 1 | |
| number of passages scored (batch plus any extra negatives) | 2 | |
| raised to the power |
In words: "for each query, take e-to-the-score of its own passage, divide by the sum of e-to-the-score over every passage in the batch, and charge minus the log of that share."
With the numbers: $-\ln \frac{e^{1}}{e^{1} + e^{0}} = -\ln \frac{2.718}{3.718} = -\ln 0.731 = 0.3133$. At temperature 0.1 the same vectors give : sharper.
Level 3: in Python
import math
# the queries
q = [[1, 0], [0, 1]]
# their passages: d[i] is q[i]'s partner
d = [[1, 0], [0, 1]]
def dot(a, b):
return sum(a_k * b_k for a_k, b_k in zip(a, b))
def info_nce(i, tau):
# exp(q_i · d_j / τ) for every j
scores = [math.exp(dot(q[i], d_j) / tau) for d_j in d]
# −ln (own passage's share)
return -math.log(scores[i] / sum(scores))
round(info_nce(0, tau=1.0), 4) # → 0.3133
print(f"{info_nce(0, tau=0.1):.6f}") # → 0.000045
It's just cross-entropy where the "classes" are the passages in the batch, so the gradient is the same softmax − onehot, pushed back through the dot products into both sets of vectors.
In code: info_nce builds the score grid, hands it to
softmax_cross_entropy with the diagonal as the right answers, and returns
the gradients for both sets of vectors; info_nce_gradient_check confirms
those gradients against small nudges of every number.
Why it matters this is how search and RAG embedding models are trained
(see primer.ml.embeddings.contrastive). Bigger batches mean more free
negatives. Easy negatives are already far away and contribute almost no
gradient; hard negatives produce most of the learning signal, which is why
mining them is the biggest lever on retrieval quality.
Chapter 6
Label smoothing: never say 100%
A good forecaster never says "100% chance of rain", because the one day they're wrong would be infinitely embarrassing. Label smoothing teaches the model the same humility: instead of "the answer is class 2, with total certainty", the target says "class 2, with 92.5% certainty, and a sliver of doubt spread over everything".
Worked example: 4 classes, correct class 2, smoothing ε = 0.1. Take 0.9 of the one-hot target (0, 0, 0.9, 0) and add 0.1/4 = 0.025 to every class: (0.025, 0.025, 0.925, 0.025). A model that puts logit 50 on the right class has plain cross-entropy ≈ 0, but smoothed cross-entropy 3 × 0.025 × 50 = 3.75.
Figure 8 · Diagram
flowchart LR O[One-hot target<br/>0, 0, 1, 0] --> MIX[Mix: 1 minus eps times one-hot<br/>plus eps/K everywhere] U[Uniform over K classes] --> MIX MIX --> T[Soft target<br/>0.025, 0.025, 0.925, 0.025] T --> CE[Cross-entropy against<br/>the soft target]
Level 3: the formula and its symbols
Symbols
| Symbol | Meaning here | In the example |
|---|---|---|
| the smoothed target distribution | (0.025, 0.025, 0.925, 0.025) | |
| "epsilon", how much certainty to give away | 0.1 | |
| number of classes | 4 | |
| a counter over the classes | 1 to 4 | |
| target weight on class | 0.925 on the right class | |
| predicted probability of class | ≈1 on the right class |
In words: "the target keeps (1 − ε) on the right answer and spreads ε evenly over all classes; the loss is cross-entropy against that softer target."
With the numbers: on the right class and elsewhere; with logits (0, 0, 50, 0) the three wrong classes each have , so .
Level 3: in Python
import math
eps, K, y = 0.1, 4, 2
# t_k
t = [(1 - eps) * (1 if k == y else 0) + eps / K for k in range(K)]
[round(t_k, 3) for t_k in t] # → [0.025, 0.025, 0.925, 0.025]
z = [0.0, 0.0, 50.0, 0.0]
m = max(z)
log_total = m + math.log(sum(math.exp(z_k - m) for z_k in z))
# ln softmax(z)_k
log_softmax = [z_k - log_total for z_k in z]
# −Σ_k t_k ln softmax(z)_k
round(-sum(t_k * ls_k for t_k, ls_k in zip(t, log_softmax)), 2) # → 3.75
In code: smoothed_targets builds the soft target t, and
smoothed_cross_entropy scores the logits against it.
Why it matters it curbs over-confidence and often improves calibration (how well the model's stated confidence matches how often it's right). It was used to train the original transformer.
Test yourself
6 questions
Answer each one out loud or on paper before you open it. If you can explain it, you know it.
Question 1The model puts 1% on the right token. What's the loss, and why so large?Think it through, then reveal
−ln(0.01) = 4.61, about 44× the loss at 90% (0.105). The log punishes confident mistakes steeply, which pushes the model toward calibrated probabilities.
Question 2Loss is 0.69 per token. What's the perplexity?Think it through, then reveal
e^0.69 = 2: as uncertain as a coin flip between two options.
Question 3Why compute cross-entropy from logits instead of from softmax outputs?Think it through, then reveal
Softmax of large logits overflows, and the log of a tiny probability underflows to −∞. Log-sum-exp subtracts the max first and never exponentiates a large number. The fused gradient is simply softmax − onehot.
Question 4When would you pick MAE over MSE?Think it through, then reveal
When outliers are noise you don't want to chase. MSE squares errors, so one huge miss dominates; MAE weighs errors linearly.
Question 5How does InfoNCE get negatives without labelling them?Think it through, then reveal
It uses the other items in the batch: for each query, every other query's positive passage is a negative. Bigger batches mean more (and harder) negatives for free.
Question 6Why do hard negatives matter?Think it through, then reveal
Easy negatives are already far away and contribute almost no gradient. Hard negatives score high and produce most of the learning signal, teaching the model to separate "on topic" from "actually answers the question".
Primary sources
The papers behind this lesson
van den Oord, Li & Vinyals, Representation Learning with Contrastive Predictive Coding (2018): Named and analysed InfoNCE, the "pick the positive out of N" contrastive loss.
The paper ↗Chen et al., A Simple Framework for Contrastive Learning of Visual Representations (SimCLR, 2020): Showed how much in-batch negatives, large batches and the temperature matter for contrastive training.
The paper ↗Radford et al., Learning Transferable Visual Models From Natural Language Supervision (CLIP, 2021): Trained image and text encoders with a symmetric InfoNCE loss on 400 million pairs, putting both in one vector space.
Read the annotated companion →The paper ↗Szegedy et al., Rethinking the Inception Architecture for Computer Vision (2015): Introduced label smoothing as a regularizer against over-confident predictions.
The paper ↗Researcher's shelf
Further reading
- Goodfellow, Bengio & Courville, Deep Learning, ch. 6 (cost functions): https://www.deeplearningbook.org/contents/mlp.html
- CS231n notes, Linear classification (softmax and cross-entropy): https://cs231n.github.io/linear-classify/
- PyTorch
cross_entropy: https://pytorch.org/docs/stable/generated/torch.nn.functional.cross_entropy.html - van den Oord et al., Representation Learning with Contrastive Predictive Coding (InfoNCE, 2018): https://arxiv.org/abs/1807.03748
- Chen et al., SimCLR (in-batch negatives and temperature, 2020): https://arxiv.org/abs/2002.05709
- Radford et al., CLIP (2021): https://arxiv.org/abs/2103.00020
- Szegedy et al., Rethinking the Inception Architecture (label smoothing, 2015): https://arxiv.org/abs/1512.00567
- Müller et al., When Does Label Smoothing Help? (2019): https://arxiv.org/abs/1906.02629
About this lesson. This is the illustrated edition of a lesson from the open-source AI Primer. Its text, figures and numbers are generated from the Primer's source at commit 048aeaa, so the two always agree: the explanation, the code that builds it and the tests that prove it.