🔴 Сложный ⏱️ 60 минут

Adam и адаптивные методы

📋 Содержание урока

Adam и адаптивные методы

Открой практически любой код обучения нейросети — учебный ноутбук, репозиторий с дипломной работой, промышленный пайплайн на PyTorch — и в подавляющем большинстве случаев строчка, отвечающая за оптимизатор, будет выглядеть как torch.optim.Adam(model.parameters(), lr=0.001) или, во всё более растущей доле случаев в последние годы, torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01). Это не случайность и не дань моде: Adam действительно стал оптимизатором по умолчанию для подавляющего большинства нейросетей, обучаемых сегодня, — от небольших учебных многослойных перцептронов до многомиллиардных языковых моделей. Понимание того, что происходит внутри этих двух строчек кода, — один из самых практически ценных кусков знаний, которые вообще можно получить из курса по машинному обучению.

В прошлом уроке про стохастический градиентный спуск ты уже познакомился с моментом: идеей накапливать «скорость» движения как экспоненциально взвешенное скользящее среднее прошлых градиентов, чтобы сгладить шумность отдельных mini-batch шагов. Момент решает одну проблему — дёрганость направления движения. Но у него остаётся слабое место: скорость обучения $\eta$ в формуле момента по-прежнему одна общая для всех параметров модели сразу. А параметры одной и той же сети могут вести себя совершенно по-разному: вес в плотном слое, который обновляется на каждом шаге обучения, и строка эмбеддинга для редкого слова, которая может не получать ни одного ненулевого градиента сотни шагов подряд, — это, по сути, два разных объекта с разным «характером» градиента, и требовать от них одной и той же скорости обучения физически неразумно.

Сегодняшний урок — про то, как эта проблема решается: у каждого параметра появляется собственная, индивидуально подстраиваемая эффективная скорость обучения. Путь к современному стандарту шёл через три шага, и каждый решал проблему предыдущего. Сначала AdaGrad — первая по-настоящему рабочая идея адаптации шага под историю градиентов конкретного параметра, но с фатальным недостатком: накопленная история только растёт, и шаг рано или поздно затухает до нуля. Затем RMSProp — простая, но принципиальная правка, которая делает эту адаптацию «незабывающей» (шаг больше не затухает необратимо). И наконец Adam — который берёт RMSProp-подобную адаптацию масштаба шага и добавляет к ней момент из прошлого урока, плюс аккуратную математическую поправку на первые шаги обучения, когда скользящие средние ещё не успели «прогреться».

Раздели сегодняшнему уроку максимальную концентрацию на практике. Формулу Adam ты будешь писать не на экзамене, а буквально при инициализации каждой своей нейросети, поэтому цель не «сдать урок», а по-настоящему понимать, что означает каждая буква в Adam(lr=0.001, betas=(0.9, 0.999), eps=1e-8) — и почему, если что-то в обучении идёт не так (лосс улетает в NaN, модель не сходится, качество на валидации хуже ожидаемого), разумное действие — заглянуть именно в эти параметры, а не менять архитектуру сети наугад.

История

Формальную идею адаптации скорости обучения под историю градиентов конкретного параметра предложили в 2011 году Джон Дучи, Элад Хазан и Йорам Сингер в статье «Adaptive Subgradient Methods for Online Learning and Stochastic Optimization» — так появился AdaGrad. Мотивация авторов была прикладной и очень конкретной: в задачах обработки естественного языка и рекламных систем того времени признаки часто были разреженными (sparse) — огромное количество бинарных индикаторов вроде «встретилось ли это редкое слово в тексте», где подавляющее большинство значений нулевые, а ненулевые значения встречаются нечасто, но при этом крайне информативны. Для таких признаков единая скорость обучения работала откровенно плохо: она либо была слишком маленькой для редких, но важных признаков, либо слишком большой для частых. AdaGrad оказался первым практичным решением именно этой проблемы, и сразу же обрёл популярность в промышленных системах ранжирования и рекламы.

Проблему затухающего шага AdaGrad публично исправил Джеффри Хинтон в 2012 году — не в виде отдельной научной статьи, а буквально в слайде лекции 6e своего курса «Neural Networks for Machine Learning» на Coursera, представив идею экспоненциально взвешенного скользящего среднего вместо полной суммы квадратов градиентов. Метод получил название RMSProp (Root Mean Square Propagation) и, несмотря на отсутствие формальной публикации, стал одной из самых цитируемых «неопубликованных» идей во всём машинном обучении — ссылка «Hinton, unpublished, 2012» встречается в тысячах научных статей.

Решающий шаг сделали в 2014 году Дидерик Кингма и Джимми Ба, объединив в одной формуле идею момента (первый момент — среднее градиентов) и идею RMSProp (второй момент — среднее квадратов градиентов), добавив аккуратную математическую коррекцию смещения для первых шагов обучения. Их статья «Adam: A Method for Stochastic Optimization» была опубликована на конференции ICLR 2015 и к сегодняшнему дню собрала более 100 000 научных цитирований — один из самых цитируемых результатов за всю историю машинного обучения. Позже, в 2017–2019 годах, Илья Лощилов и Франк Хуттер заметили и математически объяснили тонкий, но важный изъян в том, как Adam взаимодействует с L2-регуляризацией, и предложили AdamW («Decoupled Weight Decay Regularization») — модификацию, которая сегодня фактически вытеснила «чистый» Adam при обучении крупных трансформерных моделей.

Мотивация: у каждого параметра свой масштаб градиента

Интуиция

Представь, что ты обучаешь сеть с эмбеддинг-слоем для словаря из ста тысяч слов и несколькими плотными слоями поверх него. Вес в плотном слое участвует в вычислении практически каждого прогноза сети и получает ненулевой градиент на каждом mini-batch шаге — его градиент стабилен, предсказуем, «привычен» к постоянному обновлению. А строка эмбеддинга, отвечающая, скажем, слову «мурена», получает ненулевой градиент только в те редкие моменты, когда это слово вообще встречается в очередном батче, — всё остальное время градиент по ней ровно нулевой. Если ты выберешь единую скорость обучения, подходящую для частого, стабильного веса, эмбеддинг редкого слова будет обучаться катастрофически медленно (тех редких моментов, когда он вообще получает сигнал, слишком мало, а шаг слишком робкий). Если же выбрать скорость под редкий, но информативный сигнал, частый вес начнёт «дребезжать» слишком крупными шагами.

Формула

Единая скорость обучения означает, что обновление любого параметра модели устроено одинаково:

Неадаптивное обновление (обычный SGD или SGD с моментом).

$$\theta_{t+1}=\theta_t-\eta\cdot g_t$$

где $\eta$ — одно общее число для всех координат вектора параметров $\theta$, независимо от того, насколько по-разному ведёт себя градиент $g_t$ у разных параметров.

Разбор примеров

Пример 1 (несовместимые требования к одному общему $\eta$). Параметр A — вес в плотном слое, получает градиент величиной примерно $0{,}1$ на каждом шаге. Параметр B — строка эмбеддинга редкого слова, получает нулевой градиент почти всегда, но раз в 200 шагов — градиент величиной $5$. Возьмём $\eta=0{,}1$ (разумное значение «под A»): шаг A на каждой итерации составит $0{,}1\times0{,}1=0{,}01$ — стабильное, аккуратное движение. Но шаг B в тот редкий момент, когда он получает сигнал, составит $0{,}1\times5=0{,}5$ — огромный скачок за один шаг, а затем 200 шагов полного застоя. Теперь возьмём $\eta=0{,}01$ (более щадящее значение «под B»): шаг B в момент сигнала станет $0{,}01\times5=0{,}05$ — уже разумнее, но шаг A на каждой итерации упадёт до $0{,}01\times0{,}1=0{,}001$ — обучение A замедлится в десять раз. Ни одно единое значение $\eta$ не подходит обоим параметрам одновременно.

Пример 2 (разный масштаб градиента по глубине сети). В глубокой сети градиент, дошедший до первого слоя (ближе к входу) после обратного распространения ошибки через много промежуточных слоёв, часто на порядки меньше градиента последнего слоя (ближе к выходу) — эффект затухающего градиента. Пусть типичный градиент весов первого слоя составляет $0{,}002$, а последнего — $1{,}5$. При единой $\eta=0{,}01$ шаг первого слоя составит $0{,}01\times0{,}002=0{,}00002$ — практически незаметное обновление, первый слой будет учиться крайне медленно, — тогда как шаг последнего слоя составит $0{,}01\times1{,}5=0{,}015$ — вполне заметное движение. Разница в скорости фактического обучения двух слоёв одной и той же сети составит около $750$ раз, хотя формально они обучаются «с одной и той же скоростью обучения».

Пример 3 (сопоставление с единой $\eta$, подобранной методом проб и ошибок). Допустим, инженер вручную перебирает единые значения $\eta$ для сети из примера 2 и находит компромисс $\eta=0{,}05$, при котором первый слой получает шаг $0{,}05\times0{,}002=0{,}0001$ (всё ещё крошечный, но хоть какой-то), а последний слой — шаг $0{,}05\times1{,}5=0{,}075$ (уже довольно рискованно большой, на грани расхождения обучения). Компромисс существует, но он плохой в обе стороны сразу: первый слой всё равно обучается медленно, а последний уже находится в зоне риска нестабильности. Именно эту дилемму — «один параметр компромисса не может одновременно удовлетворить объекты с разным масштабом» — и решают адаптивные методы, о которых пойдёт речь дальше.

Почему это важно

Проблема несовместимых масштабов градиента у разных параметров — не надуманная академическая тонкость, а повседневная реальность любой сколько-нибудь глубокой или широкой нейросети: эмбеддинги против плотных слоёв, ранние слои против поздних, редкие признаки против частых. Ручной подбор отдельной скорости обучения для каждого параметра (а их в современных моделях миллиарды) физически невозможен. Отсюда прямая практическая необходимость: скорость обучения должна подстраиваться автоматически, индивидуально для каждого параметра, на основе истории его собственных градиентов — именно эту задачу и решают AdaGrad, RMSProp и Adam.

AdaGrad: адаптация через накопленную сумму квадратов градиентов

Интуиция

Идея AdaGrad предельно прямая: заведи для каждого параметра отдельный «счётчик активности» — накопленную сумму квадратов всех градиентов, которые этот параметр когда-либо получал, — и дели скорость обучения на квадратный корень из этого счётчика. Параметр, который постоянно получает крупные градиенты, быстро накапливает большой счётчик и начинает получать всё более и более осторожные (маленькие) шаги. Параметр, который получает мелкие или редкие градиенты, копит счётчик медленно и продолжает получать относительно крупные шаги. Возведение в квадрат перед суммированием важно по двум причинам: оно делает счётчик нечувствительным к знаку градиента (не важно, «плюс» или «минус» — важна только величина активности) и штрафует по-настоящему крупные градиенты значительно сильнее, чем мелкие.

Формула

AdaGrad. Для каждого параметра $\theta$ независимо накапливается сумма квадратов всех прошлых градиентов $G_t=\sum_{\tau=1}^t g_\tau^2$, а обновление происходит по формуле

$$\theta_{t+1}=\theta_t-\frac{\eta}{\sqrt{G_t+\varepsilon}}\,g_t$$

где $\eta$ — базовая (одна общая) скорость обучения, а $\varepsilon$ — маленькая константа (обычно порядка $10^{-8}$), которая нужна исключительно для того, чтобы не делить на ноль, пока $G_t$ ещё равен нулю на самом первом шаге.

Разбор примеров

Пример 1 (полная трассировка трёх шагов и наблюдаемое затухание коэффициента). Пусть $\eta=0{,}5$, $\theta_0=0$, а последовательность градиентов равна $g_1=4$, $g_2=-3$, $g_3=2$ ($\varepsilon$ считаем пренебрежимо малым и опускаем в расчётах).

Шаг 1: $G_1=4^2=16$. $\sqrt{16}=4$. Эффективный коэффициент $\eta/\sqrt{G_1}=0{,}5/4=0{,}125$. Обновление: $0{,}125\times4=0{,}5$. $\theta_1=0-0{,}5=-0{,}5$.

Шаг 2: $G_2=16+(-3)^2=16+9=25$. $\sqrt{25}=5$. Коэффициент: $0{,}5/5=0{,}1$. Обновление: $0{,}1\times(-3)=-0{,}3$. $\theta_2=-0{,}5-(-0{,}3)=-0{,}2$.

Шаг 3: $G_3=25+2^2=29$. $\sqrt{29}\approx5{,}385$. Коэффициент: $0{,}5/5{,}385\approx0{,}0929$. Обновление: $0{,}0929\times2\approx0{,}1857$. $\theta_3=-0{,}2-0{,}1857=-0{,}3857$.

Обрати внимание на сам коэффициент $\eta/\sqrt{G_t}$: $0{,}125\to0{,}1\to0{,}0929$ — он неуклонно уменьшается с каждым шагом, даже несмотря на то, что величина градиентов не убывает ($4$, $3$, $2$ — всё ещё вполне заметные значения). Это и есть механизм AdaGrad в действии: чем больше «активности» накопилось у параметра, тем осторожнее становится его шаг, независимо от текущего градиента.

Пример 2 (долгосрочное затухание при постоянном градиенте — демонстрация фатальной проблемы). Пусть градиент параметра стабильно равен $g=1$ на каждом шаге (простейший мыслимый случай — никакого усложнения). Тогда $G_t=t$, а эффективный коэффициент шага равен $\eta/\sqrt t$. При $\eta=0{,}5$: после $t=100$ шагов коэффициент составит $0{,}5/\sqrt{100}=0{,}5/10=0{,}05$ — уже в $10$ раз меньше исходного. После $t=10\,000$ шагов: $0{,}5/\sqrt{10\,000}=0{,}5/100=0{,}005$ — в $100$ раз меньше исходного. После $t=1\,000\,000$ шагов: $0{,}5/\sqrt{1\,000\,000}=0{,}5/1000=0{,}0005$ — в $1000$ раз меньше. И это при том, что сам градиент всё это время оставался ровно тем же самым, равным $1$, — параметр объективно всё ещё «нуждается» в обновлении, но AdaGrad накопил столько истории, что шаг практически сошёл на нет. Именно это и называется проблемой затухания AdaGrad: накопленная сумма $G_t$ только растёт (это же сумма квадратов, все слагаемые неотрицательны), а значит эффективная скорость обучения монотонно стремится к нулю, независимо от того, продолжает ли обучение реально нуждаться в движении.

Пример 3 (сравнение частого и редкого параметра — исходная сильная сторона AdaGrad). Возьмём параметры A и B из мотивационного раздела: A получает $g=0{,}2$ на каждом из четырёх шагов подряд, B получает $g=0$ на первых трёх шагах и $g=8$ только на четвёртом. Без адаптации (обычный SGD с $\eta=0{,}1$) шаг A на любом шаге составил бы $0{,}1\times0{,}2=0{,}02$, а шаг B в момент сигнала — $0{,}1\times8=0{,}8$, то есть в $40$ раз больше. С AdaGrad: $G_A$ после четырёх шагов равен $4\times0{,}2^2=0{,}16$, $\sqrt{0{,}16}=0{,}4$, коэффициент $0{,}1/0{,}4=0{,}25$, шаг A на четвёртом шаге $0{,}25\times0{,}2=0{,}05$. Для B накопленная сумма содержит только один ненулевой вклад: $G_B=8^2=64$, $\sqrt{64}=8$, коэффициент $0{,}1/8=0{,}0125$, шаг B $0{,}0125\times8=0{,}1$. Итог: шаг B ($0{,}1$) теперь всего вдвое больше шага A ($0{,}05$), а не в $40$ раз, как было бы без адаптации, — крупный, но редкий сигнал больше не создаёт непропорционально огромного скачка именно у того параметра, который его получил.

Почему это важно

AdaGrad стал первым методом, который по-настоящему решил задачу индивидуальной адаптации шага под историю конкретного параметра, и именно за счёт этого показал впечатляющие результаты на задачах с разреженными признаками — ровно то, для чего он и разрабатывался. Но пример 2 демонстрирует принципиальный и неустранимый в рамках самого AdaGrad изъян: поскольку $G_t$ — это сумма, которая может только расти, эффективная скорость обучения асимптотически стремится к нулю независимо от того, нужно ли обучению ещё двигаться. Для коротких по числу шагов задач классической разреженной линейной регрессии это не успевало стать проблемой, но для обучения глубоких нейросетей, которое может занимать сотни тысяч или миллионы шагов, затухание AdaGrad практически гарантированно «замораживает» обучение задолго до сходимости к хорошему решению. Именно эта проблема и стала прямым стимулом для появления RMSProp.

RMSProp: экспоненциальное скользящее среднее вместо полной суммы

Интуиция

Идея правки, которую предложил Хинтон, элегантно проста: вместо того чтобы суммировать квадраты градиентов за всю историю обучения без ограничения (как это делает $G_t$ в AdaGrad), нужно вести экспоненциально взвешенное скользящее среднее квадратов градиентов — тот же самый механизм «затухающей памяти», который ты уже видел в формуле момента в прошлом уроке, только применённый не к самим градиентам, а к их квадратам. Скользящее среднее не растёт без ограничения — оно колеблется около текущего типичного масштаба квадрата градиента, «забывая» далёкую историю по экспоненциальному закону. Если параметр долго получал крупные градиенты, а затем стал получать мелкие, RMSProp довольно быстро «осознает» изменение и вернёт шагу более уверенный размер — в отличие от AdaGrad, который навсегда «помнит» всю ту накопленную крупную активность.

Формула

RMSProp. Для каждого параметра ведётся экспоненциально взвешенное скользящее среднее квадратов градиентов

$$s_t=\beta\,s_{t-1}+(1-\beta)\,g_t^2$$

где $\beta\in[0,1)$ — коэффициент затухания (типичное значение $\beta=0{,}9$), а обновление параметра происходит по формуле

$$\theta_{t+1}=\theta_t-\frac{\eta}{\sqrt{s_t+\varepsilon}}\,g_t$$

Формально это в точности та же структура, что и у AdaGrad, с единственной, но принципиальной заменой: полная растущая сумма $G_t$ заменена на скользящее среднее $s_t$, которое стремится к устойчивому значению, а не растёт неограниченно.

Разбор примеров

Пример 1 (трассировка тех же трёх шагов, что и для AdaGrad — прямое сравнение). Возьмём те же градиенты $g_1=4$, $g_2=-3$, $g_3=2$, тот же $\eta=0{,}5$, $\theta_0=0$, $\beta=0{,}9$, $s_0=0$.

Шаг 1: $s_1=0{,}9\times0+0{,}1\times16=1{,}6$. $\sqrt{1{,}6}\approx1{,}265$. Коэффициент: $0{,}5/1{,}265\approx0{,}395$. Обновление: $0{,}395\times4\approx1{,}581$. $\theta_1\approx-1{,}581$.

Шаг 2: $s_2=0{,}9\times1{,}6+0{,}1\times9=1{,}44+0{,}9=2{,}34$. $\sqrt{2{,}34}\approx1{,}530$. Коэффициент: $0{,}5/1{,}530\approx0{,}327$. Обновление: $0{,}327\times(-3)\approx-0{,}980$. $\theta_2\approx-1{,}581-(-0{,}980)=-0{,}601$.

Шаг 3: $s_3=0{,}9\times2{,}34+0{,}1\times4=2{,}106+0{,}4=2{,}506$. $\sqrt{2{,}506}\approx1{,}583$. Коэффициент: $0{,}5/1{,}583\approx0{,}316$. Обновление: $0{,}316\times2\approx0{,}632$. $\theta_3\approx-0{,}601-0{,}632=-1{,}233$.

Коэффициент шага снижается заметно медленнее, чем в AdaGrad: $0{,}395\to0{,}327\to0{,}316$ — почти стабилизировался уже к третьему шагу, тогда как у AdaGrad на тех же данных он продолжал бы монотонно убывать без остановки.

Пример 2 (устойчивая неподвижная точка при постоянном градиенте — прямое опровержение проблемы AdaGrad). Возьмём тот же мысленный эксперимент, что и во втором примере AdaGrad: постоянный градиент $g=1$ на каждом шаге. Для скользящего среднего можно найти неподвижную точку $s^*$, решив уравнение $s^*=\beta s^*+(1-\beta)\cdot1^2$, откуда $s^*(1-\beta)=(1-\beta)$, то есть $s^*=1$ — причём это верно при любом $\beta\in[0,1)$. Иными словами, сколько бы шагов ни прошло — сто, десять тысяч, миллион, — скользящее среднее $s_t$ стабилизируется около значения $1$ (равного квадрату постоянного градиента) и больше не растёт. Эффективный коэффициент шага стабилизируется на уровне $\eta/\sqrt1=\eta$ — ровно исходное значение, без всякого затухания. Это в точности устраняет проблему, продемонстрированную для AdaGrad в предыдущем разделе: коэффициент шага $0{,}5/\sqrt t$ там неуклонно падал к нулю при росте $t$, а здесь коэффициент $\eta/\sqrt{s_t}$ остаётся равным $\eta$ сколь угодно долго, пока градиент сохраняет тот же масштаб.

Пример 3 (быстрая адаптация к скачку градиента и последующее восстановление). Пусть параметр долго получал небольшие градиенты, и накопленное скользящее среднее стабилизировалось на уровне $s_3=0{,}05$ (соответствует стабильному масштабу градиента около $0{,}22$). На четвёртом шаге происходит резкий всплеск: $g_4=10$ (например, редкий, но очень информативный пример попал в mini-batch). Тогда $s_4=0{,}9\times0{,}05+0{,}1\times100=0{,}045+10=10{,}045$ — скользящее среднее резко подскочило, эффективный коэффициент шага $\eta/\sqrt{s_4}$ мгновенно (в пределах того же шага) сжался, защищая параметр от неадекватно огромного скачка. Дальше, если на пятом шаге градиент возвращается к обычному малому масштабу $g_5=0{,}2$: $s_5=0{,}9\times10{,}045+0{,}1\times0{,}04=9{,}0405+0{,}004=9{,}0445$ — эффект всплеска всё ещё чувствуется (шаг временно приглушён по сравнению с состоянием до всплеска), но, в отличие от AdaGrad, где всплеск навсегда добавился бы к неубывающей сумме, здесь $s_t$ продолжит экспоненциально затухать в последующие шаги (при $\beta=0{,}9$ эффективная «память» составляет порядка $1/(1-\beta)=10$ шагов) и постепенно вернётся к типичному для этого параметра уровню.

Почему это важно

RMSProp сохраняет главное практическое достоинство AdaGrad — индивидуальную адаптацию шага под масштаб градиента каждого отдельного параметра — и одновременно устраняет его фатальный недостаток: обучение больше не «замораживается» из-за одной лишь длительности процесса. Это делает RMSProp пригодным для обучения глубоких нейросетей на сотнях тысяч и миллионах шагов, где AdaGrad практически неизбежно затухал бы задолго до завершения обучения. RMSProp сам по себе уже был широко распространённым, рабочим оптимизатором в первой половине 2010-х годов — но у него оставалась ровно та же слабость, что и у обычного SGD без момента: он ничего не знает о направлении движения на предыдущих шагах, только о масштабе. Соединение этой адаптации масштаба с моментом из прошлого урока — это и есть идея Adam.

Adam: момент и RMSProp в одной формуле, плюс коррекция смещения

Интуиция

Название Adam расшифровывается как Adaptive Moment Estimation — «оценка адаптивного момента», и это название описывает метод предельно точно. Adam одновременно ведёт две скользящие оценки: первый момент $m_t$ — экспоненциально взвешенное скользящее среднее самих градиентов (это буквально тот же механизм момента, который ты разбирал в прошлом уроке — направление и «инерция» движения), и второй момент $v_t$ — экспоненциально взвешенное скользящее среднее квадратов градиентов (это в точности механизм RMSProp — адаптивный масштаб шага для каждого параметра). Итоговое обновление делит первую оценку на квадратный корень из второй: направление движения (с инерцией) масштабируется по историческому размаху градиента конкретного параметра.

Но есть тонкость, из-за которой к этим двум идеям нужно добавить третью — коррекцию смещения. Обе скользящие средние стартуют с нулевого значения: $m_0=0$, $v_0=0$. На самых первых шагах обучения, пока «память» ещё не накопила достаточно наблюдений, эти скользящие средние оказываются искусственно занижены — смещены к нулю сильнее, чем должны бы быть на самом деле. Без исправления этого смещения самые первые шаги обучения получались бы неоправданно маленькими и робкими именно в момент, когда параметры модели ещё далеки от разумных значений и им нужно двигаться уверенно.

Формула

Adam. Для каждого параметра независимо ведутся две скользящие оценки:

$$m_t=\beta_1 m_{t-1}+(1-\beta_1)\,g_t\qquad\text{(первый момент — среднее градиентов, как момент из прошлого урока)}$$

$$v_t=\beta_2 v_{t-1}+(1-\beta_2)\,g_t^2\qquad\text{(второй момент — среднее квадратов градиентов, как в RMSProp)}$$

с начальными значениями $m_0=0$, $v_0=0$. Далее обе оценки корректируются на смещение:

$$\hat m_t=\frac{m_t}{1-\beta_1^t}\qquad\hat v_t=\frac{v_t}{1-\beta_2^t}$$

и итоговое обновление параметра равно

$$\theta_{t+1}=\theta_t-\eta\cdot\frac{\hat m_t}{\sqrt{\hat v_t}+\varepsilon}$$

Типичные значения гиперпараметров по умолчанию: $\beta_1=0{,}9$, $\beta_2=0{,}999$, $\varepsilon=10^{-8}$, а базовая скорость обучения $\eta$ обычно берётся заметно меньше, чем в обычном SGD — типично $\eta=0{,}001$.

Разбор примеров

Пример 1 (полная числовая трассировка трёх шагов Adam — момент, второй момент и коррекция смещения на каждом шаге). Пусть $\beta_1=0{,}9$, $\beta_2=0{,}999$, $\eta=0{,}001$, $\varepsilon$ пренебрежимо мал, $\theta_0=0$, $m_0=v_0=0$, а последовательность градиентов $g_1=4$, $g_2=-3$, $g_3=2$ (те же значения, что и в примерах AdaGrad и RMSProp выше, — чтобы удобно сравнить все три метода на одинаковых входных данных).

Шаг 1: $m_1=0{,}9\times0+0{,}1\times4=0{,}4$. $v_1=0{,}999\times0+0{,}001\times16=0{,}016$. Коррекция смещения: $1-\beta_1^1=0{,}1$, значит $\hat m_1=0{,}4/0{,}1=4$; $1-\beta_2^1=0{,}001$, значит $\hat v_1=0{,}016/0{,}001=16$. $\sqrt{\hat v_1}=4$. Обновление: $\eta\cdot\hat m_1/\sqrt{\hat v_1}=0{,}001\times4/4=0{,}001$. $\theta_1=0-0{,}001=-0{,}001$.

Обрати внимание на удивительный факт: после коррекции смещения $\hat m_1=4$ и $\hat v_1=16$ в точности равны $g_1$ и $g_1^2$ — то есть на самом первом шаге коррекция полностью восстанавливает исходный, ничем не сглаженный градиент. Это не совпадение: при $t=1$ скользящее среднее состоит буквально из одного слагаемого, и деление на $1-\beta^1$ ровно компенсирует единственный вес $(1-\beta)$, которым это слагаемое вошло в сумму.

Шаг 2: $m_2=0{,}9\times0{,}4+0{,}1\times(-3)=0{,}36-0{,}3=0{,}06$. $v_2=0{,}999\times0{,}016+0{,}001\times9=0{,}015984+0{,}009=0{,}024984$. Коррекция: $1-\beta_1^2=1-0{,}81=0{,}19$, $\hat m_2=0{,}06/0{,}19\approx0{,}3158$; $1-\beta_2^2=1-0{,}998001=0{,}001999$, $\hat v_2=0{,}024984/0{,}001999\approx12{,}498$. $\sqrt{\hat v_2}\approx3{,}535$. Обновление: $0{,}001\times0{,}3158/3{,}535\approx0{,}0000893$. $\theta_2=-0{,}001-0{,}0000893\approx-0{,}0010893$.

Шаг 3: $m_3=0{,}9\times0{,}06+0{,}1\times2=0{,}054+0{,}2=0{,}254$. $v_3=0{,}999\times0{,}024984+0{,}001\times4=0{,}024959+0{,}004=0{,}028959$. Коррекция: $1-\beta_1^3=1-0{,}729=0{,}271$, $\hat m_3=0{,}254/0{,}271\approx0{,}9373$; $1-\beta_2^3=1-0{,}997003=0{,}002997$, $\hat v_3=0{,}028959/0{,}002997\approx9{,}663$. $\sqrt{\hat v_3}\approx3{,}109$. Обновление: $0{,}001\times0{,}9373/3{,}109\approx0{,}0003015$. $\theta_3=-0{,}0010893-0{,}0003015\approx-0{,}0013908$.

Сравни итоговый масштаб шагов Adam с шагами AdaGrad и RMSProp на тех же градиентах: там шаги были порядка $0{,}1$–$1{,}5$ за счёт $\eta=0{,}5$, здесь — порядка $0{,}001$ за счёт $\eta=0{,}001$. Это не случайное отличие, а прямое следствие того, как подбирают $\eta$ для Adam на практике — к этому мы вернёмся ниже.

Пример 2 (почему коррекция смещения важна именно на первых шагах и почти не важна на поздних — числовая иллюстрация). Коэффициент коррекции момента равен $1/(1-\beta_1^t)$, а коэффициент коррекции второго момента — $1/(1-\beta_2^t)$. Посчитаем оба при $\beta_1=0{,}9$, $\beta_2=0{,}999$ для нескольких значений $t$:

При $t=1$: коэффициент для $m$ равен $1/(1-0{,}9)=1/0{,}1=10$; коэффициент для $v$ равен $1/(1-0{,}999)=1/0{,}001=1000$. Без коррекции сырые оценки $m_1$ и $v_1$ оказались бы занижены в $10$ и в $1000$ раз соответственно — колоссальное искажение.

При $t=10$: $\beta_1^{10}=0{,}9^{10}\approx0{,}3487$, коэффициент $1/(1-0{,}3487)\approx1{,}536$; $\beta_2^{10}\approx0{,}99004$ (поскольку $0{,}999^{10}\approx1-10\times0{,}001=0{,}990$ при малом отклонении), коэффициент $1/(1-0{,}99004)\approx100{,}4$. Коррекция для $m$ уже почти незаметна ($1{,}5$ раза), а для $v$ всё ещё огромна ($100$ раз) — потому что при $\beta_2=0{,}999$ «память» второго момента примерно на два порядка длиннее, чем «память» первого момента при $\beta_1=0{,}9$.

При $t=100$: коэффициент для $m$ составляет примерно $1/(1-0{,}9^{100})\approx1{,}00003$ — практически единица, коррекция полностью исчезла. Коэффициент для $v$ составляет примерно $1/(1-0{,}999^{100})\approx1/(1-0{,}9048)\approx10{,}5$ — всё ещё заметная поправка.

При $t=1000$: коэффициент для $v$ составляет примерно $1/(1-0{,}999^{1000})\approx1/(1-0{,}3677)\approx1{,}58$ — коррекция уже небольшая. При $t=10\,000$ она падает практически до единицы.

Этот расчёт наглядно показывает: коррекция смещения — не косметическая деталь формулы, а поправка колоссального масштаба именно в первые десятки–сотни шагов, и она плавно и естественно исчезает по мере накопления истории.

Пример 3 (разный масштаб градиента для двух параметров под Adam — прямое продолжение мотивационного примера). Вернёмся к параметрам A (плотный слой, стабильный градиент $g\approx0{,}1$ на каждом шаге) и B (эмбеддинг редкого слова, $g=0$ почти всегда и $g=5$ в редкий момент сигнала) из раздела мотивации. Предположим, что для A скользящие средние уже вышли на устойчивый режим: $\hat m_t^{A}\approx0{,}1$, $\hat v_t^{A}\approx0{,}01$ (квадрат типичного градиента), $\sqrt{\hat v_t^A}\approx0{,}1$. Эффективное отношение $\hat m_t^A/\sqrt{\hat v_t^A}\approx0{,}1/0{,}1=1$. Для B в момент, когда наконец пришёл сигнал $g=5$ (после долгого нулевого периода): даже если $\hat v_t^B$ уже накопил заметный масштаб из-за одного крупного прошлого всплеска (скажем, $\hat v_t^B\approx20$, $\sqrt{\hat v_t^B}\approx4{,}47$), а $\hat m_t^B$ после этого же всплеска составляет что-то порядка $\hat m_t^B\approx4$ (аналогично примеру 1 выше, где при $t=1$ коррекция полностью восстанавливает исходный градиент), отношение $\hat m_t^B/\sqrt{\hat v_t^B}\approx4/4{,}47\approx0{,}89$ — того же порядка, что и у A ($\approx1$), несмотря на то что «сырые» градиенты этих двух параметров в момент сравнения отличались в $50$ раз ($5$ против $0{,}1$). Именно это свойство — приведение относительного масштаба шага разных параметров к сопоставимой величине — и есть главная практическая ценность Adam: одна и та же базовая скорость обучения $\eta$ даёт разумные по масштабу эффективные шаги и для часто обновляемых, и для редко обновляемых параметров, без какой-либо ручной настройки под конкретный слой или конкретный параметр.

Почему это важно

Формула Adam концентрирует в себе всё, что было разобрано в этом уроке до сих пор: инерцию направления движения (первый момент, тот же механизм, что и момент SGD из прошлого урока), индивидуальную адаптацию масштаба шага под историю каждого параметра (второй момент, механизм RMSProp) и аккуратную математическую поправку, которая делает эти скользящие средние надёжными с самого первого шага, а не только после долгого «разогрева». Именно сочетание всех трёх идей в одной формуле — а не какая-то одна из них по отдельности — и объясняет, почему Adam оказался настолько универсально рабочим методом на огромном разнообразии архитектур и задач, что стал практическим стандартом по умолчанию.

Гиперпараметры Adam: β₁, β₂ и ε на практике

Интуиция

У Adam три гиперпараметра сверх базовой скорости обучения $\eta$, и у каждого — своя понятная роль. $\beta_1$ управляет тем, насколько «длинная память» у оценки направления движения — чем ближе к единице, тем дольше момент помнит прошлые градиенты и тем плавнее реагирует на шум. $\beta_2$ управляет тем же самым, но для оценки масштаба (второго момента), и на практике почти всегда берётся значительно ближе к единице, чем $\beta_1$, — то есть оценка масштаба «забывает» историю значительно медленнее, чем оценка направления. $\varepsilon$ — техническая, но не второстепенная деталь: маленькая константа в знаменателе, которая не даёт формуле сломаться в ситуации, когда накопленный второй момент близок к нулю.

Разбор примеров

Пример 1 (почему у $\beta_1$ и $\beta_2$ разные типичные значения — сравнение «длины памяти»). Эффективную длину памяти экспоненциально взвешенного среднего с коэффициентом $\beta$ можно грубо оценить как $1/(1-\beta)$ шагов (после стольких шагов вклад самого раннего наблюдения затухает примерно в $e$ раз). При $\beta_1=0{,}9$: длина памяти $1/(1-0{,}9)=10$ шагов — оценка направления движения «помнит» примерно последние десять шагов. При $\beta_2=0{,}999$: длина памяти $1/(1-0{,}999)=1000$ шагов — оценка масштаба градиента усредняется по значительно более длинной истории. Это осознанный выбор: направление движения должно реагировать относительно быстро на смену тренда (иначе момент будет «проскакивать» повороты рельефа функции потерь), а оценка масштаба, наоборот, выигрывает от усреднения по более длинному окну — она должна быть статистически надёжной, а не дёргаться от каждого отдельного шумного mini-batch.

Пример 2 (что произойдёт, если по ошибке взять $\beta_2=0{,}9$ вместо $0{,}999$). Если задать $\beta_2$ таким же маленьким, как $\beta_1$ (например, $\beta_2=0{,}9$), длина памяти второго момента упадёт до тех же $10$ шагов, что и у первого. Оценка $\hat v_t$ станет заметно более шумной и будет резко скакать от одного крупного mini-batch градиента к другому — а поскольку эта оценка стоит в знаменателе формулы обновления, шумный знаменатель означает шумный, нестабильный эффективный шаг: обучение будет то резко замедляться после случайного крупного градиента, то резко ускоряться после нескольких мелких — вместо плавной, статистически усреднённой адаптации, которую даёт большое значение $\beta_2$.

Пример 3 (роль $\varepsilon$: защита от деления на 0/0). Предположим, у некоторого параметра (например, у редко используемой строки эмбеддинга, которая ещё ни разу не встретилась с начала обучения) градиент был равен ровно нулю на всех прошедших шагах. Тогда $m_t=0$ и $v_t=0$ для этого параметра, а после коррекции смещения $\hat m_t=0/(1-\beta_1^t)=0$ и $\hat v_t=0/(1-\beta_2^t)=0$. Если бы в знаменателе формулы обновления стояло просто $\sqrt{\hat v_t}$ без $\varepsilon$, это было бы деление $0/0$ — неопределённая операция, которая на практике в вычислениях с плавающей точкой нередко превращается в NaN (not a number). С добавленным $\varepsilon=10^{-8}$ знаменатель равен $\sqrt0+10^{-8}=10^{-8}$, числитель тоже равен нулю, и всё выражение корректно вычисляется как $0/10^{-8}=0$ — обновление для этого параметра честно равно нулю (что и логично: раз градиента никогда не было, менять параметр не на основании чего), но без падения в неопределённость.

Почему это важно

Типичные значения по умолчанию — $\beta_1=0{,}9$, $\beta_2=0{,}999$, $\varepsilon=10^{-8}$ — не произвольные магические числа, а результат продуманного компромисса: достаточно короткая память для направления движения, чтобы момент оставался отзывчивым к смене рельефа функции потерь, достаточно длинная память для оценки масштаба, чтобы она была статистически надёжной и не дёргалась от каждого отдельного шумного mini-batch, и маленькая, но ненулевая константа, защищающая формулу от численного краха в граничных случаях. Эти значения настолько хорошо работают на подавляющем большинстве задач, что в реальной практике их меняют существенно реже, чем базовую скорость обучения $\eta$, — но знать, что каждое из них означает, критично для осмысленной диагностики, когда что-то в обучении идёт не так.

AdamW: разделение weight decay (затухания весов) и адаптивного шага

Интуиция

Классическая L2-регуляризация добавляет к функции потерь штраф $\frac\lambda2\|\theta\|^2$, что при дифференцировании добавляет к градиенту слагаемое $\lambda\theta$ — и именно так исторически реализовывался параметр weight_decay (затухание весов) в оптимизаторах: штрафное слагаемое $\lambda\theta$ просто подмешивалось к «настоящему» градиенту задачи ещё до того, как этот суммарный градиент попадал в формулу обновления. Для обычного SGD это абсолютно эквивалентно честному weight decay — прямому уменьшению веса на долю $\eta\lambda$ на каждом шаге. Но у Adam есть промежуточный шаг — деление на $\sqrt{\hat v_t}$, — и вот тут кроется проблема: если штрафное слагаемое $\lambda\theta$ подмешано в сырой градиент до того, как он попадёт в оценку второго момента, то штраф весов, «спрятанный» внутри градиента, будет тоже поделён на $\sqrt{\hat v_t}$ вместе с настоящим градиентом задачи. А это означает, что реальная сила регуляризации перестаёт быть постоянной величиной $\lambda$ и начинает непредсказуемо зависеть от исторического масштаба градиента конкретного параметра — для параметров с исторически крупными градиентами штраф оказывается искусственно подавлен, а для параметров с исторически мелкими градиентами — искусственно усилен. Это ровно противоположно тому, что подразумевает сама идея weight decay: единая, предсказуемая сила стягивания всех весов к нулю. AdamW решает эту проблему предельно прямо: убирает штраф из формулы градиента и добавляет его отдельным слагаемым прямо в формулу обновления весов, минуя адаптивный делитель целиком.

Формула

AdamW. Первый и второй моменты вычисляются точно так же, как в обычном Adam, но по «чистому» градиенту задачи $g_t$ — без всякого подмешивания штрафа регуляризации. Weight decay применяется отдельным, независимым слагаемым прямо к весам при обновлении:

$$\theta_{t+1}=\theta_t-\eta\left(\frac{\hat m_t}{\sqrt{\hat v_t}+\varepsilon}+\lambda\,\theta_t\right)$$

где $\lambda$ — коэффициент weight decay. Слагаемое $\lambda\theta_t$ входит в формулу напрямую, минуя деление на $\sqrt{\hat v_t}$, — сила стягивания к нулю остаётся постоянной и одинаковой для всех параметров, независимо от истории их градиентов.

Разбор примеров

Пример 1 (числовое сравнение подавленного и одинакового штрафа — «горячий» и «холодный» параметр). Пусть $\theta=5$, $\lambda=0{,}05$, $\eta=0{,}01$. Рассмотрим два параметра с сильно разной историей: «горячий» параметр с накопленным $\hat v_t=64$ (значит $\sqrt{\hat v_t}=8$) и «холодный» параметр с $\hat v_t=0{,}0025$ (значит $\sqrt{\hat v_t}=0{,}05$) — в обоих случаях изолируем именно вклад регуляризации, полагая вклад «настоящего» градиента задачи равным нулю (например, обучение уже почти сошлось в этой координате).

Вариант со старым, «слитым» L2 (штраф подмешан в градиент до деления на $\sqrt{\hat v_t}$): вклад регуляризации в сырой градиент равен $\lambda\theta=0{,}05\times5=0{,}25$. После прохождения через адаптивный делитель для горячего параметра эффективная сила штрафа составит примерно $\eta\times0{,}25/8=0{,}01\times0{,}03125=0{,}0003125$. Для холодного параметра — примерно $\eta\times0{,}25/0{,}05=0{,}01\times5=0{,}05$. Отношение силы штрафа между холодным и горячим параметром: $0{,}05/0{,}0003125=160$ раз — притом что оба параметра имеют одинаковое значение $\theta=5$ и один и тот же коэффициент $\lambda=0{,}05$, что явно неправильно с точки зрения самой идеи регуляризации.

Вариант с AdamW (штраф отдельным слагаемым): сила штрафа равна $\eta\lambda\theta=0{,}01\times0{,}05\times5=0{,}0025$ — одна и та же величина для обоих параметров, независимо от того, был ли их накопленный второй момент большим или маленьким. Именно это и означает «декаплинг» (decoupling) в названии AdamW — регуляризация отделена от адаптивной механики и работает предсказуемо.

Пример 2 (сколько шагов нужно чистому weight decay, чтобы уменьшить вес вдвое). Если временно отвлечься от градиента задачи и рассмотреть чистое действие weight decay в AdamW, каждый шаг умножает вес на множитель $(1-\eta\lambda)$: $\theta_{t+1}=\theta_t(1-\eta\lambda)$. При $\eta=0{,}001$ и $\lambda=0{,}05$ произведение $\eta\lambda=0{,}00005$. Чтобы найти число шагов $n$, за которое вес уменьшится вдвое, решим $(1-0{,}00005)^n=0{,}5$. Используя приближение $\ln(1-x)\approx-x$ для малых $x$: $n\approx\ln(0{,}5)/(-0{,}00005)=-0{,}6931/(-0{,}00005)\approx13\,863$ шага. Это показывает, что при типичных для практики малых значениях $\eta\lambda$ действие weight decay растянуто на тысячи шагов — регуляризация работает как медленное, постоянное «стягивающее давление», а не как резкий скачок, что и требуется от разумной регуляризации.

Пример 3 (алгебраическое сведение к обычному Adam при $\lambda=0$). Если положить $\lambda=0$ в формуле AdamW, слагаемое $\lambda\theta_t$ обращается в ноль, и формула в точности превращается в исходную формулу Adam: $\theta_{t+1}=\theta_t-\eta\cdot\hat m_t/(\sqrt{\hat v_t}+\varepsilon)$. Это подтверждает, что AdamW не заменяет собой Adam каким-то радикально иным алгоритмом, а строго обобщает его: при отсутствии регуляризации оба метода идентичны, а расхождение появляется исключительно в способе применения weight decay при $\lambda\ne0$.

Почему это важно

Проблема, которую решает AdamW, долгое время оставалась незамеченной именно потому, что для обычного SGD «слитый» и «раздельный» weight decay полностью эквивалентны — и лишь при переходе к адаптивным методам вроде Adam скрытая нестыковка становится реальной практической проблемой. Сегодня AdamW — не нишевая техническая тонкость, а фактический стандарт при обучении практически всех крупных языковых моделей и трансформерных архитектур: BERT, GPT-подобные модели, LLaMA и подавляющее большинство современных больших нейросетей обучаются именно AdamW, а не «чистым» Adam с устаревшим слитым L2. Если ты видишь torch.optim.AdamW в чужом коде вместо torch.optim.Adam(weight_decay=...) — это осознанный, а не случайный выбор, и разница между ними не косметическая.

Практика: 30 заданий

Базовые задания (1–10)

Задание 1: AdaGrad. Текущий вес $\theta=1$, накопленная сумма $G_t=9$, текущий градиент $g_t=3$, $\eta=0{,}6$ ($\varepsilon$ считать пренебрежимо малым). Найти новое значение веса.


Задание 2: Объясни своими словами, в чём заключается проблема затухания AdaGrad и почему она особенно опасна при обучении нейросети на миллионах шагов.


Задание 3: RMSProp. $\beta=0{,}9$, предыдущее скользящее среднее $s_{t-1}=2$, текущий градиент $g_t=4$. Найти $s_t$.


Задание 4: Продолжая задание 3: найти эффективное обновление веса при $\eta=0{,}3$, $g_t=4$, $s_t=3{,}4$ (из задания 3).


Задание 5: Adam. $\beta_1=0{,}9$, $m_0=0$, $g_1=6$. Найти $m_1$.


Задание 6: Adam. $\beta_2=0{,}999$, $v_0=0$, $g_1=6$. Найти $v_1$.


Задание 7: Используя результат задания 5 ($m_1=0{,}6$) и $\beta_1=0{,}9$, найти скорректированную оценку $\hat m_1$.


Задание 8: Используя результат задания 6 ($v_1=0{,}036$) и $\beta_2=0{,}999$, найти скорректированную оценку $\hat v_1$.


Задание 9: Используя результаты заданий 7 и 8 ($\hat m_1=6$, $\hat v_1=36$), найти обновление веса Adam при $\eta=0{,}002$, $\theta_0=0$ ($\varepsilon$ пренебрежимо мал).


Задание 10: Верно ли утверждение: «$\varepsilon$ в формуле Adam — это чисто косметическая деталь, которую можно безопасно убрать»? Обоснуй.


Средние задания (11–20)

Задание 11: AdaGrad, два шага. $\eta=0{,}4$, $\theta_0=0$, $g_1=5$, $g_2=-4$. Найти $\theta_1$ и $\theta_2$.


Задание 12: RMSProp, два шага, те же градиенты. $\beta=0{,}9$, $\eta=0{,}4$, $\theta_0=0$, $s_0=0$, $g_1=5$, $g_2=-4$. Найти $\theta_1$ и $\theta_2$.


Задание 13: Adam, два шага, те же градиенты. $\beta_1=0{,}9$, $\beta_2=0{,}999$, $\eta=0{,}01$, $\theta_0=0$, $m_0=v_0=0$, $g_1=5$, $g_2=-4$. Найти $m_1,v_1,\hat m_1,\hat v_1,\theta_1$ и затем $m_2,v_2,\hat m_2,\hat v_2,\theta_2$.


Задание 14: Найти коэффициент коррекции смещения для первого момента $1/(1-\beta_1^t)$ при $\beta_1=0{,}9$, $t=5$.


Задание 15: Найти коэффициент коррекции смещения для второго момента $1/(1-\beta_2^t)$ при $\beta_2=0{,}999$, $t=5$.


Задание 16: При постоянном градиенте $g=1$ и $\eta=0{,}5$ сравни эффективный коэффициент шага AdaGrad $\eta/\sqrt{G_t}$ на шаге $t=1$ и на шаге $t=1\,000\,000$.


Задание 17: При тех же условиях (постоянный градиент $g=1$, $\eta=0{,}5$), но для RMSProp с любым $\beta<1$, найти неподвижную точку $s^*$ скользящего среднего и объяснить, почему коэффициент шага не затухает со временем.


Задание 18: AdamW против слитого L2 в Adam. $\theta=5$, $\lambda=0{,}05$, $\eta=0{,}01$. «Горячий» параметр имеет $\sqrt{\hat v_t}=8$, «холодный» — $\sqrt{\hat v_t}=0{,}05$. Найти вклад регуляризации в обновление для обоих параметров (а) при слитом L2 (штраф $\lambda\theta$ делится на $\sqrt{\hat v_t}$) и (б) при AdamW (штраф отдельным слагаемым).


Задание 19: Объясни, почему $\beta_2$ в Adam почти всегда берут значительно ближе к единице, чем $\beta_1$ (например, $0{,}999$ против $0{,}9$), а не одинаковыми.


Задание 20: Объясни, почему типичное значение $\eta$ для Adam (например, $0{,}001$) значительно меньше типичных значений $\eta$ для обычного SGD (например, $0{,}01$–$0{,}1$), хотя оба используют одну и ту же букву $\eta$ в формуле.


Продвинутые задания (21–30)

Задание 21: Adam, три шага с новой последовательностью градиентов. $\beta_1=0{,}9$, $\beta_2=0{,}999$, $\eta=0{,}005$, $\theta_0=0$, $m_0=v_0=0$, $g_1=-2$, $g_2=6$, $g_3=-1$. Найти $\theta_1,\theta_2,\theta_3$ с полной трассировкой $m_t,v_t,\hat m_t,\hat v_t$.


Задание 22: AdaGrad, сравнение частого и редкого параметра без адаптации и с адаптацией. Параметр A получает $g=0{,}2$ на каждом из четырёх шагов. Параметр B получает $g=0$ на первых трёх шагах и $g=8$ на четвёртом. $\eta=0{,}1$. Найти (а) шаги без адаптации (обычный SGD) на четвёртом шаге для обоих параметров и (б) шаги AdaGrad на четвёртом шаге для обоих параметров.


Задание 23: RMSProp, реакция на всплеск градиента. $\beta=0{,}9$, $\eta=0{,}2$. Известно, что $s_3=0{,}05$ (стабильный малый масштаб). На четвёртом шаге происходит всплеск $g_4=10$. Найти $s_4$, затем при $g_5=0{,}1$ найти $s_5$ и эффективное обновление на пятом шаге. Сравни его с тем, каким было бы обновление на пятом шаге, если бы всплеска не было (то есть $s_5=0{,}05$ по-прежнему).


Задание 24: Сравнение AdaGrad и RMSProp на одинаковой последовательности градиентов $g=[3,-2,1]$, $\eta=0{,}2$ для обоих, $\theta_0=0$, для RMSProp $\beta=0{,}9$. Найти $\theta_3$ для каждого метода.


Задание 25: Покажи, что формула первого момента Adam $m_t=\beta_1 m_{t-1}+(1-\beta_1)g_t$ структурно совпадает с формулой скорости момента из урока про SGD $v_{t+1}=\beta v_t+(1-\beta)\nabla\ell(\theta_t)$. Что тогда является настоящим нововведением Adam по сравнению с SGD с моментом?


Задание 26: AdamW, чистое действие weight decay (без вклада градиента задачи). $\eta=0{,}001$, $\lambda=0{,}05$. Сколько шагов потребуется, чтобы вес уменьшился вдвое?


Задание 27: В коде обучения стоит torch.optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.9999), eps=1e-8). Что нестандартно в этой настройке и как это повлияет на поведение второго момента по сравнению с типичным значением $\beta_2=0{,}999$?


Задание 28: Adam, четыре шага с чередующимся по знаку градиентом. $\beta_1=0{,}9$, $\beta_2=0{,}999$, $\eta=0{,}01$, $\theta_0=0$, $m_0=v_0=0$, $g=[3,-3,3,-3]$. Найти $\hat v_t$ на каждом шаге и объяснить, почему $\hat v_t$ стабилизируется около одного и того же значения, несмотря на смену знака градиента, тогда как $\hat m_t$ колеблется и уменьшается по модулю.


Задание 29: Покажи алгебраически, что при $\lambda=0$ формула AdamW превращается ровно в формулу обычного Adam.


Задание 30: Почему на практике никогда не используют $\varepsilon=0$ в формуле Adam, даже несмотря на то, что $\varepsilon$ обычно берут крайне маленьким (порядка $10^{-8}$) и он почти не влияет на величину шага в обычных условиях?


Частые ошибки

Ошибка 1: считать RMSProp «просто улучшенной версией AdaGrad» без понимания механизма. На самом деле разница принципиальная и единственная: AdaGrad суммирует квадраты градиентов за всю историю без ограничения, RMSProp заменяет эту растущую сумму на экспоненциально взвешенное скользящее среднее. Именно эта единственная замена устраняет фатальное затухание шага до нуля — без понимания этого механизма легко упустить, почему вообще нужен отдельный метод.

Ошибка 2: путать роли $\beta_1$ и $\beta_2$ или ставить им одинаковые значения. $\beta_1$ отвечает за память первого момента (направление движения) и обычно берётся заметно меньше единицы (типично $0{,}9$, память около $10$ шагов), а $\beta_2$ отвечает за память второго момента (масштаб шага) и берётся значительно ближе к единице (типично $0{,}999$, память около $1000$ шагов). Одинаковые значения обоих коэффициентов делают знаменатель формулы обновления заметно более шумным, чем задумано.

Ошибка 3: недооценивать значение коррекции смещения, считая её «формальностью». Как показано в разборе примеров, коэффициент коррекции при $t=1$ достигает $10$ для первого момента и $1000$ для второго — без коррекции первые шаги обучения были бы неоправданно робкими именно тогда, когда параметры модели ещё далеки от разумных значений и нуждаются в уверенном движении.

Ошибка 4: использовать weight_decay в старом стиле Adam(weight_decay=...), думая, что это эквивалент AdamW. Как показано в разделе про AdamW, подмешивание штрафа $\lambda\theta$ в сырой градиент перед делением на $\sqrt{\hat v_t}$ делает реальную силу регуляризации зависимой от исторического масштаба градиента конкретного параметра — для «горячих» параметров штраф подавляется, для «холодных» — усиливается. Если нужен честный, предсказуемый weight decay, нужно явно использовать torch.optim.AdamW.

Ошибка 5: переносить скорость обучения, подобранную для SGD, напрямую в Adam. Из-за встроенной адаптивной нормализации эффективный шаг Adam примерно равен самой величине $\eta$, а не $\eta$, умноженной на немасштабированный градиент, — поэтому типичные значения $\eta$ для Adam ($0{,}001$ и меньше) на порядок-два меньше типичных значений для SGD ($0{,}01$–$0{,}1$). Слишком большая $\eta$ в Adam — одна из самых частых причин расхождения обучения (лосс улетает в NaN) на первых же шагах.

Ошибка 6: считать, что раз Adam адаптивный, ему не нужны расписание скорости обучения (learning rate schedule) или разогрев (warmup). На практике, особенно при обучении трансформерных моделей, плавный разогрев скорости обучения (warmup) в первые сотни–тысячи шагов почти всегда используется вместе с Adam/AdamW — именно потому, что оценки моментов на самых первых шагах статистически ненадёжны (мало наблюдений усреднено), и резкий полноценный шаг сразу с первой итерации может дестабилизировать обучение ещё до того, как коррекция смещения и накопленная история успеют сделать оценки надёжными.

Главное запомнить

  • AdaGrad делит скорость обучения на $\sqrt{G_t}$, где $G_t$ — накопленная сумма квадратов всех прошлых градиентов конкретного параметра; у каждого параметра при этом своя, индивидуальная история.

  • Проблема AdaGrad: $G_t$ монотонно растёт (сумма неотрицательных слагаемых), поэтому эффективный шаг $\eta/\sqrt{G_t}$ неизбежно затухает до нуля, независимо от того, нужно ли обучению ещё двигаться — критично при длительном обучении нейросетей.

  • RMSProp заменяет растущую сумму $G_t$ на экспоненциально взвешенное скользящее среднее $s_t=\beta s_{t-1}+(1-\beta)g_t^2$ — у него есть устойчивая неподвижная точка, и шаг больше не затухает необратимо со временем.

  • Adam = первый момент $m_t$ (то же самое, что момент из прошлого урока — среднее градиентов, инерция направления) + второй момент $v_t$ (то же самое, что RMSProp — среднее квадратов градиентов, адаптивный масштаб) + коррекция смещения.

  • Коррекция смещения $\hat m_t=m_t/(1-\beta_1^t)$, $\hat v_t=v_t/(1-\beta_2^t)$ нужна потому, что $m_0=v_0=0$ искусственно занижает скользящие средние на первых шагах; коррекция стремится к единичному множителю по мере роста $t$.

  • Типичные значения по умолчанию: $\beta_1=0{,}9$ (короткая память направления), $\beta_2=0{,}999$ (длинная память масштаба), $\varepsilon=10^{-8}$ (защита от деления на 0/0), базовая $\eta$ обычно порядка $0{,}001$.

  • AdamW отделяет weight decay от адаптивного деления на $\sqrt{\hat v_t}$: штраф $\lambda\theta_t$ добавляется в формулу обновления напрямую, а не подмешивается в сырой градиент — это делает силу регуляризации одинаковой для всех параметров, независимо от их истории.

  • Подавляющее большинство современных больших языковых моделей и трансформерных архитектур обучаются именно AdamW, а не «чистым» Adam со слитым L2.

  • $\varepsilon$ — не техническая мелочь: без него параметры с нулевой историей градиента (например, редкие эмбеддинги) рискуют получить неопределённость $0/0$ и NaN.

  • Adam — не универсальная панацея: иногда тщательно настроенный SGD с моментом даёт лучшее итоговое качество на тестовых данных, хотя и сходится медленнее; выбор оптимизатора остаётся эмпирическим инженерным решением.

Связь с темами курса

В уроке 286 ты уже разобрал момент — экспоненциально взвешенное скользящее среднее градиентов, которое сглаживает шумные шаги SGD. Первый момент Adam $m_t$ в этом уроке — не новая идея, а буквально тот же самый механизм, применённый к тому же градиенту, только теперь дополненный второй скользящей оценкой (масштаба) и коррекцией смещения. Если формула $m_t=\beta_1 m_{t-1}+(1-\beta_1)g_t$ кажется знакомой — это потому, что она структурно идентична формуле момента из прошлого урока с $\beta_1$ на месте $\beta$. Урок 286 также кратко упоминал RMSProp и Adam как рабочие названия, не раскрывая формул, — сегодняшний урок как раз даёт то самое глубокое погружение, которое там было обещано «на будущее».

С уроком 292 про покоординатный спуск этот урок роднит идея, что не обязательно двигать все параметры «одинаково»: покоординатный спуск оптимизирует по одной координате за раз, полностью фиксируя остальные, а адаптивные методы вроде Adam оптимизируют все координаты одновременно, но с индивидуальным, подстраиваемым масштабом шага для каждой — два разных, но родственных по духу ответа на одну и ту же проблему разнородности параметров модели.

Следующий урок 294 про методы нулевого порядка пойдёт ещё дальше в сторону практичности: что делать, когда даже градиент недоступен или слишком дорог для вычисления, — ситуация, с которой Adam и все его предшественники в этом уроке принципиально не справляются, поскольку все они построены вокруг наличия градиента как отправной точки.

Интересные факты

Статья Кингмы и Ба «Adam: A Method for Stochastic Optimization» (ICLR, 2015) — одна из самых цитируемых работ за всю историю машинного обучения: количество научных цитирований этой статьи превысило 100 000, что ставит её в один ряд с самыми влиятельными результатами всей области, наравне со статьями про сами архитектуры нейросетей.

RMSProp формально никогда не был опубликован как отдельная научная статья — Джеффри Хинтон представил идею в слайде 6e своего курса «Neural Networks for Machine Learning» на Coursera в 2012 году. Несмотря на это, ссылка «Hinton, unpublished, 2012» встречается в тысячах научных статей — редкий случай, когда неформальный образовательный материал стал полноценной точкой отсчёта для целого направления исследований.

Оригинальное доказательство сходимости Adam из статьи 2015 года позже оказалось не совсем корректным: в статье «On the Convergence of Adam and Beyond» (ICLR 2018) было показано, что в определённых искусственно сконструированных сценариях Adam может не сходиться, и предложена модификация AMSGrad как формальное исправление. Тем не менее на практике этот теоретический изъян почти никогда не проявляется в реальных задачах обучения нейросетей, и «классический» Adam остался огромно популярным, несмотря на формально неидеальное доказательство.

Название «Adam» — это не имя человека и не аббревиатура от чьей-то фамилии, а сокращение от «Adaptive Moment Estimation» (адаптивная оценка моментов) — распространённое заблуждение состоит в том, что метод назван в честь одного из авторов.

AdamW долгое время оставался относительно нишевой технической правкой — многие фреймворки годами предлагали только старый «слитый» способ применения weight_decay внутри Adam. Массовый переход индустрии на явный AdamW как стандарт произошёл во многом благодаря обучению крупных трансформерных моделей, где корректная, предсказуемая регуляризация оказалась особенно критичной для стабильности обучения на огромных масштабах.

Лайфхаки

Типичные диапазоны скорости обучения для Adam/AdamW на практике: около $10^{-3}$ для обучения относительно небольших моделей «с нуля»; от $10^{-4}$ до $3\times10^{-4}$ — распространённый выбор при дообучении (fine-tuning) трансформерных моделей; иногда вплоть до $10^{-5}$ для деликатного дообучения очень больших предобученных языковых моделей, где слишком агрессивный шаг рискует разрушить уже выученные представления.

Если при обучении лосс резко улетает в NaN на первых же шагах с Adam или AdamW — первым делом попробуй уменьшить $\eta$ на порядок, прежде чем менять архитектуру, инициализацию весов или искать баг в данных; чрезмерная скорость обучения — одна из самых частых и самых легко проверяемых причин.

Adam не идеален для любой задачи без исключений: в некоторых классических задачах компьютерного зрения (например, обучение сверточных сетей на классических бенчмарках с нуля) тщательно настроенный SGD с моментом и продуманным расписанием скорости обучения иногда даёт заметно лучшее итоговое качество на тестовых данных, хотя обычно сходится заметно медленнее, чем Adam. Если время на эксперименты позволяет, стоит сравнить оба варианта, а не полагаться на Adam как единственно верный выбор по умолчанию.

Используй torch.optim.AdamW вместо torch.optim.Adam(weight_decay=...), если тебе действительно нужна регуляризация — как показано в разборе AdamW, разница между этими вариантами не косметическая, а может отличаться в десятки раз по фактической силе штрафа для разных параметров модели.

Почти всегда сочетай Adam или AdamW с расписанием скорости обучения — как минимум с warmup (плавным ростом $\eta$ в первые несколько сотен–тысяч шагов) и часто с последующим плавным затуханием к концу обучения; адаптивность Adam решает проблему разного масштаба градиента между параметрами, но не отменяет общую пользу от продуманного расписания $\eta$ во времени.

Если нужно сравнить несколько запусков обучения с разными гиперпараметрами Adam, в первую очередь варьируй $\eta$ и $\lambda$ (weight decay) — это параметры с наибольшим практическим влиянием на результат; $\beta_1$, $\beta_2$ и $\varepsilon$ в подавляющем большинстве задач можно смело оставлять на значениях по умолчанию и трогать их только при наличии конкретной, воспроизводимой проблемы, для решения которой изменение именно этих величин обосновано теоретически (как в задании 27 этого урока).

Adam — редкий пример алгоритма, который одновременно математически интересен (сочетание двух скользящих моментов и точная коррекция их смещения) и абсолютно приземлённо практичен: разобравшись в этом уроке, ты понимаешь не абстрактную теорию, а буквально то, что происходит внутри каждого запуска model.fit() или цикла обучения на PyTorch, который тебе только предстоит написать. Пока существуют глубокие нейросети с миллионами и миллиардами разнородных по масштабу параметров, задача, которую решает Adam, никуда не денется — и именно поэтому эта формула, придуманная в 2014 году, продолжает оставаться первой строчкой кода почти в каждом современном проекте машинного обучения.

Понял тему? Закрепи в боте! 🚀

Попрактикуйся на задачах и получи персональные рекомендации от AI

💪 Начать тренировку
💬 Есть вопрос? Спроси бота!