tags: [machine-learning, cross-validation, model-evaluation, k-fold, stratified-k-fold, sampling-bias, overfitting] source: https://www.youtube.com/watch?v=7062skdX05Y related:
- "[[GridSearchCV and RandomizedSearchCV]]"
- "[[Precision, Recall, F1 Score]]"
- "[[R² and Adjusted R²]]"
A single train_test_split is fragile — shift the random_state and your accuracy metric can swing wildly, proving it reflects the shuffle, not the model. Cross-Validation (CV) fixes this by rotating which data block acts as the test fold across
[ Standard 1-Pass Split ] [ K-Fold Cross-Validation ]
┌──────────────────────────┐ ┌──────────────────────────────────────┐
│ Train (70%): shuffled │ │ Fold 1: [TEST][Train][Train][Train] │
├──────────────────────────┤ ──────────► │ Fold 2: [Train][TEST][Train][Train] │
│ Test (30%): leftover │ │ Fold 3: [Train][Train][TEST][Train] │
└──────────────────────────┘ └──────────────────────────────────────┘
(fluctuates wildly with random_state) (averages folds → stable metric)
Changing random_state=0 → random_state=50 can swing accuracy from 85% → 87%. That swing is a data artifact, not a model property. CV eliminates this by ensuring every row is used for both training and validation across the full pipeline.
Sets
⚠️ Nearly identical training sets across folds → highly correlated models → high variance in scores despite low bias.
Splits
For binary labels with global positive ratio
Ensures minority classes appear in every fold at the same rate as the full dataset.
| Pass | Test Block | Train Block | Output |
|---|---|---|---|
| Fold 1 | Rows 1–200 | Rows 201–1000 | Accuracy₁ |
| Fold 2 | Rows 201–400 | Rows 1–200 ∪ 401–1000 | Accuracy₂ |
| Fold 3 | Rows 401–600 | Rows 1–400 ∪ 601–1000 | Accuracy₃ |
| Fold 4 | Rows 601–800 | Rows 1–600 ∪ 801–1000 | Accuracy₄ |
| Fold 5 | Rows 801–1000 | Rows 1–800 | Accuracy₅ |
Final metric = mean of all 5 accuracy scores → stable, bias-resistant performance estimate.
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold, StratifiedKFold, cross_val_score
def execute_cross_validation_audit_pipeline(X, y, fold_count=5):
base_estimator = LogisticRegression(random_state=42)
# Standard K-Fold
kfold_engine = KFold(n_splits=fold_count, shuffle=True, random_state=42)
kfold_scores = cross_val_score(base_estimator, X, y, cv=kfold_engine, scoring='accuracy')
# Stratified K-Fold (preserves class ratios)
stratified_engine = StratifiedKFold(n_splits=fold_count, shuffle=True, random_state=42)
stratified_scores = cross_val_score(base_estimator, X, y, cv=stratified_engine, scoring='accuracy')
comparison_summary = pd.DataFrame({
'Fold_Index': range(1, fold_count + 1),
'Standard_KFold_Accuracy': kfold_scores,
'Stratified_KFold_Accuracy': stratified_scores
})
return comparison_summary, kfold_scores.mean(), stratified_scores.mean() Fold_Index Standard_KFold_Accuracy Stratified_KFold_Accuracy
1 0.862500 0.854167
2 0.829167 0.850000
3 0.870833 0.858333
4 0.841667 0.854167
5 0.845833 0.850000
---------------------------------------------------------------------
Standard K-Fold Global Mean: 85.00%
Stratified K-Fold Global Mean: 85.33% ← tighter variance across folds
Validate demographic response predictors across small consumer groups. Stratification ensures minority high-income buyers appear in every fold.
Validate multi-axis sequential features predicting long-range price changes.
⚠️ Critical Stress Point — Data Leakage: Never apply standard K-Fold or Stratified K-Fold to time-series data. Shuffling mixes future records into past training loops — the model memorises the future and collapses on live deployment. UseTimeSeriesSplitinstead (rolling walk-forward windows only).
[!warning] Never Scale Before Splitting Fitting
StandardScaleron the full dataset before CV causes data leakage — the scaler has already seen the test fold's statistics. Always fit the scaler inside the training fold only, never on the full matrix before splitting.
[!warning] Time-Series Invalidation Standard K-Fold shuffles rows, destroying temporal ordering. Use
TimeSeriesSplitfor any sequential data (price logs, sensor streams, trading signals).
[!warning] High CV Score ≠ No Overfitting If preprocessing (scaling, encoding) is applied globally before folds are created, high CV scores can still reflect leakage rather than genuine generalisation.
| Framework | Complexity | Bias / Variance Risk | Best For |
|---|---|---|---|
| LOOCV |
|
Low bias, high variance | Small high-stakes datasets (<100 rows) |
| Standard K-Fold | Medium — |
Risk of sampling bias on imbalanced data | Large, balanced class distributions |
| Stratified K-Fold | Medium — |
Low bias, stable variance | Imbalanced classification (fraud, anomaly detection) |
| TimeSeriesSplit | Low-to-medium — expanding window | Prevents look-ahead leakage | Sequential data, trading pipelines, time-series |
- Why does fitting
StandardScaleron the full dataset before creating CV folds constitute data leakage? - How does choosing
$K=5$ vs$K=20$ affect the bias-variance trade-off in your validation scores? - What causes a model to score high on CV training folds but fail on live production data?
- Why does LOOCV produce high-variance scores despite using nearly the full dataset for training in each fold?
- [[GridSearchCV and RandomizedSearchCV]]
- [[Precision, Recall, F1 Score]]
- [[R² and Adjusted R²]]
- [[TimeSeriesSplit]]