Every August, a rumor moves through Class 12 WhatsApp groups: this year's board paper has "leaked," and a photographed question sheet is already circulating before the exam bell rings. If it were true, a topper's 98% would tell you nothing about whether they understood thermodynamics or coordinate geometry. It would only tell you they could read a photograph the night before. The score would still say "98%" on the report card. The number would be real. What it claims to measure would not be.
This is exactly the failure mode this chapter is about, except the "exam" is a machine learning benchmark, the "leak" happens at the scale of hundreds of billions of web pages, and almost nobody photographs anything on purpose. A large language model's pretraining corpus is built by crawling the open internet. The internet also happens to contain solved copies, discussion-forum reposts, and translated versions of nearly every well-known benchmark, because researchers publish their test sets so other researchers can use them. When a model is pretrained on that crawl and later evaluated on those same benchmarks, some fraction of the "test" it is taking was sitting in its "textbook" all along. This is called data contamination, the pipeline built to catch and remove it before training is called decontamination, and the resulting inflated score is test set leakage. Grade 8-11 you learned to hold out a test set and never touch it during training. Grade 12 research methods asks the harder question: what if the test set touched your training data before you ever saw either one?
What counts as leakage
"Leakage" is a family of failures, not one bug, and it is worth separating them because each has a different fix.
- Target/feature leakage. A feature that is only known after the outcome occurs sneaks into the training features. A hospital readmission model that includes "discharge disposition code" as a predictor is cheating: that code is assigned once the outcome is already determined. Fix: audit every feature's timestamp relative to the label's timestamp.
- Temporal leakage. Rows are shuffled randomly before splitting, so a model trained on data from March can be evaluated on data from January, effectively "predicting the past" using information from the future relative to some test rows. Fix: split chronologically, never randomly, for any time-ordered problem.
- Group leakage. The same underlying entity (a customer, a patient, a document) contributes multiple rows, and a random split scatters that entity's rows across both train and test. The model partially memorizes entity-specific quirks rather than learning a generalizable pattern. Fix: split by group identifier (e.g.
GroupKFoldkeyed on customer ID), never by row. - Benchmark/corpus contamination. The exact text of a test item, or a close paraphrase of it, appears inside the pretraining or fine-tuning corpus. This is the dominant leakage risk in modern LLM research, because pretraining corpora are scraped at a scale no human can manually audit, and it is the focus of the rest of this chapter.
Why this breaks the evaluation contract
A held-out test accuracy is only a trustworthy estimate of real-world generalization under one condition: the model's parameters were fit using zero information about which specific items would appear in the test set, directly or indirectly. Statisticians call this the requirement that train and test be drawn independently, with the test set never entering the fitting process. Break that condition and test accuracy stops estimating "how well does this model generalize" and starts estimating some blend of generalization and memorization, in proportions you don't know unless you measure them.
The dangerous part is that this blend still looks completely normal. A contaminated model does not throw an error or show a suspiciously perfect score on every item. It scores unusually well on the contaminated slice and normally on everything else, and the two slices average together into one number that looks perfectly plausible. A team comparing two model checkpoints, or two labs comparing their published leaderboard numbers, has no way to tell from the headline score alone whether they are comparing reasoning ability or crawl overlap. This is precisely why Sainz et al. (EMNLP Findings 2023, "NLP Evaluation in Trouble: On the Need to Measure LLM Data Contamination for each Benchmark") argue that contamination has to be measured and reported per benchmark, not assumed away, because a clean-looking leaderboard number carries no built-in warning label.
Detecting contamination: three lines of defense
Because pretraining corpora run into the trillions of tokens, you cannot manually read every document, and you cannot even compare every pair of documents directly: checking all pairs among a trillion tokens' worth of documents is a computation that no cluster on Earth finishes before the sun burns out. Real decontamination pipelines use three filters of increasing subtlety and decreasing cheapness, and each one catches leakage the previous one misses.
Stage 1, exact-hash matching. Normalize whitespace and case, hash each document with something like SHA-256, and store the benchmark's hashes in a set. A candidate training document is rejected if its hash appears in that set. This is essentially free computationally (hashing and a set lookup are both close to constant time per document), but it only catches byte-for-byte identical text. Change one word, one punctuation mark, or the paragraph order, and the hash changes completely, and the duplicate slides through untouched.
Stage 2, fuzzy matching via shingles and MinHash. Break each document into overlapping word n-grams ("shingles") and compare documents by the Jaccard similarity of their shingle sets, catching paraphrases and light edits that exact hashing misses. Computing this directly for every pair is still too slow at web scale, so real pipelines use MinHash: each document's shingle set is compressed into a short signature (dozens to a few hundred numbers) such that the fraction of matching positions between two signatures is an unbiased estimator of the true Jaccard similarity, and Locality-Sensitive Hashing (LSH) buckets similar signatures together so only plausible near-duplicates ever get compared directly. This is the method Lee, Ippolito, Nystrom, Zhang, Eck, Callison-Burch, and Carlini formalize in "Deduplicating Training Data Makes Language Models Better" (ACL 2022): they call it NearDup, pair it with an exact-substring method built on suffix arrays, and show that deduplicating corpora like C4 this way measurably reduces verbatim memorization in the resulting language models.
Stage 3, semantic/embedding filtering. Encode both the benchmark item and the candidate document with an embedding model and flag high cosine similarity. This is the only stage that can catch contamination where the surface text has been rewritten enough that even shingle overlap drops, or, if a multilingual embedding model is used, where the benchmark item has been translated into another language entirely. It is also the most expensive stage, since it needs a forward pass through a neural encoder per document, which is why it typically runs last, on the much smaller set of candidates that survived stages 1 and 2.
The worked example below traces exactly how a one-word paraphrase defeats stage 1 but produces a far higher similarity signal under stage 2 than an unrelated document would (Jaccard 0.5 vs. near 0), even though this particular single-word edit at k=3 falls just short of a typical production flagging threshold, illustrating why real pipelines tune shingle length and thresholds carefully rather than relying on a single fixed cutoff, which is the mechanism the diagram after it visualizes end to end.
Worked example: tracing a near-duplicate through stages 1 and 2
Take a genuine benchmark question and a training-corpus document that paraphrases it by swapping exactly one word:
train_doc = "the mitochondria is the powerhouse of the cell and produces atp"
test_q = "the mitochondria is the powerhouse of the cell that produces atp"
Both are eleven-word sentences; the only difference is "and" in train_doc versus "that" in test_q, at word position 8 (0-indexed). Here is the full pipeline check, stage 1 then stage 2, with nothing left :
import hashlib
def shingles(text, k=3):
"""Return the set of k-word shingles (n-grams) in text."""
words = text.lower().split()
return {tuple(words[i:i + k]) for i in range(len(words) - k + 1)}
def jaccard(set_a, set_b):
return len(set_a & set_b) / len(set_a | set_b)
def normalize(text):
return " ".join(text.lower().split())
def doc_hash(text):
return hashlib.sha256(normalize(text).encode("utf-8")).hexdigest()
train_doc = "the mitochondria is the powerhouse of the cell and produces atp"
test_q = "the mitochondria is the powerhouse of the cell that produces atp"
# Stage 1: exact-hash check
benchmark_index = {doc_hash(test_q)}
print(doc_hash(train_doc) in benchmark_index) # False
# Stage 2: fuzzy shingle check
train_shingles = shingles(train_doc, k=3)
test_shingles = shingles(test_q, k=3)
print(round(jaccard(train_shingles, test_shingles), 3)) # 0.5
Trace stage 1 by hand first. SHA-256 has the avalanche property: changing even one character of the input produces a completely different digest with overwhelming probability. Since normalize(train_doc) and normalize(test_q) differ in one word, their hashes are two unrelated 64-character hex strings, so the membership test returns False. The near-duplicate walks straight through stage 1.
Now trace stage 2. Splitting each sentence into words gives eleven tokens each, positions 0 through 10, identical everywhere except position 8. With k=3, each document yields 11 - 3 + 1 = 9 trigrams, at offsets i = 0 through 8. Offsets 0 through 5 don't touch position 8 in either document, so those six trigrams are byte-for-byte identical between train_doc and test_q: (the, mitochondria, is), (mitochondria, is, the), (is, the, powerhouse), (the, powerhouse, of), (powerhouse, of, the), (of, the, cell). Offsets 6, 7, and 8 each include position 8, so all three diverge: the train side gives (the, cell, and), (cell, and, produces), (and, produces, atp), while the test side gives (the, cell, that), (cell, that, produces), (that, produces, atp), six distinct trigrams found in only one document each. The intersection has 6 shingles; the union has 6 + 3 + 3 = 12. Jaccard similarity is 6 / 12 = 0.5, exactly what the code prints. A production pipeline flags anything above a tuned threshold, commonly somewhere in the 0.7-0.85 range depending on shingle length, so a real system would need a slightly larger shared span to trigger a flag at k=3, but the direction of the result is the whole point: a single swapped word barely moves the fuzzy-match score, while it destroys the exact-hash match completely. That gap between the two stages is precisely why relying on exact-hash dedup alone leaves a benchmark vulnerable to any paraphrase, reformatting, or light edit, which is why Brown et al.'s GPT-3 paper ("Language Models are Few-Shot Learners," NeurIPS 2020, Appendix C) already had to measure contamination using flexible n-gram overlap rather than exact matching, after finding that a bug in their own filtering pipeline had let some benchmark overlap through despite an intended exact-style filter.
Note also what shingle length does to sensitivity: using k=1 (single words as a set) on the same pair gives 9 unique words per sentence, 8 shared, union 10, Jaccard 0.8, high enough to look suspicious for almost any two sentences on the same topic, benchmark leakage or not. Long shingles (research pipelines often use double digits, following the 13-gram overlap Brown et al. used for GPT-3's contamination analysis) are specific enough that a high score is genuinely rare unless real overlap exists, which is why shingle length is a tuned hyperparameter of the decontamination pipeline, not an arbitrary choice.
Worked example: how much does leakage inflate a reported score
Detection tells you contamination exists; it does not by itself tell you how much it distorts a headline metric. Build the arithmetic explicitly. Suppose a 200-item benchmark is audited with the MinHash pipeline above and 30 items (15%) turn out to be near-duplicates of documents in the model's pretraining corpus, leaving 170 genuinely unseen items. Suppose further, as a modeling assumption grounded in how memorization behaves, that the model answers the 30 contaminated items correctly 96% of the time (near ceiling, because it has effectively memorized them, not reasoned them out) while its true generalization accuracy on genuinely unseen items is 62%. The reported accuracy anyone sees on the leaderboard is the accuracy over all 200 items combined:
reported = (n_clean · acc_clean + n_leaked · acc_leaked) / n_total
reported = (170 × 0.62 + 30 × 0.96) / 200 = (105.4 + 28.8) / 200 = 134.2 / 200 = 0.671
A single leaderboard number of 67.1% is reported, while the model's actual generalization ability, the number that predicts how it will perform on genuinely new questions after deployment, is 62%. The 5.1-point gap is entirely a property of the contaminated 15% of the benchmark; it says nothing about the model getting better at the underlying task.
Common misconception
The misconception to correct directly: "My evaluation is clean because I never trained on my test set myself." This confuses two very different training events. When you fine-tune or evaluate a foundation model, you are almost never starting from randomly initialized weights, you are starting from a base model that already underwent a pretraining run on a web-scale crawl, one you did not build and typically cannot fully inspect. Contamination can be inherited from that upstream pretraining corpus even if your own downstream pipeline never touches the test set at all. This is also why a small train/test accuracy gap in your own fine-tuning run is not proof of a clean evaluation: that gap only measures overfitting relative to your training run. If the base model already saw the benchmark during pretraining, both your "train" and "test" numbers can be inflated by the same upstream leak, together, so the gap between them stays small while both are wrong in the same direction. Ruling out contamination requires checking the benchmark against the base model's pretraining data (or, when that corpus isn't public, against a proxy like a large web crawl) with the kind of pipeline described above, not just watching your own fine-tuning curves.
Active recall
Attempt each question before reading its answer.
Q1. Why does deduplicating a pretraining corpus using only SHA-256 exact-hash matching fail to guarantee a contamination-free benchmark?
Q2. Using the same per-group accuracies as the worked example (96% on leaked items, 62% true generalization on clean items), suppose a stricter audit reclassifies more of the 200-item benchmark as contaminated, raising the leaked count from 30 to 50 items (170 clean items become 150). Recompute the reported accuracy and the inflation gap versus the true 62%.
Q3. Now suppose forensic analysis on the original scenario reveals two corrections at once: the 30 "leaked" items were actually near-duplicates rather than exact duplicates, so the model's accuracy on them is really 80% (imperfect memorization of a paraphrase, not the 96% assumed for a verbatim copy), and MinHash detection additionally reclassifies 10 more originally-clean items as leaked, so the leaked count rises from 30 to 40 (clean count falls from 170 to 160). Recompute the reported accuracy and the inflation gap, then state whether the gap widened or narrowed compared to the original 5.1-point gap, and explain which of the two corrections is driving that direction.
Q4. A bank trains a loan-default model on transaction-level rows. Each customer contributes many rows, spread across several years. Rows are shuffled and randomly split 80/20 into train and test. What leakage type is this, and what is the correct fix?
Q5. Three contamination scenarios: (a) a benchmark question copied verbatim onto a public forum, (b) the same question paraphrased and posted before the model's pretraining cutoff, (c) the same question translated into Hindi and posted online. For each, name which decontamination stage (exact-hash, fuzzy shingle/MinHash, or semantic embedding) is the first one capable of catching it, and explain why the earlier stages fail on (c) specifically.
Q6. A team reports train accuracy of 91% and test accuracy of 89% on their fine-tuned model and concludes there is no contamination because the gap is small. Explain why this reasoning is insufficient.
A1. SHA-256 (and any exact-hash scheme) maps even a single-character change in the input to a completely different, unrelated digest. It only flags byte-for-byte identical text after normalization. Any paraphrase, reordering, translation, or light edit of a benchmark item produces a different hash and passes straight through, exactly as traced in the worked example, where changing one word out of eleven left the SHA-256 check returning False while the underlying trigram Jaccard similarity was still 0.5.
A2. reported = (150 × 0.62 + 50 × 0.96) / 200 = (93 + 48) / 200 = 141 / 200 = 0.705. Reported accuracy rises to 70.5%, an 8.5-point gap above the true 62% (versus 5.1 points at 15% contamination). Increasing the contaminated fraction, holding the per-group accuracies fixed, widens the inflation gap, because a larger share of the blended average is being pulled toward the near-ceiling leaked score.
A3. reported = (160 × 0.62 + 40 × 0.80) / 200 = (99.2 + 32) / 200 = 131.2 / 200 = 0.656. Reported accuracy is 65.6%, a 3.6-point gap above 62%, narrower than the original 5.1-point gap. Two effects are acting in opposite directions here: raising the leaked count from 30 to 40 (as in Q2) pushes the gap wider on its own, but correcting the leaked-item accuracy down from 96% to 80% (because near-duplicate memorization is far less perfect than verbatim memorization) pulls the gap narrower, and by more. The accuracy correction dominates the fraction increase, so the net gap shrinks. The general lesson: the size of the inflation gap depends on both how much of the benchmark is contaminated and how perfectly the model has memorized the contaminated portion, and a naive audit that only counts "how many items overlap" without checking "how well were they memorized" can get the direction of the correction wrong.
A4. Group leakage. The same customer's rows land in both train and test, so the model can partially memorize customer-specific behavior (a customer's typical spending pattern, say) rather than learning patterns that generalize to new customers. The reported test accuracy overstates performance on genuinely new applicants. Fix: split by customer ID (e.g. GroupKFold keyed on the customer), guaranteeing no customer's rows appear in both sets, ideally combined with a chronological cutoff if default risk also drifts over time.
A5. (a) Exact-hash catches it immediately, since the text is byte-for-byte identical after normalization. (b) Exact-hash fails (the wording differs), but fuzzy shingle/MinHash catches it, since most word n-grams are still shared. (c) Both exact-hash and fuzzy shingle/MinHash fail, because they operate on the surface text and a different language produces almost entirely different characters and word n-grams with essentially zero overlap. Only a semantic filter using a multilingual (cross-lingual) embedding model, one specifically trained so that a sentence and its translation land close together in embedding space, can flag (c); a monolingual embedding model would miss it too.
A6. Train/test gap only measures overfitting relative to this team's own fine-tuning run, on top of whatever the base model already knew. If the base model's pretraining corpus already contained the benchmark, that contamination inflates both train and test performance together, in the same direction, by roughly the same amount, so the gap between them can stay small even while both numbers are wrong. A small gap rules out overfitting during fine-tuning; it does not rule out contamination inherited from upstream pretraining, which requires directly checking the benchmark against the pretraining corpus (or a public proxy for it) with an exact/fuzzy/semantic pipeline, not inference from the fine-tuning curves alone.
Practice Exercises
Now it is time to practice! Complete these challenges to solidify your understanding:
- Exercise 1: Write a short program that demonstrates the core concept from this chapter. Test it with at least 3 different inputs.
- Exercise 2: Find a real-world example where data decontamination and test set leakage is used in an Indian company (like TCS, Infosys, Flipkart, or ISRO). Write a paragraph explaining the connection.
- Exercise 3: Create a mind-map connecting data decontamination and test set leakage to at least 3 other topics you have studied.
Key Takeaways — Summary and Recap
Let us recap what we covered: the core ideas behind data decontamination and test set leakage, how they connect to real-world applications, and why they matter for your journey in computer science. Remember these key points as you move forward. For competitive exam preparation (CBSE, JEE, BITSAT), focus on understanding the WHY behind each concept, not just the WHAT.