Модель хороша ровно настолько, насколько честна её оценка. На собесе любят задачи «найдите утечку» и «как бы вы валидировали», и это отличает тех, кто реально выводил модели в прод.
Train, validation, test
- Train: на нём учим.
- Validation: на ней выбираем модель, гиперпараметры, порог, момент остановки.
- Test: смотрим один раз в конце, чтобы получить честную оценку.
Каждый выбор по валидации немного подгоняет модель под неё, поэтому без отдельного теста итоговая оценка завышена.
Кросс-валидация
k-fold: делим данные на k частей, k раз учим на k - 1 и проверяем на оставшейся, усредняем. При 1 000 объектах и 5 фолдах модель учится на 800 и проверяется на 200.
Варианты:
- Стратифицированный: сохраняет долю классов в фолдах. Нужен при дисбалансе.
- GroupKFold: объекты одной группы (пользователь, пациент, магазин) целиком в одной части.
- TimeSeriesSplit: обучение на прошлом, проверка на будущем.
- Вложенная CV: внутренний цикл подбирает гиперпараметры, внешний оценивает всю процедуру.
Время и группы
Если модель будет предсказывать будущее, валидация тоже должна быть «в будущем». Перемешанный k-fold на временных данных подсматривает вперёд: соседние дни похожи.
трейн: янв-сен | зазор | валидация: окт | тест: ноя
Зазор нужен, когда цель считается по окну (покупки за 7 дней): иначе хвост трейна видит период валидации.
Если у пациента 20 снимков, случайное разбиение по снимкам учит модель узнавать пациента. В проде придут новые пациенты, значит, и в валидации должны быть новые.
Утечки
Утечка это информация, недоступная в момент реального прогноза. Типичные виды:
- Признаки из будущего: «число звонков коллекторов» в модели дефолта.
- Следствия цели: статус «возврат оформлен» в модели возвратов.
- Препроцессинг на всех данных: скейлер, target encoding, отбор признаков, PCA, SMOTE, обученные до разбиения.
- Группы и дубли в трейне и валидации одновременно.
- Артефакты сбора: id или порядок строк коррелирует с классом.
Защита от препроцессинговых утечек это Pipeline:
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
pipe = make_pipeline(StandardScaler(), LogisticRegression())
scores = cross_val_score(pipe, X, y, cv=5, scoring="roc_auc")
Признак утечки: подозрительно высокая метрика и один признак с огромной важностью.
Adversarial validation
Чтобы проверить, похож ли тест на трейн, обучите классификатор отличать их. AUC около 0.5 значит похожи, а важные признаки этого классификатора и есть сдвиг.
Подбор гиперпараметров
- Grid search: перебор сетки. Мало разных значений каждого параметра.
- Random search: при том же бюджете перебирает больше значений важных параметров, обычно эффективнее.
- Байесовская оптимизация (Optuna, TPE): учится на прошлых попытках.
Learning rate и коэффициенты регуляризации задают в логарифмической шкале. Early stopping тоже выбор по валидации, учитывайте это при финальной оценке.
Частые ошибки
- Обучать скейлер, отбор признаков или SMOTE до разбиения.
- Перемешивать временные данные.
- Разрезать группы между трейном и валидацией.
- Многократно смотреть на тест и выбирать по нему.
Что спросят на собесе
- Зачем нужен отдельный тест, как устроена кросс-валидация.
- Как валидировать временные ряды и данные с группами.
- Найдите утечку в описании задачи.
- Grid, random и байесовский поиск, что такое вложенная CV.