Eval study notes · Coding practice set
Start here. The situation decides the tool, so this page goes situation first and mechanics second. It answers the three questions from the pilot video, when to use McNemar, when to use Wilson, and where the probability in a confidence interval comes from, and then gives one table for everything else.
Most of the confusion in eval statistics comes from three unrelated numbers that all get called “p” or “probability”. Keep them in separate boxes.
A confidence interval answers one question: how much would this score move if the benchmark had been written with different questions? Here is the 72% example from the video, step by step.
The formula to remember is SE = √(p̂(1 − p̂)/n). The square root of n is why halving the error bar takes four times as many questions.
Use it when two versions of something answer the same questions, and each answer is right or wrong. Situations on an eval platform where this comes up:
Why it beats comparing two error bars. On the same questions, easy questions are easy for both models and hard ones are hard for both. Those cancel out of the difference. Only the questions where the two disagree carry evidence. McNemar is a coin-flip test on just those disagreements.
How to compute it. Count b = questions A got right and B got wrong, and c = the reverse. If the two models were equally good, each disagreement would be a fair coin. The exact test is a binomial test of b out of b + c at one half.
from scipy.stats import binomtest
b, c = 40, 55 # A-only wins, B-only wins
binomtest(b, b + c, 0.5).pvalue # 0.15The paired interval on the difference is (c − b)/n ± 1.96·√(b + c − (c − b)²/n)/n. For the example that gives 3.0 ± 3.8 points, from −0.8 to +6.8. Report that interval first and the p-value second.
When it is not the right tool:
| Situation | Use instead |
|---|---|
| Different question sets (two benchmark versions, two user groups) | Unpaired two-proportion test, or the Newcombe interval for the difference |
| Scores are not right/wrong (judge score 1 to 10, F1, BLEU) | Paired t-test on per-question differences, or a paired bootstrap |
| Several samples per question (pass@1 averaged over 8 samples) | Average per question first, then a paired bootstrap or paired t-test on those averages |
| Three or more systems | Pairwise McNemar with a Holm correction (or Cochran’s Q as an overall test first) |
One line for the interview: “On the same items I never compare two error bars. I look at the discordant pairs. McNemar is a sign test on the questions where the two disagree, and it is why the platform must store per-question results.”
Use it for a single pass rate whenever n is small or the rate is near 0% or 100%. In practice, use it by default: when the simple interval is fine, Wilson agrees with it.
Situations where the simple ±1.96·SE interval (the “Wald” interval) breaks:
Why Wald breaks. It assumes a symmetric bell around p̂, but near 0 or 1 the true distribution is lopsided and squashed against the wall. Wilson solves the problem the other way round. It asks which true values of p would make the observed score unsurprising, which keeps it inside 0 to 1 and gives it honest coverage.
from statsmodels.stats.proportion import proportion_confint
proportion_confint(1, 30, method="wilson") # (0.006, 0.167)
proportion_confint(1, 30, method="beta") # Clopper-Pearson, more conservativeIf guaranteed coverage matters, as in a safety sign-off, use Clopper-Pearson (“exact”). It is a bit wider than Wilson.
| You have | You want to know | Reach for | Read |
|---|---|---|---|
| One pass rate | How precise is it? | Wilson interval (Wald is fine for large n and mid-range scores) | Ch 7 |
| One score that is not a pass rate (mean judge score, F1, p95 latency) | How precise is it? | Bootstrap: resample questions, recompute, take the 2.5th and 97.5th percentiles | Ch 7 |
| Questions that come in groups (5 questions per passage, tasks per repo) | How precise is it, honestly? | Cluster bootstrap or clustered standard errors; resample the groups | Ch 7 |
| Two systems, same questions, right/wrong | Is the gap real? | McNemar, plus the paired interval on the difference | Ch 8 |
| Two systems, same questions, continuous scores | Is the gap real? | Paired t-test, paired bootstrap or permutation test | Ch 8 |
| Two systems, different questions | Is the gap real? | Two-proportion test or Newcombe interval | Ch 8 |
| A benchmark, before you run it | Can it even detect a 2-point change? | Minimum detectable effect ≈ 2.8 × SE of the difference | Ch 9 |
| k samples per problem | What is pass@k? | The unbiased estimator 1 − C(n−c, k)/C(n, k) | Ch 9 |
| Many benchmarks × checkpoints, or many slices | Which movements are real? | Holm for release gates; Benjamini-Hochberg for dashboards | Ch 10 |
| An LLM judge and some human labels | Can I trust the judge? | Cohen’s kappa plus the confusion matrix; human-human agreement as the ceiling | Ch 11 |
| Pairwise preference votes | A ranking with error bars | Bradley-Terry fit, with bootstrap rank ranges | Ch 11 |
| A score that looks too good | Is the test set leaked? | Contamination checks: n-gram overlap, perturbed or fresh versions | Ch 12 |
| Live users | Did the change help? | A/B test randomised by user, CUPED, no peeking without a sequential method | Ch 12 |