У попередній темі ми провели пряму крізь хмару точок. Провели чесно: записали суму квадратів залишків, узяли похідну, прирівняли до нуля й отримали готову формулу. Підставив дані — маєш відповідь. Один рядок коду, жодних налаштувань, точний результат.
Ця розкіш закінчується майже одразу. Варто замінити пряму на щось трохи складніше — і рівняння, яке треба розвʼязати, перестає розвʼязуватися. Не «важко розвʼязується», а не має запису через звичні операції взагалі. Тоді лишається інший шлях: не шукати відповідь одним обчисленням, а підповзати до неї. Саме цей спосіб — градієнтний спуск — і навчає сьогодні майже все, що називають машинним навчанням.
Розбиратися будемо на дошці оголошень про продаж вживаних телефонів — тому самому наборі даних, що й у решті курсу. Сімсот оголошень, у кожного відомий рік випуску телефона й ціна, яку просить продавець. Серед оголошень трапляються й шахрайські, з навмисно заниженою ціною, — це частина шуму, з яким моделі доводиться жити.
ПостановкаКоли формули просто немає
Модель, яку ми підбираємо, максимально проста — два числа:
Словами: ціна телефона складається з базової вартості плюс надбавка за кожен рік
свіжості. Символ w (вага, weight) — на скільки тисяч гривень
дорожчає телефон за один рік новизни. Символ b (зсув, bias) —
скільки коштує найстаріший телефон у вибірці, тобто випуску 2018 року.
Віднімаємо 2018, щоб b мав зрозумілий сенс, а не був ціною телефона нульового
року. Ціни всюди рахуємо в тисячах гривень.
Для такої моделі формула є — ми вивели її минулого разу. Тож навіщо тоді спуск? Бо формула — це виняток, а не правило. Ось три причини, з яких вона зникає:
- Функція втрат перестає бути квадратичною. Уже логістична регресія мінімізує логарифмічну втрату, і рівняння «похідна дорівнює нулю» не розвʼязується в замкненому вигляді. Не тому, що ніхто не додумався, — розвʼязку в елементарних функціях просто не існує.
- Параметрів забагато. Формула лінійної регресії вимагає обернути матрицю розміром «кількість ознак × кількість ознак». На десятку ознак це мить, на десятках тисяч — неприйнятно, а в нейромережах параметрів мільйони.
- Дані не влазять у памʼять. Формула хоче бачити всю матрицю одночасно. Спуск дивиться на дані порціями і взагалі не потребує тримати їх усі поруч.
Тому далі ми свідомо забудемо, що формула існує, і знайдемо ті самі w
і b наосліп. Наприкінці порівняємо — і числа мають зійтися.
ЛандшафтФункція втрат як ландшафт
Щоб рухатись до відповіді, спершу треба вміти сказати, наскільки поточна відповідь погана. Беремо середній квадрат помилки:
Читається так: для кожного оголошення візьми справжню ціну, відніми ту, яку
прогнозує модель, піднеси різницю до квадрата — і всі ці квадрати усередни.
Тут x_i — вік телефона в роках від 2018, y_i — справжня ціна
з оголошення, n — кількість оголошень (у нас 700), а Σ означає
«склади по всіх оголошеннях». Навіщо вона тут: це єдине число, яким ми міряємо якість
пари (w, b), — і саме його зменшуватимемо.
Тепер найважливіший зсув у голові. L(w, b) — це не одне число, а
ціле поле чисел. Кожній парі (w, b) відповідає своя
величина втрати. Якщо відкласти w по горизонталі, b —
по вертикалі, а втрату показати кольором, вийде карта місцевості: пляма в центрі —
дно долини, а замкнені криві навколо неї — лінії однакової втрати.
Ось ця карта для наших семисот оголошень. Точки на ній підписані реальними значеннями втрати:
Ландшафт втрат
Кожна точка карти — своя пара (w, b). Колір — величина втрати L. Хрест позначає найнижчу точку.
Тепер задача навчання формулюється однією фразою: знайти найнижчу точку цього
ландшафту. І тут головна складність. Ми не бачимо карти. Карту намалював компʼютер,
перебравши сотні тисяч пар (w, b), — для двох параметрів це можливо, для
мільйона ні. Уяви натомість, що тебе висадили десь на цьому схилі в густому тумані.
Видимість — метр. Ти не знаєш ні де дно, ні в який бік воно. Але одне ти знаєш точно:
у який бік нахилена земля просто під ногами. І цього виявляється досить.
ПохіднаПохідна — це напрямок схилу
Похідна має репутацію страшної речі з підручника. Насправді вона відповідає на дуже побутове питання:
і наскільки різко?
Ось і все. Число, яке відповідає на це питання, і називається похідною. Знак каже напрямок: додатна — праворуч угору, там гірше; відʼємна — праворуч униз, там краще. Величина каже крутизну: похідна −12 означає значно крутіший схил, ніж похідна −0.5.
Перевіримо це буквально, на калькуляторі. Щоб числа можна було звірити руками, візьмемо
шість оголошень — по одному типовому на кожен рік випуску — і тимчасово зафіксуємо
b = 4.2. Тоді змінна лишиться одна: w.
| рік випуску | x = рік − 2018 | ціна y, тис. грн |
|---|---|---|
| 2018 | 0 | 4.2 |
| 2019 | 1 | 5.9 |
| 2020 | 2 | 7.1 |
| 2021 | 3 | 9.7 |
| 2022 | 4 | 11.0 |
| 2023 | 5 | 12.4 |
Рахуємо похідну чисельно
Візьмемо w = 1.0 — тобто припущення «телефон дорожчає на тисячу гривень
за рік». Втрата виходить
Тепер зробимо той самий «крихітний крок праворуч»: збільшимо w на 0.001
і перерахуємо втрату наново:
Втрата впала. Отже, рухатись праворуч вигідно. Наскільки різко — покаже
відношення зміни втрати до зміни w:
Це число і є похідною, порахованою чисельно. Ніякої магії: два обчислення функції й одне ділення. Мінус означає «праворуч униз».
Рахуємо похідну формулою
Тепер виведемо формулу й переконаємось, що вона дає те саме число. Виведення — чотири кроки, кожен з однією думкою. Перед ними — два позначення, які в цих кроках знадобляться; обидва прості, просто виглядають страшнувато.
∂L / ∂w читається
«часткова похідна L по w» і означає рівно те, що ми щойно міряли руками:
наскільки зміниться L, якщо крихітно зсунути тільки w, а решту чисел
не чіпати. Слово «часткова» — саме про це «тільки»: у нас параметрів два, і ми
щоразу питаємо про один із них, тримаючи другий нерухомим. Кругле ∂ від прямої d
відрізняється лише цим: пряме d пишуть, коли змінна взагалі одна.
Читати вголос можна як завгодно — «де-ель по де-ве» цілком годиться.w рахують помилку e,
а вже потім її підносять до квадрата. Для таких двоповерхових виразів є одне правило,
його називають ланцюговим (chain rule): похідна зовнішньої дії,
помножена на похідну внутрішньої.
Побутова перевірка, чому саме множення. Уяви три шестерні. Перша крутиться втричі швидше за другу, друга — вдвічі швидше за третю. Тоді перша швидша за третю в 3 · 2 = 6 разів, а не в 3 + 2. Похідні — це теж «у скільки разів швидше змінюється одне, коли змінюється друге», тож вони так само перемножуються.
Обидві похідні тут — зі шкільного мінімуму: похідна квадрата
(u²)′ = 2u, а похідна лінійного виразу — це просто коефіцієнт при змінній.
Більше нічого з математичного аналізу для цього виведення не знадобиться.Втрата — це середнє з окремих доданків. Візьмемо один. Позначимо помилку
(залишок) на i-му оголошенні:
Усередині — лінійна залежність від w, зовні — квадрат. Застосовуємо
ланцюгове правило: похідна зовнішньої дії, помножена на похідну внутрішньої.
Зовнішня дія — квадрат, її похідна дорівнює подвоєному аргументу, тобто
2·e_i. Внутрішня — це e_i = y_i − (w·x_i + b):
коли w підростає на одиницю, прогноз підростає на x_i,
а помилка на стільки ж зменшується — тому внутрішня похідна дорівнює
−x_i. Перемножуємо:
Похідна суми дорівнює сумі похідних, а сталий множник 1/n просто
виноситься. Отже:
Словами: помнож кожну помилку на вік телефона, склади все, поділи на кількість
оголошень і візьми з мінусом та подвій. Множник x_i — це вага голосу:
старий телефон (великий x) сильніше тягне w на себе, бо на
ньому нахил прямої проявляється помітніше. Обʼєкт із x = 0 не впливає
на w взагалі.
Для b викладки ті самі, тільки внутрішня похідна дорівнює −1
замість −x_i, бо b входить у прогноз з коефіцієнтом одиниця:
Пара цих двох чисел разом і називається градієнтом (gradient). Градієнт — це не нове поняття, а просто список усіх похідних одразу: по одній на кожен параметр.
Підставимо числа. При w = 1.0 і b = 4.2 прогноз і помилки
такі:
| x | y | прогноз w·x + b | помилка e | x · e |
|---|---|---|---|---|
| 0 | 4.2 | 4.2 | 0.0 | 0.0 |
| 1 | 5.9 | 5.2 | +0.7 | +0.7 |
| 2 | 7.1 | 6.2 | +0.9 | +1.8 |
| 3 | 9.7 | 7.2 | +2.5 | +7.5 |
| 4 | 11.0 | 8.2 | +2.8 | +11.2 |
| 5 | 12.4 | 9.2 | +3.2 | +16.0 |
| сума | — | — | +10.1 | +37.2 |
Тепер за формулою:
Порівняй: чисельно вийшло −12.3908, за формулою — −12.4000. Різниця в третьому знаку, і вона повністю пояснюється тим, що крок 0.001 усе-таки не нульовий: чисельна оцінка міряє нахил січної, а формула — нахил дотичної. Зменш крок до 0.000001 — розбіжність зникне.
А ось як це виглядає. Крива нижче — втрата L(w) при фіксованому
b = 4.2. Пряма, що торкається кривої, — та сама дотична, нахил якої ми
щойно рахували. Постав початкову точку й тисни «зробити крок»:
Кулька котиться схилом
Шість оголошень, b = 4.2, швидкість навчання зафіксована на 0.02. Кожен клік — одна ітерація.
ПравилоКрок за кроком, руками
Правило оновлення — один рядок, і в ньому вся суть методу:
Стрілка означає «стало новим значенням». Символ η (грецька «ета») —
швидкість навчання (learning rate): додатне число, яке ми обираємо самі
й яке каже, наскільки великий крок робити. Похідна дає лише напрямок і крутизну, а
довжину кроку задає η.
Чому мінус. Похідна показує напрямок зростання втрати. Нам
треба навпаки — тому йдемо в протилежний бік. Мінус розвертає напрямок. Перевір на нашому
числі: похідна дорівнює −12.4, отже w − η·(−12.4) = w + η·12.4 — тобто
w зростає. Саме те, що треба: втрата ж падала праворуч. Якби похідна була
додатною, той самий мінус потягнув би w ліворуч.
Прокрутімо чотири ітерації руками. Швидкість навчання η = 0.02, старт
w = 1.0, зсув усе ще зафіксований на 4.2:
| ітерація | w | втрата L(w) | похідна | крок −η·похідна | нове w |
|---|---|---|---|---|---|
| 0 | 1.0000 | 4.2717 | −12.4000 | +0.2480 | 1.2480 |
| 1 | 1.2480 | 1.7603 | −7.8533 | +0.1571 | 1.4051 |
| 2 | 1.4051 | 0.7529 | −4.9738 | +0.0995 | 1.5045 |
| 3 | 1.5045 | 0.3488 | −3.1501 | +0.0630 | 1.5675 |
| 4 | 1.5675 | 0.1868 | −1.9950 | +0.0399 | 1.6074 |
Прочитай цю таблицю по стовпцях — вона показує всю поведінку методу одразу. Втрата падає: 4.27 → 1.76 → 0.75 → 0.35 → 0.19. Похідна слабшає за модулем: −12.4 → −7.85 → −4.97 → −3.15 → −2.00. І тому крок сам собою коротшає: 0.248 → 0.157 → 0.100 → 0.063 → 0.040. Ніхто цього не програмував — так влаштований схил: біля дна він пологіший.
Якщо продовжити, w зупиниться на 1.6764 із втратою
0.0782. Точнісінько те саме значення дає формула найменших квадратів
для цих шести оголошень при тому самому зафіксованому b = 4.2:
w = Σx·(y − b) / Σx² = 92.2 / 55 = 1.6764. Спуск не наблизився до
відповіді — він у неї прийшов.
w і b. Якщо оновити w, а похідну по
b рахувати вже з новим w, це буде інший алгоритм — іноді
він працює, іноді розходиться, і зловити таку помилку в коді дуже важко, бо помилки
вона не викидає.КрокШвидкість навчання
Швидкість навчання — найважливіший параметр, який ти задаєш руками. Похідна не має одиниць вимірювання, зручних для кроку: вона каже «схил такий-то», а на скільки саме зсунутись — вирішуєш ти. І тут рівно дві катастрофи, між якими треба протиснутись.
Замала. Крок мікроскопічний, кожна ітерація зсуває w
на волосину. Метод працює правильно, просто повзе. При η = 0.005 наші шість
оголошень навіть за 30 ітерацій дають w = 1.639 — до відповіді 1.676 ще
далеко. На реальній задачі це означає години обчислень замість хвилин.
Завелика. Крок перестрибує дно й опиняється на протилежному схилі —
причому вище, ніж був. Наступна похідна там більша, крок довший, і система вибухає.
При η = 0.12 той самий спуск за 30 ітерацій відлітає до w = −159
із втратою в сотні тисяч. Класична ознака в логах — втрата, що росте, а потім
перетворюється на NaN.
Між ними є вікно. Для наших шести оголошень воно закінчується рівно на
η = 0.109: до цього значення спуск збігається, після — розлітається.
Число не випадкове — воно визначається крутизною чаші. Прожени повзунок від краю
до краю й подивись на всі три режими:
Три долі одного спуску
Ліворуч — траєкторія по кривій втрат. Праворуч — як падає (або росте) втрата з ітераціями; шкала логарифмічна.
η < 2 / c, де c — крутизна
чаші. Крутизна — це друга похідна: перша похідна каже, як швидко
змінюється втрата, друга — як швидко змінюється сама перша похідна, тобто наскільки
різко схил повертає вгору. У пласкої миски друга похідна мала, у вузької склянки —
велика. Для нашої втрати її легко порахувати руками: похідна дорівнює
−(2/n)·Σ x·e, а всередині e множник при w
дорівнює −x, отже друга похідна — це (2/n)·Σ x².
Підставляємо шість наших років x = 0…5:
Σ x² = 0 + 1 + 4 + 9 + 16 + 25 = 55, звідси
c = 2 · 55 / 6 = 18.33 і поріг 2 / 18.33 = 0.109.
Та сама крутизна дає й найкращий крок, а не лише найгірший. Для чаші ідеальний крок дорівнює
1 / c = 1 / 18.33 = 0.0545: при такому
η одне-єдине оновлення потрапляє точно в дно, звідки б ти не стартував.
Половина від порогу розльоту — ось де оптимум. Саме тому крива на схемі нижче має
дно приблизно там. Для нейромереж цю крутизну ніхто не знає наперед, і
η підбирають експериментом: запускають на кілька сотень кроків із різними
значеннями й дивляться, яка крива втрат падає найшвидше й не вибухає. Звідси й практика
розкладу швидкості (learning rate schedule): починають із більшого кроку,
щоб швидко дістатися потрібної області, і поступово зменшують, щоб акуратно сісти на дно.η = 0.05 доходить до дна за три кроки, η = 0.09 — уже за
шістнадцять, а η = 0.105 — аж за вісімдесят чотири, бо починає стрибати
через дно туди-сюди. Залежність не монотонна: спершу швидшає, потім різко гіршає,
потім вибухає. Ось уся ця залежність одним малюнком:Ціна кроку: скільки ітерацій до дна
Для кожної швидкості навчання від 0.001 до 0.130 — скільки ітерацій треба, щоб w підійшло до відповіді 1.6764 ближче ніж на 0.001. Ті самі шість оголошень, старт скрізь w = 1.0.
МежіЯми, сідла й чесна межа методу
Досі все виглядало надійно: є схил — котимося вниз — приходимо на дно. Так і буде, але тільки для нашої задачі. Функція втрат лінійної регресії опукла (convex): у неї форма чаші, дно рівно одне, і з якої точки не почни — прийдеш у ту саму відповідь. Це рідкісний подарунок, і ним варто скористатися, поки він є.
Для складніших моделей ландшафт зовсім інший. Там трапляються:
- Локальні мінімуми — ямки на схилі. Земля навколо йде вгору, похідна дорівнює нулю, спуск зупиняється. Але справжнє дно долини — далеко внизу, за перевалом, і потрапити туди зі своєї ямки метод не може.
- Сідла (saddle points) — місця, де вздовж одного напрямку схил іде вгору, а вздовж іншого вниз, як перевал між двома вершинами. Похідна теж нульова. Спуск біля сідла не зупиняється зовсім, але сповільнюється так, що це важко відрізнити від зупинки. Сідло потребує щонайменше двох напрямків, тому на одновимірному розрізі нижче його не намалювати — там видно лише ямку й плато.
- Плато — довгі майже рівні ділянки. Похідна крихітна, кроки крихітні, навчання виглядає мертвим, хоча формально триває.
Три кульки, три різні долі
Один нерівний ландшафт, три старти, однакова швидкість навчання й однакова кількість кроків у кожної кульки.
Це чесна межа методу: градієнтний спуск не гарантує глобального мінімуму. Він гарантує лише, що ти не підеш угору. Твердження «спуск завжди знаходить найкращу відповідь» правильне для опуклої задачі й неправильне для будь-якої іншої.
СтохастикаСтохастичний спуск
Повернімось до всіх семисот оголошень і порахуємо ціну одного кроку. Щоб обчислити градієнт чесно, треба пройти всі 700 рядків: для кожного порахувати прогноз, помилку, домножити на вік, скласти. І все це — заради одного оновлення двох чисел. Такий варіант називають повним (batch gradient descent).
Для 700 рядків це дрібниця. Для датасету з мільярда прикладів — катастрофа: один крок триває годину, а кроків треба тисячі.
Ідея стохастичного градієнтного спуску (stochastic gradient descent, SGD) до образливого проста. Точний градієнт нам, власне, не потрібен — потрібен приблизний напрямок униз. То візьмімо не всі 700 оголошень, а одне випадкове, порахуймо градієнт по ньому й одразу зробімо крок. Одне оголошення — погана оцінка загального схилу, вона може вказати геть убік. Але вона у середньому правильна, а коштує в 700 разів дешевше. За той самий обчислювальний бюджет ми зробимо 700 кроків замість одного.
Компроміс між крайнощами — партія (mini-batch): беремо не одне оголошення й не всі, а 32, або 64, або 256. Оцінка градієнта стає точнішою (шум усереднюється), а крок усе одно набагато дешевший за повний. Практично весь сучасний машинний навчальний код працює саме так.
Скільки прикладів іде на один крок
Дошка з 700 оголошень. Рожевим позначено ті, які алгоритм дивиться, щоб зробити одне оновлення параметрів.
Тепер найцікавіше: що з цього виходить на практиці. Обидві траєкторії нижче стартують
з точки (0, 0) з тією самою швидкістю навчання й витрачають однаковий
обчислювальний бюджет. Різниця лише в тому, на скількох прикладах рахується кожен крок:
Стохастичний проти повного
Бірюзова траєкторія — повний градієнт. Рожева — партіями. Однаковий бюджет переглядів, швидкість навчання 0.005 в обох.
Головний висновок звучить контрінтуїтивно: гірші кроки, зроблені частіше, перемагають кращі кроки, зроблені рідко. Точність напрямку окупається гірше, ніж кількість оновлень.
МоментМомент: чому шар котиться швидше
Подивись ще раз на карту втрат: долина витягнута. Уздовж неї схил пологий, упоперек — крутий. Звичайний спуск у такій долині поводиться безглуздо: він бачить переважно крутий поперечний схил і майже не бачить пологого поздовжнього. Тому він скочується до осі долини, перескакує її, повертається — і повзе вздовж крихітними кроками. Це та сама проблема різних масштабів, про яку йшлося в темі про лінійну регресію.
Тепер уяви замість точки важкий шар. Він теж відчуває поперечний схил, але має інерцію: за кілька метрів руху вздовж долини він розганяється, і поперечні поштовхи вже не встигають його розвертати. Поперечні коливання гасяться самі — вони щоразу спрямовані в різні боки й взаємно віднімаються. А поздовжній рух завжди в один бік, тому накопичується.
Математично це два рядки замість одного:
Тут v — швидкість, накопичена величина: памʼять про те,
куди ми рухались досі. Коефіцієнт β (бета) каже, яку частку минулої швидкості
зберігати; на практиці беруть від 0.8 до 0.95. Читається так: нова швидкість — це
більша частина старої плюс сьогоднішній градієнт; параметр зсуваємо не за градієнтом,
а за швидкістю.
Що саме накопичується — важливо зрозуміти точно. Якщо градієнт кілька кроків підряд
дивиться в один бік, доданки складаються, і v виростає приблизно
в 1/(1−β) разів: упʼятеро при β = 0.8, удесятеро
при β = 0.9. Якщо ж градієнт
хитається туди-сюди, сусідні доданки гасять один одного, і v лишається малою.
Момент буквально підсилює стабільні напрямки й приглушує метушню.
Із моментом і без
Та сама карта втрат, той самий старт, та сама швидкість навчання 0.005 і рівно 80 кроків у кожної траєкторії. Коефіцієнт моменту 0.8.
Момент — найпростіший із сімейства прийомів, які роблять спуск розумнішим. Далі в цьому сімействі йдуть методи, що підбирають окрему швидкість навчання для кожного параметра за історією його градієнтів, — найвідоміший з них Adam. Але всередині кожного з них досі та сама конструкція: порахуй градієнт, зроби крок проти нього.
ДаліКуди це веде далі
Зберемо весь метод у чотири рядки. Це повний цикл навчання будь-якої моделі, від нашої прямої до мовної моделі на сто мільярдів параметрів:
- Візьми поточні параметри й порахуй на них втрату.
- Порахуй градієнт — по одній похідній на кожен параметр.
- Зсунь кожен параметр на
−η ·його похідну. - Повтори.
Змінюється в цьому циклі напрочуд мало:
| тема | що змінюється | що лишається |
|---|---|---|
| 12 · лінійна регресія | є ще й точна формула | та сама MSE, той самий цикл |
| 14 · логістична регресія | втрата стає логарифмічною | той самий цикл, формули вже немає |
| 33 · нейронні мережі | параметрів мільйони | той самий цикл, градієнт рахує зворотне поширення (тема 34) |
У наступній темі ми візьмемо ту саму
дошку оголошень, але питатимемо не «яка ціна», а «чи це шахрайство». Відповідь стане
«так» або «ні», прямої вже не вистачить, і функція втрат зміниться на логарифмічну.
А от навчатись модель буде рівно тим самим спуском, який ти щойно прокрутив руками
на калькуляторі, — з тим самим правилом w ← w − η · ∂L/∂w.
Ще далі, у темі про нейромережі, параметрів стане мільйони, і зʼявиться окремий алгоритм — зворотне поширення помилки — який рахує всі ці похідні за один прохід. Але це буде спосіб дістати градієнт. Що з ним робити далі, ти вже знаєш.