Машинне навчання · Блок 5 · Тема 22

Один розкол бреше

Крос-валідація — це не «ще одна метрика». Це спосіб дізнатися, наскільки можна довіряти самій оцінці якості.

Ти навчив модель, відклав 20% даних, поміряв на них якість і отримав акуратне число: 0.87. Питання, яке варто поставити собі негайно: а якби ти відклав інші 20%? Скільки б вийшло тоді — 0.87? 0.91? 0.79?

Це не риторика. Кожна оцінка якості, яку ти коли-небудь бачив, — це випадкова величина. Вона залежить не тільки від моделі, а й від того, які саме обʼєкти випадково опинилися в тестовій частині. Одне число без уявлення про його розкид — це як сказати «температура 20 градусів», не уточнивши, чи це середнє за рік, чи одне вимірювання термометром, який бреше на десять градусів.

Крос-валідація розвʼязує рівно цю проблему. Ідея настільки проста, що її часто недооцінюють: не обирати одне розбиття, а прогнати всі по черзі й усереднити. Далі ми розберемо, чому це працює, скільки саме шуму воно знімає, коли звичайна крос-валідація дає катастрофічно оптимістичну відповідь і що з цим робити.

01 / ПроблемаЧому одна цифра нічого не варта

Почнемо з того, що ми взагалі хочемо виміряти. Наша справжня мета — це очікувана помилка на нових даних: якщо модель завтра побачить обʼєкт, якого не було в навчанні, наскільки вона помилиться в середньому?

Etest = 𝔼(x,y)~P [ L( y, f̂(x) ) ]

Ця величина недосяжна: розподіл P ми не знаємо, у нас є лише скінченна вибірка з нього. Тому ми її оцінюємо — відкладаємо частину даних, робимо вигляд, що вони «нові», і міряємо помилку на них. Оцінка виходить незміщеною за побудовою, але в неї є дисперсія, і ця дисперсія тим більша, чим менша відкладена частина.

Уяви крайній випадок: відклали одну точку. Оцінка — це помилка на одному обʼєкті. Вона може бути нуль (пощастило), може бути жахливою (трапився викид). Відклали двадцять точок — уже краще, але якщо серед них випадково зібралися три складні обʼєкти, оцінка буде песимістичною. Це не проблема моделі. Це проблема вимірювального приладу.

Друга біда простого hold-out розбиття — ми викидаємо дані з навчання. Відклавши 30%, ми міряємо якість моделі, навченої на 70% даних. Але у виробництво піде модель, навчена на всіх 100%. А більше даних — це майже завжди менша помилка. Отже, оцінка hold-out систематично песимістична: вона описує гіршу модель, ніж та, яку ми насправді збираємось використати.

Два дефекти, які не зводяться один до одного. Велика відкладена частина → менша дисперсія оцінки, але сильніше зміщення (модель навчена на малих даних). Мала відкладена частина → менше зміщення, але оцінка шумить. Одне розбиття не дозволяє виграти в обох напрямках одночасно. K-Fold дозволяє.

02 / МеханікаK-Fold крок за кроком

Алгоритм K-Fold крос-валідації складається з чотирьох дій. Розберімо кожну.

крок 1 · перемішати й порізати

Перемішуємо дані у випадковому порядку й ділимо на K приблизно рівних частин — фолдів. Якщо n не ділиться на K, перші кілька фолдів беруть на одну точку більше. Перемішування обовʼязкове: якщо дані відсортовані за міткою або за датою, послідовне розрізання дасть фолди, які взагалі не схожі на всю вибірку.

крок 2 · прогнати K разів

Для кожного i від 1 до K: беремо фолд номер i як валідаційний, решту K−1 фолдів склеюємо в навчальну частину. Навчаємо модель з нуля — не донавчаємо, не переносимо ваги — і міряємо метрику на відкладеному фолді.

si = L( Di, f̂−i ) ,   де f̂−i навчена на D \ Di
крок 3 · усереднити

Підсумкова оцінка — середнє по фолдах:

CV(K) = ⅟K Σi=1..K si

Разом із середнім завжди рахують і розкид — стандартне відхилення по фолдах. Воно каже, наскільки метрика залежить від того, які саме обʼєкти потрапили у валідацію.

крок 4 · перенавчити на всьому

Жодна з K моделей не є фінальною. Вони існували лише для того, щоб дати оцінку. Фінальну модель навчають ще раз — на всіх даних, — а число CV(K) вважають прогнозом її якості.

Зверни увагу на важливу деталь у кроці 2: кожна точка рівно один раз побуває у валідації й рівно K−1 разів у навчанні. Тобто на оцінку впливає кожен обʼєкт вибірки, а не випадкові 20%.

В інтерактиві нижче — синтетична вибірка з 30 точок і поліноміальна модель. Рухай K і дивись на дві речі одразу: як міняється схема розбиття зверху й наскільки стрибають окремі фолди на стовпчиках знизу. Бірюзова риска — усереднена оцінка цього розбиття, сіра пунктирна — оцінка LOOCV, тобто K-Fold, доведеної до краю: кожна модель там навчається на 29 з 30 точок, майже як фінальна. Саме тому LOOCV зручно взяти за орієнтир «як було б без втрати навчальних даних». Ми повернемось до неї в розділі 07.

Окремо виведено «очікувану оцінку» — середнє CV(K) по 350 різних перемішуваннях. Це те, що дав би метод у середньому; конкретне розбиття завжди відхиляється від нього в той чи інший бік.

Інтерактив 1 · K-Fold зсередини

Угорі — хто навчає, хто перевіряє. Унизу — оцінка кожного фолда та їхнє середнє.

оцінка CV
очікувана
LOOCV
навчання
Спробуй крайні значення: при K = 2 кожна модель бачить лише 15 з 30 точок — очікувана оцінка помітно вища за LOOCV, тобто песимістична. При K = 10 модель навчається на 27 точках, і очікувана оцінка сходиться з сірим пунктиром. Але окремі фолди при цьому стрибають сильніше: у кожному лише три точки. І звертай увагу, як гуляє конкретне розбиття від натискання «перемішати заново» — це і є та сама випадковість, заради якої існує вся ця лекція.

03 / ДисперсіяСкільки шуму знімає усереднення

Тепер найважливіше питання: наскільки взагалі краще стає від усереднення? Тут легко відбутися словами «менше дисперсія», але це якраз той випадок, коли варто побачити число.

Проведемо чесний експеримент. Візьмемо ту саму вибірку й 350 разів перемішаємо її по-новому. Для кожного перемішування порахуємо дві оцінки:

Гістограми нижче показують, як розкидані ці 350 оцінок. Ліворуч ширша, розпливчаста — hold-out. Праворуч вужча, зібрана — K-Fold. Числа під ними — стандартні відхилення й у скільки саме разів усереднення звузило розподіл.

Інтерактив 2 · Одне розбиття проти K-Fold

350 незалежних перемішувань тієї самої вибірки. Однакові моделі, однаковий розмір навчання.

σ hold-out
σ K-Fold
шум знижено в
Чому не рівно √K: якби K оцінок були незалежними, усереднення звузило б розподіл рівно в √K разів. У житті діють два ефекти в різні боки. Фолди перекриваються навчальними частинами — сусідні моделі бачать майже ті самі точки, їхні помилки скорельовані, і це зменшує виграш. Зате K-Fold перевіряє модель на всіх 30 точках, а одне hold-out розбиття — лише на 30/K з них: при K = 10 це три точки, і така оцінка шумить катастрофічно. Другий ефект зазвичай сильніший, тому реальний виграш може перевищити √K.
Практичний висновок. Різниця між двома конкурентними моделями має сенс лише тоді, коли вона більша за розкид оцінки. Якщо CV дає 0.812 ± 0.030 проти 0.818 ± 0.028 — це однакові моделі, і вибір між ними за середнім є самообманом. Саме тому cross_val_score варто дивитися разом зі .std(), а не лише .mean().

04 / СтратифікаціяКоли класи розподілені нерівно

Випадкове перемішування здається безпечним, доки класи збалансовані. Але уяви задачу виявлення шахрайства: 3% транзакцій — шахрайські, 97% — нормальні. Вибірка з 200 транзакцій містить 6 шахрайських. Розбиваємо на 5 фолдів випадково — і з відчутною ймовірністю в якийсь фолд не потрапить жодної.

Що тоді станеться? Метрики на кшталт precision, recall чи ROC-AUC на такому фолді просто не визначені — ділити нема на що. Бібліотека або поверне nan, або тихо підставить нуль, і твоє середнє буде сміттям. А в іншому фолді, куди рідкісний клас потрапив у подвійній кількості, метрика буде неправдоподібно високою.

Стратифікований K-Fold лікує це тривіально: замість того щоб різати всю вибірку разом, він ріже кожен клас окремо, а потім склеює шматки. У результаті частка кожного класу в кожному фолді збігається з часткою в усій вибірці з точністю до одного обʼєкта.

Інтерактив 3 · Дисбаланс і стратифікація

80 обʼєктів, 5 фолдів. Угорі — склад кожного валідаційного фолда. Унизу — збалансована точність.

метрика
розкид ±
зіпсовано
перекіс складу
Правило: для будь-якої класифікації бери StratifiedKFold за замовчуванням — при збалансованих класах він працює так само, як звичайний, а при дисбалансі рятує. Для регресії аналог — стратифікація за квантилями цільової змінної.

05 / ГрупиКоли рядки не незалежні

Уся математика вище тримається на одному припущенні: рядки вибірки незалежні. На практиці воно ламається постійно, і ламається тихо.

Приклади, які трапляються майже в кожному проєкті:

Симптом завжди однаковий: крос-валідація показує чудові числа, а в продакшені модель провалюється. Причина в тому, що метрика відповідала на питання «наскільки добре модель упізнає вже бачені сутності», тоді як реальна задача — «наскільки добре вона працює на нових сутностях».

Лікування — групова крос-валідація: розбивати не рядки, а групи. Усі записи однієї групи цілком потрапляють або в навчання, або у валідацію, ніколи не розділяючись. У scikit-learn це GroupKFold і StratifiedGroupKFold.

Питання, яке треба ставити щоразу. «Що саме модель побачить уперше в момент реального використання?» Якщо нового користувача — розбивай за користувачами. Якщо новий день — за датами. Якщо нову лікарню — за лікарнями. Розбиття має імітувати ту саму новизну, з якою модель зустрінеться в житті. Усе інше — це вимірювання не тієї величини.

06 / ЧасВитік із майбутнього

Окремий і найпідступніший випадок залежності — час. Тут звичайна крос-валідація не просто трохи оптимістична, вона дає принципово неправильну відповідь.

Причина проста. Перемішавши часовий ряд, ми поклали точку за 15 березня в навчальну частину, а точку за 14 березня — у валідаційну. Модель тренувалася, знаючи майбутнє відносно того моменту, який вона потім «прогнозує». У житті такої розкоші не буде: прогнозуючи завтрашній попит, ти маєш лише вчорашні дані.

Правильна схема — розширювальне вікно (forward chaining): навчаємось на перших m точках, перевіряємось на наступних; потім навчаємось на m + h, перевіряємось на наступних; і так далі. Навчальна частина завжди строго передує валідаційній. У scikit-learn це TimeSeriesSplit.

В інтерактиві — ряд зі 160 точок. Перші 140 доступні для валідації, останні 20 — «справжнє майбутнє», яке ми ніколи не чіпаємо й використовуємо лише як арбітра. Модель тут навмисно проста: прогноз = середнє трьох найближчих у часі навчальних точок. Повзунок керує автокореляцією ряду: чим вона вища, тим сильніше сусідні точки схожі одна на одну — і тим більшим стає витік.

Усі три числа усереднено по 20 незалежних реалізаціях ряду. Без цього вони б стрибали від однієї реалізації до іншої настільки, що порівнювати їх було б нечесно — що, до речі, ще одна ілюстрація головної теми лекції.

Інтерактив 4 · Часовий ряд: три способи поміряти

Перемикай схему розбиття. Три числа рахуються завжди — порівнюй їх між собою.

звичайна CV
часова CV
справжнє майбутнє
Що читати: порівнюй кожну оцінку зі «справжнім майбутнім» — це єдине чесне число. Зверни увагу, що при автокореляції 0.00 ряд перетворюється на білий шум, сусіди перестають бути схожими, і обидві схеми дають майже те саме: витік зникає разом із залежністю в даних.

07 / LOOCVКрайній випадок: K = n

Що станеться, якщо довести K до максимуму — зробити його рівним кількості обʼєктів? Кожен фолд міститиме рівно одну точку. Це Leave-One-Out Cross-Validation, LOOCV.

Плюси очевидні. Навчальна частина містить n − 1 обʼєктів — практично всю вибірку. Отже, оцінка майже не зміщена: ми міряємо якість тієї самої моделі, яку потім навчимо на всьому. Крім того, у LOOCV немає випадковості розбиття: результат детермінований, перезапуск дасть той самий відповідь до останньої цифри.

Мінуси теж очевидні. Треба навчити модель n разів. Для 100 обʼєктів це прийнятно, для 100 000 — ні. Але є важливий виняток: для лінійних моделей LOOCV рахується за один прохід завдяки формулі через діагональ матриці-проєктора («капелюшної» матриці H = X(XᵀX)⁻¹Xᵀ):

CVLOO = ⅟n Σ ( (yi − ŷi) / (1 − hii) )2

Тобто для звичайної й гребеневої регресії LOOCV коштує стільки ж, скільки одне навчання. Саме тому RidgeCV у scikit-learn за замовчуванням підбирає α саме через LOOCV — це безкоштовно.

Є й тонший мінус, про який сперечаються. Часто пишуть, що LOOCV має високу дисперсію оцінки: моделі навчені на майже однакових даних, тому їхні помилки сильно скорельовані, і усереднення дає мало. Це правдоподібна інтуїція, але вона не універсальна — для лінійної регресії, наприклад, показано протилежне. Практичний висновок скромніший: виграш від дуже великого K зазвичай не окуповує вартості.

08 / Вибір KЧому на практиці 5 або 10

Тепер зберемо все докупи. Вибір K — це компроміс двох властивостей самої оцінки (не моделі!):

Kрозмір навчаннязміщення оцінкивартість
K = 250% данихвелике: міряємо гіршу модель2 навчання
K = 580% данихпомірне5 навчань
K = 1090% данихмале10 навчань
K = nмайже 100%практично нульn навчань

Логіка така. Зі зростанням K навчальна частина росте, тому оцінка дедалі точніше описує ту модель, яку ми справді збираємось використати — зміщення падає. Але кожен фолд стає меншим, окремі оцінки sᵢ шумлять сильніше, а самі моделі стають дедалі більш схожими між собою, тому усереднення знімає дедалі менше шуму. Плюс вартість росте лінійно.

Емпірично зʼясувалося, що криві «зміщення» і «вартість» перетинаються десь у зоні K = 5…10, і це стало галузевим стандартом. Практичні орієнтири:

09 / ПошукНавіщо це при підборі

Найчастіше крос-валідацію застосовують не для звіту, а для вибору: який степінь полінома, яка глибина дерева, яке α регуляризації. Схема така: перебираємо кандидатів, для кожного рахуємо CV-оцінку, беремо переможця. Саме тому все попереднє в цій темі важливе — оцінка, за якою обирають, мусить бути чесною, інакше вибір буде зроблено за шумом.

Перебирати можна трьома способами. Сітка проходить усі комбінації зі списків, які ти задав. Випадковий пошук кидає задану кількість випадкових точок. Байєсівська оптимізація вибирає наступну точку з огляду на те, що вже перевірено. Чому випадковий пошук зазвичай виграє в сітки, скільки все це коштує і як не обдурити себе результатом — предмет окремої теми 24 · Підбір гіперпараметрів.

Тут важливе інше: як виглядає простір, у якому доводиться шукати. Подивись на теплокарту нижче — саме її форма пояснює, чому одні стратегії знаходять дно швидше за інші.

Інтерактив 5 · Три стратегії пошуку на одній теплокарті

Фон — справжня CV-помилка для кожної пари (степінь, λ). Точки — те, що стратегія встигла перевірити.

знайдено
можливий мін.
програш до оптимуму
Що видно: світла зона має форму літери «Г». Низькі степені (нижня смуга) працюють при будь-якому малому λ — їм просто нема чого роздувати. Високі степені рятує тільки достатнє λ (вертикальна смуга), а без нього вони провалюються в темряву. Праворуч, при λ > 10, гине все: штраф придушує модель до горизонтальної прямої. Зверни увагу й на те, що дно долини дуже пласке — десятки різних пар дають майже однакову помилку, і ганятися за третім знаком після коми тут безглуздо.

10 / ВкладеністьЧому «найкраща CV-оцінка» бреше

А тепер найтонше місце всієї теми — те, на якому спотикаються навіть досвідчені люди.

Ти перебрав 30 варіантів гіперпараметрів, для кожного порахував 5-Fold CV, обрав найкращий і записав у звіт його CV-оцінку. Питання: чи чесне це число?

Ні. І ось чому. Кожна CV-оцінка складається з двох частин: справжньої якості моделі й випадкового шуму (памʼятаєш гістограми з розділу 3?). Коли ти береш мінімум із 30 зашумлених чисел, ти систематично обираєш не лише найкращу модель, а й найудачливіший шум. Мінімум із 30 випадкових величин у середньому менший за їхнє справжнє значення — це чиста статистика, ніякої містики.

𝔼[ minj ŝj ] < minj 𝔼[ ŝj ]

Іншими словами: ті самі дані одночасно обрали модель і оцінили її. Це та сама помилка, що й вимірювати якість на навчальній вибірці, тільки на рівень вище — перенавчання не параметрів, а гіперпараметрів.

Розвʼязок — вкладена крос-валідація. Два цикли:

зовнішній цикл · оцінка

Ділимо дані на K_out фолдів. Один відкладаємо — його не побачить ніхто, включно з процедурою підбору.

внутрішній цикл · вибір

На решті K_out − 1 фолдів запускаємо повноцінний підбір гіперпараметрів зі своєю внутрішньою крос-валідацією. Він обирає переможця, нічого не знаючи про відкладений фолд.

і тільки потім · вимірювання

Навчаємо модель з обраними гіперпараметрами на всій внутрішній частині й міряємо на відкладеному зовнішньому фолді. Повторюємо K_out разів, усереднюємо.

Ключова властивість: у вкладеній схемі вибір і оцінка ніколи не бачать одних і тих самих даних. Тому число на виході чесне.

Інтерактив нижче показує масштаб проблеми в максимально чистому вигляді. Щоб відокремити ефект вибору від усього іншого, кандидати підібрані так, що вони насправді однаково хороші: кожен додає до базової моделі рівно одну випадкову ознаку-шум, ніяк не повʼязану з ціллю. Жоден кандидат не кращий за інших — тому будь-яка «перемога» когось із них є чистим щастям на CV.

Повзунок — кількість кандидатів, серед яких іде вибір. Три криві: оптимістична оцінка простої CV, справжня помилка обраної моделі (на незалежній вибірці з 220 точок) і оцінка вкладеної CV. Усе усереднено по 30 незалежних наборах даних, щоб криві не тремтіли.

Інтерактив 6 · Оптимізм росте з кількістю кандидатів

30 незалежних наборів даних. Усі кандидати рівні за якістю — і все одно «переможець» виглядає кращим, ніж є.

проста CV
справжня
вкладена CV
Головне: при одному кандидаті вибирати нема з чого — і рожева крива лежить вище сірої: без вибору крос-валідація навіть трохи песимістична, бо тренує моделі на 80% даних. Далі рожева невпинно повзе вниз і перетинає сіру: це і є оптимізм від вибору. Бірюзова — вкладена CV — тримається на своєму сталому невеликому запасі й нікуди не дрейфує, скільки б кандидатів ти не перебирав. Саме сталість, а не точність до відсотка, робить її чесною.
Коли вкладеність не потрібна. Якщо в тебе є повноцінна відкладена тестова вибірка, яку ти чіпаєш рівно один раз наприкінці, — вона виконує роль зовнішнього циклу, і вкладена CV не обовʼязкова. Вкладеність потрібна тоді, коли даних мало й окремий тест виділити шкода, або коли треба чесно порівняти між собою кілька алгоритмів, кожен зі своїм підбором гіперпараметрів.

11 / ПрактикаЧекліст і типові помилки

Крос-валідація — процедура, у якій легко зробити непомітну помилку й отримати число, що приємно виглядає й нічого не означає. Ось короткий список того, що варто перевірити.

помилкащо виходитьяк правильно
масштабування до CVscaler бачив валідацію → витікPipeline: усі перетворення всередині фолда
відбір ознак до CVсильний оптимізмвідбір — крок пайплайну, не препроцесинг
SMOTE до CVсинтетичні копії витікаютьбалансування лише на навчальній частині фолда
звичайний KFold на часівитік із майбутньогоTimeSeriesSplit
випадковий KFold на групахмодель упізнає сутністьGroupKFold
звіт за best_score_оптимізм від виборувкладена CV або окремий тест
тільки .mean()різниця в шумі здається значущоюдивитись і .std()

І одна думка на завершення. Крос-валідація не робить модель кращою — вона взагалі нічого не змінює в моделі. Вона змінює тебе: дає чесну картину того, що ти маєш, і рятує від найдорожчої помилки в машинному навчанні — від упевненості, побудованої на випадково вдалому розбитті.

Число без розкиду — це не результат. Результат — це число разом із чесною відповіддю на питання «а якби дані були трохи іншими?».

Далі в практиці. У practice.ipynb ти реалізуєш K-Fold вручну на NumPy, порівняєш із cross_val_score, зловиш витік через масштабування поза пайплайном, побудуєш GroupKFold для датасету з дублікатами та зробиш вкладену CV для порівняння двох алгоритмів.

Далі в темі

Теорію прочитано. Тепер закріпи її на практиці.