Contamination: when the test leaked
Models learn from huge amounts of web text, which can include the very test questions used to judge them. Learn the signs, and how held-out and fresh test sets protect you.
In 60 seconds
Contamination: when the test leaked
Models learn from huge amounts of web text, which can include the very test questions used to judge them. Learn the signs, and how held-out and fresh test sets protect you.
How the test gets in
- Web crawls: benchmark questions and answers are posted on GitHub, blogs, forums and course notes. Large crawls pick them up.
- Rewritten copies: a question translated into another language or reworded in a tutorial still teaches the answer.
- Fine-tuning data: instruction datasets sometimes include benchmark-style items, or items generated by models that saw them.
- Your own leaks: you paste test cases into prompts while debugging, or add failed test items to training data "to fix them".
Signs that a score may be contaminated
| Sign | What you see | Why it points to leakage |
|---|---|---|
| Old beats new | Much better on items published long ago than on similar new items. | Old items had more time to spread across the web before training. |
| Paraphrase drop | Accuracy falls when you reword questions or change the numbers. | Real skill survives rewording. Memory of the exact text does not. |
| Verbatim recall | Given the start of a test item, the model completes it word for word. | It has seen the item text, not just the topic. |
| Too good to be true | A huge jump on one benchmark but no change on related tasks. | Real improvements usually spread across similar tasks. |
Checking for overlap
def ngrams(text, n=8):
words = text.lower().split()
return {tuple(words[i:i + n]) for i in range(len(words) - n + 1)}
def overlap(test_item, training_ngrams, n=8):
grams = ngrams(test_item, n)
if not grams:
return 0.0
return len(grams & training_ngrams) / len(grams)
training_text = "... your fine-tuning data joined together ..."
train_grams = ngrams(training_text)
score = overlap("the test question you want to check goes here in full", train_grams)
print(f"overlap: {score:.0%}")Protecting your evaluation
- 1
Hold data out
Split off test data before any training or prompt tuning, and never look at it while tuning. - 2
Keep it private
Do not post your test set online or paste it into public tools. - 3
Refresh it
Add fresh items written after the model's training cutoff, for example from this month's real user questions. - 4
Compare old and new
If the model does much better on old items than new ones, investigate.
Watch and read more
Lab
A small contamination check: an n-gram overlap script plus an old-versus-reworded test.
The problem
- Copy the starter script. Paste in any text you are allowed to use as a stand-in for "training data" (for example, a public-domain article or your own notes).
- Write five "test items": two copied from inside that text, one heavily reworded from it, and two brand new.
- Run the script and record the overlap for each item. Which items does it catch? Which does it miss?
- Write two sentences on what this check can and cannot tell you.
Practice lab
Edit the code and run it. Python runs in your browser — nothing is sent to our server.
No output. Add print(...) to see values.
You are done when
Hard questions
Try to answer before you reveal. If you can answer these, you understood the lesson.
Q1Why is the number 8 a trade-off in an 8-gram check?Reveal
Short n-grams (say 3 words) match common phrases like "one of the", so many clean items look contaminated. Long n-grams (say 20 words) only match near-exact copies, so light edits hide real leaks. A middle value balances false alarms against misses. Teams often try several values and look at the matches by hand.
Q2A vendor says their model "has no contamination" on a public benchmark. What would you ask?Reveal
Ask how they checked: which training data they searched, what matching method and threshold they used, whether they checked for paraphrases and translations, and whether they report results on items published after their training cutoff. Then run your own reworded or fresh items. A claim without a method is not evidence.
Questions people ask
Is contamination the same as cheating?
Usually not. Most contamination is accidental: training sets are so large that nobody reads them all. But the effect is the same as cheating for the person reading the score, so it must be checked.
Can I check a closed model for contamination?
Only indirectly, because you cannot see its training data. Use the signs: compare old and new items, reword questions, and test whether it completes test items word for word. Treat the results as evidence, not proof.
If a model has memorised an answer, is that always bad?
Not for users: remembering facts is useful. It is bad for measurement, because the test was meant to check a skill such as reasoning or coding on new problems. A memorised answer tells you nothing about the next new problem.
How often should I refresh my private test set?
There is no fixed rule. Add new real examples regularly, especially after your product or users change. Keep a stable core set so you can compare over time, plus a fresh slice that no model could have seen.
Lesson test
5 questions. Get 3 right (60%) to pass and complete this lesson.