Подбор гиперпараметров на тех же фолдах: откуда берётся слишком красивая оценка
Вы перебрали десятки комбинаций параметров и записали лучший score как ожидаемое качество модели. Но каждая комбинация видела одни и те же проверочные фолды. Победитель мог оказаться удачливым именно на них. Чем больше попыток, тем выше риск выбрать случайное отклонение как улучшение. Для отчёта о будущем качестве нужно проверить не только модель с выбранными параметрами, а всю процедуру выбора.
Один круг выбирает, другой оценивает
GridSearchCV использует внутренние фолды, чтобы выбрать параметры. Затем внешний фолд остаётся нетронутым до момента оценки найденной конфигурации. В каждом внешнем проходе подбор запускается заново только на внешнем train, а результат считается на внешнем test. Это вложенная кросс-валидация. Она отвечает на вопрос: как работает наш способ подобрать и обучить модель на новых данных?
Ниже классификатор SVC выбирает C и тип ядра. Мы выводим лучший внутренний балл при поиске по всей таблице и отдельные внешние баллы. На маленьком Iris они могут оказаться близкими; смысл не в обязательном большом расхождении, а в независимости внешней оценки.
from sklearn.datasets import load_iris
from sklearn.model_selection import GridSearchCV, StratifiedKFold, cross_val_score
from sklearn.svm import SVC
X, y = load_iris(return_X_y=True)
inner = StratifiedKFold(n_splits=3, shuffle=True, random_state=1)
outer = StratifiedKFold(n_splits=3, shuffle=True, random_state=2)
search = GridSearchCV(SVC(), {"C": [0.1, 1, 10], "kernel": ["linear", "rbf"]}, cv=inner)
search.fit(X, y)
outer_scores = cross_val_score(search, X, y, cv=outer)
print("лучший внутренний балл:", round(search.best_score_, 3))
print("внешние баллы:", [round(float(x), 3) for x in outer_scores])
print("средний внешний:", round(outer_scores.mean(), 3))
assert len(outer_scores) == 3В прогоне лучший внутренний балл равен 0,98, а средний внешний — 0,96. Это иллюстрирует оптимизм выбранного максимума, но внешняя оценка не обязана быть ниже на каждом наборе: фолды малы и случайно различаются. Сохраняйте все внешние баллы рядом со средним, чтобы видеть разброс процедуры. Если вы после просмотра внешних результатов ещё раз меняете сетку параметров, внешний цикл уже стал частью подбора и перестаёт быть независимым отчётом.
Строка best_score_ показывает максимум среди проверенных конфигураций на внутренних фолдах. Массив внешних баллов получен после повторного подбора внутри каждого внешнего train. Последний результат используйте для оценки процесса; после этого для выпуска обучите поиск на всех доступных обучающих данных и сохраните выбранный артефакт. Если есть отдельный финальный тест, держите его закрытым до окончания проектных решений.
Где здесь может остаться утечка
Все операции, параметры которых зависят от данных, должны быть внутри объекта, переданного в поиск: заполнение пропусков, масштабирование, отбор признаков. Иначе внешние тестовые строки уже участвовали в подготовке матрицы до первого фолда. Если записи относятся к одним клиентам или идут во времени, внутренний и внешний splitter должны соблюдать соответствующую границу. Обычные случайные фолды из примера годятся лишь для независимых наблюдений.
Вложенный цикл дороже обычного: каждый внешний фолд запускает много внутренних обучений. Для первого приближения можно уменьшить сетку или использовать отдельные train/validation/test с честным финальным test. Не стоит выдавать балл лучшей конфигурации на валидации за результат на будущем потоке. Величина смещения зависит от данных, модели и числа проверенных вариантов.
Курс «ML Engineer / Data Scientist: табличные модели в продакшн» проводит через корректную валидацию, настройку моделей, историю экспериментов и подготовку воспроизводимого пайплайна. Это позволяет отделять выбор модели от отчётной проверки ещё до разработки batch/API-инференса. Откройте программу курса.
Продолжить на практике
- ML Engineer / Data Scientist: табличные модели в продакшн ↗
Практический курс для тех, кто хочет получить навыки ML Engineer / Data Scientist на реальных табличных задачах. В курсе Вы не просто обучите CatBoost/LightGBM, а соберёте полноценный production-style пайплайн: подготовка данных, feature engineering (генерация признаков), корректная валидация, защита от leakage (утечек), тюнинг через Optuna, интерпретация через SHAP, batch-инференс и REST API для предсказаний.
