Что такое cross-validation
Зачем нужна cross-validation
Обычный train/test split имеет проблему: метрика модели зависит от конкретного разделения. При одном split AUC 0.85, при другом 0.78. Какой оценке доверять?
Cross-validation (CV) решает это через повторные оценки на разных разделениях. Получаем несколько метрик и их среднее, что даёт более стабильный estimate качества модели.
Cross-validation — стандарт в ML. На соревнованиях Kaggle все winners используют CV для валидации своих моделей.
K-fold cross-validation
Самая распространённая схема. Данные делятся на k равных частей (folds). Каждая часть по очереди становится test set, остальные — train set.
K = 5 — стандарт. Данные режутся на 5 частей, модель обучается 5 раз. Получаем 5 метрик, считаем среднее и стандартное отклонение.
K = 10 — даёт больше вариативности, но дольше. Для маленьких датасетов лучше.
Leave-one-out — крайний случай, k = n. Каждый example проверяется отдельно. Очень затратно вычислительно, используется редко.
Stratified K-fold
Когда данные несбалансированы (например, 5% positive), обычный K-fold может создать folds, где в test set вообще нет positive. Это портит evaluation.
Stratified K-fold сохраняет пропорции классов в каждом fold. Если в общей выборке 5% positive, в каждом fold будет примерно столько же.
Для классификации stratified — default. Для регрессии используют обычный K-fold.
Time series cross-validation
Для временных рядов нельзя случайно перемешивать данные. Обучать модель на будущем и предсказывать прошлое — data leakage.
Time series CV работает по-другому. Первый fold — предсказание последнего периода на основе всех предыдущих. Второй — на основе всех кроме последнего. И так далее.
Fold 1: train [t1...t4], test [t5]
Fold 2: train [t1...t5], test [t6]
Fold 3: train [t1...t6], test [t7]Это симулирует реальную production ситуацию, где вы всегда предсказываете future.
Больше таких примеров с разборами — в Telegram-тренажёре. Короткие сессии, прогресс по темам, объяснения после каждого ответа.
Пример в Python
Простой K-fold:
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
scores = cross_val_score(model, X, y, cv=5, scoring='roc_auc')
print(f'Mean AUC: {scores.mean():.3f} (+/- {scores.std():.3f})')Stratified K-fold:
from sklearn.model_selection import StratifiedKFold, cross_val_score
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=skf, scoring='roc_auc')Time series:
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_idx, test_idx in tscv.split(X):
X_train, X_test = X[train_idx], X[test_idx]
# train and evaluateЗачем нужен средний и std
Cross-validation даёт не одну цифру, а распределение. Среднее — ваша оценка модели. Стандартное отклонение — насколько стабильна модель.
AUC 0.85 с std 0.01 — хорошая стабильная модель. AUC 0.85 с std 0.1 — модель нестабильна, на одних данных работает отлично, на других плохо. Это сигнал проблем (маленькая выборка, нестабильные features, overfitting).
Cross-validation для hyperparameter tuning
CV — основа правильного tuning гиперпараметров. Вместо одного train/validation split использовать K-fold для каждого hyperparameter combination.
Grid Search с CV:
from sklearn.model_selection import GridSearchCV
params = {
'max_depth': [3, 5, 10],
'min_samples_split': [2, 5, 10]
}
grid = GridSearchCV(model, params, cv=5, scoring='roc_auc')
grid.fit(X_train, y_train)
print(f'Best params: {grid.best_params_}')
print(f'Best CV AUC: {grid.best_score_:.3f}')Для каждой combinati параметров делается 5-fold CV, выбирается лучшая.
Nested cross-validation
Более строгий подход. Когда вы используете CV для выбора hyperparameters, а потом для оценки модели — это может быть оптимистично.
Nested CV: outer loop для evaluation, inner loop для tuning. Дорого, но даёт самую честную оценку.
Используется в academic research и Kaggle. В production обычно хватает обычного CV.
Если готовишься к собесу — бот @kariernik_bot закрывает 80% технических вопросов. SQL, Python, A/B, продуктовые метрики — всё в одном месте.
Ограничения
CV дороже вычислительно. K-fold CV занимает в k раз больше времени. Для больших датасетов это может быть проблемой.
CV не защищает от всех проблем. Если в данных есть data leakage (target попал в features), CV его не поймает. Если данные сильно сдвинулись между train и production, CV может overstate качество.
Для очень маленьких датасетов (< 100) CV — обязательна, но результаты очень нестабильны. Надо признавать высокую variance.
Типичные ошибки
Первая — делать feature engineering на всех данных, потом CV. Это leakage: скалирование и нормализация должны быть внутри fold.
# Правильно
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
pipe = Pipeline([
('scaler', StandardScaler()),
('model', RandomForestClassifier())
])
scores = cross_val_score(pipe, X, y, cv=5)Вторая — использовать shuffle для time series. Data leakage в самом явном виде.
Третья — слишком мало folds. K = 2 даёт слишком variable оценку. Минимум K = 3, стандарт 5-10.
Четвёртая — игнорировать std. Одно среднее не даёт полной картины. Всегда показывайте std или CI.
Читайте также
FAQ
K = 5 или K = 10?
K = 5 — стандарт. K = 10 — для маленьких датасетов. K = 3 — если вычислительный бюджет ограничен.
CV нужна всегда?
Для ML-моделей на серьёзных задачах — да. Для быстрых прикидок хватает train/test split.
Почему CV может быть неправильной?
Data leakage в features, нарушение time dependencies, не учёт groups в данных (один пользователь может попасть в train и test).
CV заменяет test set?
Нет. CV — для выбора модели и hyperparameters. Финальная оценка — на отдельном test set, который не видела модель.