Ти навчив модель, відклав 20% даних, поміряв на них якість і отримав акуратне число: 0.87. Питання, яке варто поставити собі негайно: а якби ти відклав інші 20%? Скільки б вийшло тоді — 0.87? 0.91? 0.79?
Це не риторика. Кожна оцінка якості, яку ти коли-небудь бачив, — це випадкова величина. Вона залежить не тільки від моделі, а й від того, які саме обʼєкти випадково опинилися в тестовій частині. Одне число без уявлення про його розкид — це як сказати «температура 20 градусів», не уточнивши, чи це середнє за рік, чи одне вимірювання термометром, який бреше на десять градусів.
Крос-валідація розвʼязує рівно цю проблему. Ідея настільки проста, що її часто недооцінюють: не обирати одне розбиття, а прогнати всі по черзі й усереднити. Далі ми розберемо, чому це працює, скільки саме шуму воно знімає, коли звичайна крос-валідація дає катастрофічно оптимістичну відповідь і що з цим робити.
ПроблемаЧому одна цифра нічого не варта
Почнемо з того, що ми взагалі хочемо виміряти. Наша справжня мета — це очікувана помилка на нових даних: якщо модель завтра побачить обʼєкт, якого не було в навчанні, наскільки вона помилиться в середньому?
x — ознаки
одного обʼєкта (рік випуску, пробіг, площа), y — його справжня відповідь.
f̂(x) — те, що прогнозує наша навчена модель; дашок над буквою означає
«оцінене з даних», а не «справжнє». L(y, f̂(x)) — функція втрат:
одне число, яке каже, наскільки боляче помилитись саме так (для регресії це зазвичай
квадрат різниці, для класифікації — нуль за влучання й одиниця за промах).
P — розподіл, з якого життя бере нові обʼєкти, а запис
(x, y) ~ P читається «пара взята з цього розподілу навмання».
І 𝔼 — велика латинська «E», математичне сподівання: середнє по
всіх можливих випадках, а не по тих кількох, що трапились. Разом формула
читається одним реченням: наскільки модель помилятиметься в середньому на обʼєкті,
взятому з життя навмання. Цю саму букву 𝔼 ми зустрінемо ще раз —
у розділі 11, де вона пояснить, чому «найкраща CV-оцінка» систематично бреше.Ця величина недосяжна: розподіл P ми не знаємо, у нас є лише скінченна
вибірка з нього. Тому ми її оцінюємо — відкладаємо частину даних, робимо вигляд,
що вони «нові», і міряємо помилку на них. Оцінка виходить незміщеною за побудовою, але
в неї є дисперсія, і ця дисперсія тим більша, чим менша відкладена частина.
Уяви крайній випадок: відклали одну точку. Оцінка — це помилка на одному обʼєкті. Вона може бути нуль (пощастило), може бути жахливою (трапився викид). Відклали двадцять точок — уже краще, але якщо серед них випадково зібралися три складні обʼєкти, оцінка буде песимістичною. Це не проблема моделі. Це проблема вимірювального приладу.
Подивимось, наскільки цей прилад хитається насправді. Візьмемо 200 рядків із набору
breast_cancer, який приходить разом зі scikit-learn, навчимо
логістичну регресію на 160 з них і поміряємо частку правильних відповідей на 40
відкладених. А потім повторимо це двадцять разів, міняючи лише зерно
перемішування — тобто те, які саме рядки випадково стали відкладеними. Дані,
модель і код не змінюються ані на символ.
Двадцять розбиттів однієї вибірки — двадцять різних чисел
Кожна крапка — окремий запуск. По горизонталі — частка правильних відповідей на відкладених 40 рядках.
Друга біда простого hold-out розбиття — ми викидаємо дані з навчання. Відклавши 30%, ми міряємо якість моделі, навченої на 70% даних. Але у виробництво піде модель, навчена на всіх 100%. А більше даних — це майже завжди менша помилка. Отже, оцінка hold-out систематично песимістична: вона описує гіршу модель, ніж та, яку ми насправді збираємось використати.
МеханікаK-Fold крок за кроком
Алгоритм K-Fold крос-валідації складається з чотирьох дій. Розберімо кожну.
Перемішуємо дані у випадковому порядку й ділимо на K приблизно рівних
частин — фолдів. Якщо n не ділиться на K,
перші кілька фолдів беруть на одну точку більше. Перемішування обовʼязкове: якщо дані
відсортовані за міткою або за датою, послідовне розрізання дасть фолди, які взагалі
не схожі на всю вибірку.
Для кожного i від 1 до K: беремо фолд номер i
як валідаційний, решту K−1 фолдів склеюємо в навчальну частину.
Навчаємо модель з нуля — не донавчаємо, не переносимо ваги —
і міряємо метрику на відкладеному фолді.
Значки читаються так: D — уся вибірка,
Di — її i-й фолд, а коса риска
\ означає «усе, крім»: D \ Di —
це вибірка без i-го фолда. Нижній індекс −i у
f̂−i нагадує, що ця модель фолда номер
i не бачила. L — та сама функція втрат, що й у
розділі 01, а si — оцінка, здобута на
i-му фолді.
Підсумкова оцінка — середнє по фолдах:
Велика грецька Σ («сигма») означає «додай усе, що йде за нею»:
запис Σi=1..K si —
це просто s₁ + s₂ + … + sK, а дріб
1/K перед сумою перетворює її на середнє.
Порахуймо руками на пʼяти числах. Фолди дали 0.81, 0.86, 0.79, 0.88 і 0.84.
Сума — 4.18, поділили на 5, вийшло CV(5) = 0.836. Разом із середнім
завжди рахують і розкид — стандартне відхилення по цих самих
пʼяти числах, тобто типове відхилення фолда від середнього; тут воно 0.033.
Розкид каже, наскільки метрика залежить від того, які саме обʼєкти потрапили
у валідацію: 0.836 ± 0.033 і 0.836 ± 0.002 — це дуже різні звіти про однакове
середнє.
Жодна з K моделей не є фінальною. Вони існували лише для того, щоб
дати оцінку. Фінальну модель навчають ще раз — на всіх даних, —
а число CV(K) вважають прогнозом її якості.
Зверни увагу на важливу деталь у кроці 2: кожна точка рівно один раз побуває у
валідації й рівно K−1 разів у навчанні. Тобто на оцінку впливає
кожен обʼєкт вибірки, а не випадкові 20%.
В інтерактиві нижче — синтетична вибірка з 30 точок і поліноміальна модель.
Рухай K і дивись на дві речі одразу: як міняється схема розбиття зверху
й наскільки стрибають окремі фолди на стовпчиках знизу. Бірюзова риска — усереднена
оцінка цього розбиття, сіра пунктирна — оцінка LOOCV, тобто K-Fold,
доведеної до краю: кожна модель там навчається на 29 з 30 точок, майже як фінальна.
Саме тому LOOCV зручно взяти за орієнтир «як було б без втрати навчальних даних».
Ми повернемось до неї в розділі 08.
Окремо виведено «очікувану оцінку» — середнє CV(K) по 350 різних перемішуваннях. Це те, що дав би метод у середньому; конкретне розбиття завжди відхиляється від нього в той чи інший бік.
K-Fold зсередини
Угорі — хто навчає, хто перевіряє. Унизу — оцінка кожного фолда та їхнє середнє.
ДисперсіяСкільки шуму знімає усереднення
Тепер найважливіше питання: наскільки взагалі краще стає від усереднення? Тут легко відбутися словами «менше дисперсія», але це якраз той випадок, коли варто побачити число.
Проведемо чесний експеримент. Візьмемо ту саму вибірку й 350 разів перемішаємо її по-новому. Для кожного перемішування порахуємо дві оцінки:
- Одне hold-out розбиття — беремо перший фолд як валідаційний,
решту як навчальну, і зупиняємось. Це рівно те, що робить
train_test_splitз одним фіксованимrandom_state. - Повний K-Fold — прогонимо всі
Kфолдів того самого розбиття й усереднимо. Навчальні частини ті самі за розміром, тож порівняння чесне.
Гістограми нижче показують, як розкидані ці 350 оцінок. Ліворуч ширша, розпливчаста — hold-out. Праворуч вужча, зібрана — K-Fold. Числа під ними — стандартні відхилення й у скільки саме разів усереднення звузило розподіл.
Одне розбиття проти K-Fold
350 незалежних перемішувань тієї самої вибірки. Однакові моделі, однаковий розмір навчання.
K оцінок були незалежними,
усереднення звузило б розподіл рівно в √K разів. У житті діють два ефекти
в різні боки. Фолди перекриваються навчальними частинами — сусідні моделі
бачать майже ті самі точки, їхні помилки скорельовані, і це зменшує виграш.
Зате K-Fold перевіряє модель на всіх 30 точках, а одне hold-out розбиття —
лише на 30/K з них: при K = 10 це три точки, і така оцінка шумить катастрофічно.
Другий ефект зазвичай сильніший, тому реальний виграш може перевищити √K.breast_cancer, що й
у розділі 01, і 200 разів перемішуємо їх заново. Для кожного перемішування рахуємо дві
речі: оцінку одного розбиття (перший фолд із пʼяти, перевірка на 40 обʼєктах) і
оцінку повного 5-Fold (середнє по всіх пʼяти фолдах, перевірка на всіх 200).
Навчальні частини в обох випадках містять 160 рядків, тож порівняння чесне.
Розкид першої величини — σ = 0.0266, другої — σ = 0.0068. Тобто усереднення звузило
розподіл у 3.92 раза — помітно більше за √5 ≈ 2.24. Причина та сама, що в підписі
до фігури: перекриття навчальних частин виграш зменшує, а перевірка на 200 обʼєктах
замість 40 його збільшує, і другий ефект тут переважив.cross_val_score варто дивитися разом зі .std(),
а не лише .mean().СтратифікаціяКоли класи розподілені нерівно
Випадкове перемішування здається безпечним, доки класи збалансовані. Але уяви задачу виявлення шахрайства: 3% транзакцій — шахрайські, 97% — нормальні. Вибірка з 200 транзакцій містить 6 шахрайських. Розбиваємо на 5 фолдів випадково — і з відчутною ймовірністю в якийсь фолд не потрапить жодної.
Що тоді станеться? Метрики на кшталт precision, recall чи ROC-AUC на такому фолді
просто не визначені — ділити нема на що. Бібліотека або поверне nan,
або тихо підставить нуль, і твоє середнє буде сміттям. А в іншому фолді, куди рідкісний
клас потрапив у подвійній кількості, метрика буде неправдоподібно високою.
Стратифікований K-Fold лікує це тривіально: замість того щоб різати всю вибірку разом, він ріже кожен клас окремо, а потім склеює шматки. У результаті частка кожного класу в кожному фолді збігається з часткою в усій вибірці з точністю до одного обʼєкта.
Дисбаланс і стратифікація
80 обʼєктів, 5 фолдів. Угорі — склад кожного валідаційного фолда. Унизу — збалансована точність.
StratifiedKFold за замовчуванням — при збалансованих класах він працює
так само, як звичайний, а при дисбалансі рятує. Для регресії аналог —
стратифікація за квантилями цільової змінної.ГрупиКоли рядки не незалежні
Уся математика вище тримається на одному припущенні: рядки вибірки незалежні. На практиці воно ламається постійно, і ламається тихо.
Приклади, які трапляються майже в кожному проєкті:
- Кілька записів на одного пацієнта. Три знімки однієї людини розлетілися по різних фолдах — модель побачила пацієнта в навчанні й «упізнала» його у валідації.
- Кадри з одного відео. Сусідні кадри майже ідентичні. Випадкове розбиття перетворює задачу «розпізнай нову сцену» на «згадай сусідній кадр».
- Дублікати й майже-дублікати. Скопійовані рядки, перезаписані товари, ретвіти одного тексту.
- Кілька транзакцій одного клієнта. Поведінка клієнта стабільна, тож модель вивчає клієнта, а не патерн шахрайства.
Симптом завжди однаковий: крос-валідація показує чудові числа, а в продакшені модель провалюється. Причина в тому, що метрика відповідала на питання «наскільки добре модель упізнає вже бачені сутності», тоді як реальна задача — «наскільки добре вона працює на нових сутностях».
Лікування — групова крос-валідація: розбивати не рядки, а групи.
Усі записи однієї групи цілком потрапляють або в навчання, або у валідацію, ніколи
не розділяючись. У scikit-learn це GroupKFold і
StratifiedGroupKFold.
ЧасВитік із майбутнього
Окремий і найпідступніший випадок залежності — час. Тут звичайна крос-валідація не просто трохи оптимістична, вона дає принципово неправильну відповідь.
Причина проста. Перемішавши часовий ряд, ми поклали точку за 15 березня в навчальну частину, а точку за 14 березня — у валідаційну. Модель тренувалася, знаючи майбутнє відносно того моменту, який вона потім «прогнозує». У житті такої розкоші не буде: прогнозуючи завтрашній попит, ти маєш лише вчорашні дані.
Правильна схема — розширювальне вікно (forward chaining): навчаємось
на перших m точках, перевіряємось на наступних; потім навчаємось на
m + h, перевіряємось на наступних; і так далі. Навчальна частина завжди
строго передує валідаційній. У scikit-learn це
TimeSeriesSplit.
В інтерактиві — ряд зі 160 точок. Перші 140 доступні для валідації, останні 20 — «справжнє майбутнє», яке ми ніколи не чіпаємо й використовуємо лише як арбітра. Модель тут навмисно проста: прогноз = середнє трьох найближчих у часі навчальних точок. Повзунок керує автокореляцією ряду: чим вона вища, тим сильніше сусідні точки схожі одна на одну — і тим більшим стає витік.
Усі три числа усереднено по 20 незалежних реалізаціях ряду. Без цього вони б стрибали від однієї реалізації до іншої настільки, що порівнювати їх було б нечесно — що, до речі, ще одна ілюстрація головної теми лекції.
Часовий ряд: три способи поміряти
Перемикай схему розбиття. Три числа рахуються завжди — порівнюй їх між собою.
ВитікПередобробка всередині фолда
Є ще один спосіб тихо зіпсувати крос-валідацію, і він не про те, як ти ріжеш дані, а про те, що робиш із ними до розрізання.
Тема 09 · Передобробка даних уже поставила тут правило: усе, що вивчається з даних — медіана для заповнення пропусків, середнє й відхилення для масштабування, список категорій, межі обрізання викидів, — вивчається лише на навчальній частині, а перевірочна це вивчене тільки отримує. Порушиш — і перевірочні рядки встигнуть вплинути на числа, за якими їх потім перевірятимуть.
У крос-валідації в цього правила зʼявляється поворот, на якому спотикаються навіть
ті, хто правило знає напамʼять. Межа між навчанням і перевіркою тут
не одна. Вона рухається: у 5-Fold вона стає в пʼять різних місць, і
кожне з них вимагає своєї медіани, свого середнього, свого списку категорій.
Один-єдиний виклик StandardScaler().fit_transform(X), зроблений
перед крос-валідацією, формально не порушує нічого — на момент виклику поділу
ще й не існувало. Фактично він порушує правило пʼять разів одразу.
X_scaled = scaler.fit_transform(X)
cross_val_score(model, X_scaled, y, cv=5)
Тут model — будь-яка модель, скажімо
LogisticRegression(). Масштабування побачило всі 200 рядків, зокрема
й ті, на яких модель потім перевірятимуть у кожному з пʼяти фолдів.
cross_val_score(pipe, X, y, cv=5)
Тепер у cross_val_score передається не таблиця чисел, а
увесь конвеєр як модель. Кожен фолд навчає його з нуля: свій
fit для масштабування, свій fit для класифікатора.
Правило «нічого не вивчається на перевірочній частині» виконується пʼять разів,
а не жодного.
Просте правило, яке рятує: у cross_val_score, GridSearchCV
й усе решта передається конвеєр, а не заздалегідь оброблена таблиця.
Усе, що ти зробив із X до цього виклику, крос-валідація перевірити вже не
може — вона про це просто не знає.
Скільки це коштує
А тепер несподіванка, заради якої тут стоїть фігура. Ціна цієї помилки різниться не в рази — вона різниться на два порядки, і залежить від однієї властивості кроку, яку назвала тема 09: чи дивиться цей крок на мітки.
Замір перший — масштабування. Ті самі 200 рядків
breast_cancer, 5-Fold, 200 різних перемішувань. Якщо
StandardScaler навчити один раз на всій таблиці, середня оцінка виходить
0.9659; якщо покласти його в конвеєр — 0.9651. Завищення — 0.0009, тобто менш ніж одна
десята відсоткового пункта. Ще промовистіше розкладаються самі перемішування: у
136 випадках зі 200 обидва порядки дали однакове до останньої цифри
число, у 47 неправильний порядок був вищим, у 17 — навіть нижчим. Різниці, по суті, немає:
середнє й відхилення дивляться лише на самі ознаки й не знають, де правильна відповідь.
Замір другий — відбір ознак. Тепер дані, у яких закономірності немає взагалі: 200 рядків, 2000 випадкових ознак, мітки кинуті монеткою. Чесна відповідь тут одна — 0.500, вгадування навмання. Беремо ті самі 5 фолдів і той самий класифікатор, але спершу вибираємо 5 «найкращих» ознак із 2000. Відбір, зроблений до крос-валідації, дає 0.681. Той самий відбір усередині конвеєра дає 0.512 — майже точно чесну відповідь. Завищення 0.169, і воно зʼявилось у 20 випадках із 20.
Це число варто звірити з сусідньою темою. Тема 03 ставила той самий дослід на звичайному поділі train/test і дістала 0.68 проти 0.50 — завищення на 18 відсоткових пунктів. У нас крос-валідація замість одного поділу, 2000 ознак замість 300 — а відповідь та сама з точністю до першого знака. Збігу тут немає: величина завищення визначається не схемою розбиття, а тим, скільки міток устиг побачити відбір. Крос-валідація не лікує витік і не помʼякшує його — вона просто дає йому пʼять нагод замість однієї.
Той самий гріх коштує по-різному
Наскільки завищується CV-оцінка, якщо крок передобробки навчити один раз на всій таблиці замість K разів усередині фолдів.
LOOCVКрайній випадок: K = n
Що станеться, якщо довести K до максимуму — зробити його рівним кількості
обʼєктів? Кожен фолд міститиме рівно одну точку. Це
Leave-One-Out Cross-Validation, LOOCV.
Плюси очевидні. Навчальна частина містить n − 1 обʼєктів — практично
всю вибірку. Отже, оцінка майже не зміщена: ми міряємо якість тієї самої моделі, яку
потім навчимо на всьому. Крім того, у LOOCV немає випадковості розбиття: результат
детермінований, перезапуск дасть той самий відповідь до останньої цифри.
Мінуси теж очевидні. Треба навчити модель n разів. Для 100 обʼєктів
це прийнятно, для 100 000 — ні. Але є важливий виняток: для лінійних моделей LOOCV
рахується за один прохід завдяки формулі через діагональ
матриці-проєктора — її називають капелюшною (hat matrix), бо вона
«надіває дашок» на y, перетворюючи справжні відповіді на прогнози
ŷ. Кому потрібен точний запис: H = X(XᵀX)⁻¹Xᵀ, де
X — таблиця ознак, Xᵀ — вона ж, перевернута навколо діагоналі
(транспонована), а ⁻¹ — обернена матриця. Для розуміння далі цей запис
не потрібен — потрібні лише числа з діагоналі H:
Розберемо, що тут написано. yi — справжня
відповідь i-го обʼєкта, ŷi — прогноз
моделі, навченої на всіх обʼєктах; їхня різниця — звичайний залишок,
який ти й так маєш після одного навчання. hii —
число від 0 до 1 із діагоналі капелюшної матриці, і воно каже, наскільки сильно цей
обʼєкт тягнув криву на себе: нуль означає «модель не помітила б його зникнення»,
близьке до одиниці — «крива проходить через нього тому, що він так захотів». Ділення на
1 − hii і є весь фокус: воно роздуває залишок рівно
настільки, наскільки обʼєкт сам собі допоміг, — і дає той залишок, який вийшов би, якби
обʼєкта в навчанні не було. Σ і 1/n далі роблять із цих чисел
середнє, як у розділі 02.
Приклад на двох числах. Нехай залишок на обʼєкті — 120 грн, а
hii = 0.4. Тоді внесок цього обʼєкта в LOOCV
дорівнює (120 / 0.6)² = 200² = 40 000: так, ніби модель без нього
промахнулась би на 200 грн, а не на 120. А для обʼєкта з
hii = 0.05 поправка майже нічого не міняє:
120 / 0.95 ≈ 126. Ось чому формула не потребує жодного повторного
навчання — уся інформація про «що було б без цього обʼєкта» вже лежить у діагоналі.
Тобто для звичайної й гребеневої регресії LOOCV коштує стільки ж, скільки одне
навчання. Саме тому RidgeCV у scikit-learn за замовчуванням
підбирає α саме через LOOCV — це безкоштовно.
Є й тонший мінус, про який сперечаються. Часто пишуть, що LOOCV має високу дисперсію оцінки: моделі навчені на майже однакових даних, тому їхні помилки сильно скорельовані, і усереднення дає мало. Це правдоподібна інтуїція, але вона не універсальна — для лінійної регресії, наприклад, показано протилежне. Практичний висновок скромніший: виграш від дуже великого K зазвичай не окуповує вартості.
Вибір KЧому на практиці 5 або 10
Тепер зберемо все докупи. Вибір K — це компроміс двох властивостей
самої оцінки (не моделі!):
| K | розмір навчання | зміщення оцінки | вартість |
|---|---|---|---|
| K = 2 | 50% даних | велике: міряємо гіршу модель | 2 навчання |
| K = 5 | 80% даних | помірне | 5 навчань |
| K = 10 | 90% даних | мале | 10 навчань |
| K = n | майже 100% | практично нуль | n навчань |
Логіка така. Зі зростанням K навчальна частина росте, тому оцінка
дедалі точніше описує ту модель, яку ми справді збираємось використати —
зміщення падає. Але кожен фолд стає меншим, окремі оцінки
sᵢ шумлять сильніше, а самі моделі стають дедалі більш схожими між собою,
тому усереднення знімає дедалі менше шуму. Плюс вартість росте лінійно.
Часто цей компроміс переказують фразою «криві зміщення й вартості перетинаються десь у зоні 5…10». Фраза красива, але порожня: зміщення міряється в одиницях помилки, вартість — у навчаннях, і перетинатись їм ніде. Твердження, яке справді можна перевірити, інше: обидва дефекти оцінки згасають швидко, а рахунок росте рівно. Фігура нижче рахує це на тій самій вибірці з 30 точок, з якою ми працювали весь час.
Що ти купуєш і що платиш за кожне збільшення K
Обидві криві показані в частках від свого значення при
K = 2 — так їх можна покласти на одну шкалу. Сірі стовпчики — кількість
навчань.
K = 2 до
K = 10 зміщення оцінки падає з 0.022 до 0.002 (лишилось 8%), розкид —
з 0.032 до 0.007 (лишилось 21%), а рахунок росте рівно впʼятеро. Після десятки
обидві криві вже лежать на дні: усе, що ти доплачуєш далі, купує лічені останні
відсотки точності оцінки. Ось і вся причина, чому галузь зупинилась на
5 і 10, а не пішла до K = n. Дрібні нерівності кривих — залишок
випадковості: кожна точка усереднена по 400 перемішуваннях, а не по нескінченності.
Зверни увагу й на ліву межу: при K = 2 дефекти найбільші саме тому, що
кожна модель бачить лише половину вибірки.Практичні орієнтири:
- K = 5 — за замовчуванням, особливо коли навчання дороге.
- K = 10 — коли вибірка невелика й хочеться меншого зміщення.
- Повторена CV (
RepeatedKFold) — коли даних зовсім мало: прогнати 5-Fold пʼять разів із різними перемішуваннями і усереднити 25 чисел. Це знімає ще й випадковість самого розбиття. - LOOCV — коли обʼєктів десятки або коли модель лінійна й формула вище робить це безкоштовним.
ПошукНавіщо це при підборі
Найчастіше крос-валідацію застосовують не для звіту, а для вибору:
який степінь полінома, яка глибина дерева, яке λ регуляризації —
тобто наскільки сильно ми штрафуємо модель за великі коефіцієнти (у
scikit-learn цей самий важіль зветься alpha, а сама
регуляризація — предмет теми 19;
тут від неї потрібне лише одне: більше λ — простіша модель).
Схема така: перебираємо кандидатів, для кожного рахуємо CV-оцінку, беремо переможця.
Саме тому все попереднє в цій темі важливе — оцінка, за якою обирають, мусить бути
чесною, інакше вибір буде зроблено за шумом.
Перебирати можна трьома способами. Сітка проходить усі комбінації зі списків, які ти задав. Випадковий пошук кидає задану кількість випадкових точок. Байєсівська оптимізація вибирає наступну точку з огляду на те, що вже перевірено. Чому випадковий пошук зазвичай виграє в сітки, скільки все це коштує і як не обдурити себе результатом — предмет окремої теми 20 · Підбір гіперпараметрів.
Тут важливе інше: як виглядає простір, у якому доводиться шукати. Подивись на теплокарту нижче — саме її форма пояснює, чому одні стратегії знаходять дно швидше за інші.
Три стратегії пошуку на одній теплокарті
Фон — справжня CV-помилка для кожної пари (степінь, λ). Точки — те, що стратегія встигла перевірити.
ВкладеністьЧому «найкраща CV-оцінка» бреше
А тепер найтонше місце всієї теми — те, на якому спотикаються навіть досвідчені люди.
Ти перебрав 30 варіантів гіперпараметрів, для кожного порахував 5-Fold CV, обрав найкращий і записав у звіт його CV-оцінку. Питання: чи чесне це число?
Ні. І ось чому. Кожна CV-оцінка складається з двох частин: справжньої якості моделі й випадкового шуму (памʼятаєш гістограми з розділу 3?). Коли ти береш мінімум із 30 зашумлених чисел, ти систематично обираєш не лише найкращу модель, а й найудачливіший шум. Мінімум із 30 випадкових величин у середньому менший за їхнє справжнє значення — це чиста статистика, ніякої містики.
Читається так: у середньому найменша з поміряних оцінок виявляється меншою за
найменшу зі справжніх. Зліва — мінімум береться першим, а вже потім усереднюємо
по повторах експерименту; справа — навпаки. ŝj —
поміряна CV-оцінка j-го кандидата, 𝔼 — те саме математичне
сподівання з розділу 01. Нерівність строга щоразу, коли оцінки шумлять хоч трохи,
і чим більше кандидатів j, тим більший розрив.
Іншими словами: ті самі дані одночасно обрали модель і оцінили її. Це та сама помилка, що й вимірювати якість на навчальній вибірці, тільки на рівень вище — перенавчання не параметрів, а гіперпараметрів.
Розвʼязок — вкладена крос-валідація. Два цикли:
Ділимо дані на K_out фолдів. Один відкладаємо — його не побачить
ніхто, включно з процедурою підбору.
На решті K_out − 1 фолдів запускаємо повноцінний підбір
гіперпараметрів зі своєю внутрішньою крос-валідацією. Він обирає переможця,
нічого не знаючи про відкладений фолд.
Навчаємо модель з обраними гіперпараметрами на всій внутрішній частині й міряємо
на відкладеному зовнішньому фолді. Повторюємо K_out разів, усереднюємо.
Ключова властивість: у вкладеній схемі вибір і оцінка ніколи не бачать одних і тих самих даних. Тому число на виході чесне.
Інтерактив нижче показує масштаб проблеми в максимально чистому вигляді. Щоб відокремити ефект вибору від усього іншого, кандидати підібрані так, що вони насправді однаково хороші: кожен додає до базової моделі рівно одну випадкову ознаку-шум, ніяк не повʼязану з ціллю. Жоден кандидат не кращий за інших — тому будь-яка «перемога» когось із них є чистим щастям на CV.
Повзунок — кількість кандидатів, серед яких іде вибір. Три криві: оптимістична оцінка простої CV, справжня помилка обраної моделі (на незалежній вибірці з 220 точок) і оцінка вкладеної CV. Усе усереднено по 30 незалежних наборах даних, щоб криві не тремтіли.
Оптимізм росте з кількістю кандидатів
30 незалежних наборів даних. Усі кандидати рівні за якістю — і все одно «переможець» виглядає кращим, ніж є.
ПрактикаЧекліст і типові помилки
Крос-валідація — процедура, у якій легко зробити непомітну помилку й отримати число, що приємно виглядає й нічого не означає. Ось короткий список того, що варто перевірити.
| помилка | що виходить | як правильно |
|---|---|---|
| масштабування до CV | scaler бачив валідацію → витік | Pipeline: усі перетворення всередині фолда |
| відбір ознак до CV | сильний оптимізм | відбір — крок пайплайну, не препроцесинг |
| SMOTE до CV | синтетичні копії витікають | балансування лише на навчальній частині фолда |
| звичайний KFold на часі | витік із майбутнього | TimeSeriesSplit |
| випадковий KFold на групах | модель упізнає сутність | GroupKFold |
звіт за best_score_ | оптимізм від вибору | вкладена CV або окремий тест |
тільки .mean() | різниця в шумі здається значущою | дивитись і .std() |
І одна думка на завершення. Крос-валідація не робить модель кращою — вона взагалі нічого не змінює в моделі. Вона змінює тебе: дає чесну картину того, що ти маєш, і рятує від найдорожчої помилки в машинному навчанні — від упевненості, побудованої на випадково вдалому розбитті.
Число без розкиду — це не результат. Результат — це число разом із чесною відповіддю на питання «а якби дані були трохи іншими?».