У попередній темі ми навчилися рахувати градієнт: один прохід назад — і на кожній вазі лежить число «наскільки я винна в помилці». Далі, здавалося б, усе просто. Відніми градієнт, помножений на крок, і повторюй. Саме так ми й робили в темі про градієнтний спуск.
Різниця в тому, де ми тепер стоїмо. Там був один параметр на опуклій кривій: гладка чаша, єдине дно, куди не постав початкову точку — прийдеш туди само. Тут — мільйон параметрів на неопуклій поверхні, яку тема про нейронні мережі описала як рельєф із ярами, плато й сідлами. Правило «крок проти градієнта» на такому рельєфі формально працює. Практично — може не дійти до кінця ніколи.
Ця лекція про надбудову над спуском, яку називають оптимізатором (optimizer). Оптимізатор не рахує градієнт і не чіпає мережу. Він отримує готовий градієнт і вирішує єдине питання: яким саме кроком зсунути кожен параметр. Кожен із методів нижче — це десяток рядків коду, і кожен вирішує конкретну біду.
01 / ДіагнозЧому одного градієнта замало
Почнімо з найпоширенішої з бід. Вона зветься яр (ravine) і виникає щоразу, коли поверхня втрат крута в одному напрямку й полога в іншому. Причина буденна: різні параметри впливають на втрату з різною силою. Ознака, виміряна в тисячах гривень, дає градієнти в сотні разів більші, ніж ознака, виміряна в частках одиниці.
Візьмімо наскрізний приклад цієї лекції — найпростішу поверхню з такою властивістю. Два параметри, чаша, мінімум у нулі:
Тут w₁ — параметр із пологого напрямку, w₂ — із крутого.
Градієнт рахується в один рядок: ∂L/∂w₁ = 0.05·w₁ і
∂L/∂w₂ = 20·w₂. Числа 0.05 і 20 — це кривизни, тобто другі
похідні: наскільки різко змінюється схил, коли рухаєшся вздовж своєї осі. Відношення
кривизн дорівнює 400, і саме воно робить чашу яром.
Тепер поставимо точку в (−4, 1) і подивимось на числа. Градієнт там
дорівнює (−0.20, +20.00) — одна складова більша за іншу у сто
разів. І ось у чому пастка. Довжину кроку η ми задаємо
одну на всі параметри. Обмежує її крутий напрямок: якщо
η перевищить 2/20 = 0.100, спуск по w₂
перестрибне дно й полетить угору, а звідти вибухне. Це та сама межа
η < 2/c, яку ми рахували в
темі про градієнтний спуск — тільки
там крутизна була одна, а тут їх дві й вирішує більша.
Візьмімо максимум, що нам дозволено: η = 0.096. Один крок зсуває
w₂ на 0.096 × 20 = 1.92 — тобто з +1.00 у
−0.92, майже дзеркально. А w₁ зсувається на
0.096 × 0.2 = 0.0192. За той самий крок, за який точка перелітає яр упоперек,
уздовж вона проповзає дві соті. Пройти треба чотири одиниці.
Порахуємо до кінця. Щоб зайти в коло радіуса 0.20 навколо мінімуму й більше з нього не вийти, простому спуску потрібно 623 кроки. За перші 120 кроків він долає вздовж 1.76 з 4.00 — і це при тому, що поверхня ідеально гладка, опукла й без жодного шуму. Проблема не в складності задачі, а в тому, що один крок на всі параметри — надто грубий інструмент.
Схема 1 · Три біди неопуклого рельєфу
Три ділянки, на яких простий спуск гальмує. Причини різні, наслідок один — кроки є, руху немає.
Дві інші біди відрізняються причиною, але не наслідком. Плато — це
велика майже рівна ділянка: градієнт крихітний, крок η·∂L/∂w теж крихітний,
і навчання виглядає завислим. Сідлова точка (saddle point) — місце, де
градієнт дорівнює нулю, але це не мінімум: в одних напрямках поверхня піднімається,
у інших опускається. У просторі з мільйоном вимірів сідел незрівнянно більше, ніж
локальних ям, і саме вони гальмують навчання найчастіше. Розділ 07 покаже це на числах.
02 / МоментМомент: накопичена швидкість
У темі про градієнтний спуск момент уже зʼявлявся — як образ важкого шара, що котиться схилом і має інерцію. Тепер розберемо не образ, а механіку: звідки береться прискорення й чому воно виходить саме таким.
Правило замість одного рядка стає двома:
Тут v — швидкість: додаткове число, яке ми зберігаємо
для кожного параметра окремо й переносимо з кроку на крок. Коефіцієнт β
(бета) каже, яку частку старої швидкості лишити; стандартне значення 0.9. Читається так:
нова швидкість — це девʼять десятих старої плюс увесь сьогоднішній градієнт; параметр
зсуваємо не за градієнтом, а за швидкістю.
Тепер найважливіше — що саме накопичується. Підставмо числа нашого яру, старт той
самий (−4, 1), η = 0.096, β = 0.9:
| крок | градієнт уздовж | швидкість уздовж | градієнт упоперек | швидкість упоперек |
|---|---|---|---|---|
| 1 | −0.2000 | −0.2000 | +20.0000 | +20.0000 |
| 2 | −0.1990 | −0.3790 | −18.4000 | −0.4000 |
| 3 | −0.1972 | −0.5384 | −17.6320 | −17.9920 |
| 4 | −0.1946 | −0.6792 | +16.9126 | +0.7198 |
| 5 | −0.1914 | −0.8026 | +15.5305 | +16.1784 |
| 6 | −0.1875 | −0.9099 | −15.5320 | −0.9714 |
| 7 | −0.1832 | −1.0020 | −13.6669 | −14.5411 |
| 8 | −0.1783 | −1.0802 | +14.2521 | +1.1651 |
Прочитай таблицю по двох парах стовпців окремо, і механіка стане очевидною.
Уздовж яру градієнт майже не змінюється: −0.200, −0.199, −0.197…
Знак той самий щокроку, тому доданки складаються, і швидкість росте: 0.20 → 0.38 →
0.54 → … → 1.08. За вісім кроків вона стала вшестеро більшою за сам
градієнт. Ростиме й далі, але не безмежно: сума нескінченної геометричної прогресії
дає межу 1/(1−β), тобто рівно вдесятеро при
β = 0.9. Ефективний крок уздовж яру став удесятеро довшим — і жодну межу
стійкості це не порушило, бо ми не чіпали η.
Упоперек яру градієнт міняє знак майже щокроку: +20, −18.4, −17.6, +16.9… Сусідні доданки віднімаються, і швидкість жодного разу не перевищила сам градієнт: 20.0, −0.40, −17.99, +0.72. Накопичуватись тут нема чому. Момент нічого не знає ні про яр, ні про напрямки — він просто складає числа. Але одна й та сама операція підсилює стабільні напрямки й приглушує метушливі.
Результат на повній дистанції: 44 кроки замість 623. Виграш у 14 разів — більший, ніж обіцяне десятикратне подовження кроку, бо накопичена швидкість заодно веде точку рівніше, і шлях виходить коротшим.
Інтерактив 1 · Що робить коефіцієнт моменту
Той самий яр, той самий крок 0.096. Змінюється одне число — скільки старої швидкості зберігати.
Нестеров: подивитись туди, куди летиш
У класичному моменті градієнт беруть у точці, де ми зараз. Момент Нестерова
(Nesterov accelerated gradient) бере його там, куди нас усе одно віднесе
накопиченою швидкістю: спершу подумки зробити крок −η·β·v, порахувати
градієнт уже там і лише тоді оновити швидкість. Різниця в одному рядку коду.
Сенс — у гальмуванні. Якщо попереду схил повертає вгору, класичний момент дізнається про це аж коли туди прилетить, а Нестеров бачить стіну на крок раніше й починає сповільнюватись завчасно. На нашому ярі виграшу немає: при кроці, на якому обидва стійкі, Нестеров дає 75 кроків проти 72 у класичного моменту. Це не дивно — для квадратичної чаші класичний момент і так оптимальний, кращого просто не існує. Перевага Нестерова в іншому: його гарантії тримаються на ширшому класі функцій, де класичний момент може взагалі не збігтись.
03 / AdaGradAdaGrad: свій крок кожному
Момент вирішує задачу з одного боку: він робить крок уздовж яру довшим. Є й протилежний захід — зробити крок упоперек коротшим. Ідея, з якої виріс цілий клас адаптивних методів, формулюється однією фразою: параметр, що регулярно отримує великі градієнти, має ходити меншими кроками; параметр, якому дістаються крихти, — більшими.
AdaGrad (adaptive gradient) реалізує це найпрямішим способом. Для кожного параметра він тримає суму квадратів усіх його градієнтів від початку навчання й ділить крок на корінь із цієї суми:
Тут s — накопичувач: своє число на кожен параметр, як і швидкість у
моменті. Квадрат потрібен, щоб знак не заважав: нас цікавить розмір градієнта,
а не напрямок. Корінь повертає величину в ті самі одиниці, що й сам градієнт, тому дріб
g/√s виходить безрозмірним. А ε — крихітне число близько
1e−8, яке стоїть там єдино заради того, щоб не ділити на нуль на першому
кроці, коли s ще порожній.
Подивись, що станеться на першому кроці нашого яру. Градієнти (−0.2, +20),
сума квадратів після одного кроку — це самі квадрати: (0.04, 400). Корінь
дає (0.2, 20). Дріб g/√s дорівнює рівно (−1, +1) —
у обох координатах одиниця зі своїм знаком. Тобто кожен параметр зсувається
рівно на η, і стократна різниця в градієнтах зникає повністю.
Точка з (−4, 1) переходить у (−3.904, 0.904).
А тепер вада, заради якої AdaGrad і варто розібрати. Сума квадратів
тільки росте — доданки невідʼємні, відніматись нема чому. Значить,
знаменник зростає, а крок згасає. Ось де опиняється w₁ на нашому ярі:
Порахуй швидкість на трьох ділянках. Перші 50 кроків — 0.023 одиниці за крок. З 50-го по 300-й — уже 0.006. З 300-го по 1000-й — 0.0016, у чотирнадцять разів повільніше за старт. Загалом AdaGrad дійшов до цілі за 1171 крок, тобто програв навіть простому спуску з його 623. Це не помилка й не невдалий приклад: так поводиться сама формула. Для задач, де більшість параметрів отримує градієнт зрідка (тексти, розріджені ознаки), згасання — благо: рідкісні ознаки зберігають довгий крок. Для навчання глибокої мережі, де кроків десятки тисяч, це смерть.
04 / RMSPropRMSProp: памʼять, що забуває
Полагодити AdaGrad можна одним символом. Замість суми, що росте назавжди, візьмімо ковзне середнє квадратів: старе поступово забувається, свіже важить більше.
Друга формула не змінилася взагалі — змінилась лише перша. Коефіцієнт ρ
(ро) грає ту саму роль, що β у моменті: 0.9 означає «памʼятаю приблизно
десять останніх кроків». Ключова відмінність від AdaGrad — множник (1 − ρ)
перед новим доданком. Через нього s лишається середнім квадратом,
а не сумою: він не росте від того, що кроків стало більше. Крок не згасає сам по собі.
Ефект на числах разючий: 48 кроків замість 1171 у AdaGrad на тому
самому ярі, з тим самим η. Метод, який ми полагодили одним множником,
став швидшим у двадцять чотири рази.
Але RMSProp має власну особливість, про яку варто знати заздалегідь. Середнє
стартує з нуля, і на перших кроках воно занижене: після одного кроку
s = 0.1·g², корінь дає 0.316·|g|, і дріб g/√s
виходить 3.16 замість очікуваної одиниці. Перший крок утричі довший
за замовлений: точка з (−4, 1) стрибає аж у (−3.696, 0.696).
Це не завжди шкодить — але це не те, про що ти просив, коли писав η = 0.096.
g/√s
лишається величиною близько одиниці. RMSProp на нашому ярі не сідає в мінімум узагалі:
він вічно тремтить на відстані приблизно 0.068 від нього, а це 0.7 від довжини кроку.
Точка бʼється об дно й відскакує. Лікується це не іншим оптимізатором, а зменшенням
η під кінець навчання — про що розділ 09.05 / AdamAdam і корекція зміщення
Дві ідеї вже є, і вони не конкурують, а доповнюють одна одну. Момент вирішує, у якому напрямку йти, згладжуючи градієнт у часі. RMSProp вирішує, наскільки довгий крок робити в кожній координаті. Adam (adaptive moment estimation) просто робить обидві речі одразу.
Перше середнє m — це згладжений сам градієнт, майже момент.
Друге, s, — згладжений квадрат градієнта, тобто RMSProp.
Стандартні коефіцієнти: β₁ = 0.9 (памʼять на десяток кроків) і
β₂ = 0.999 (памʼять на тисячу). Другий коефіцієнт більший навмисно:
оцінка розміру градієнта має бути стабільнішою за оцінку напрямку.
Обидва середні стартують із нуля, тому на початку вони занижені. Ділення на
1 − βᵗ, де t — номер ітерації, повертає їх на місце.
З ростом t знаменник прямує до одиниці, і корекція сама собою зникає.
Напрямок бере згладжений градієнт, довжину — його ж власний розмір. Дріб
m̂/√ŝ — це, грубо кажучи, «на скільки стандартних відхилень градієнт
відрізняється від нуля»: величина порядку одиниці незалежно від того, у чому
вимірюється параметр.
Чому корекція справді потрібна — видно тільки на числах. Уяви, що градієнт увесь
час однаковий і дорівнює g. Тоді після t кроків
m = (1 − β₁ᵗ)·g і s = (1 − β₂ᵗ)·g². Без корекції фактична
довжина кроку виходить
замість замовленої η. Підставмо стандартні 0.9 і
0.999: на першій ітерації виходить 3.16·η, на десятій —
6.53·η, на сотій — 3.24·η, і лише після тисячі
ітерацій відношення спадає до 1.26. З корекцією воно дорівнює рівно
одиниці на кожній ітерації.
Схема 2 · Що коштує пропущена корекція зміщення
У скільки разів фактичний крок Adam довший за замовлений, якщо не ділити на 1 − βᵗ. Горизонтальна вісь логарифмічна.
β₁, β₂: якби вони були однаковими, крок і справді виходив би
коротшим. При стандартних значеннях усе навпаки. У практиці цієї теми ти побачиш, як
на кроці η = 0.5 пропущена корекція псує першу епоху навчання: втрата
1.7594 замість 0.3417.06 / ГонкаГонка на ярі
Чотири методи, одна поверхня, один старт, один крок η = 0.096 на всіх.
Однаковий крок тут — не спроба зробити комусь приємно, а спосіб побачити різницю
механізмів, а не різницю налаштувань. Ціль одна: зайти в коло радіуса 0.20
навколо мінімуму й більше з нього не вийти.
Інтерактив 2 · Гонка на ярі
Тисни «крок» і дивись, хто куди їде. Лічильники показують, скільки кроків до цілі потрібно кожному.
Числа виходять такі: простий спуск — 623 кроки, момент — 44, RMSProp — 48, Adam — 54. AdaGrad, якого немає на картинці, щоб не заплутувати лінії, — 1171. Два висновки, і другий важливіший.
Перший: усе, що має памʼять, виграє на порядок. Не на відсотки — у чотирнадцять разів. Другий: Adam тут не перший. Він програє і моменту, і RMSProp, бо тягне два накопичувачі замість одного, і на такій простій поверхні згладжування напрямку лише додає інерції там, де вона не потрібна. Adam — це не «завжди найшвидший», а «майже ніколи не катастрофічний».
07 / СідлоСідло
Яр — задача про співвідношення градієнтів. Сідло — задача про їхню абсолютну величину, і саме тут різниця між методами найдраматичніша. Візьмімо поверхню:
У точці (0, 0) градієнт дорівнює нулю. Але це не дно: уздовж
w₁ поверхня йде вгору, а вздовж w₂ — вниз, до двох справжніх
мінімумів у (0, +1) і (0, −1). Класичне сідло. Поставимо точку
майже на гребені, у (1.2, 0.02), візьмемо η = 0.08 і спитаємо:
скільки кроків потрібно, щоб зʼїхати з гребеня, тобто дійти до |w₂| > 0.9.
Градієнт у напрямку виходу дорівнює −0.0024. Простий спуск помножить
його на η й зсунеться на 0.000192 — дві десятитисячні.
Наступного кроку градієнт трохи більший, зсув трохи більший, і так далі: точка
відповзає від гребеня по геометричній прогресії зі знаменником 1.0096. Щоб піднятись
від 0.02 до 0.9, їй потрібно 485 кроків. Перші шістдесят із них
піднімають w₂ аж до 0.0355 — на екрані це нерухома точка.
Адаптивні методи ділять цей самий крихітний градієнт на його ж власний масштаб. Дріб виходить порядку одиниці — і крок повний, ніби градієнт великий. RMSProp вибирається за пʼять кроків, Adam за 12, AdaGrad за 29. Момент — за 72: він теж накопичує, але накопичує маленькі числа, тому виграє лише в десять разів, а не в сто.
Інтерактив 3 · Сідло: хто злазить із гребеня
Лінії рівня сідлової поверхні. Старт майже на самому гребені, де градієнт майже нульовий.
Це і є головна причина, з якої адаптивні методи захопили глибоке навчання. У просторі з мільйоном вимірів точка майже завжди сидить біля якогось сідла: щоб мати справжній локальний мінімум, поверхня має йти вгору в усіх мільйонах напрямків одразу, а досить одного напрямку вниз — і це вже сідло. Метод, який реагує на напрямок градієнта, а не на його довжину, у такому рельєфі просто не залипає.
08 / ВибірЩо обирати
Таблиця 1 · Пʼять методів поруч
Стовпці «яр» і «сідло» — кроки до цілі з двох інтерактивів вище, при кроці 0.096 і 0.08 відповідно. AdaGrad пораховано так само, просто не намальовано, щоб не плутати лінії.
| метод | що накопичує | памʼять | яр | сідло | коли брати |
|---|---|---|---|---|---|
| SGD | нічого | 0 | 623 | 485 | коли поверхня добре обумовлена або є час на підбір |
| + момент | швидкість | 1× | 44 | 72 | класика для згорткових мереж і великих партій |
| AdaGrad | суму квадратів | 1× | 1171 | 29 | розріджені ознаки, короткі навчання |
| RMSProp | середній квадрат | 1× | 48 | 5 | рекурентні мережі, нестаціонарні задачі |
| Adam | і те, і те | 2× | 54 | 12 | розумне замовчування майже для всього |
Практична порада коротка. Adam — розумне замовчування. Він майже ніколи не поводиться катастрофічно, він майже не потребує підбору кроку, і на новій задачі з ним ти отримаєш робочий результат за перший вечір. Це велика цінність.
І поруч із цим — друге твердження, яке звучить як заперечення, але ним не є. SGD із моментом при добрих налаштуваннях часто дає кращий фінальний результат. На мережі з практики цієї теми з однаковим кроком 0.05 Adam виходить на втрату 0.2233 і точність 0.9250, а простий спуск — лише 0.3025 і 0.8944. Але дай простому спуску з моментом його власний крок, у двадцять разів більший, і він дає 0.2141 і 0.9222 — тобто наздоганяє Adam.
Суперечності немає, бо два твердження про різне. Adam хороший з коробки: він
сам масштабує крок, тому працює при майже будь-якому η. SGD із моментом
хороший після налаштування: у нього менше рухомих частин, він не переоцінює
напрямки з рідкісним сигналом і зазвичай зупиняється в ширшому мінімумі, який краще
узагальнюється. Правило, з яким важко помилитись: прототип на Adam, фінальний
прогін — на тому, що виграв у чесному порівнянні. І перевіряти це порівняння
треба на валідації, а не на навчальній втраті.
09 / РозкладРозклад швидкості навчання
Ми вже бачили, що жоден із методів не сідає в мінімум точно: RMSProp на нашому ярі
вічно тремтить на відстані 0.068, а це майже довжина його кроку. Причина спільна для
всіх: довжина кроку задана наперед і не знає, далеко ми чи близько. Лікують це
розкладом швидкості навчання (learning rate schedule) — заздалегідь
описаним планом, як η зменшується з часом.
Схема 3 · Три розклади кроку
Як довжина кроку змінюється протягом навчання. Початкове значення в усіх однакове.
Навіщо кожен із трьох. Ступінчастий (step decay) — найстаріший і
найпростіший: помножити η на 0.1 на заданих епохах. Він досі стандарт у
класичних згорткових мережах, бо його легко відтворити. Косинусний
(cosine annealing) робить те саме плавно, без стрибків, і зазвичай дає трохи кращий
результат просто тому, що не витрачає час на життя із завеликим кроком між сходинками.
Розігрів (warmup) вирішує іншу задачу — не кінець навчання, а початок. На перших ітераціях ваги випадкові, градієнти великі й неузгоджені, а в адаптивних методах до цього додається ще й недооцінений знаменник: ми щойно бачили, що без корекції зміщення перші кроки виходять до 6.5 раза довшими. Повний крок на такому старті легко відкидає мережу в область, з якої вона довго вибирається. Розігрів — страховка від цього: перші кілька сотень ітерацій крок росте від майже нуля до робочого, і мережа встигає дійти до розумної області, перш ніж почне рухатись швидко.
10 / ДаліКуди далі
Підсумок теми вміщається у пʼять рядків:
- яр — крок обмежує найкрутіший напрямок, а йти треба вздовж найпологішого; звідси 623 кроки там, де вистачило б сорока;
- момент — накопичує швидкість; уздовж стабільного напрямку вона
доростає до
1/(1−β)градієнтів, упоперек метушливого не росте взагалі; - адаптивні методи — ділять крок на власний масштаб градієнта; AdaGrad робить це сумою і згасає до нуля, RMSProp — ковзним середнім і не згасає;
- Adam — момент і RMSProp разом, плюс корекція зміщення, без якої
ηозначає не те, що написано; - сідло — місце, де адаптивність вирішує все: 5 кроків проти 485.
Важливо, чого оптимізатор не робить. Він не змінює ні мережу, ні градієнт: зворотне поширення лишається тим самим проходом назад по тому самому графу. Він не лікує затухання градієнта — цим займається вибір активацій та ініціалізації, тема про які йде в цьому блоці раніше. І він не покращує узагальнення: швидше дійти до мінімуму навчальної втрати не означає краще працювати на нових даних. Розрив між цими двома речами — окрема велика тема, і наступна лекція блоку, про регуляризацію мереж, займається саме нею: dropout, спад ваг, рання зупинка. До речі, спад ваг (weight decay) — це прийом, який живе рівно на межі двох тем, бо реалізують його всередині оптимізатора, і в Adam для цього довелося вигадати окремий варіант, AdamW.
practice.ipynb ти напишеш усі
пʼять оптимізаторів з нуля — кожен близько десятка рядків — і проженеш їх по ярі, по
сідлу й по справжній мережі з теми про зворотне поширення. Побачиш ті самі 623 і 44
кроки на власному коді, порівняєш криві втрат і вимкнеш у Adam корекцію зміщення, щоб
подивитись, що з цього вийде при різних швидкостях навчання.Далі в темі
Теорію прочитано. Тепер закріпи її на практиці.