SKSurrogate
sksurrogate/concepts / nested cross-validation

What is nested cross-validation?

Nested cross-validation keeps the step that chooses a model separate from the step that estimates its generalisation error. The inner loop selects; the outer loop scores the selection procedure on data the inner loop never saw. Without that separation, the reported score is the score you optimised, and it is optimistic by construction.

inner: selection outer: estimate repeated CV: interval, not a test

Why a single split misleads

When the same folds are used to tune and to score, every configuration is evaluated on the same data and the winner's score inherits the selection. The bias grows with the number of candidates and shrinks with the size of the data — which is exactly the regime where expensive problems live. Nested CV answers a different question: not "how well does the chosen configuration score on these folds" but "how well does this selection procedure score on unseen folds".

What SKSurrogate's nested_cv_evaluation does

nested_cv_evaluation computes inner-CV scores and outer-fold estimates for the estimator you supply, then evaluates it on the untouched validation partition. It stores fold-level predictions and metrics, mean outer scores, repeated-CV confidence intervals and the final validation score in task metadata. Mixed-type DataFrame features are preprocessed inside each training fold, and unseen categories are ignored at scoring time rather than being folded into the vocabulary — the leakage that quietly inflates a score. Group-aware splitters preserve group labels through the evaluation path.

Two limits are stated plainly in the documentation and should be repeated here: the function does not search among estimators or tune hyperparameters, and it does not replace the search layer. It is the evaluation half — the part that scores what the search produced without re-using the search's own optimised number. Tuning happens in SurrogateRandomCV / AML; the evaluation asks how that result behaves on partitions it never touched.

Reading the result honestly

Results carry an evaluation protocol: registered partition fingerprints, the CV splitter specification, the repeat seed policy, the preprocessing path, the scoring identity and whether a validation set was used. When two results are compared with compare_repeated_cv_results, known mismatches are rejected and the output reports whether alignment could be verified — opaque custom scorers, missing fingerprints and older result dictionaries remain explicitly unverified, which is a signal, not a gap to paper over.

The paired comparison reports the candidate-minus-reference mean and median repeat-score difference, a percentile bootstrap interval and paired Cohen's d_z. It is deliberately descriptive: the resampling unit is a complete repeat-level score difference, repeats reuse observations, and the interval summarizes variation across repeats. It is not an independent-sample significance test, and excluding zero is not a significance claim. BCa intervals do not resolve dependence between repeats either; for a confirmatory comparison, define the independent unit and the comparison family before looking at the data.

Time series

For ordered observations, pass TimeSeriesSplit for both the inner and outer loops and choose a gap that excludes observations between the training and test windows. Rows must already be in chronological order: the package does not infer timestamps or sort them, and it does not construct lag, rolling or horizon features — doing that without future leakage is the caller's responsibility.

What it does not settle

Nested CV estimates the procedure's error on the distribution your folds were drawn from. It says nothing about a shifted deployment population, and it does not produce prediction intervals — that is split conformal. Reporting one number from one split is still the most common way to overstate a result; the API reference is in the tracking and evaluation guide.

© 2019–2026 Mehdi Ghasemi · MIT licence sksurrogate v0.3.0 · scikit-learn-native