From GPT to Production
Evaluating LLMs
Benchmarks, evals, and measuring what matters
Training a model is the easy half; the hard, unglamorous half is proving it got better - and every measuring stick, from perplexity to public benchmarks to crowd-voted Elo, quietly lies to you in a different way.
You changed a hyperparameter, retrained overnight, and now you have two model checkpoints. Which one is better? For a classifier you'd check accuracy and be done. For a model that writes essays, proves theorems, and cracks jokes, "better" splinters into a dozen questions with no clean answer. This lesson walks the full ladder - from a single intrinsic number (perplexity), up through benchmarks with real answer keys, to the messy frontier of judging open-ended text with LLM-as-judge and crowd-voted Elo arenas - and shows exactly where each rung cracks.
Perplexity: one honest number for a language model
Before any benchmark, there is a purely intrinsic measure that needs no answer key at all - just held-out text the model never trained on. Feed it real sentences and ask: how much probability did it put on the tokens that actually came next? Average the surprise, exponentiate, and you get perplexity. It is nothing more than the cross-entropy loss you already minimize, wrapped in an exp.
Perplexity = the model's effective branching factor
Imagine a model so lost that, at every position, it treats words as equally plausible for what comes next. Its perplexity is - it is "as confused as a fair 20-sided die." A perplexity of means it has narrowed the real choice down to a coin-flip's worth of uncertainty; a perfect oracle that always puts on the true token scores exactly . So lower is better, and the number reads as "how many equally-likely tokens is the model choosing between?" Drop perplexity from to and the model went from a hopeless muddle to a genuinely sharp predictor.
Perplexity is exactly the exponential of cross-entropy
Let the model assign probability to each true next token across tokens of held-out text. The cross-entropy is the mean negative log-probability, and perplexity is its exponential:
A worked pass: suppose the average surprise on your validation set is nats per token. Then - the model is, on average, as uncertain as if it were guessing uniformly among words. Halve nothing, just shave the loss to and perplexity falls to . Because is monotonically increasing, lower cross-entropy loss lower perplexity a better language model - the same ranking, in a more interpretable unit.
Perplexity is cheap, reproducible, and honest - but it only measures how well the model predicts held-out text of the same kind. It cannot tell you whether an answer is helpful, factual, safe, or well-written. Two models with identical perplexity can differ wildly in whether you'd actually ship them. For that, we need tasks with a notion of right.
Benchmarks: give the model an answer key
A benchmark is a fixed set of tasks where the correct answer is known, so scoring is mechanical. Three canonical ones you will see quoted on every model card:
- MMLU - 57 subjects of four-choice trivia (law, biology, history...). Metric: accuracy, the fraction answered correctly.
- HumanEval - write a Python function from a docstring; the code is run against hidden unit tests. Metric: pass@k, did any of samples pass.
- GSM8K - grade-school word problems. Metric: exact-match on the final numeric answer.
Multiple-choice and unit-tested code are gold: no human judgment, no ambiguity, a script computes the score. But the moment a benchmark becomes famous, it starts to rot - for one specific, insidious reason.
Data contamination: the answer key leaked into training
Benchmarks live on the public internet. The internet is exactly what LLMs are trained on. So a model can score brilliantly by memorizing the test it was accidentally trained on - never reasoning through a single question. This is data contamination, and it silently inflates leaderboards: a model that "gets 92% on GSM8K" may just have seen GSM8K. The tell is a suspiciously high score that collapses on a freshly written, held-out variant of the same task. It's the reason serious evaluation now leans on private test sets, freshly minted questions, and dynamic arenas that the model could not have seen. You'll feel this failure in the demo below - flip the contamination switch and watch a mediocre model ace everything.
When there is no answer key: judges and arenas
"Write a poem about autumn." "Explain recursion to a five-year-old." There is no single correct string, so accuracy is meaningless. Two families of methods fill the gap. LLM-as-judge: hand both candidate answers to a strong model (say GPT-4-class) and ask which is better - cheap, fast, and correlates decently with humans, though it inherits biases (it favors longer answers, the first-shown answer, and outputs that sound like itself). Pairwise arenas (like LMSYS Chatbot Arena): show anonymous head-to-head pairs to a crowd of humans, collect thousands of votes, and convert those wins and losses into an Elo rating - the same chess system that turns "who beat whom" into a single comparable number.
Elo: turn pairwise votes into a ranking
Each model carries a rating . Before a battle, its expected score against (a win-probability between and ) is a logistic function of the rating gap; after the result (win, tie, loss) comes in, we nudge the rating toward reality by a step :
Beat someone you were expected to beat ( near ) and you gain almost nothing; upset a much stronger model ( near ) and you leap. No absolute "quality" is ever measured - the whole ranking emerges from who beat whom. In the demo, : every model starts at , and one clean win moves the pair by .
The evaluation harness - run it yourself
Two modes, one machine. In Benchmark mode, a toy model answers five multiple-choice items and we tally accuracy live - then you flip the contamination switch to watch that score become a lie. In Arena mode, you become the judge: read two anonymous answers, vote (or let an LLM judge auto-vote), and watch the Elo leaderboard re-rank in real time. Perturb it - keep voting for the underdog and drag it to the top.
Evaluation harness accuracy, judges & Elo
A pen costs $4. How much do 7 pens cost?
Which organelle produces most of a cell's ATP?
Which snippet returns the last item of Python list xs?
In which year did World War II end?
Half of 30, plus 5, equals?
Score
accuracy = -
Honest run: the model must reason, not recall.
The 'aha': every measuring stick bends differently
Run the benchmark honestly and the toy model scores . Now tick Contaminate and run again - a perfect , earned by memorizing, not thinking. That is a real leaderboard lie in miniature. Then switch to the Arena: notice the score is never absolute. A model only rises by beating others, and if you vote against the crowd you can personally drag a weak model to rank #1 - which is precisely why arena rankings need thousands of independent votes and why the LLM-judge button, though convenient, quietly imposes one model's taste on everyone. There is no single number for "good." Perplexity, accuracy, and Elo each answer a different, narrower question - and knowing which lie you're being told is the actual skill.
import math
# --- Perplexity: exp of the mean per-token cross-entropy ---
# log_probs = log p(x_t | x_<t) the model gave the TRUE next tokens
mean_nll = -sum(log_probs) / len(log_probs) # cross-entropy, in nats
perplexity = math.exp(mean_nll) # PPL = e^H (lower is better)
# --- Benchmark accuracy: mechanical, needs an answer key ---
accuracy = sum(pred == gold for pred, gold in zip(preds, golds)) / len(golds)
# --- Elo update after one head-to-head battle ---
K = 32
def expected(r_a, r_b):
return 1 / (1 + 10 ** ((r_b - r_a) / 400))
def update(r_a, r_b, score_a): # score_a: 1 win, 0.5 tie, 0 loss
e_a = expected(r_a, r_b)
r_a += K * (score_a - e_a)
r_b += K * ((1 - score_a) - (1 - e_a))
return r_a, r_b # winner rises, loser falls, sum preservedCheck yourself
A model's perplexity on held-out text falls from 40 down to 8. That tells you the model has:
An LLM scores far above its true ability on a public benchmark. The usual culprit is that the test:
Recall: why is perplexity just e^(cross-entropy), and what does a perplexity of 20 intuitively mean?
Nail down the link between training loss and the headline eval number. Try to state it, then check.
Lock it in
- Perplexity = : reads as the effective branching factor. Lower is better, but it only measures next-token prediction on held-out text.
- Benchmarks (MMLU, HumanEval, GSM8K) give mechanical scores - but rot once the test leaks into training data (contamination).
- LLM-as-judge is cheap but biased toward length, position, and self-similarity.
- Elo arenas turn pairwise human votes into a ranking - no absolute quality is ever measured.
- No single number means "good" - knowing which lie each metric tells is the actual evaluation skill.
Primary source
The Hugging Face LLM Course walks through evaluation practically - perplexity, standard benchmarks, and the pitfalls of judging generated text - with runnable notebooks. For the rigorous version, Stanford's CS336 (Language Models from Scratch) devotes lectures to evaluation methodology, contamination, and the statistics of leaderboard comparisons. Between the two you'll see every idea here as both code and careful theory.
Ask your teacher
Here's the thread that ties this lesson back to the training loop: perplexity is just - the exponential of the very loss you drive down with gradient descent. It reads as "how many equally-likely tokens is the model choosing between," so lower cross-entropy loss = lower perplexity = a better language model. That single equivalence is why "just minimize next-token loss" quietly optimizes the headline eval number too. Want me to go deeper on how Elo confidence intervals are computed for arenas, why LLM-judge verbosity bias creeps in, or how to build a contamination-proof private eval set? Ask and I'll build the follow-up.