Python · cell 1
Contamination means that a model has seen the test before it is scored. The test questions, their answers, or close copies of them were in its training data, or the model looked them up while it was being tested. It matters because a contaminated score looks exactly like an honest one. A model that memorized the answers and a model that can solve the problems post the same number, and the number alone cannot tell you which one you have.
Two experiments from 2024 show how large the effect can be. The first wrote a new copy of a benchmark. GSM8K is a set of grade-school math word problems, and nearly every lab reports its 1,319-question test set. Scale AI paid writers to produce 1,205 new problems in the same style, matched to GSM8K in how often people solve them, how many steps they take, and how large their answers are. The new set, GSM1k, was kept private (Zhang et al. 2024). A model that has learned grade-school math should score about the same on both sets. Several did not. The worst models lost up to 8 points on GSM1k. The Phi and Mistral families lost points at almost every size and version, and the Yi, Xwin, Gemma, and CodeLlama families showed the same pattern more weakly. Across models, the gap grew with how likely the model was to write out GSM8K's own test problems.
The second experiment used the calendar. LiveCodeBench collects programming-contest problems and tags each one with the date it was released (Jain et al. 2024). DeepSeek's 33-billion-parameter base model for code passed about 60% of the LeetCode problems released in May 2023. It passed almost none of those released in September 2023, the month the DeepSeek models came out. Problems from a second contest site, AtCoder, showed no such drop. The likely reading is that the older LeetCode problems were in its training data.
§22.1 treated an evaluation as an estimator. The benchmark score , the model's average grade on the published items, is meant to estimate , its expected grade on fresh items written the same way, items it cannot have seen. Average over everything random in it, the draw of items and the sampled answers. When that average differs from , the measurement is biased. Contamination is the part of that bias that comes from the model having met the test items before.
This section answers three questions you can ask from outside the lab that trained the model. Did the model see the test? Can you prove it to a skeptic, with a p-value that means what it says? And how many points of the score did the leak buy? Each question has its own tools, and each tool is blind to some kinds of leak, so we start by naming the kinds.
Think of a student who saw the exam beforehand. They might have seen the exact paper, the paper with the answers filled in, a study guide that rephrased the questions, a translated copy, or only the answer key. Each leaves a different trace. A student who memorized the paper can recite the questions word for word. One who studied a rephrased guide cannot recite them, but still gets them right. One who saw only the key can give the answers but not the questions. A detective who checks only for word-for-word recitation catches the first student and misses the rest.
Language models leak in the same ways, and in a few more. Benchmarks are posted on the web, quoted in papers, pasted into homework forums, and copied into instruction datasets. A model that saw a benchmark can write synthetic training problems that carry pieces of it into a second model, one that never saw the original (§8.4). The risk compounds when models train on their own outputs, as in synthetic-data and self-training loops. And an agent that can browse the web may look the answer up while it is being tested.
In plain words, the leakage bias is how much better the model does on the items that exist than it would do on new items written the same way. It is usually positive. Nothing in the definition forces a sign, though, and a model can see an item and gain nothing from it.
That last point is the most common confusion in this area. Seeing an item is not the same as benefiting from it. Magar and Schwartz (2022) tested this directly. They pretrained BERT encoders (§7.1) on Wikipedia mixed with labeled examples from downstream tasks, then fine-tuned them on each task. Comparing examples seen in pretraining with examples not seen, they measured two separate effects. Memorization means the model recognizes the leaked examples. Exploitation means it scores higher on them. Some tasks showed both. Others showed memorization with no exploitation. How strong each effect was depended on factors such as the number of copies and the size of the model.
So every audit has two questions. Detection asks whether an item was seen. Effect asks how much seeing it helped. Here is how the kinds of leak meet the detectors this section builds. Read the table now as a map, and again at the end as a summary.
| kind of leak | n-gram overlap with training data | per-example membership inference | order test | fresh, perturbed, or time-split items |
|---|---|---|---|---|
| verbatim questions | yes | weakly, needs many copies | yes, if the file kept its order | yes, if the copy helped |
| questions with answers | yes | weakly | yes, if ordered | yes |
| paraphrases, translations | rarely | barely | rarely | yes |
| answers only | no | no | no | yes |
| derived synthetic data | no | no | no | yes |
| test-time leaks | no | no | no | only if fresh items are not online |
| needs | the training data | log-probabilities | log-probabilities, an exchangeable benchmark | new items of the same difficulty |
The last column is different in kind. The first three detectors look for the cause, traces of the items in the model or in its data. The last one measures the effect, a score that does not carry over to new items. Only the effect matters for the leakage bias. Only the causes can be checked without writing new items. An interviewer who asks "how would you check for contamination" wants to hear both halves.
If you have the training data, the first check is a text search. An n-gram is a run of consecutive words. The overlap check of §8.3 lowercases every text, strips its punctuation, lists every n-gram of every test item, and looks each one up in the training data. Two rules turn the matches into a verdict.
Decontamination is the fix on the training side. You remove every training document that overlaps a benchmark you plan to report. §14.2 also showed the hole in it. Yang et al. (2023) trained 13-billion-parameter models on rephrased and translated copies of test sets, Llama 2 for MMLU (multiple-choice questions in 57 subjects) and GSM8K, and CodeLlama for HumanEval (Python functions written from a description). The copies passed n-gram decontamination, and the models matched or beat GPT-4 on all three. Both earlier sections built these tools. Here we look at what their reports mean.
The cell checks the counts. Each item's matches all come from one page, so the coverage against the whole crawl equals the coverage against a single training example.
Now read the output as an auditor would. The any-match rule with flags items 1, 3, and 5. Item 5 is a false positive. Its question never leaked, and the match is a stock phrase. The coverage rule (more than 50%) clears it and still flags items 1 and 3. Item 2 is a false negative under both rules, a leaked item that passes, because the paraphrase changed every run of eight words. And the answer key on page 3 leaked all five answers without tripping either rule, since an n-gram check reads only the question text.
Three lessons carry over to real audits. The threshold trades false alarms against misses, and both are real. A clean report covers only what the check can see. And none of this works without the training data, which rules it out for every closed model.
Paraphrases have a practical fix (Yang et al. 2023). Embed each test item as a vector, retrieve its nearest training examples, and ask a language model whether each pair is a rephrasing. With that check, the authors found rephrasings of 8.5% of HumanEval's 164 problems in a sample of RedPajama-Data-1T and of 15.9% in a sample of StarCoder-Data, two public pretraining sets.
Without the training data, the only witness is the model. Membership inference asks it a narrow question about one piece of text. Was this exact text in your training set? A member is a text that was in the training data, and a non-member is one that was not. We frame the question as a hypothesis test and grade it with the rates of §0.11. The true positive rate is the fraction of real members that the attack flags. The false positive rate is the fraction of non-members it flags by mistake.
In plain words, an attack ranks texts by how familiar they look to the model. Its AUC is how often it ranks a real member above a real non-member, with ties counted as half. The AUC alone can mislead (Carlini et al. 2022). An attack can reach a decent AUC by being slightly better than chance on every example while being unable to flag any single example with confidence. A contamination audit needs the confident flags, so report the TPR at a low FPR as well.
The simplest score is the loss attack, the mean log-probability of the text's tokens. For a text of tokens it is
where is the model's probability of token given the tokens before it. This is the held-out likelihood of §6.6. Training raises the likelihood of training text, so members should score higher. The trouble is that some texts are likely under any model, such as a license header or a list of numbers. Others are unlikely under any model, such as a page of rare names. So the raw likelihood mixes "seen in training" with "easy to predict". Better attacks remove the second part, and two are standard.
The zlib ratio calibrates against a generic compressor. Text that zlib also compresses well is easy for everyone, so dividing by its compressed length cancels part of the "easy" signal. Carlini et al. (2021) used it to pull memorized text out of GPT-2, the extraction attack of §19.7. We use the total log-likelihood over the compressed length in bytes,
Up to the constant , this is minus the ratio of the model's code length for to zlib's. Carlini et al. divide the per-token mean instead of the total. The two differ by the token count, and implementations vary, so say which one you use.
Min-K% Prob calibrates against the text itself (Shi et al. 2023). Its hypothesis is that an unseen text contains a few outlier tokens with very low probability, and a seen text has fewer of them. So it averages only the lowest-probability tokens.
Why should the low tail carry the signal? Because one training step raises low probabilities far more than high ones. Take a token the model gives probability 0.9 and another it gives 0.01. The result below says one gradient step of size adds at most to the first token's log-probability. It adds at least to the second's, both to first order, at least 49 times as much. The cleanest version of the argument is for a step taken directly on the logits, the scores that the softmax turns into probabilities.
From the softmax derivative of §0.2, , so , where is the one-hot vector for . The step is . The first-order change is the gradient dotted with the step, . The sum is at least 0. It is at most , because the are nonnegative. That gives both bounds. ∎
In plain words, a token the model already predicts with probability near 1 gains almost nothing from being trained on. A token it found surprising gains between about and in log-probability. So training on a text erases its surprises first. The mean log-probability averages those gains with many near-zero gains on easy tokens, which look the same for members and non-members. The low tail concentrates them. A real step moves the parameters, not the logits, and it changes other positions too. So this is the idealization behind the heuristic, not a guarantee.
Implement three membership scores and their evaluation. All three scores must be oriented so that a higher score means "more likely a member".
min_k_prob(token_logprobs, frac=0.2) returns , the mean of the lowest of the given log-probabilities. Treat as exact, so a product that lands on a whole number in exact arithmetic is not rounded up by floating-point error.zlib_ratio(logprob_sum, text) returns the total log-likelihood divided by the length in bytes of zlib.compress(text.encode("utf-8")).auroc(scores, labels) returns the ROC AUC of scores for binary labels (1 for a member), counting ties as one half, without calling a library AUC function. It raises ValueError if either class is empty.Let us try the three scores on a model small enough to train in a cell. The model is a character 5-gram model, one of the count-based language models of §3.1. It predicts each character from the four before it, using counts from its training text. Add- smoothing adds a small count to every possible next character, so a character never seen after a context still gets a small probability. The training text is 1,500 machine-written word problems plus half of a 300-item benchmark of the same kind. That half was chosen at random and leaked once. Members and non-members come from the same generator, so the only difference between them is membership.
All three attacks work, with AUCs between 0.69 and 0.73. All three are also weak in the sense that matters. At a 5% false positive rate they catch about one member in ten. If you had to say of one particular item "this was in the training set", you could not.
These AUCs happen to match the range reported on WikiMIA (Shi et al. 2023), a benchmark built to test these attacks on real models. There, on the original texts, the loss and zlib attacks scored 0.70 to 0.72 on LLaMA-30B and LLaMA-65B, and Min-K% Prob scored 0.74. That looked like a success. It was not quite one.
WikiMIA's members are Wikipedia articles about events, created before 2017, which many models trained on. Its non-members are articles about events, created in 2023 or later, which those models cannot have seen. That split guarantees the labels are right. It also makes the two groups talk about different things.
Das, Zhang, and Tramèr (2024) asked whether you need the model at all. They built blind baselines, classifiers that never query any model and look only at the text. A bag-of-words classifier separates WikiMIA's members from its non-members with an AUC of 0.99 and a TPR of 94.7% at 5% FPR. The best published attack reached 0.84 and 43.2%. On all 8 published membership-inference evaluation sets they examined, a blind baseline beat the best attack.
Here is the same thing in miniature. The non-members now come from a "later" generator whose problems drift to a different mix of topics. The model has seen every one of those topics in its training text. The blind baseline is a naive Bayes classifier. It learns from half the items how much more often each word appears in members than in non-members, and it scores a text by adding up those log-odds.
The blind classifier reaches an AUC of 0.92 and beats every attack that queried the model. It learned only that "eggs" and "cards" appear in later problems and "apples" and "books" in earlier ones. On an evaluation set where a blind classifier wins, you cannot tell how much an attack learned from the model, because the attack can score well by detecting the drift.
Duan et al. (2024) ran the clean experiment. They used Pythia, a suite of open models from 160 million to 12 billion parameters trained on the Pile, an open corpus of about 800 GB of text. They drew members and non-members from the same domains and the same time, and found almost every attack near chance.
In an interview, this is the answer to "why not just run Min-K% on the benchmark?" On one item seen once, it cannot tell you anything you can defend.
An AUC of 0.55 is useless for one item and can be decisive for a thousand. A benchmark audit does not need to classify items. It only needs to decide whether the benchmark as a whole was seen, and weak evidence on each item adds up across many items. The next result says how fast.
For independent and , the difference is . So , and ties have probability zero. The difference of two means of scores each has mean and variance . So the statistic has mean . ∎
The separation is fixed by the attack, but the expected grows with the square root of the number of items. So an attack barely better than chance becomes decisive on a large enough set, as the next example shows.
The cell checks the arithmetic and then runs 500 simulated audits of that size.
The simulated audits average a near 4, and 93% of them reach . A signal too weak to flag any single item is strong enough to convict the set. This is the idea behind dataset inference (Maini et al. 2024). It combines several attacks that each carry a little signal for the data at hand, and it runs one statistical test over a whole set of documents. It separated the training and test splits of different Pile subsets with p-values below 0.1 and no false positives.
The catch is in the theorem's setup. You need non-members from the same distribution, such as a held-out split written at the same time. That is exactly what membership-inference evaluations kept getting wrong. The next test removes the need.
A benchmark is published as a file, and a file has an order. Suppose the order carries no meaning, so any shuffle of the items is as natural as the published one. A model that never saw the file then has no reason to prefer the published order. A model that trained on the file, start to finish, has learned that item 18 follows item 17, and it does prefer it. Oren et al. (2023) turned that asymmetry into a test with a guaranteed false positive rate, using nothing but log-probabilities.
Here is the idea with three items, A, B, and C, published in the order ABC. They can be arranged in orders. Score each order by the model's log-probability of the three items written out in that order. If the model never saw the file, the published order ABC is just one of six orders, and it lands on top with probability . A model that read ABC many times puts it on top every time. A file of three items can never give a p-value below , which is why real tests use many items and many shuffles.
The argument needs one property of the benchmark, defined next.
The test is a lottery among orderings. Score the published order and random shuffles by the model's log-probability of the whole file. If the model never saw the file, the published order is one more random order, and it lands at the very top with probability . In symbols, write for the model's log-probability of the examples concatenated in the given order, with a fixed separator between them. Draw independent uniformly random permutations , and set and . The null hypothesis is that the model's parameters are independent of , which is what "the model never saw this benchmark" means statistically. The permutation p-value is
In plain words, the p-value is the published order's rank among all orderings, counting from the most likely, divided by . Ties are counted against the published order.
Step 1, the orderings are exchangeable. Let be one more uniform permutation, independent of everything else. For a fixed , exchangeability gives , and and the shuffles are independent of , so . Reordering a reordered dataset composes the permutations, , so
Given , each for is uniform, because composing with a fixed permutation is a bijection of the permutation group, and they are independent. So are i.i.d. uniform permutations, independent of . Relabeling i.i.d. variables does not change their joint law. So the sequence on the right is exchangeable, and so is , which applies the same function with the same to each entry.
Step 2, a counting fact. For let , which counts itself, so . For any real numbers and any integer , the set has at most elements. Indeed, if is nonempty, pick with the smallest . Every has , so , and .
Step 3, combine. By exchangeability every has the same distribution as , so
Since takes values in multiples of , . Without ties the are a permutation of , so exactly and is uniform. ∎
This is a theorem. It holds for any model and any exchangeable benchmark, with no assumption about how the model works. Three consequences follow.
The cell simulates 4,000 innocent models. Each scores a 30-item benchmark and 20 shuffles, and the cell counts how often each version of the test rejects.
The floor of is a limit set by compute. A p-value of needs a million shuffles, each a full pass of the model over the benchmark. Oren et al. get around it with a sharded likelihood comparison test. Split the examples into contiguous shards, blocks of consecutive examples whose sizes differ by at most one. In shard , compare the published-order log-likelihood with the average over shuffles of that shard,
Then test whether the have mean zero with a one-sided -test. The statistic is , where is the mean and the sample standard deviation of the . It is compared against Student's distribution with degrees of freedom.
Why is this valid? Under the null, each has mean zero. Step 1 of the proof above, applied within one shard, says the published shard and each of its shuffles have the same distribution. When the examples are i.i.d., the are also independent, because the shards are disjoint and the model is fixed. So the -test is valid in the limit as the number of shards grows (Theorem 2 of Oren et al.). Unlike the permutation test, its guarantee is asymptotic rather than exact.
Why does it have power? The gain is not more signal. In the toy model below, a memorized order adds a bonus to the log-likelihood for every adjacent pair of items in published order. A random order of items has on average such pairs, so the published order of the whole benchmark gains about over a random one. Suppose the other effects of context on the log-likelihood have standard deviation per pair. Then either test sees a standardized signal of roughly . The gain is that the -test turns that signal into a p-value directly, with no floor. A of 7 gives without shuffles.
Each shard's mean needs only a few dozen shuffles. Oren et al. found diminishing returns beyond about 25 shuffles per shard and a sweet spot of 10 to 20 shards for 1,000 examples. Their audits of real models used 50 shards with 250 shuffles each.
The cell runs the three cases.
The widget runs the same toy model on a 1,000-item benchmark, with the sharded test on 50 shards of 20 items and 25 shuffles each. Each copy of the file in training adds a little to the bonus . On the left, the bars are the log-likelihoods of the shuffled orders and the line is the published order. On the right are both p-values as the copies grow.
You get a benchmark as a list examples and a black box seq_logprob(list_of_examples) -> float, which returns the model's log-probability of those examples concatenated in the given order. Implement both tests.
permutation_pvalue(examples, seq_logprob, P, rng) returns the permutation p-value with the correction. Draw each shuffle with rng.permutation.sharded_test(examples, seq_logprob, n_shards, m, rng) returns the p-value of the sharded test. Split the examples into n_shards contiguous shards whose sizes differ by at most one, the larger ones first (as np.array_split does). For each shard, compute its published-order log-probability minus the mean over m shuffles of that same shard. Return the one-sided p-value of a -test that these differences have a mean greater than zero.Here is the test as you would run it on an open model with Hugging Face. It cannot run in your browser, but every line maps to the cell above. The one practical constraint is the context window. Each shard is scored as a single sequence, so choose a number of shards that makes each shard fit.
Before you trust a small p-value from this code, run it on a negative control, a model trained before the benchmark existed or on unrelated data. Oren et al. used BioMedLM and GPT-2. If the control also fires, the file is not exchangeable, and the result says nothing about contamination. On MMLU, they set aside the 14 of 57 subject files on which a negative control fired.
Closed models that return only text need a different kind of test, one that asks the model to complete or fill in a test item. Neither test below gives a p-value with a guarantee, but both work through an API.
The tests so far look for traces of the leak. The most decisive evidence looks for its effect instead. Score the model on new items written to the same specification. If its score holds up, the leak did not matter, whatever the model saw. If the score drops, something did not generalize, and you have measured how much.
There are three ways to get new items.
A drop on new items is not yet a measurement of contamination, because the new items may just be harder. GSM1k matched GSM8K's human solve rates, step counts, and answer sizes for exactly this reason. Time splits cannot be matched that way. Next month's problems are whatever next month brings, harder or easier. The fix is a control group.
Take a reference model that cannot have seen any of the problems, for example one whose training data predates them all. Score it on the same problems. Whatever happened to difficulty over time happened to it too. Subtract its before-minus-after change from the target model's, and the shared trend cancels. Econometricians call this a difference in differences.
The example subtracted four accuracies. The general statement writes the same estimate as an average of per-problem differences between the two models, which gives its error bar, and it names the one assumption the subtraction rests on.
Take expectations problem by problem. and . Subtracting and using parallel trends leaves . The rearrangement is algebra. The two period means are over disjoint sets of independent problems, so their variances add. ∎
In plain words, the reference model measures how much harder the later problems are, and whatever drop is left over is the leak. The pairing matters. A and R are scored on the same problems, so a problem that is hard for both cancels inside . That makes the interval tighter than one built from four separate accuracies, and the more often the two models pass and fail the same problems, the larger the gain.
The assumption matters too. Parallel trends says that without the leak, A's accuracy would have fallen by the same amount as R's. It is most plausible when R is about as strong as A. A much weaker reference near the floor cannot drop as much as A does, which biases the estimate up. A reference that also saw the old problems, such as a newer model, makes the estimate too small, because older problems have had more time to spread across the web. LiveCodeBench's AtCoder curves play the same role from a different angle. They show the same model on a second platform whose problems did not leak.
The cell simulates 500 time-split benchmarks of 40 problems a month for two years. Problems get harder over time, and model A memorized 30% of the before-problems it would otherwise fail.
The naive drop of about 31 points is mostly the difficulty trend. The difference in differences recovers the planted effect of about 11.6 points on average, and its intervals cover it 94% of the time, close to the promised 95%. Notice the standard error, about 4 points with 960 problems. Ignoring the pairing would give 4.4 points. The gain is modest here, because most of the spread in a single pass or fail is the luck of one attempt, which pairing cannot cancel. The standard error shrinks like one over the square root of the number of problems, so LiveCodeBench's 511 problems give about points, an interval near points on a single model. Only large leaks, like DeepSeek's, are visible this way. That is the number to quote when an interviewer asks whether a 5-point post-cutoff drop proves anything.
Implement time_split(release, cutoff, model_pass, ref_pass, n_boot, rng). release holds each problem's release date as an ISO string such as "2024-03-17", and cutoff is the model's training cutoff in the same format. A problem released on the cutoff date counts as before. model_pass and ref_pass are the 0/1 outcomes of the model and of a reference model that saw none of the problems. Return a dict with five entries.
"before" and "after", each a tuple (accuracy, lo, hi) for the model, with the Wilson interval from the wilson helper."naive_drop", the model's before accuracy minus its after accuracy."did", the difference in differences."did_ci", a 95% percentile interval (lo, hi) from n_boot bootstrap resamples. Each resample draws before-problems from the before period and after-problems from the after period, with replacement and keeping each period's size. It uses the same resampled problems for the model and the reference.Suppose an overlap check, or a vendor's report, has split a benchmark into dirty and clean items. The tempting estimate of what contamination bought is the dirty items' accuracy minus the clean items' accuracy. Before we test that estimate, here is the identity that says what we are estimating.
. The sum has nonzero terms only for dirty items, and those terms add up to . A fresh draw from is clean, and the items are such draws before any leak, so . ∎
So we need . In the language of causal inference it is an average treatment effect on the treated, the mean effect of a treatment over the units that actually received it. Here the treatment is the leak and the treated units are the dirty items. The naive dirty-minus-clean gap estimates it only if dirty and clean items would have been equally hard without the leak, and they rarely are. Long items have more n-grams, so they are flagged more often. Popular items get quoted more. Older items have had longer to spread. Each of those properties also predicts difficulty.
The fix is to compare only items of similar difficulty. Group the items into strata, bins of similar difficulty. Compare dirty with clean inside each bin, and average the comparisons.
In stratum , , where is the mean effect on that stratum's dirty items, and . By the assumption, the difference has expectation . Summing with weights , the dirty items' shares, gives , the mean effect over dirty items. Given the counts, the accuracies are independent averages of Bernoulli outcomes. So the variance is the sum of their variances, each multiplied by . ∎
In plain words, compare dirty and clean items only among items that are equally hard, then average those comparisons in proportion to where the dirty items are. The weights are the dirty items' shares because the question is what the leak did to the dirty items. Weighting by clean items or by all items answers a different question, and when the effect differs by stratum it gives a different number.
The assumption is the weak point, as in every observational analysis. A proxy that misses the real cause of difficulty leaves bias behind. So stratify on the best difficulty measure you have, and say which one you used.
The cell runs the estimator on the example's numbers.
The estimate recovers the 8-point effect, but its 95% interval is about points with these counts. Even a correct estimator needs many dirty items to pin down a small effect.
Two published methods tie detection to effect more tightly. ConTAM (Singh et al. 2024) chooses the overlap threshold so that the flagged items are the ones that actually show a gain. ConStat (Dekoninck et al. 2024) defines contamination directly as performance that does not generalize. It compares a model's score on a benchmark with its score on a second benchmark for the same task, relative to a set of reference models. It reported high levels of contamination in several popular models, including Mistral, Llama, Yi, and the top three models of the Open LLM Leaderboard at the time.
Implement leak_effect(dirty, correct, difficulty) for 0/1 arrays dirty and correct and a real-valued difficulty proxy. Form five strata by quintiles, with edges = np.quantile(difficulty, [0.2, 0.4, 0.6, 0.8]) and h = np.searchsorted(edges, difficulty, side="right"), which takes values 0 to 4. Return a dict with four entries.
"naive", the dirty accuracy minus the clean accuracy."stratified", the estimate from the theorem. Skip strata that lack dirty or clean items and renormalize the weights over the rest."se", its standard error."inflation", the fraction of dirty items times "stratified", the estimated leakage bias of the headline score.Every test so far is detective work after the fact. A canary is the benchmark author's foresight, a unique random string placed in the benchmark so that its spread can be traced later. BIG-bench, a collaborative benchmark of more than 200 tasks, put one random identifier into every file (Srivastava et al. 2022). The string lets anyone building a training corpus filter BIG-bench out, and the authors asked every publication that quotes BIG-bench tasks to include it too. The string serves two purposes. Honest trainers can filter on it. And anyone can later test whether a model has memorized it, which would mean that documents carrying it were trained on.
The test is the canary test of §19.7 applied to a benchmark. Score the true canary and random strings of the same format by the model's log-probability. Find the canary's rank among all , counting from the most likely, with ties counted against it. If the model never saw the canary, the canary is one more random string of that format, exchangeable with the others. The argument of the permutation theorem then gives the valid p-value
In plain words, a canary ranked first among itself and 999 random strings has . §19.7 measured memorization by exposure, the number of bits of the secret string the model gives away, which is of the number of candidates minus of the canary's rank. Over these candidates, is exactly that exposure, with ties counted against the canary.
Canaries are cheap, and they are not enough. They work only if every crawler and trainer honors them. Dropping documents with the string removes only the documents that kept it. Quoting a few items in a blog post without the canary, reformatting the files, or pasting a problem into a forum all leave the canary behind. A positive result is strong evidence that canaried documents were trained on, though not which ones. A negative result proves nothing, since a long random string is hard to memorize from a handful of copies. The order test, by comparison, needed about 4 copies of a whole file.
Prevention goes further. Jacovi et al. (2023) proposed three strategies for public test data.
GSM1k took the strongest option and kept its problems private. Its authors committed to release them once three open models from different lineages scored above 95%, or in June 2025, whichever came first.
Implement canary_test(canary_logp, random_logps, alpha=0.05, n_tests=1) for M random strings. It returns (rank, p, reject).
rank / (M + 1).reject is whether p <= alpha / n_tests, a Bonferroni correction for checking several models or benchmarks.If even rank 1 could not reach the corrected level, raise ValueError, since the test is then powerless by design.
In practice every per-token attack comes from one forward pass. Here is the batched version, with padding, of the log-probabilities, Min-K% Prob, and the Min-K%++ score, computed from a causal language model's logits and checked against a plain loop. With a real model you would pass model(ids).logits, and nothing else changes.
No single check covers every kind of leak, so serious evaluation stacks them. Here are the options side by side.
| method | what you need | catches | misses | gives you |
|---|---|---|---|---|
| n-gram overlap, decontamination (§8.3, §14.2) | the training data | verbatim and lightly edited copies | paraphrases, translations, answer keys, synthetic data | a flag per item |
| embedding search plus a model judge (Yang et al. 2023) | the training data, compute | paraphrases, translations | answer keys, distant derivations | a flag per item |
| per-example membership inference | log-probabilities, same-distribution non-members | heavily duplicated items | almost everything at one copy | a weak score per item |
| dataset inference (Maini et al. 2024) | log-probabilities, same-distribution non-members | a seen dataset, even with weak per-item signal | leaks without surface traces | a p-value per dataset |
| order test (Oren et al. 2023) | log-probabilities, an exchangeable file | the file seen in order a few times | shuffled or scattered copies | a p-value with a guarantee |
| guided completion, slot guessing | text from an API | memorized text and wrong options | leaks the model will not recite | strong hints, no guarantee |
| fresh, perturbed, or time-split items | new items, a reference model | the effect of any leak | nothing, if items are truly new and matched | an estimate of the inflation |
| canaries (BIG-bench) | the author's foresight, trainers' cooperation | trained-on canaried documents | copies without the string | a p-value per model |
| private held-out sets (GSM1k) | trust in the holder | everything | nothing, until they leak | a clean score |
The practice that follows from the table has five parts.
The widget turns the map from the start of the section into numbers you can change. It is an illustrative model, with parameters set to match the findings above, not measured data. Each row is a kind of leak, with every item of a 1,000-item benchmark leaked that way into the training data of a model whose clean accuracy is 60%. The n-gram and Min-20% Prob columns are the share of leaked items flagged, with the Min-K% threshold set for 5% false alarms. The order test column is the chance that the test fires on the whole file at level 0.05. The last column is the points the leak adds. Click a row to read it.