Модуль 11. Кросс-валидация и подбор модели

Цель модуля: перестать верить одной цифре. После модуля вы делаете k-fold кросс-валидацию вручную и понимаете, что она возвращает, выбираете сплит под данные (стратификация, время, группы), подбираете гиперпараметры без утечки и знаете таблицу «какая модель когда» — от baseline до бустинга.

Перед началом: модули 2–3 (обучение, ошибки), 8 (утечка данных) и 10 (одни данные — разные вердикты). Инструменты: песочница сайта (чистый Python), сниппет ниже. Трудоёмкость: ~1.5 ч (чтение 45 мин · сниппет и практика 35 мин).

🔥 Оценка 0.89 оказалась удачным фолдом

Классификатор оттока показал на отложенной выборке ROC-AUC 0.89 — команда выдохнула и покатила пилот. Через месяц на новых данных метрика просела до 0.74, и никто не понимал почему: код тот же, данные похожи. Разбор показал две вещи. В отложенную половину случайно не попали сложные редкие случаи — те, что встретились позже. И по этой же половине выбирали модель и настройки, а потом её же докладывали как «независимую» проверку: выбор лучшего варианта по шумной метрике — уже подгонка под выборку.

Мораль: одна отложенная выборка — не оценка, а лотерейный билет, особенно на малых и упорядоченных данных. Честный ответ — интервал: среднее ± разброс.

📖 11.1. Почему один holdout — лотерея

Оценка на одной выборке — случайная величина. Она зависит от того, какие именно строки в неё попали: одна половина даёт 0.89, другая — 0.74, и обе формально «честные». Чем меньше данных, тем больше разброс; на сотне строк разница между половинами легко достигает десятков процентов.

Второй источник обмана — порядок строк. Данные почти всегда упорядочены: по времени, по id, по региону. «Первые 10 против последних 10» — чаще сравнение двух эпох, а не случайная проверка: в сниппете ниже holdout даёт 4.21, и цифра держится на том, какие строки попали в половину.

Третий, самый коварный: многократный выбор. Если вы подбираете модель, гиперпараметры или порог по отложенной выборке и затем докладываете лучшую цифру на ней же, выборка перестала быть проверкой. Лекарство то же, что в оценке LLM (модуль 8 курса «Дообучение LLM»): одна выборка — для выбора, другая — для экзамена.

📖 11.2. K-fold по шагам: каждая точка поработает валидацией

K-fold кросс-валидация чинит разом все три проблемы. Механика:

  1. Перемешать строки (если они независимы) и разбить на K равных фолдов.
  2. Повторить K раз: обучение на K−1 фолдах, метрика на оставшемся.
  3. Итог — среднее метрик по фолдам плюс стандартное отклонение.

Каждая точка ровно один раз побывала в валидации и K−1 раз — в обучении. Это честнее одного holdout, и данные используются полнее: каждая строка работает и в обучении, и в проверке. Цена — обучение дороже в K раз.

Главное, что нужно забрать: разброс — тоже результат. Он говорит, насколько оценке можно верить. Если средние двух моделей отличаются меньше разброса (3.9 ± 0.4 против 4.1 ± 0.5 — это не «вторая хуже», а «на этих данных мы их не различаем»), выбирайте более простую модель.

Дефолт — K = 5 или 10; малое K (3) быстрее, но грубее, leave-one-out дорог и шумно оценивает разброс. Число фолдов и seed перемешивания — часть протокола: не зафиксировали — цифры не воспроизводятся.

📖 11.3. Сплит под данные: стратификация, время, группы

Случайное перемешивание — не универсальный ответ. Три специальных случая:

Классификация → стратификация. Разбивайте так, чтобы доля классов сохранялась в каждом фолде: при редком классе (2% положительных) случайный фолд легко получится без положительных примеров — и метрика станет бессмысленной.

Временные данные → сплит по времени. Обучение — только на прошлом, валидация — на будущем; перемешивать нельзя, иначе модель «увидит будущее» и вы получите ту же утечку, что в модуле 8, только через порядок строк. Для прогнозов нагрузки, цен и оттока это единственный корректный протокол.

Карта утечек: время, агрегаты, CV

Группы → GroupKFold. Строки одного клиента или сессии не должны попадать и в обучение, и в валидацию: модель узнает клиента и «проверится» на нём же. В групповом сплите целые группы уходят в фолд целиком.

И привычка из llm-курса: утечка прячется в статистиках, посчитанных до разбиения. Все преобразования фитируются на обучающей части фолда (модуль 8).

📖 11.4. Сниппет: пять фолдов, две модели

Запустите kfold-cv.py: 20 точек y = 3x + 7 с шумом, две модели — «константа» (предсказывает среднее) и «линия» (МНК). Вывод:

text
K-fold, K=5: каждый фолд по 4 точек

 фолд  RMSE среднего   RMSE линии
    1          19.10         4.28
    2          14.66         3.47
    3          14.58         3.21
    4          19.20         4.26
    5          21.29         4.10

Модель «константа»: RMSE = 17.76 ± 2.68
Модель «линия»:     RMSE = 3.87 ± 0.44

Для контраста — один holdout (первые 10 против последних 10): RMSE 4.21
  — цифра зависит от того, какие строки попали в половину: на
  отсортированных по x данных это особенно заметно.

Читайте вывод в три приёма.

Первое: модель вообще нужна? Константа ошибается на 17.76, линия — на 3.87: линия бьёт baseline более чем в четыре раза. Это замер, который всегда делают первым (11.6): пока простая модель не побита, сложная не нужна.

Второе: разброс — это информация. У константы ±2.68, у линии ±0.44: оценка линии держится в узком коридоре, у константы гуляет широко. Колонка фолдов показывает, откуда разброс: константа на фолде 3 даёт 14.58, на фолде 5 — 21.29 (разница почти в половину); у линии фолд 3 даёт 3.21, фолд 1 — 4.28. Докладывать лучший фолд нельзя — это тот же обман, что лучший день из недели.

Третье: holdout даёт 4.21 — цифра в стороне и от среднего, и от разброса линии. Причина: первые 10 точек против последних 10 — по x это левый край против правого, распределения разные. CV усредняет по всем кускам.

📖 11.5. Подбор гиперпараметров: grid, random, nested CV

Гиперпараметры (глубина дерева, K у kNN, learning rate у бустинга) не подбираются обучением, но и не берутся с потолка. Их выбирают по данным — значит, и выбор надо проводить внутри CV, иначе оценка снова оптимистична.

Grid search. Сетка значений каждого параметра, перебор всех комбинаций, сравнение по среднему CV. Число обучений = произведение размеров сетки × K: три параметра по пять значений при K = 5 — 625 обучений. Просто и предсказуемо, но дорого на многих параметрах.

Random search. Случайные комбинации из диапазонов при том же бюджете покрывают больше разных значений, и важные параметры находятся быстрее (неважные всё равно не влияют). Шаблон: сначала грубый случайный поиск, затем узкая сетка вокруг лучшей точки.

Nested CV — кратко. Внешний CV оценивает процедуру «подбор + обучение» целиком, внутренний выбирает настройки. Нужен, когда сравнивают сами процедуры; для отчёта «модель 1 против модели 2» достаточно одного уровня с одинаковыми фолдами.

Ловушка из модуля 8: преобразования — масштаб, кодирование, заполнение пропусков — считаются только на обучающей части фолда; в sklearn это Pipeline внутри cross_val_score. Финальная цифра — на test, который трогали ровно один раз.

📖 11.6. Какая модель когда

Правило первое и последнее: сначала baseline, потом усложнение. Baseline (константа, затем линейная модель) задаёт планку честности: если бустинг не бьёт линейную модель значимо, берите линейную — она проще и объяснимее. Сравнение — всегда на одном протоколе: те же фолды, та же метрика.

СемействоКогда адекватноПлюсыМинусы
Линейные (2–3)сотни строкбыстро, объяснимонелинейность — руками
Дерево (4)сотни строкправила, объяснимопереобучается
Лес (5)тысячи строкстабильнее дереваслабее бустинга
Бустинг (6)тысячи+лучшая точностьнастройка, сложнее

Подробности. Линейные модели хороши как разведка знаков и величин (модуль 3); дерево — первый нелинейный инструмент (модуль 4); лес — способ усреднить нестабильность дерева (модуль 5); бустинг — рабочая лошадка табличных задач (модуль 6). Экзотика (kNN, SVM, нейросети) для табличных — не первая остановка: kNN чувствителен к масштабу (модуль 8), нейросети обычно не бьют бустинг.

📖 11.7. Компромиссы

  • Holdout vs k-fold. Быстрый holdout годится для разведки, финальная цифра — по CV: меньше данных — сильнее перевес в пользу CV.
  • K большой vs маленький. Больше фолдов — стабильнее оценка и дороже; малое K быстрее, но шумнее. K = 5 — рабочий дефолт.
  • Стратификация vs случайность. Стратификация сохраняет редкие классы, но усложняет протокол; при дисбалансе она обязательна.
  • Grid vs random search. Grid исчерпывающий и предсказуемый, но растёт произведением размеров; random при том же бюджете исследует шире.
  • Тюнинг vs больше данных. Прирост от данных и фич обычно больше, чем от последних процентов гиперпараметров: сначала данные, потом сетки. И помните: CV — лабораторная оценка на прошлом, а в бою распределение дрейфует, поэтому метрику модели нужно мониторить после выката.

💻 Практика

  1. (15 мин) Сниппет. Запустите kfold-cv.py. Найдите фолд, «счастливый» для константы, и фолд, «несчастный» — как меняется вывод, если смотреть только на них? Почему докладывают среднее?
  2. (15 мин) K=10. Поменяйте K = 5 на K = 10 (фолды станут по 2 точки) и перезапустите. Стал ли разброс уже? Что происходит с оценкой — и почему не стоит гнаться за большим K на маленьких данных?
  3. (10 мин) Свой протокол. Для своей задачи выпишите: какой сплит нужен (стратификация / время / группы), какое K, какая метрика и как выглядит итоговая строка результата в отчёте.
  4. (10 мин) Бюджет поиска. Три гиперпараметра по пять значений, K = 5: сколько обучений у полного grid? А если бюджет — 25 обучений, что даст random search и почему?

🧩 Quiz "Проверь себя"

1. В выводе сниппета holdout дал 4.21, а CV линии — 3.87 ± 0.44. Как это читать?
2. Чем опасна оценка на одной отложенной выборке?
3. Вы подобрали гиперпараметры по val и докладываете метрику на этом же val. Что не так?
4. Когда нельзя перемешивать строки перед разбиением?
5. Что, кроме среднего, обязательно даёт k-fold?

✅ Итоги модуля

  • Объяснить, почему один holdout — лотерея, и назвать три причины.
  • Провести k-fold по шагам и прочитать результат как «среднее ± разброс».
  • Выбрать сплит под данные: стратификация, время или группы.
  • Показать на сниппете, как разброс меняется между фолдами.
  • Подобрать гиперпараметры внутри CV, не допустив утечки.
  • Назвать таблицу «какая модель когда» и правило «сначала baseline».

🔗 Мостик к Модулю 12

Инструменты собраны: данные чистим (модули 8, 10), модель выбираем честно (модуль 11), мерим на отложенном test. Осталось собрать из этого один проект — от задачи и метрики до отчёта «выкатывать или нет». Модуль 12 — капстоун на табличных данных: полный цикл за уик-энд, с кодом на sklearn и критериями приёмки.