The answer is a feature
A chargeback flag copied from the label predicts fraud perfectly. The model learns the copy, not the behaviour.
target_leakage
chargeback_flag
roc_auc = 1.000
Data leakage audit · Python CLI
A leaked column can make any model look perfect. LeakLens reads your code and your data, finds the leaks that inflate your metrics, and tells you what to fix — before the numbers reach production.
leaky_transactions.csv
Leaky table
1 high · 1 medium · 0 low
Prerecorded output: leaky_transactions.csv, target is_fraud, generated by leaklens.leakage.scan and score().
Proposed patch — not applied
- df = df.drop(columns=["chargeback_flag"])
- df = df.drop(columns=["txn_id"])
+ df = df.drop(columns=["chargeback_flag", "txn_id"])
Nothing was applied and no new scan ran.
LeakLens reports the leak; you change the code.
100 recorded in the second replay after both columns are dropped. Open that recorded run.
The problem
Leakage is not a bug in the model. It is information arriving from the future, from the test set, or from the label itself. The score goes up and the model gets worse.
A chargeback flag copied from the label predicts fraud perfectly. The model learns the copy, not the behaviour.
target_leakage
chargeback_flag
roc_auc = 1.000
Duplicated transactions land on both sides of the split. The held-out score measures memorisation, not generalisation.
split_overlap
20 test rows (10.0%)
also appear in train
Scaling, SMOTE or feature selection runs before the split. Every learned statistic has already looked at the held-out rows.
preprocess_before_split
StandardScaler.fit_transform()
feeds the split
Real rows, not an illustration
This field plots rows from the checked-in split fixture. Amber points are test rows, blue points are train rows, and red points exist on both sides.
Drag to rotate, or focus the canvas and use the arrow keys. Coordinates are log amount, hour and distance — the same features the exact-overlap check hashes. This is a 2-D projection of a 3-D scene, drawn locally with canvas; no WebGL or external library.
drag to rotate · arrow keys when focused
Coverage
More than thirty detector kinds across code and data. Each finding names the exact rule, the line or column, and a concrete fix. Severity reflects how confident the check is — not how scary the label sounds.
A single feature alone predicts the label above the threshold (cross-validated ROC AUC, R² or balanced accuracy).
A high-cardinality category is nearly pure with respect to the target, leaking the label through conditional entropy.
A column is almost entirely unique values. It looks like an identifier and usually memorises rows instead of generalising.
Exact row hashes appear in both train and test. The held-out score is partly a memory test.
Feature vectors match across the split while the label is ignored. Conflicting labels are reported as high, otherwise medium.
Rows match after lower-casing text, stripping whitespace and rounding numbers to four decimals. No fuzzy matching is claimed.
A scaler, encoder, imputer or transformer is fitted before train_test_split, so held-out rows shape the transform.
Target means or aggregates are mapped into a feature before the split. The encoding has already seen the held-out labels.
SelectKBest, RFE or SelectFromModel chooses features using the full label before the split.
A search or manual loop fits on the test partition. The test set stops being held out.
Cross-validation runs over the test partition, reusing it for training and validation.
train_test_split shuffles a frame sorted by a time column, so future rows can enter training.
A split or sampler omits random_state, so every run produces different partitions and results are not reproducible.
Notebook cells have non-monotonic execution counts, so the saved output may not match the visible source order.
No detectors in this category.
Also covered: group and entity overlap (--group), near-duplicate images across folders (leaklens images), notebook cell ordering, fit-on-eval-data, preprocessing outside CV, frequency encoding, fill/one-hot/temporal leakage, and repeated test-set reuse. The full list with severities lives in the documentation.
Interactive demo
The demo replays real output from the Python scanners: code snippets with the offending line highlighted, dataset previews with the trust-score arithmetic, and split checks with the duplicated rows called out.
The browser does not run Python. The page is labelled prerecorded wherever results appear; run the CLI for a live audit.
SelectKBest.fit_transform() runs before the split at advanced_leaks.py:26
df['city'].map(city_target_mean) turns a target-derived statistic into a feature before the split.
train_test_split() shuffles data that was sorted by a time column.
Regression, not marketing
Every case is deterministic and checked into the repository. The benchmark exists to catch regressions; it is not an accuracy claim for arbitrary real-world code.
synthetic fixtures These counts are authored regression cases, not a population accuracy estimate. A perfect score here proves the rules still fire on the cases we wrote.
Real-world evidence is thinner. The evaluation in docs/REALWORLD.md has only 8 labels: 5 confirmed leaks and 3 legitimate notebooks, drawn from 60 public notebooks. The other 52 are unlabelled and excluded, so treat its precision and recall as a smoke check, not a population estimate.
Run them yourself: python -m leaklens.bench, python -m leaklens.bench_advanced, python -m leaklens.bench_data. Generated reports live in docs/BENCHMARK.md and docs/DATA_BENCHMARK.md.
Get started
The base install handles CSV, JSON, dataset audits, code scans and every report format. Optional extras add Parquet, Excel, images, the model arena, and the API/UI.
Python 3.10 or newer. No account, API key or network call required for a standard audit.
python -m pip install --upgrade leaklens-mldoctor verifies Python, the entry point, core dependencies and optional extras without reading secrets.
leaklens doctorScan a dataset with its label, compare a split, or scan a whole Python project for pipeline leaks.
leaklens --scan . --explainFind target leakage, identifiers and overlap, then write a shareable report or a CI gate.
leaklens train.csv --target label --test test.csvleaklens train.csv --target label --fail-under 80leaklens train.csv --target label --html report.htmlParse Python and notebooks for ordering, encoding, tuning and group-CV mistakes — no execution, no data upload.
leaklens --scan src/ --explainleaklens --scan . --sarif leaklens.sarifleaklens --scan notebooks/ --jsonHonest limits
A clean audit is evidence, not proof. These boundaries are deliberate and documented so you can judge a result instead of trusting a badge.
The code scanner parses source and notebooks. Data-dependent leaks that only appear at runtime, through dynamic dispatch or external state, can be missed.
Target leakage is a cross-validated score above a threshold (default 0.95). A strong but legitimate feature can be flagged for review; a weak leak can fall below it.
Hashing is bounded to 50k rows for feature vectors and 20k for near-duplicates. Near-duplicate matching only normalises case, whitespace and four decimal places; it is not fuzzy matching.
LeakLens looks for label and split leakage in ML workflows. It does not classify personal data, redact columns, or replace a security review.
Every finding ships with a remediation hint, but the change is yours. Moving a transform into a pipeline can alter results and must be re-validated.
GitHub Pages serves static files, so the demo replays fixtures produced by the real scanners at build time. It never pretends to execute Python, and no backend is required.