Оптимизация

Практика: оптимизаторы с нуля

Написать GD, момент и Adam, увидеть обусловленность своими глазами и измерить batch size

Шаг 73 из 117 · ~45 мин

Блок в одной формуле

Всё, что было, сводится к одному числу и способам от него не зависеть.

итераций  κ для GD,κ для момента,1 для Ньютона\text{итераций} \ \sim \ \htmlData{k=kappa}{\kappa} \ \text{для GD}, \qquad \sqrt{\htmlData{k=kappa}{\kappa}} \ \text{для момента}, \qquad 1 \ \text{для Ньютона}
средствочто делает с κ\kappaцена
подбор шаганичего
моментκκ\kappa \to \sqrt\kappa в оценкеO(d)O(d) памяти
Adamуменьшает эффективную κ\kappa по диагонали2×O(d)2 \times O(d) памяти
Ньютонделает κ=1\kappa = 1O(d3)O(d^3)
стандартизацияуменьшает саму κ\kappaпочти бесплатно
нормализация слоёвто же, внутри сетинебольшая

Две последние строки — почему блок заканчивается не оптимизаторами, а нормализацией.

Что реализовать

1. Четыре оптимизатора, один интерфейс

Напишите функцию, принимающую градиент и состояние, для каждого из: GD, момент, Нестеров, Adam. Все четыре умещаются в двадцать строк, если состояние передавать явно.

Проверки, которые обязаны сойтись:

  • GD на f=a2x2f = \frac{a}{2}x^2: после kk шагов xk=x0(1αa)kx_k = x_0(1 - \alpha a)^k точно. Сравните с замкнутой формой, а не «на глаз»;
  • Adam из нулевого состояния: при любом ненулевом постоянном градиенте первый шаг равен α\alpha с точностью до 10710^{-7}. Если получилось 0.1α0.1\alpha — забыта коррекция m^\hat m; если 31α31\alpha — забыта коррекция v^\hat v;
  • момент при β=0\beta = 0 обязан совпадать с GD побитово.

2. Плохо обусловленная квадратика

Возьмите f=12(x2+κy2)f = \frac12(x^2 + \kappa y^2) и проверьте четыре предсказания блока:

что проверитьожидание
порог устойчивости GDрасходимость строго при α>2/κ\alpha > 2/\kappa
при α=2/κ\alpha = 2/\kappa ровновечные колебания, лосс не падает
число шагов GD до 10610^{-6}κ2ln106\approx \frac{\kappa}{2}\ln 10^{6}
момент с β,α\beta^*, \alpha^*в κ\approx\sqrt\kappa раз меньше шагов

Для κ=100\kappa = 100 последние две строки дают 691691 и 6969 — сверьтесь. И проверьте формулу β=(κ1κ+1)2\beta^* = \left(\frac{\sqrt\kappa-1}{\sqrt\kappa+1}\right)^2: при κ=100\kappa = 100 это 0.6690.669, а не привычные 0.90.9.

3. Batch size и learning rate

Соберите игрушечную стохастическую задачу: fi(x)=12(xci)2f_i(x) = \frac12(x - c_i)^2 с разбросанными cic_i. Тогда шум градиента известен точно, и можно проверить:

  • дисперсия градиента по батчу равна σ12/B\sigma_1^2/B — измерьте на B=1,4,16,64B = 1, 4, 16, 64;
  • при постоянном шаге итерат не сходится, а гуляет с дисперсией ασ2a(2αa)\frac{\alpha\sigma^2}{a(2-\alpha a)}. Измерьте после отбрасывания первых итераций;
  • правило α/B=const\alpha/B = \text{const} сохраняет уровень шума. Проверьте на четырёх парах и найдите, где оно начинает ломаться — это происходит, когда α\alpha подходит к 2/L2/L.

Третий пункт — самый содержательный, потому что даёт «критический размер батча» не как эмпирическое наблюдение, а как следствие формулы.

4. Траектория на 2D-ландшафте

Нарисуйте линии уровня и путь каждого оптимизатора. Обязательный набор картинок:

  1. круглая чаша — GD попадает за один шаг при α=1/L\alpha = 1/L, момент перелетает;
  2. вытянутая чаша — GD зигзагует, момент идёт почти прямо;
  3. повёрнутая вытянутая чаша — Adam теряет преимущество, потому что диагональ не видит поворота;
  4. банан Розенброка — ни один параметр не хорош на всём пути.

Третья картинка — самая полезная и чаще всего пропускаемая. Сравните две матрицы Гессе: diag(1,100)\text{diag}(1, 100) и (50.549.549.550.5)\begin{pmatrix} 50.5 & 49.5 \\ 49.5 & 50.5\end{pmatrix}. Спектр у них один и тот же, а диагонали совершенно разные — вот и вся разница между полным и диагональным предобусловливанием.

лосс, логарифмическая шкала

шаг α 0.02
итераций 200
шум 0
f в конце
1.12e-3
обусловленность κ
1
порог 2/L
2
 
сходится

Эталон: κ = 1. Поставьте α = 1 и убедитесь, что GD попадает в минимум за одну итерацию, а момент проскакивает.

5. Нормализация как предобусловливание

Самое короткое и самое убедительное упражнение блока:

  1. возьмите линейную регрессию на данных, где один признак имеет масштаб 10410^4;
  2. посчитайте κ\kappa матрицы XXX^\top X — получится около 10810^8;
  3. обучите градиентным спуском, логируя лосс. Он будет ползти;
  4. стандартизуйте признаки, пересчитайте κ\kappa — упадёт до десятков;
  5. обучите снова тем же методом и тем же шагом.

Разница в скорости будет на порядки, при том что оптимизатор не менялся. Это и есть главный вывод блока, полученный своими руками.

Что должно сойтись

проверкаожидание
GD на квадратике против замкнутой формысовпадение до 101210^{-12}
порог расходимостировно 2/L2/L, не «примерно»
момент при β=0\beta = 0 против GDпобитово одинаково
первый шаг Adam из нуляα\alpha с точностью 10710^{-7}
дисперсия градиента по батчуσ12/B\sigma_1^2/B в пределах шума измерения
κ\kappa до и после стандартизацииразница в 10610^610710^7 раз

И одна вещь, которая сойтись не должна: не пытайтесь получить теоретические β\beta^* и α\alpha^* в реальном обучении. Они требуют знать μ\mu и LL, которых нет. Формулы нужны, чтобы понимать порядок величин и то, откуда берутся привычные значения — а не чтобы их подставлять.

Источники

Проверки

0 из 2
  1. Блок целиком

    Отметьте все верные утверждения, связывающие уроки блока.

  2. Спуск против момента на квадратике

    Функция f(x,y)=12(x2+κy2)f(x,y) = \tfrac12\big(x^2 + \kappa y^2\big), старт всегда из (1,1)(1, 1). Обе реализации — тяжёлый шарик в форме

    vβv+g,zzαvv \leftarrow \beta v + g, \qquad z \leftarrow z - \alpha v

    при β=0\beta = 0 это в точности обычный спуск.

    Реализуйте optimiser_compare(kappa, alpha, beta, steps) — верните [gd_loss, momentum_loss, gd_stable, momentum_better]:

    • gd_loss — значение ff после steps шагов при β=0\beta = 0;
    • momentum_loss — то же при переданном beta;
    • если итерация улетела (значение не конечно или координата превысила 1015010^{150}), верните для этого прогона Infinity;
    • gd_stable = 1.0, если α<2/κ\alpha < 2/\kappa (порог обычного спуска), иначе 0.0;
    • momentum_better = 1.0, если momentum_loss < gd_loss, иначе 0.0.

    Обязательная проверка, которая должна выполняться побитово: при beta = 0 два первых числа обязаны совпасть. Если не совпали — в реализации момента лишний множитель.

    функция optimiser_compare

    Загрузка редактора…

    Ctrl/⌘ + Enter