Skip to main content

Detect benchmark contamination in large language models

Project description

benchleak 🔍

Did this model train on the test set? Find out in one command.

When a model scores 90% on GSM8K or MATH, was it genuinely capable or did it memorise the benchmark during training? Benchleak answers that with a mathematical membership-inference test on the model's own token probabilities. No LLM judges, no API calls, runs locally on any HuggingFace causal LM.

Status

🚧 Early alpha. All three detectors are implemented and runnable end to end: the pre-training detector (Min-K% Prob), the fine-tuning detector (probabilistic variation, SPV-MIA), and the RL-post-training detector (Self-Critique entropy).

Install

pip install benchleak

This pulls in torch, transformers, and datasets. To work from a clone instead, run pip install -e . in the repo root.

Usage

benchleak --model Qwen/Qwen2.5-0.5B --benchmark gsm8k

The model can be any HuggingFace-format causal LM, given as a Hub id or a local checkpoint directory. GGUF, llama.cpp, and Ollama formats are not supported.

Private or gated models

For a repository that requires authentication, provide a HuggingFace token. Any of these work:

huggingface-cli login                  # cached credential, picked up automatically
export HF_TOKEN=hf_xxx                  # environment variable
benchleak --model my/private-model --benchmark gsm8k --hf-token hf_xxx

Speed

Scoring runs one forward pass per sample. On a CPU-only machine the default --limit 200 can take many minutes; use a smaller --limit for a quick look, or --device cuda / --device mps to use a GPU.

benchleak: pre-training contamination report
====================================================
Model:       Qwen/Qwen2.5-0.5B
Benchmark:   gsm8k
Detector:    min-k% prob
Samples:     200 benchmark vs 200 reference

Separation (AUC):   0.71   [HIGH]
Significance (p):   3.2e-08
Flag thresholds:    AUC >= 0.6, p < 0.05

Verdict: LIKELY CONTAMINATED

Known benchmarks (gsm8k, math, arc-challenge, truthfulqa) work by name. For any other Hub dataset, pass the path plus its text column(s):

benchleak --model my/model --benchmark some/dataset --field question --field answer

Your own benchmark from a local file

Point --benchmark at a local file instead of a Hub id. Supported formats:

  • .txt: one passage per line (no --field needed)
  • .jsonl / .json / .csv: name the text column(s) with --field
benchleak --model my/model --benchmark ./my_benchmark.jsonl --field question --field answer

Local files are read with the standard library, so this path needs neither a network connection nor the datasets package.

Choosing a detector

--detector pretrain (the default) runs Min-K% Prob, which targets memorisation from pre-training. --detector sft runs probabilistic variation (SPV-MIA), which targets memorisation from fine-tuning and is the right choice when you suspect a model was fine-tuned on a benchmark:

benchleak --model my/model --benchmark gsm8k --detector sft

The fine-tuning detector paraphrases each sample and compares log-likelihoods, so it is roughly an order of magnitude slower than the pre-training detector. By default it downloads a T5 paraphrasing model; pass --perturber word to avoid the download (lower quality) and --n-perturbations to trade speed for stability. See docs/how-it-works-sft.md for the method and caveats.

--detector rl runs Self-Critique, which targets memorisation from RL post-training (RLVR/GRPO) — the phase where likelihood-based detectors fail. It is the right choice for an RL/reasoning-tuned instruct model:

benchleak --model my/model --benchmark gsm8k --detector rl

Here each sample is treated as a problem to solve: the detector generates a response, asks the model to redo it along a different reasoning path, and measures how similar the two answers' entropy curves stay (a contaminated problem can't deviate). Because it generates two responses per sample it is the slowest detector — use a small --limit and tune --max-new-tokens. It needs a model with a chat template. See docs/how-it-works-rl.md.

How it works

The benchmark is scored against a reference set of text the model is not expected to have memorised. Min-K% Prob assigns each text the mean log-probability of its least-likely k% of tokens. Memorised text has fewer surprising tokens and scores higher. The tool then measures how strongly the benchmark's scores separate from the reference's, reported as an AUC (U / nm from a Mann-Whitney test) with a significance p-value. AUC ≈ 0.5 means the benchmark looks like fresh data; AUC well above 0.5 is the memorisation signature of contamination.

A small reference set ships with the tool so it runs out of the box. For the cleanest signal, supply your own domain-matched reference data with --reference my_reference.txt (one passage per line).

For the full reasoning (why a reference set is needed, the choice of test, and where the method can mislead), see docs/how-it-works-pretrain.md.

Phase Method Status
Pre-training Min-K% probability (Shi et al. 2024) ✅ implemented
SFT Probabilistic variation / SPV-MIA (Fu et al. 2024) ✅ implemented
RL post-training Self-Critique entropy (Tao et al. 2025) ✅ implemented

Caveats

  • A verdict needs ≥ 5 samples per side; the significance test cannot reach p < 0.05 below that.
  • The bundled reference is general-domain prose. Comparing it against a narrow-domain benchmark (e.g. math) can confound domain with memorisation; prefer a domain-matched --reference for results you intend to publish.

Troubleshooting

ModuleNotFoundError: No module named '_lzma' when loading a benchmark. The datasets library needs Python's lzma module, which is absent from some Python builds (commonly pyenv on macOS compiled without the xz library). Install xz and rebuild Python:

brew install xz
LDFLAGS="-L$(brew --prefix xz)/lib" CPPFLAGS="-I$(brew --prefix xz)/include" \
  pyenv install -f <your-python-version>

Then reuse or recreate your virtual environment. benchleak detects this case and prints the same guidance.

Citation

Implements methods from:

  • Shi et al., 2024. Detecting Pretraining Data from Large Language Models (arXiv:2310.16789)
  • Fu et al., 2024. Membership Inference via Self-Prompt Calibration
  • Tao et al., 2025. Detecting Data Contamination from RL Post-training (arXiv:2510.09259)

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

benchleak-0.3.0.tar.gz (39.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

benchleak-0.3.0-py3-none-any.whl (30.3 kB view details)

Uploaded Python 3

File details

Details for the file benchleak-0.3.0.tar.gz.

File metadata

  • Download URL: benchleak-0.3.0.tar.gz
  • Upload date:
  • Size: 39.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for benchleak-0.3.0.tar.gz
Algorithm Hash digest
SHA256 88c4ea84e226acbfffefe8047ff82ef0b6a38dd1d0acb9935ab46070410cdabf
MD5 edc15b758520c1629fab1dd092d6b428
BLAKE2b-256 ef15fb80864cd48d15d50c2630e7ce331bea1ad1a0432fc3136434c1eff73a04

See more details on using hashes here.

File details

Details for the file benchleak-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: benchleak-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 30.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for benchleak-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e47108c1cdc2ef04e0836411122f3fea589dbe0c75164969778fe5bb972c5bcc
MD5 b9d750c14f35894f4bc5abae1d04f27f
BLAKE2b-256 b76d9b9555e56272ae25e74ba1f344f9fa588906ac8a26c94aaf23ebc6b97e21

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page