Ланцюжок виправлень

Ліс садить сотню дерев одночасно й просить їх проголосувати. Бустинг садить їх по черзі — і кожне наступне дивиться не на дані, а на те, що попередні недорахували.

Уяви редактора, який вичитує чужий рукопис. Перший прохід ловить грубе: пропущені коми, зламані відмінки. Другий прохід уже не шукає ком — вони виправлені; він шукає те, що лишилось. Третій працює з тим, що проґавив другий. Жоден окремий прохід не геніальний, жоден не переписує текст заново — і все ж після десятка проходів рукопис чистий.

У попередній темі ми зібрали ансамбль іншим способом: сто дерев виросли незалежно, кожне на своїй бутстреп-вибірці, і відповідь дало голосування. Це ансамбль-натовп. Сьогодні буде ансамбль-конвеєр: моделі зʼявляються по черзі, і кожна наступна працює виключно з тим, що недоробили попередні. Метод називається бустинг (boosting — «підсилення»), а його найпоширеніший різновид — градієнтний бустинг.

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

Два підходиДві філософії ансамблю

Ансамбль — це набір моделей, чиї відповіді зливають в одну. Способів зібрати такий набір рівно два, і вони протилежні за задумом.

Два слова, без яких далі не обійтись. Помилка будь-якої моделі складається з двох різних бід. Зміщення (bias) — це наскільки модель груба сама по собі: пряма лінія ніколи не опише криву залежність, скільки оголошень їй не показуй, і ця недостача нікуди не дінеться. Дисперсія (variance) — це наскільки модель смикається від вибірки до вибірки: навчи глибоке дерево на одній сотні оголошень, потім на іншій сотні з тієї самої дошки — і на той самий телефон отримаєш дві помітно різні ціни. Груба, але стійка модель має велике зміщення й малу дисперсію; гнучка, але нервова — навпаки. Ліки від двох бід різні, і саме на цьому розходяться ліс і бустинг. Докладний розбір пари був у темі 17; для цієї теми вистачить абзацу вище.

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

Бустинг — це послідовність. Дерева тут навмисне слабкі: три-чотири питання, і все. Одне таке дерево має велике зміщення — воно грубо, приблизно описує залежність. Але кожне наступне додається саме туди, де попередні недобрали, і сума поступово згинається під форму даних. Бустинг бʼє по зміщенню.

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

Паралельно проти послідовно

Ліворуч — ліс: усі дерева бачать ті самі дані й голосують. Праворуч — бустинг: кожне дерево бачить лише те, що недорахували попередні.

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

МатеріалНавіщо навмисне слабкий учень

Слабкий учень (weak learner) — модель, яка ледь краща за найпримітивнішу відповідь. Для регресії найпримітивніша відповідь — середня ціна по всій дошці; слабкий учень має бути хоч трохи точнішим за неї. Крайній випадок такої моделі має власне імʼя: пеньок (stump) — дерево глибиною один, тобто єдине питання й дві відповіді. На восьми оголошеннях, з якими ми зараз працюватимемо, він виглядає так: «рік випуску до 2021 включно?» — так, 3 133 грн; ні, 12 000 грн. Оце й уся модель.

Здається дивним: у нас є алгоритм, який уміє будувати повноцінні дерева, — навіщо свідомо себе обмежувати? Відповідь у тому, що́ саме ансамбль уміє виправляти.

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

Ось сорок пʼять оголошень із нашої дошки і чотири моделі на них. Три перші — одне дерево різної глибини, навчене одразу на ціні. Четверта — ланцюжок із двадцяти семи дерев глибини 2, той самий, що буде далі в цій лекції:

модельпомилка на навчальних, грнпомилка на нових, грн
пеньок, глибина 12 7152 572
дерево, глибина 39471 367
дерево, глибина 1001 575
27 дерев глибини 28871 181

Читаємо рядки. Пеньок нікуди не годиться: 2 572 грн помилки — це майже ціна дешевого телефона. Дерево глибини 10 запамʼятало навчальні дані до нуля й на нових працює гірше, ніж дерево глибини 3, — класичне перенавчання. А ланцюжок із дерев глибини 2, жодне з яких саме по собі не варте нічого, дає 1 181 грн — на 186 грн краще за найкраще дерево в цій таблиці. Слабкість учня тут не вада, а умова роботи методу.

Таблиця, щоправда, порівнює одиночні дерева з одним ланцюжком — а твердження було про інше: що ланцюжок із глибоких дерев гірший за ланцюжок із мілких. Перевірмо саме його. Нижче три ланцюжки на тих самих 45 оголошеннях, з тим самим кроком 0.1 і тією самою довжиною; різниця лише в глибині дерева-учасника.

Три ланцюжки, три глибини учасника

Помилка на 300 нових оголошеннях залежно від кількості дерев. Крок 0.1 в усіх трьох, вісь кількості дерев логарифмічна. Кружечок — дно кривої.

Глибокий учасник опускає ланцюжок не нижче, а вище. Ланцюжок пеньків доходить до 1 178 ₴, ланцюжок дерев глибини 2 — до 1 181 ₴, а ланцюжок дерев глибини 10 спиняється на 1 400 ₴ і псується потім найшвидше. Причина — та, про яку йшлося абзацом вище: поправки додаються, а разом із ними додається й розкид кожного учасника. ⚠️ Але не читай це як «чим мілкіше, тим краще»: на цій дошці ознака одна, рік випуску, тож глибині просто нема чого ловити — взаємодій між ознаками тут не існує. На справжній таблиці з десятками колонок глибина 3–8 окупається саме взаємодіями, і саме тому робочий діапазон такий, а не «пеньок завжди».
Скільки саме «слабкий». На практиці пеньки беруть рідко: вони не бачать взаємодій ознак. Пеньок відповідає лише на одне питання, тому не може сказати «свіжий рік і велика памʼять». Дерево глибини d ловить взаємодії до d ознак одночасно. Робочий діапазон майже завжди 3–8 — досить, щоб бачити пари й трійки ознак, і замало, щоб запамʼятати окреме оголошення.

МеханікаНавчання на залишках

Тепер найважливіше. Візьмімо вісім оголошень — по одному на кожні два роки — і пройдімо ланцюжок руками.

Крок нульовий: моделі ще немає, тому найкраща відповідь — середня ціна. Складаємо всі вісім і ділимо на вісім:

F0 = (2 000 + 2 200 + 2 400 + 2 900 + 3 800 + 5 500 + 8 800 + 15 200) / 8 = 5 350 ₴

Позначення F (від function) — це прогноз усього ансамблю, а нижній індекс — скільки дерев у нього вже додано. F0 — ансамбль із нуля дерев, тобто просто константа.

Тепер визначимо головне слово теми. Залишок (residual) — це різниця між фактом і поточним прогнозом:

ri = yi − F(xi)

Тут y — справжня ціна оголошення, F(x) — те, що каже ансамбль, а r — недорахована частина. Для найдорожчого телефона 2024 року залишок після нульового кроку становить 15 200 − 5 350 = 9 850 грн. Модель недооцінила його майже на десять тисяч.

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

Fm(x) = Fm−1(x) + ν · hm(x)

Читаємо посимвольно. Fm−1 — ансамбль, який був; hm — щойно навчене на залишках дерево; ν (грецька «ню») — швидкість навчання, число від 0 до 1, яке каже, яку частку поправки взяти. Дерево додається не цілком, а вкороченим — щоб ансамбль не стрибав через ціль. Чому саме так і що буває при ν = 1, розберемо в §5; поки що візьмімо ν = 0.5, тобто «додаємо половину знайденої поправки».

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

Перше дерево (глибина 2, тобто чотири листки) розбило вісім оголошень на чотири групи й для кожної порахувало середній залишок. Чотири найстаріші телефони, 2010–2016 років, константа переоцінила в середньому на 2 975 грн — їхній залишок вийшов відʼємним. Пару 2018 і 2020 — на 700 грн. А от телефон 2022 року вона недооцінила на 3 450 грн, телефон 2024-го — аж на 9 850 грн. Беремо половину кожної поправки й додаємо до 5 350. Ось що вийшло, і ось що з цього вчитиме друге дерево:

рікфактпрогноз F1залишок r1внесок 2-гопрогноз F2залишок r2
20102 0003 863−1 863−7153 148−1 148
20122 2003 863−1 663−7153 148−948
20142 4003 863−1 463−7153 148−748
20162 9003 863−963−7153 148−248
20183 8005 000−1 200−7154 285−485
20205 5005 000+500+2505 250+250
20228 8007 075+1 725+8637 938+862
202415 20010 275+4 925+2 46312 738+2 462

Порахуй кілька клітинок очима — арифметика тут уся на поверхні. Рядок 2024: прогноз після першого дерева 10 275, факт 15 200, залишок 15 200 − 10 275 = 4 925. Друге дерево внесло +2 463, отже 10 275 + 2 463 = 12 738, і новий залишок 15 200 − 12 738 = 2 462. Був 9 850, став 4 925, став 2 462. Рядок 2016: залишок стиснувся з −963 до −248. Рядок 2018: з −1 200 до −485.

Тепер зведімо всі вісім залишків в одне число — корінь із середнього квадрата помилки (RMSE, метрика з теми про метрики):

дерев у ланцюжку0123456
RMSE, грн4 2902 1861 115587302161102
найбільший залишок, грн9 8504 9252 4631 231616308169

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

Залишки крок за кроком

Ті самі вісім оголошень. Угорі — прогноз ансамблю, унизу — стовпчики залишків. Сірі контури позаду — залишки на старті, з чого все починалось.

RMSE, ₴—
серед. модуль, ₴—
найбільший, ₴—
падіння, разів—
Що видно на верхньому графіку: прогноз ансамблю — це завжди сходинки, бо сума дерев лишається кусково-сталою функцією. З кожним деревом сходинок більшає й вони точніше облягають точки. Вісім дерев на восьми оголошеннях — це вже майже запамʼятовування; на справжніх даних так робити не можна, і в §7 буде видно чому.

ЗбіркаАлгоритм у пʼять рядків

Тепер увесь градієнтний бустинг для регресії можна записати повністю. Задано навчальну вибірку, кількість дерев M, швидкість навчання ν й обмеження на одне дерево (глибина, мінімальний розмір листка):

  1. Почни з константи: F0 дорівнює середній ціні по навчальній вибірці.
  2. Для кожного кроку m = 1…M порахуй залишки всіх обʼєктів: ri = yi − Fm−1(xi).
  3. Навчи мілке дерево hm передбачати ці залишки за тими самими ознаками.
  4. Онови ансамбль: Fm = Fm−1 + ν · hm.
  5. Кінцевий прогноз — сума константи й усіх дерев із їхніми множниками.

Ніякої ваги обʼєктів, ніякого перезважування вибірки — лише зміна цільової змінної на кожному кроці. Це варто підкреслити, бо історично першим бустингом був AdaBoost (1995), і він працює інакше: вибірка там лишається тою самою, а змінюються ваги обʼєктів — після кожної ітерації неправильно класифіковані приклади важчають, і наступна модель приділяє їм більше уваги. Градієнтний бустинг (Джером Фрідман, 1999) замінив цю ідею на загальнішу: замість ваг — залишки, замість «правильно чи ні» — будь-яка диференційовна функція втрат. AdaBoost виявився окремим випадком градієнтного бустингу з експоненційною втратою. «Диференційовна» тут означає просто «плавна»: у такої функції в кожній точці можна спитати, куди й наскільки круто вона нахилена. Що це дає, буде видно в §6.

КрокШвидкість навчання

Повернімось до множника ν. Навіщо навмисне вкорочувати поправку, якщо дерево вже знайшло її оптимальну величину?

Відповідь найлегше побачити на тому самому оголошенні 2024 року. У нашому ланцюжку воно опинялось у власному листку — саме одне, — тому дерево щоразу вгадувало його залишок точно. Підставмо це в правило оновлення. Якщо h дорівнює залишку r, то новий залишок дорівнює:

rнов = y − (F + ν·r) = (y − F) − ν·r = r − ν·r = (1 − ν) · r

Тобто кожен крок множить залишок на (1 − ν). При ν = 0.5 залишок ділиться навпіл: 9 850 → 4 925 → 2 463 → 1 231 → 616 → 308. Саме ці шість чисел стоять у рядку «найбільший залишок» — вони не «схожі» на геометричну прогресію, вони і є нею. Сьоме число того рядка, 169, з прогресії випадає, і це не помилка: після шостого дерева залишок телефона 2024 року становить уже 154 грн, і найбільшим став залишок іншого оголошення — 2018 року. Воно весь час ділило листок із сусідами, а не сиділо в ньому саме, тому його поправка щоразу була усередненою й залишок танув повільніше.

Порахуймо, скільки кроків треба, щоб недоплата за цей телефон упала нижче 500 грн:

швидкість навчаннямножник за крокзалишок після 1 крокукроків до 500 грн
1.000 грн1
0.50.54 925 грн5
0.10.98 865 грн29

Тобто ν = 1 закриває помилку за один крок, а ν = 0.1 — за двадцять девʼять. Виглядає так, ніби великий крок кращий у всьому. Пастка в слові «точно»: дерево вгадало залишок точно лише на навчальних даних. У залишку сидить не тільки закономірність, а й шум — випадкове відхилення ціни цього конкретного продавця. Крок ν = 1 заганяє в ансамбль і сигнал, і шум одним рухом, без права на перегляд. Малий крок додає потроху: сигнал, який повторюється в багатьох оголошеннях, накопичується від дерева до дерева, а шум, який у кожного оголошення свій, накопичуватись не встигає.

Чотири швидкості навчання на одному наборі

45 оголошень для навчання, 300 нових для перевірки. Показано помилку на нових оголошеннях залежно від кількості дерев. Вісь кількості дерев — логарифмічна.

помилка тут, ₴—
дно цього кроку—
дерев до дна—
дно при 1.0—
Компроміс на числах. Крок 1.0 доходить до свого дна за чотири дерева — і це дно дорівнює 1 359 ₴. Крок 0.3 доходить до 1 182 ₴ за девʼять дерев, крок 0.1 — до 1 181 ₴ за двадцять сім. Виграш від зменшення кроку реальний, але скінченний: нижче ≈1 180 ₴ дно вже не опускається, і крок 0.05 платить пʼятдесятьма трьома деревами за 1 185 ₴ — на чотири гривні гірше. Зате менший крок псується повільніше: на двохсотому дереві крок 1.0 дає 1 566 ₴, а крок 0.05 — 1 351 ₴.
Тому їх крутять парою. learning_rate і n_estimators — не два незалежні гіперпараметри, а один із двома ручками: зменшив крок удвічі — приблизно вдвічі збільш кількість дерев. Робочий рецепт: постав learning_rate у діапазон 0.05–0.1, а кількість дерев підбирай ранньою зупинкою з §7. Ще менший крок майже ніколи не окупається: дно вже не опускається, а рахувати доводиться вдвічі-втричі довше. Про те, як узагалі шукають такі пари, — тема про підбір гіперпараметрів.

МістокЧому це градієнтний спуск

Слово «градієнтний» у назві досі виглядало прикрасою: ми ніде не рахували жодної похідної, а просто віднімали прогноз від факту. Зараз стане видно, що це одне й те саме.

У темі про градієнтний спуск правило було таке: щоб зменшити втрату, зрушуй параметр проти градієнта. Виведімо, у що воно перетворюється тут.

Що означає значок ∂ і слово «похідна». Далі буде запис ∂L / ∂F. Читається він так: «наскільки зміниться втрата L, якщо трішечки посунути прогноз F». Це чутливість однієї величини до іншої, і в неї є знак та величина. Знак каже куди: якщо число додатне, то збільшення прогнозу збільшує втрату — отже, прогноз треба зменшувати. Величина каже наскільки круто: чим більше число, тим сильніше втрата реагує на зсув. Побутова картинка — схил під ногами: похідна це його крутизна й напрямок, і щоб спуститись, ти йдеш проти неї. Сам значок ∂ — округла форма латинської d від «different»; він означає лише те, що в задачі є й інші величини, а ми зараз ворушимо саме цю, тримаючи решту нерухомо. Рахувати похідні самому в цій лекції не доведеться: єдина, яка тут потрібна, виписана нижче повністю, і її можна перевірити арифметикою.
крок 1 · втрата на одному оголошенні

Візьмімо звичайну квадратичну втрату, з половинкою попереду — вона потрібна лише щоб двійка від похідної скоротилась і формула була охайною:

L(y, F) = ½ · (y − F)2

Тут y — справжня ціна, F — число, яке видав ансамбль для цього оголошення. Далі ми диференціюємо саме по F, а не по вагах моделі — і в цьому весь фокус.

крок 2 · похідна по прогнозу

Похідна квадрата дає множник 2, який скорочується з половинкою, а внутрішня похідна (y − F) по F дорівнює −1:

∂L / ∂F = −(y − F) = −r

Словами: градієнт втрати по прогнозу — це залишок зі знаком мінус. А отже, антиградієнт (напрямок, куди треба рухатись) — це рівно залишок.

Перевіримо це без жодної похідної, самим лише додаванням. Візьмімо телефон 2024 року: y = 15 200, F = 5 350, залишок 9 850. Втрата L = ½ · 9 8502 = 48 511 250. Тепер піднімімо прогноз рівно на одну гривню, до 5 351: залишок стає 9 849, а втрата — ½ · 9 8492 = 48 501 400.5. Втрата впала на 9 849.5 — тобто майже точно на той самий залишок 9 850, і саме зі знаком мінус. Ось що́ означає «∂L/∂F дорівнює −9 850»: одна гривня прогнозу коштує приблизно 9 850 одиниць втрати, і коштує вона їх у мінус, бо прогноз рухається в правильний бік.

крок 3 · крок спуску

Правило спуску «відійди від градієнта на ν кроку» у позначеннях цієї теми виглядає так:

Fнов = F − ν · (∂L/∂F) = F + ν · r

Порівняй із правилом бустингу Fm = Fm−1 + ν·hm. Вони збігаються, щойно дерево h передбачає залишок r. Дерево — це і є наближення антиградієнта.

крок 4 · підставмо числа

Оголошення 2024 року: y = 15 200, F0 = 5 350. Градієнт: −(15 200 − 5 350) = −9 850. Антиградієнт: +9 850. Перше дерево виділило це оголошення в окремий листок і поставило туди рівно 9 850. Крок із ν = 0.5:

F1 = 5 350 + 0.5 · 9 850 = 10 275 ₴

Саме це число стоїть у таблиці §3 у рядку 2024. Ми щойно зробили крок градієнтного спуску, навіть не назвавши його так.

Тепер найважливіше — у якому просторі робиться крок. У темі 13 ми рухали число: вага w зсувалась на −η·∂L/∂w, де η (грецька «ета») — довжина кроку, той самий за роллю множник, що ν тут. Після кроку модель лишалася тою самою прямою, просто з іншим нахилом. Тут ми рухаємо функцію: до F додається ціле нове дерево. Прогноз для кожного оголошення робить свій крок проти градієнта, а дерево — це спосіб задати всі ці кроки однією компактною формулою.

Звідси й назва: градієнтний спуск у просторі функцій. І звідси ж випливає узагальнення, заради якого все й затівалось. Якщо взяти іншу втрату, антиградієнт буде іншим — і дерева вчитимуться вже не на залишках. Для абсолютної втрати |y − F| похідна дорівнює ±1, тож дерева вчаться на знаках помилок: модель стає стійкою до викидів, бо оголошення з помилкою в 50 000 грн тягне рівно стільки ж, скільки помилка в 50 грн. Для класифікації беруть логістичну втрату з теми 14, і антиградієнт стає різницею між міткою та передбаченою ймовірністю. Механіка ланцюжка при цьому не змінюється жодним рядком.

Той самий спуск у двох просторах

Ліворуч — те, що було в темі 13: крок змінює число. Праворуч — те, що робить бустинг: крок додає функцію.

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

МежаКоли дерев забагато

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

Перевіримо це на 45 навчальних оголошеннях і 300 нових, яких модель не бачила.

Ланцюжок дерев і рання зупинка

Швидкість навчання 0.1, дерева глибини 2. Угорі — прогноз ансамблю на дошці оголошень, унизу — дві помилки як функції кількості дерев.

на навчальних, ₴—
на нових, ₴—
дно на нових, ₴—
дно на дереві №—
Прожени повзунок від краю до краю. На першому десятку дерев обидві криві падають разом. Біля 27-го дерева тестова досягає дна — 1 181 ₴ — і розвертається вгору, тоді як навчальна продовжує падати аж до 408 ₴ на двохсотому. Це та сама картина перенавчання, тільки віссю складності тут служить кількість кроків ланцюжка.

Розвʼязок стандартний і називається рання зупинка (early stopping). Відкладаємо частину навчальних даних у валідаційну вибірку, після кожного дерева міряємо на ній помилку й припиняємо, щойно вона перестала покращуватись протягом заданої кількості кроків підряд. У бібліотеках це параметр на кшталт early_stopping_rounds=50: «якщо пʼятдесят дерев поспіль не дали покращення — зупиняйся й повертай найкращий стан».

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

Подивімось, скільки саме коштує ця чесність. Відкладімо з наших 45 оголошень 11 у валідацію, навчімо ланцюжок на решті 34 і зупинімось за правилом «пʼятдесят дерев поспіль без покращення». Далі порівняймо два способи обрати довжину ланцюжка — по валідації й підглядаючи в тестові 300:

як обрано кількість деревобрано деревпомилка на 300 нових, грн
чесно: мінімум на валідації з 11 оголошень451 336
підгляданням: мінімум на самому тесті321 301

Різниця — 35 грн, і читати її треба уважно. Число 1 301 не бреше саме по собі: ланцюжок із 32 дерев справді дає на цих 300 оголошеннях таку помилку. Бреше воно як оцінка майбутнього: щоб його дістати, ми обрали довжину ланцюжка під ці конкретні 300 оголошень, тобто підігнали гіперпараметр під ту саму вибірку, якою потім хвалимось. На наступних 300 оголошеннях така перевага не повториться, бо мінімум там припаде на інше дерево. Чесна процедура дає 1 336 грн — і саме це число можна називати вголос.

Чому обидва числа вищі за 1 181 із фігури. Бо тут ланцюжок учився на 34 оголошеннях замість 45: одинадцять пішли у валідацію й моделі вже не дістались. Це теж ціна ранньої зупинки, і на маленьких вибірках вона помітна — саме про це буде мова в §10. Порівнювати між собою треба ці два числа, а не кожне з них із фігурою.

І друге спостереження, назад з інтерактиву 3. Навчальна помилка падає монотонно й ніколи не розвертається — тому за нею неможливо помітити момент, коли стало гірше. У бустингу не буває «крива навчання сама покаже»: без відкладених даних ти не бачиш взагалі нічого.

КеруванняГоловні ручки

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

Ручки бустингу та їхні компроміси

Імена наведено як у scikit-learn; у XGBoost і LightGBM вони називаються трохи інакше, але означають те саме.

ручкащо робитьбільше означаєз чим торгується
n_estimatorsдовжина ланцюжкаточніше на навчальних, ризик перенавчанняпрямо з learning_rate
learning_rateчастка дерева, що додаєтьсяшвидше, але грубіше й із вищим дномобернено з n_estimators
max_depthскладність одного дереваловить взаємодії ознак і швидше перенавчаєтьсяз довжиною ланцюжка
num_leavesте саме в LightGBM, але через кількість листківдерево росте вглиб нерівномірноз min_data_in_leaf
min_samples_leafскільки оголошень має бути в листкугрубіші дерева, менше листків на одну точкуз глибиною
subsampleчастка рядків на одне дерево0.5–0.8 додає різноманітності й пришвидшуєтрохи більше шуму в кривій
colsample_bytreeчастка ознак на одне деревояк mtry в лісі: декорелює дереваз силою окремого дерева
reg_lambdaштраф на величину значень у листкахобережніші поправкиз умінням дотягтись до складних ділянок
Порядок підбору. Спершу зафіксуй малий learning_rate і великий n_estimators з ранньою зупинкою. Потім підбирай глибину або кількість листків — це друга за важливістю ручка. І лише в кінці, якщо є час, — subsample, частку ознак і регуляризацію. Розробники XGBoost радять задавати max_depth явно, а LightGBM — num_leaves: у цих бібліотек значення за замовчуванням щедрі, і без явного обмеження перенавчання настає швидко.

ІнструментиТри реалізації

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

Спільне для всіх трьох — гістограмний пошук розрізу. Класичне дерево перебирає всі можливі пороги між сусідніми значеннями ознаки; при мільйоні рядків це мільйон кандидатів на кожну ознаку в кожному вузлі. Гістограмний підхід спершу розкладає значення ознаки по 255 кошиках і перебирає лише межі кошиків. Точність розрізу трохи падає, швидкість зростає на порядок — і на великих даних це вирішальна перевага.

Чим три бібліотеки відрізняються по суті

Без реклами: усі три роблять те саме, різниця в тому, як саме.

рисаXGBoostLightGBMCatBoost
як росте дереворівнями: спершу весь поточний рівеньлистям: ділить той листок, що дає найбільший виграшсиметрично: один і той самий розріз на весь рівень
головне гальмоmax_depthnum_leavesdepth
категоріальні ознакиісторично — кодування наперед; власна підтримка зʼявилась пізніше й вмикається окремим прапорцемвміє напрямувміє напряму, з упорядкованою статистикою по цілі
сильна сторонанайдовша історія, найбільше опцій і документаціїшвидкість на великих таблицяхнайменше клопоту з коробки, менше витоку при кодуванні категорій
типова пасткалегко забути обмежити глибинуріст листям перенавчається швидше за ріст рівнямиповільніший на малих даних
Ріст листям проти росту рівнями — головна архітектурна різниця. LightGBM щоразу ділить той листок, який дає найбільше зменшення втрати, тому дерево виходить кривобоким: одна гілка глибока, інша коротка. Це точніше за той самий бюджет листків — і саме тому небезпечніше: обмежувати треба num_leaves, бо max_depth тут майже нічого не стримує.
Що з цієї таблиці старіє, а що ні. Рядок «як росте дерево» — архітектурне рішення, і воно тримається роками. А от імена параметрів, їхні значення за замовчуванням і перелік того, що бібліотека «вміє напряму», з версіями змінюються: те, чого торік не було, цьогоріч зʼявляється прапорцем, а післязавтра стає типовою поведінкою. Тому запамʼятовуй із цієї таблиці принцип, а точні імена бери з документації тієї версії, яку ти встановив.

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

МежіКоли бустинг не потрібен

Найкращий алгоритм на табличних даних — не означає «завжди правильний вибір». Є три ситуації, у яких бустинг варто відкласти.

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

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

Немає часу на налаштування. Ліс має по суті одну ручку (max_features) і працює зі значеннями за замовчуванням. Бустинг має мінімум дві повʼязані пари й вимагає ранньої зупинки. Якщо потрібна базова лінія за пʼять хвилин — бери ліс. Якщо потрібен максимум і є година — бери бустинг.

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

ПідсумокЩо варто винести

Бустинг — це відповідь на питання «а що, як робити моделі не паралельно, а по черзі». Відповідь виявилась потужнішою, ніж очікували, бо разом із нею прийшов цілий спосіб думати про навчання.

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