How model-selection and validation choices create apparently excellent ML performance that disappears under the matching evaluation design.
Small, justified subsets of learners are fit on documented synthetic DGPs. The invalid workflow's number is kept next to the scientifically matched number. Those draws are method checks, not empirical findings.
Dr. Pavanam Thomas · pavanamthomas · thomaspavanam@gmail.com
A high score is not evidence that the estimand was the intended one. Perfect ROC-AUC can coexist with worse Brier score and log loss.
Read in this order:
CASE_STUDY_MODEL_SELECTION_FAILURE.md— three error locations (preprocessor, splitter, innerbest_score_) that manufacture a headline.docs/failures_and_corrections.md— the ten designed failures and the tests that keep them visible.docs/leakage_taxonomy.mdanddocs/validation_design.md— named leaks and named estimands.src/mslab/experiments/— each module returnsnaive_score,correct_score, diagnostics, and awhat_failedparagraph.tests/— property checks (disjoint splits, future sentinels, group isolation, train-only scalers, nested inner/outer indices, informative vs noise, ECE, seed 2026, majority recall 0).src/mslab/models/catalogue.py— a catalogue exists;JUSTIFIED_SUBSETSforbids running every model on every DGP.
Reproduce from a clean clone:
python -m pip install -e .
python -m pytest
python scripts/run_all.pyPython 3.11 or newer. Printed numbers come from that script. They are not hard-coded in this README.
- Estimand first. Every splitter docstring in
validation/designs.pynames the risk it estimates. GroupKFold is not “more rigorous KFold”; it is a different sampling unit. - Leakage is a line of code. The invalid preprocessor is
fit_preprocessor_on_full_frame. The valid one isfit_preprocessor_on_train. Tests compare scaler means. - Nested CV does not report the inner score.
nested_cv_evaluatereturns the outer mean. Innerbest_score_is a diagnostic. - Metrics are functionals. Accuracy, ROC-AUC, PR-AUC, Brier, and ECE answer different questions. Majority accuracy on prevalence 5% is a closed-form trap: expected accuracy = 0.95, recall = 0.
- Permutation importance is not causal. I use it, and I say so in
docs/model_interpretation_limits.md. SHAP is not a dependency. - Justified subsets. Logistic for leakage and snooping; a tree grid for search optimism; a shallow tree for covariate shift (local leaves, source support); a forest for impurity ranks and overconfident probabilities; a dummy next to a forest when there is no signal.
- Synthetic DGPs with ground truth. Informative vs noise, correlated proxy, grouped intercepts, rare events, time plus a future sentinel, covariate shift. No observational extract.
These are the design, not accidents:
- Supervised selection fitted on all rows inflates holdout AUC.
- Shuffled KFold with entity codes overstates risk for a new entity.
- A post-outcome sentinel plus a shuffled split overstates forward risk.
GridSearchCV.best_score_sits above nested outer accuracy.- Majority accuracy is high; rare-class recall is zero.
- Full-sample correlation screening scored on the same rows overstates AUC.
- Impurity ranks a correlated proxy that has structural coefficient 0.
- Forest ROC-AUC looks strong while ECE does not.
- Source holdout is not target risk under covariate shift.
- Forest training accuracy on labels independent of x is memorisation.
Locked by docs/failures_and_corrections.md and the tests cited there.
Tests check properties, not merely that a function returns an object:
- Holdout indices partition
{0,…,n−1}with empty intersection. TimeSeriesSplittrains only on earlier times. The future sentinel is in the raw DGP and is dropped on the correct path.GroupKFoldnever shares an entity; shuffled KFold does on the grouped DGP.- Leaky scaler means match a full-frame fit and differ from the training-fold scaler.
- Nested CV audits: outer test ∩ inner train = ∅.
- On
noise_features_dgp, mean permutation importance of informative columns exceeds noise. - True Bernoulli probabilities have small ECE; an overconfident 0.01/0.99 map has larger ECE.
- Seed 2026 reproduces experiment scores.
- Majority classifier: accuracy ≥ 0.90, recall = 0 on the 5% DGP.
python -m pip install -e .
python -m pytest
python scripts/run_all.pyscripts/run_all.py writes figures under outputs/figures/ and a numeric summary to outputs/tables/run_summary.csv. Those files are regenerable. The source of truth is the code plus the tests.
CI installs the package, runs pytest, and runs scripts/run_all.py with MPLBACKEND=Agg.
Closed-form identity that does not depend on a lucky draw: a majority classifier on prevalence 5% has expected accuracy 0.95 and recall 0. Simulation quantities are printed by the script.
- The DGPs are stylised. They are tools for checking procedures, not models of a clinic, a credit book, or a platform.
- A score gap on a designed DGP is not a meta-analysis of published papers.
- Nested CV estimates the risk of a selection procedure under the splitter's sampling model. It is not unbiased in finite samples in a theorem proved here.
- Permutation importance, impurity, and coefficients are not causal effects. Correlation between x1 and x2 is enough to make unique attribution unidentified.
- No result in this repository is a claim about a deployed system, a client, or a production latency.
- Related causal identification work lives in a separate laboratory.
Purged/embargoed splits beyond TimeSeriesSplit(gap=...) are not implemented.
Importance-weighted target-risk estimation under covariate shift is not
implemented. Nested CV estimates the risk of a selection procedure under the
splitter's sampling model; it is not proved unbiased here. See ROADMAP.md.
machine-learning-model-selection-lab/
├── README.md
├── CASE_STUDY_MODEL_SELECTION_FAILURE.md
├── LICENSE
├── CITATION.cff
├── pyproject.toml
├── docs/
├── src/mslab/
│ ├── dgp/
│ ├── preprocess/
│ ├── validation/
│ ├── selection/
│ ├── metrics/
│ ├── models/
│ ├── importance/
│ └── experiments/
├── scripts/run_all.py
├── tests/
├── outputs/
└── .github/workflows/ci.yml
There is no observational dataset. Every draw is generated in code. Provenance and regeneration: docs/data_policy.md.
- statistical-reasoning-validation — probability identities, interval coverage, classification denominators.
- econometrics-causal-inference-lab — estimands when the question is causal.
- ai-response-evaluation-benchmarks — structured review of quantitative arguments.
See CITATION.cff. Licence: MIT, Copyright 2026 Dr. Pavanam Thomas.