Та сама дошка оголошень про вживані телефони, та сама задача: за відносною ціною й віком акаунта відрізнити приманку від чесного продавця. Модель уже є — у темі про k найближчих сусідів вона впізнавала шахрайство з точністю 0.92. Але зверни увагу на слово, яке ми тоді сказали мимохідь: «дев'ять сусідів». Звідки взялася дев'ятка?
Її ніхто не обчислив. Її написала людина. І поки вона написана навмання, уся акуратна арифметика поверх неї стоїть на піску. Ця тема — про те, як таких чисел не вгадувати, а підбирати, і як не обдурити себе результатом підбору.
01 / РозрізненняДва види чисел у моделі
Візьмемо найзрозумілішу модель курсу — логістичну регресію — і навчимо її на 520 оголошеннях нашого архіву. Після навчання всередині неї лежать три числа:
Ці три числа знайшов сам алгоритм. Ніхто їх не вигадував: він рухався градієнтним спуском, доки помилка на навчальних оголошеннях не перестала спадати, і зупинився саме тут. Це параметри моделі — те, заради чого навчання взагалі існує.
А тепер подивись на рядок, який запустив це навчання. Там було ще одне число —
C = 1.0, сила регуляризації. Його не знаходив ніхто: воно стояло в коді
до першої ітерації. Постав замість нього 0.01, запусти те саме
навчання на тих самих даних — і алгоритм зупиниться в іншому місці:
Інші ваги, інша модель, інша якість: оцінка на крос-валідації падає з
0.892 до 0.767. Число C не є параметром
моделі — воно керує тим, які параметри навчання знайде. Такі числа звуться
гіперпараметрами (hyperparameter).
Схема 1 · Що задає людина, а що знаходить алгоритм
Одна модель, один запуск навчання. Ліворуч — те, що написано в коді заздалегідь; праворуч — те, що з'явилось у моделі після навчання.
C зсередини. Тому
крутити цю ручку може тільки хтось ззовні — і саме цим ми зайняті всю тему.Різниця формулюється одним реченням: параметр модель знаходить із даних під
час навчання, гіперпараметр людина задає до навчання. Перевірка проста — спитай
себе, чи змінилося б це число, якби ти дав алгоритму інші дані, не змінивши жодного рядка
коду. Ваги змінилися б. C — ні.
| модель | параметри — знаходить навчання | гіперпараметри — задаєш ти |
|---|---|---|
| лінійна регресія | коефіцієнт при кожній ознаці та вільний член | чи є штраф на ваги і який (alpha) |
| логістична регресія | ті самі ваги | C, тип штрафу, метод оптимізації |
| поліноміальна регресія | коефіцієнти многочлена | степінь многочлена |
| kNN | нічого — модель це сам архів | k, вид відстані, ваги сусідів |
| наївний Баєс | частоти слів і апріорні ймовірності класів | згладжування α |
| дерево рішень | яка ознака і який поріг у кожному вузлі | максимальна глибина, мінімум об'єктів у листі |
| нейромережа | усі ваги зв'язків | кількість шарів і нейронів, швидкість навчання |
Два рядки цієї таблиці варті окремої уваги, бо ламають зручну картинку.
kNN не має параметрів узагалі. Його «навчання» — це запам'ятати
таблицю; жодного числа алгоритм не обчислює. Уся модель складається з даних і
гіперпараметрів. Тому підбір k для kNN — це не налаштування деталі, а
вибір самої моделі.
А масштабувальник — навпаки, має параметри. У нашому конвеєрі перед
kNN стоїть StandardScaler, і він рахує з даних чотири числа: середнє й
стандартне відхилення кожної ознаки. На навчальних 520 оголошеннях вийшло 0.883 і 0.332
для відносної ціни, 178.3 і 105.7 для віку акаунта. Ці чотири числа — повноцінні
параметри: вони прийшли з даних. А от рішення взяти саме
StandardScaler, а не MinMaxScaler, — гіперпараметр.
'euclidean' проти 'manhattan'), спосіб масштабування, навіть сам
алгоритм — усе це вибір, зроблений до навчання. Формально вибір між kNN і наївним Баєсом
теж є гіперпараметром, просто дуже великим. Саме тому інструменти підбору вміють
перебирати цілі конвеєри, а не лише числа всередині них.02 / НеможливістьЧому їх не вивчити разом із вагами
Питання, яке виникає негайно: якщо ваги ми знаходимо мінімізацією помилки, чому б не
знайти так само й k? Перебрати всі значення, порахувати помилку на навчальних
даних, узяти найменшу. Спробуймо — і подивимось, що вийде.
Ось точність нашого kNN на тих самих 520 навчальних оголошеннях, на яких він і навчався:
| k | точність на навчанні | оцінка крос-валідації | точність на 280 відкладених |
|---|---|---|---|
| 1 | 1.0000 | 0.8885 | 0.8750 |
| 3 | 0.9538 | 0.9404 | 0.9071 |
| 9 | 0.9404 | 0.9250 | 0.9214 |
| 25 | 0.9173 | 0.9058 | 0.9179 |
| 51 | 0.8942 | 0.8827 | 0.8929 |
Мінімум помилки на навчанні стоїть при k = 1, і точність там рівно
1.0000. Причина дурна: найближчий сусід навчального оголошення — воно
саме, тож помилитись ніде. Але на відкладених даних це найгірший рядок таблиці —
0.8750, гірше за всі інші k.
Це не властивість kNN. Це загальне правило, і воно варте того, щоб сформулювати його
жирним: гіперпараметр майже завжди керує складністю моделі, а помилка на навчанні
монотонно спадає зі складністю. Степінь многочлена, глибина дерева, кількість
нейронів, 1/k у сусідах — усе це те саме. Тому оптимізація гіперпараметра по
навчальній помилці не «іноді помиляється», а завжди впирається в найскладніший
доступний варіант.
Схема 2 · Три криві, які розходяться
Той самий kNN на дошці оголошень, k від 1 до 51. Одна крива міряє модель на даних, на яких вона вчилась, дві інші — на тих, яких вона не бачила.
Є й друга причина, суто технічна. Ваги ми підбираємо градієнтним спуском, бо помилка
від них залежить гладко: зсунув вагу на тисячну — помилка змінилась на тисячну. Від
k нічого гладко не залежить: k ціле, і між 8 і 9 немає нічого.
А гіперпараметр «вид відстані» взагалі не число. Похідну по такому не візьмеш, тож
залишається єдиний спосіб: перебрати варіанти й поміряти кожен.
03 / ЧесністьНа чому міряти під час підбору
Отже, перебираємо й міряємо. Питання одне: міряємо на чому?
На навчальній вибірці не можна — щойно показали чому. Спокуса номер два — міряти на
тестовій: вона ж якраз відкладена, модель її не бачила. Подивись на таблицю вище ще раз:
найкраща точність на відкладених 280 оголошеннях дорівнює 0.9286 і досягається при
k = 17. Візьмімо сімнадцять?
Ні. І причина не в тому, що це «трохи негарно». Тестова вибірка існує рівно для одного: дати одне число, яке ніщо не встигло зіпсувати. Щойно ти обрав по ній хоч один гіперпараметр, вона перестала бути незалежною — інформація про неї просочилась у модель через твоє рішення. І тепер у тебе немає жодної вибірки, щоб сказати, скільки модель дає насправді. Ти витратив єдиний одноразовий вимірювач на службову задачу.
Правильний розподіл ролей ми вже розібрали в темі Train / Validation / Test: навчальна частина підбирає параметри, валідаційна — гіперпараметри, тестова один раз наприкінці називає результат. Проблема в тому, що валідаційну частину доводиться відрізати від навчальної, а даних завжди шкода.
Саме цю проблему знімає крос-валідація:
замість одного відрізаного шматка вона по черзі робить валідаційним кожен фолд і усереднює.
Кожне оголошення один раз побуває перевіркою і K−1 разів навчанням, і жоден
рядок не пропадає. Для підбору це рівно те, що треба, тому GridSearchCV у
назві має ці дві літери.
Наш підбір k виглядає так. Беремо 520 навчальних оголошень, ріжемо на
5 стратифікованих фолдів, для кожного k рахуємо середню точність по фолдах.
Перемагає k = 3 з оцінкою 0.9404 ± 0.0128. Тестові
280 оголошень при цьому не чіпались жодного разу — і коли ми нарешті їх відкрили,
обрана модель дала 0.9071.
k = 7 — 0.9385. Різниця 0.002 при розкиді по фолдах 0.013.
Це не «трійка краща за сімку», це два однакові результати. Реальний висновок такий:
годиться будь-яке невелике непарне k, і вибір конкретного значення — питання
смаку, а не даних.04 / ОптимізмПрокляття переможця
Тепер найважливіше в темі — і те, що найчастіше пропускають.
Перечитай два останні числа. Найкраща оцінка при підборі — 0.9404. Та сама модель на справді відкладених даних — 0.9071. Розрив 0.033, і він у передбачуваний бік: оцінка, за якою обирали, виявилась завищеною.
Механізм чистий і не має нічого спільного з машинним навчанням. Кожна оцінка на валідації складається з двох доданків: справжньої якості моделі й випадкового шуму конкретного розбиття. Коли ти береш максимум із багатьох таких оцінок, ти обираєш не лише найкращу модель — ти заодно обираєш найудачливіший шум. Максимум із багатьох зашумлених чисел систематично більший за максимум їхніх справжніх значень. Це називають прокляттям переможця (winner's curse).
Зліва — те, що ти бачиш у звіті: середнє значення найкращої з перебраних оцінок. Справа — найкраща зі справжніх якостей, тобто те, на що модель має право розраховувати завтра. Нерівність строга завжди, коли в оцінок є хоч якийсь шум, а він є завжди.
Інтерактив 1 · Звідки береться завищення
Кожна пара точок — один кандидат: рожева — його оцінка на валідації, бірюзова — його справжня якість. Пошук бачить лише рожеві й бере найвищу.
Тепер — чесна перевірка на наших даних, бо один розбіг у 0.033 ще нічого не доводить.
У ньому змішані дві причини: завищення від вибору й звичайна різниця між двома вибірками.
Розділимо їх експериментом. Візьмемо дванадцять різних розбиттів дошки на навчальну й
відкладену частини, а сітку скоротимо до тринадцяти значень k. Для кожного порахуємо дві речі: наскільки оцінка завищена для
обраного за крос-валідацією k і наскільки — для
заздалегідь зафіксованого k = 9, який ніхто не обирав.
| що міряємо | середній розрив «оцінка мінус правда» |
|---|---|
| фіксоване k = 9 | −0.0071 — оцінка навіть трохи занижена |
| обране за CV | −0.0030 |
| різниця | +0.0041 — це і є ціна вибору |
Чотири тисячних. Менше, ніж лякає теорія, — і ця розбіжність сама по собі корисна.
Причина ось у чому: тринадцять перебраних значень k — це не тринадцять
незалежних кандидатів. Модель із k = 21 і модель із k = 25
майже однакові, їхні помилки на тих самих фолдах збігаються, тож і шум у них спільний.
Максимум із тринадцяти скорельованих чисел зміщений приблизно так само, як
максимум із кількох незалежних.
Ліки називаються вкладеною крос-валідацією (nested cross-validation). Ідея в тому, щоб не мати одну вибірку, яка одночасно й обирає, і оцінює. Циклів стає два.
Схема 3 · Вкладена крос-валідація
Зовнішній цикл ділить дані й міряє. Внутрішній цикл живе всередині кожного зовнішнього фолда й тільки обирає гіперпараметри.
Прогнали це на всіх 800 оголошеннях дошки: п'ять зовнішніх фолдів, усередині кожного —
повний підбір k по п'яти внутрішніх. Результат такий.
- Середня внутрішня оцінка переможця (те, що показав би звичайний
GridSearchCV): 0.9344. - Чесна зовнішня оцінка: 0.9275 ± 0.0085.
- Різниця 0.0069 — рівно те завищення, яке вкладеність прибирає.
І окрема деталь, помітна лише у вкладеній схемі: по п'яти зовнішніх фолдах підбір обрав
різні k — 7, 5, 9, 19, 9. Тобто «найкраще k» не є властивістю
задачі, воно міняється від однієї вибірки до іншої. Ще один аргумент не сприймати
переможця сітки надто серйозно.
05 / ПеребірСітка проти випадкового пошуку
Ми весь час казали «перебираємо варіанти», не уточнюючи, як саме. Способів три, і між першими двома є контрінтуїтивна різниця, заради якої й написаний цей розділ.
Сітка
Пошук по сітці (grid search) — це повний перебір: задаєш список значень для кожного гіперпараметра й пробуєш усі комбінації. Просто, відтворювано, тривіально паралелиться. Вада одна, зате арифметична: кількість комбінацій — це добуток довжин списків. Чотири гіперпараметри по шість значень дають не 24 варіанти, а 64 = 1296.
Випадковий пошук
Випадковий пошук (random search) не будує сітки взагалі. Ти задаєш діапазон для кожного гіперпараметра й кількість спроб, а кожна спроба бере випадкову точку з цих діапазонів. Звучить як капітуляція: замість акуратного перебору — кидання дротиків. Але при однаковому бюджеті він зазвичай виграє, і причина не у везінні.
Ось вона. У реальних задачах гіперпараметри не рівноцінні: із п'яти ручок дві щось вирішують, а три майже ні. Сітка про це не знає й ділить бюджет порівну між осями. Візьми сітку 5 × 5 = 25 спроб: важливу вісь вона обмацає рівно в п'яти точках, а решту 20 запусків витратить на повторення тих самих п'яти значень при різних неважливих. Випадковий пошук за ті самі 25 спроб дасть 25 різних значень важливої осі — бо жодні дві його точки не збігаються по жодній координаті.
Інтерактив 2 · Однаковий бюджет, різне покриття
Два гіперпараметри. Горизонтальна вісь вирішує майже все, вертикальна майже нічого — фон залежить від неї так слабко, що це непомітно оком.
Перевіримо на наших даних, а не на картинці. Візьмемо повну сітку з трьох ручок kNN:
26 значень k, два способи зважувати сусідів, дві відстані — разом 104
комбінації, помножені на 5 фолдів, тобто 520 навчань. Переможець:
k = 17, ваги за відстанню, манхеттенська відстань, оцінка 0.9404, на
відкладених 0.9321.
Тепер той самий простір випадковим пошуком, 26 спроб — 130 навчань, учетверо дешевше. Він знаходить ту саму комбінацію з тим самим числом 0.9404. Уся різниця в тому, що 390 навчань не відбулися.
Байєсівська оптимізація
Третій спосіб коротко, бо повне занурення в нього — окрема тема. Замість того щоб
кидати точки навмання, можна після кожної спроби будувати сурогатну модель
залежності «гіперпараметри → якість» і обирати наступну точку там, де очікуване покращення
найбільше: частково поблизу вже знайденого доброго, частково там, де ще нічого не відомо.
Це байєсівська оптимізація, і реалізації в optuna,
scikit-optimize чи hyperopt. Вона окупається, коли одне навчання
коштує довго — години на великій нейромережі, — і не окупається, коли навчання триває
секунду: сама розумна процедура вибору точки тоді дорожча за спробу.
06 / ЦінаСкільки моделей ти насправді навчаєш
Найчастіша неприємність підбору не інтелектуальна, а арифметична: людина запускає
GridSearchCV, іде пити каву й повертається до процесу, який доведеться
вбити. Порахуймо, щоб такого не було.
Ця формула — уся математика розділу, але множники в ній ростуть по-різному. Кількість згорток входить лінійно: перейшов з 5 на 10 — стало вдвічі довше. А кількість ручок стоїть у показнику: додав п'яту ручку з шістьма значеннями — і робота збільшилась ушестеро.
Інтерактив 3 · Рахунок за перебір
Покрути три повзунки й подивись, як швидко нешкідливі числа перетворюються на дні. Одне навчання прийнято за 0.8 секунди.
Що з цим роблять на практиці:
- Дві хвилі замість однієї. Спершу груба сітка по широкому
логарифмічному діапазону —
0.001, 0.01, 0.1, 1, 10. Потім тонка сітка навколо переможця. Дві хвилі по 5 точок покривають діапазон краще, ніж одна з 25. - Відсів на половині шляху.
HalvingGridSearchCVуscikit-learnдає всім кандидатам маленьку частину даних, викидає половину найгірших, решті дає вдвічі більше даних — і так далі. Безнадійні комбінації відпадають, поки навчання ще дешеве. - Менше згорток на етапі відсіву. Три фолди для грубого пошуку й десять для фінальної перевірки — цілком законно.
n_jobs = −1. Комбінації незалежні, тому перебір масштабується по ядрах майже ідеально. Це найдешевше прискорення з усіх.
07 / Вибір ручокЩо крутити, а що ні
Останнє й найпрактичніше. У більшості моделей десятки аргументів, але вирішують один-два. Порівняй два наші власні виміри.
kNN, гіперпараметр k: оцінка гуляє від 0.8827 при
k = 51 до 0.9404 при k = 3. Розмах 0.058 —
учетверо більший за розкид самої оцінки. Цю ручку крутити треба обов'язково.
Логістична регресія, гіперпараметр C: 0.7673 при
C = 0.01, далі 0.8808, потім 0.8923 — і при C рівному 1, 10
чи 100 оцінка не змінюється взагалі. Тобто змістовна частина діапазону —
це два порядки внизу, а все, що вище одиниці, — марно витрачені запуски.
Звідси процедура, яка економить більше часу, ніж будь-яка розумна бібліотека: перш ніж будувати сітку, прогони кожну ручку окремо в широкому діапазоні й подивись на розмах метрики. Ручка, у якої розмах менший за розкид крос-валідації, у сітку не потрапляє. Зазвичай після такої перевірки з десяти аргументів у сітці лишаються два.
Схема 4 · Пам'ятка: головна ручка кожної моделі
З чого починати підбір і що станеться, коли крутнеш убік.
| модель | головний гіперпараметр | що робить | у який бік крутити |
|---|---|---|---|
| kNN | n_neighbors | скільки сусідів голосує | більше — гладша межа й менше реакції на шум; менше — межа повторює кожну точку |
| поліноміальна регресія | degree | степінь многочлена | більше — гнучкіше й нестійкіше; рідко потрібно понад 3 |
| гребенева, Lasso | alpha | сила штрафу на ваги | більше — простіша модель; шукати по логарифмічній шкалі |
| логістична регресія | C | зворотна сила штрафу | більше C — слабший штраф; діапазон 0.01…10 зазвичай вичерпний |
| SVM з ядром RBF | C і gamma | жорсткість межі й радіус впливу точки | крутити тільки разом, обидва по логарифмічній сітці |
| наївний Баєс | alpha | згладжування Лапласа | більше — обережніші ймовірності; нуль ламає модель |
| дерево рішень | max_depth | скільки питань поспіль | більше — точніше на навчанні й гірше на нових даних |
| випадковий ліс | max_features | скільки ознак бачить кожен розріз | менше — дерева різноманітніші; кількість дерев просто беруть великою |
| градієнтний бустинг | learning_rate | внесок одного дерева | менший крок вимагає більше дерев; крутити пару разом |
| будь-що на градієнтному спуску | learning_rate | довжина кроку | завеликий — розліт, замалий — не встигне дійти |
scikit-learn обрані непогано: вони
рідко бувають найкращими, але майже ніколи не бувають катастрофічними.alpha,
C, gamma і швидкості навчання значуща не різниця, а
відношення: між 0.001 і 0.01 різниця така сама за змістом, як між 1 і 10.
Рівномірна сітка від 0 до 10 з кроком 0.5 повністю пропустить цікаву зону й перевірить
двадцять разів те саме. Тому в RandomizedSearchCV для таких ручок беруть
loguniform, а в сітці пишуть [0.001, 0.01, 0.1, 1, 10].08 / ПідсумокЩо варто винести
Блок про якість моделі закінчується там, де почався. Ми навчилися міряти, навчилися не довіряти одному вимірюванню — і тепер бачимо, що сам процес пошуку найкращої моделі створює нову, тоншу форму самообману. Кожен рівень чесності відкриває наступний рівень нечесності поверхом вище.
| ідея | суть | чому важливо |
|---|---|---|
| параметр | навчання знаходить із даних | ваги, пороги, частоти |
| гіперпараметр | людина задає до навчання | керує тим, які параметри вийдуть |
| не по навчанню | помилка на навчанні спадає зі складністю | такий підбір завжди обере найскладніше |
| не по тесту | вибір робить тест залежним | лишаєшся без жодного чесного числа |
| крос-валідація | підбір на фолдах навчальної частини | дані не витрачаються на валідацію |
| прокляття переможця | максимум зашумлених оцінок завищений | «найкраща CV» — не прогноз якості |
| вкладена CV | вибір і оцінка на різних даних | єдиний спосіб чесно порівняти алгоритми |
| випадковий пошук | той самий бюджет, більше значень важливої осі | сітка марнує спроби на неважливі ручки |
| бюджет | комбінації в показнику, згортки — множником | рахувати до запуску, а не після |
І головна думка теми, якщо лишити одну. Найкраща оцінка, отримана під час підбору, — це не очікувана якість моделі. Це найкраще з того, що показав шум на твоїй валідації. Очікувану якість дає лише вимірювання, у якому підбір не брав участі: окремий тест, відкритий один раз, або зовнішній цикл вкладеної крос-валідації.
Далі починається блок про дерева й ансамблі, і там усе сказане знадобиться одразу. У дерева рішень ручок помітно більше, ніж у kNN: глибина, мінімальний розмір листа, критерій розрізу, обмеження на кількість листків. У випадкового лісу й градієнтного бустингу їх більше десятка, вони взаємодіють між собою, а одне навчання коштує вже не мілісекунди. Саме там сітка з розділу 6 перетворюється на добу очікування, а вміння відрізнити дві важливі ручки від восьми байдужих стає не теорією, а щоденною роботою.
practice.ipynb ти підбереш
k руками через крос-валідацію, звіриш свої числа з GridSearchCV
до останнього знака, проженеш RandomizedSearchCV з учетверо меншим бюджетом,
побачиш завищення переможця у власних числах і закінчиш вкладеною крос-валідацією.Далі в темі
Теорію прочитано. Тепер закріпи її на практиці.