Evaluating Models

Every model launch comes with a table of scores. This page teaches you to read that table sceptically and to build a better one yourself: what each kind of benchmark really measures, how scores get inflated, how to compute pass@k and a confidence interval, and why any number you optimise hard enough stops meaning what it did.

Two models are tested on the same fifty questions. Model A scores 82%, model B 76%. The team ships A, writes a blog post about the six-point gain, and moves on. Run the numbers you will compute below and it turns out that two models with identical true ability, tested on fifty questions, differ by five points or more about half the time. The six points may be real. They may be a coin landing heads. Most of what goes wrong in model evaluation is not exotic; it is this — a confident number that cannot bear the weight put on it.

Why generative models are hard to measure

On Measuring Models you evaluated classifiers: each test example has one right label, and accuracy, precision and recall follow by counting. A language model breaks every part of that comfort.

That is why Liang and colleagues’ Holistic Evaluation of Language Models (HELM, 2022) argued for measuring many scenarios against seven kinds of metric — accuracy, calibration, robustness, fairness, bias, toxicity and efficiency — rather than crowning a model on one leaderboard column. You do not need HELM’s scale, but you do need its attitude: decide what you care about, then measure each part separately.

What the big benchmarks measure

Public benchmarks fall into three broad families. Each measures something real and something narrower than its name suggests.

Example: MMLU (Hendrycks et al., 2020) — about 16,000 four-option questions across 57 subjects from elementary maths to law and medicine. Scoring is cheap and objective: did the model pick the right letter? Random guessing gives 25%.

It measures breadth of recall and exam-style reasoning. It does not measure whether the model can write, explain, or act on that knowledge, and multiple choice lets a model succeed by eliminating wrong options. Frontier models score around 90% or higher, so the benchmark has largely saturated and harder successors are used instead As of 2026.

Debated How far leaderboard positions reflect real usefulness is argued. In “The Leaderboard Illusion” (2025), Singh and colleagues reported that some providers tested many private model variants on Chatbot Arena and published only the best, inflating their rank; the arena’s organisers disputed parts of the analysis.

Other views
  • Arenas are the best we have: fresh, varied prompts from real people are hard to overfit, unlike a fixed test set.
  • Arenas measure charm: votes reward pleasant, long, well-formatted answers; preference is not correctness.
  • Neither replaces your own tests: public scores tell you which models to try, not which one works for your users.

Contamination, saturation and pass@k

Contamination means test items, or close paraphrases, were in the training data. A model can then “answer” by recall rather than skill, and its score says little about new problems. With web-scale pretraining (see Pretraining and Scaling Laws) it is hard to rule out, because benchmarks are published, discussed and copied all over the web. Signs to watch for: suspiciously high scores on an old benchmark but not on a freshly written equivalent; a model completing a benchmark question word for word from its first half; or performance that drops on problems dated after the model’s knowledge cutoff. Defences include private held-out sets, regularly refreshed questions and canary strings that let builders filter benchmark files out of training data.

Saturation is the opposite end: when top models all score in the high 90s, remaining differences are mostly noise and label errors in the benchmark itself. A benchmark’s useful life is often only a few years.

For code, the standard metric needs a little care. If a model has k attempts at a problem, pass@k is the probability that at least one attempt passes the tests. The naive way to estimate it — run exactly k samples and check — is very noisy. Chen et al. (2021) instead generate n ≥ k samples, count the c correct ones, and compute the exact chance that a random k-subset contains at least one:

pass@k = 1 − C(n − c, k) ⁄ C(n, k)averaged over problems, where C(a, b) is “a choose b”. C(n − c, k) counts the ways to pick k attempts that all failed; dividing by C(n, k), all ways to pick k attempts, gives the chance of an all-fail subset.
from math import comb

def pass_at_k(n, c, k):
    """Chance that at least one of k samples (drawn from n, c of them correct) passes."""
    if n - c < k:
        return 1.0
    return 1 - comb(n - c, k) / comb(n, k)

n, c = 20, 3                     # 20 attempts at one problem, 3 passed the tests
for k in [1, 5, 10]:
    print(f"pass@{k:<2d} = {pass_at_k(n, c, k):.4f}")
print("naive 1-(1-c/n)^5 =", round(1 - (1 - c / n) ** 5, 4))
pass@1  = 0.1500
pass@5  = 0.6009
pass@10 = 0.8947
naive 1-(1-c/n)^5 = 0.5563

pass@1 is simply 3/20. With five tries the chance of a pass jumps to 60%, and with ten to 89%. The last line shows the tempting shortcut of treating the attempts as independent draws with replacement: it gives 56%, not 60%, because sampling without replacement from a fixed set of 20 makes repeat failures less likely. Two lessons: always state k, and remember that a high pass@10 describes a workflow where something checks ten answers — not a model that is right the first time.

Models judging models

For open-ended answers, a cheap alternative to human raters is to ask a strong model to grade: LLM-as-judge. Zheng et al. (2023) found that a strong judge agreed with human experts about as often as humans agreed with each other — over 80% on their pairwise comparisons — which made the approach mainstream. They also documented its biases:

Mitigations are mostly good experimental hygiene: run each comparison twice with the order swapped and count only consistent verdicts; give the judge a concrete rubric and, where possible, a reference answer; ask for a short justification before the verdict; and, above all, check the judge against a sample of human labels before trusting it. Compare these two judge prompts:

Vague
Which answer is better, A or B?
Better

You are grading answers to a customer question about a bank’s fees. A reference answer written by a staff member is provided.

Score the candidate answer on three criteria, each 0, 1 or 2: (1) factual agreement with the reference; (2) whether it answers the question that was asked; (3) whether it avoids promising anything the reference does not support. Length and style do not count.

First write one sentence of reasoning per criterion, then output a line of the form SCORES: x, y, z.

QUESTION: …
REFERENCE: …
CANDIDATE: …

The better prompt grades one answer against a reference instead of two answers against each other, which removes position bias; it names criteria and explicitly discounts length; and it asks for a parseable output your code can read. You still owe it a calibration check.

Building your own eval set

Consensus For any real application, a small evaluation set drawn from your own use case is worth more than any public leaderboard. The Builder track introduced the idea of an evaluation set on Shipping an AI App; here is how to make one that can bear decisions.

  1. Start from real inputsCollect (anonymised, with permission) the questions users actually ask — including the short, misspelt and mixed Bahasa Melayu–English ones — not the tidy ones you imagine.
  2. Write down what “good” meansA reference answer, a list of facts that must appear, a test that must pass, or a rubric. If two colleagues disagree on the grade, fix the rubric before you grade models.
  3. Cover the awkward cases on purposeQuestions with no answer in your documents, off-topic and adversarial requests, personal data, edge cases from past incidents. Tag each item so you can report scores by category.
  4. Freeze it, and keep a hidden partVersion the set. Keep a slice nobody looks at while tuning prompts, so you have one honest number at the end.
  5. Report uncertaintyRun several samples per item if outputs vary, and always give a confidence interval — the next section shows how.
  6. Grow it from failuresEvery bug report becomes a new test item. Over months the set comes to describe your product better than any document.

Statistical noise: why small evals mislead

An accuracy measured on n questions is an estimate, and its uncertainty shrinks only with the square root of n. To get a 95% confidence interval for a proportion, the textbook “±1.96 × √(p(1 − p)/n)” behaves badly near 0% and 100% and for small n. The Wilson score interval (Wilson, 1927) is a better default; the bootstrap — resample your results with replacement thousands of times and see how much the mean moves — is a general alternative that works for any metric.

centre = ( p̂ + z² ⁄ 2n ) ⁄ ( 1 + z² ⁄ n ), half-width = z · √( p̂(1 − p̂) ⁄ n + z² ⁄ 4n² ) ⁄ ( 1 + z² ⁄ n )p̂ is the observed accuracy, n the number of questions, and z = 1.96 for 95% confidence. The interval is centre ± half-width.
import numpy as np

def wilson(correct, n, z=1.96):              # 95% Wilson score interval
    p = correct / n
    centre = (p + z**2 / (2 * n)) / (1 + z**2 / n)
    half = z * np.sqrt(p * (1 - p) / n + z**2 / (4 * n**2)) / (1 + z**2 / n)
    return centre - half, centre + half

def bootstrap(results, reps=10_000, seed=42):
    rng = np.random.default_rng(seed)
    means = rng.choice(results, size=(reps, len(results))).mean(axis=1)
    return np.percentile(means, [2.5, 97.5])

for correct, n in [(41, 50), (820, 1000)]:
    lo, hi = wilson(correct, n)
    results = np.array([1] * correct + [0] * (n - correct))
    blo, bhi = bootstrap(results)
    print(f"{correct}/{n} = {correct/n:.0%}   Wilson {lo:.3f}-{hi:.3f}   bootstrap {blo:.3f}-{bhi:.3f}")
41/50 = 82%   Wilson 0.692-0.902   bootstrap 0.700-0.920
820/1000 = 82%   Wilson 0.795-0.843   bootstrap 0.796-0.843

Both lines report 82%. On 50 questions the plausible range is roughly 69% to 90% — twenty points wide. On 1,000 it narrows to under five. The two methods agree closely, which is reassuring; the bootstrap is the one to reach for when your metric is not a simple proportion. Now the scenario from the opening, simulated directly: two models with the same true accuracy of 80%, each tested on n questions, ten thousand times.

import numpy as np
rng = np.random.default_rng(42)

true_acc, trials = 0.80, 10_000          # two models with the SAME true accuracy
for n in [50, 200, 1000]:
    a = rng.binomial(n, true_acc, size=trials) / n
    b = rng.binomial(n, true_acc, size=trials) / n
    gap = np.abs(a - b)
    print(f"{n:5d} questions: gap of 5+ points in {np.mean(gap >= 0.05):.0%} of runs, "
          f"largest gap {gap.max():.0%}")
   50 questions: gap of 5+ points in 53% of runs, largest gap 30%
  200 questions: gap of 5+ points in 21% of runs, largest gap 17%
 1000 questions: gap of 5+ points in 0% of runs, largest gap 7%

With 50 questions, equal models differ by five points or more in more than half of all runs, and occasionally by thirty. Even 200 questions leave a one-in-five chance of a five-point phantom gap. When both models answer the same questions, a paired comparison — counting only the items where they disagree — is more sensitive than comparing two overall scores, and it is what you should use for A/B decisions. The general rule: before you celebrate a difference, ask how big a difference noise alone would produce.

Goodhart’s law and the limits of scores

“When a measure becomes a target, it ceases to be a good measure.”Marilyn Strathern’s phrasing of Goodhart’s law, “Improving ratings: audit in the British University system” (1997)

The economist Charles Goodhart made the original observation about monetary targets in 1975; Strathern’s compact version is the one everyone quotes. Goodhart’s law haunts machine learning because optimisation is what we do. Tune prompts against a fixed eval set for weeks and you will overfit it, exactly as a model overfits training data. Train against a learned reward and the policy finds the reward model’s blind spots. Compete on a public leaderboard and the field collectively overfits the benchmark — through contamination, selective reporting, or simply thousands of design choices each kept because it nudged the number up.

The question

Your team’s new prompt raises the internal eval score from 78% to 84%, but you have tuned against the same 300 items for a month. Ship it?

Choose one to hear its case.

This guide’s framing No score is the truth about a model; each is a narrow, noisy view. Use several kinds of evidence that fail differently — automatic metrics, a calibrated judge, human review of samples, and live monitoring after release (see MLOps and Serving) — and trust conclusions that all of them support.

Quick check

A model generates 10 solutions to a problem and 2 pass. What is pass@1?

With k = 1 the formula reduces to c/n = 2/10.

Model A scores 80% and model B 76% on 50 questions. What can you conclude?

Each 95% interval is roughly ±10 points at this size; equal models often differ by more than four points.

Which practice best reduces position bias in an LLM judge?

Swapping order cancels a preference for first or second place; inconsistent verdicts are treated as ties.

// Pause and reflect

  1. Think of a model comparison you have seen reported (a launch post, a colleague’s slide). How many test items was it based on, and would the gap survive a confidence interval?
  2. What three kinds of item would you put in an eval set for an assistant your organisation might deploy, and how would you grade each?
  3. Where in your own work is a measure in danger of becoming a target?

Your notes are saved privately in this browser.

Try this

Take any two scikit-learn classifiers on load_breast_cancer, record which test items each gets right, and compare them three ways: the difference of overall accuracies with Wilson intervals, a bootstrap of the difference (resample item indices, recompute both accuracies on the same resample), and a count of items where exactly one model is right. Which method lets you say the most with the fewest items?

Key terms on this page

Sources & further reading