Собесов

DataLearn ML-101: Cross-validation — какая стратегия для какой задачи

ML / Data ScienceВалидацияСредняяMiddle

Условие

Выберите стратегию валидации для каждого случая:

  1. Прогноз дневной выручки на 7 дней вперёд.
  2. Классификация фотографий собак по породе (10000 фото, 50 пород).
  3. Антифрод: 0.5% положительных, 50 млн строк, дрейф паттернов мошенничества.
  4. Recommender system: предсказать клики на следующей неделе.
  5. Группы данных: 1000 пациентов с 10 МРТ каждый — предсказать диагноз.

Решение

Стратегии

Стратегия Когда
K-Fold iid данные, классическая задача
Stratified K-Fold классификация, дисбаланс
Group K-Fold есть «группы» (пациент, пользователь), данные внутри группы коррелированы
Time-Series Split (rolling/expanding) временная зависимость, нельзя «подсмотреть будущее»
Stratified Group K-Fold группы + дисбаланс
Leave-One-Group-Out мало групп, важно протестировать на каждой
Nested CV гиперпараметры + честная оценка ошибки

По кейсам

1. Прогноз выручки (7 дней): Time-Series Split с expanding window. Никогда не shuffle — обучение на прошлом, оценка на будущем. Обязательно gap = горизонт прогноза, чтобы не смотреть в фичи из будущего.

from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5, test_size=7, gap=0)

2. Породы собак (10К фото, 50 классов): Stratified K-Fold (k=5) — балансировать классы в фолдах (некоторые породы редкие, выпадут в один фолд). Если фото взяты с одного источника (Instagram пользователя) — добавить Group K-Fold.

3. Антифрод (50M строк, 0.5%, дрейф): Time-Series Split + Stratified. Мошенничество меняется во времени — нельзя валидироваться на «прошлом». 5 фолдов с rolling-окном (train: 60 дней, val: 7 дней). Дополнительно out-of-time test на последних 30 днях (никогда не участвуют в обучении).

4. Рекоммендер: Time-Based split + leave-future-out. Train: до недели N. Val: неделя N. Test: неделя N+1. Внутри пользователя — без shuffle. Метрики: NDCG@k, Recall@k на последней неделе. Доп. вариант — leave-one-out per user (последний клик в test), но дороже.

5. МРТ пациентов: Group K-Fold по patient_id — никогда не должно быть так, что МРТ одного пациента в train и в val. Иначе модель «запомнит» пациента, а не научится диагностировать.

from sklearn.model_selection import GroupKFold
gkf = GroupKFold(n_splits=5)
for tr, va in gkf.split(X, y, groups=patient_ids):
    ...

Nested CV

Если делать grid-search и репортить честный score:

from sklearn.model_selection import GridSearchCV, cross_val_score
 
outer_cv = StratifiedKFold(5, shuffle=True, random_state=0)
inner_cv = StratifiedKFold(3, shuffle=True, random_state=0)
 
gs = GridSearchCV(estimator, param_grid, cv=inner_cv)
scores = cross_val_score(gs, X, y, cv=outer_cv)
print("Unbiased CV:", scores.mean(), "±", scores.std())

Leakage-чеклист

  1. Target leakage: фича вычислена с использованием target.
  2. Train-test contamination: фичи, посчитанные на полном датасете до сплита (StandardScaler.fit(X) до сплита).
  3. Group leakage: разные строки одного пациента/пользователя в train и val.
  4. Temporal leakage: окно агрегации фичи захватывает будущее относительно target.

Подводные камни

  1. shuffle=True для временных рядов — катастрофа: модель «подсматривает» в будущее.
  2. Stratified без random_state — фолды непрозрачно меняются между запусками.
  3. Scaler / encoder fit на all data — leakage. Pipeline решает это.
  4. K=5 vs K=10: K=10 даёт меньше bias оценки, но дороже. На больших данных K=5 ок.
  5. CV-score падает на test — обычно leakage в фичах или нарушение группировки.
  6. Out-of-time для антифрода обязателен: CV-AUC может быть 0.95, OOT-AUC = 0.70 из-за дрейфа.
  7. Stratify в Time-Series Split — нет нативного, но можно делать stratified внутри окна вручную.

Эталонный ответ

  1. Выручка — Time-Series Split, expanding/rolling, без shuffle.
  2. Собаки — Stratified K-Fold; если есть «источник» — Group K-Fold поверх.
  3. Антифрод — Time-Series + Stratified + Out-of-Time test (учитывать дрейф).
  4. Рекоммендер — Time-Based split, метрики на следующей неделе.
  5. МРТ пациентов — Group K-Fold по patient_id.

Общие правила: никогда не shuffle временные данные; всегда уважать группировку; fit препроцессинга — только на train (через Pipeline); для честной оценки гиперов — nested CV.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти