Adam - оптимизатор, который почти три десятилетия держит позицию стандарта по умолчанию. Его запускают на классификации изображений, языковых моделях, рекомендательных системах. Параметры копируют из туториала: β₁=0.9, β₂=0.999, ε=1e-8. В 80% случаев это работает. В оставшихся 20% обучение превращается в хаос: loss взрывается на ровном месте, модель сходится к случайной политике, метрики застревают на плато без надежды на улучшение. Разработчик тратит дни на перебор архитектур и данных, а проблема сидит в трёх строчках конфига оптимизатора.
Эта статья построена вокруг реального RL-кейса. Агент обучался в нестационарной среде с частой сменой динамики. Стандартный Adam давал скачки функции потерь на порядок и нулевую сходимость. Снижение β₂ до 0.95 и β₁ до 0.5 вывело обучение на стабильную траекторию за считанные итерации. Дальше разберём механику каждого параметра, научимся диагностировать патологию по поведению v_t и дадим таблицу конкретных настроек под разные классы задач.
Когда Adam перестаёт быть «королём оптимизаторов»
Дефолтные значения β₁=0.9, β₂=0.999, ε=1e-8 выбраны под стационарные задачи. Классификация ImageNet, регрессия на фиксированном датасете, детекция объектов - здесь распределение градиентов меняется медленно, а ландшафт функции потерь стабилен от эпохи к эпохе. Adam накапливает долгую историю первых и вторых моментов и плавно ведёт модель к минимуму.
Сбой происходит в нестационарных средах. Reinforcement Learning - классический пример: агент меняет стратегию, распределение посещаемых состояний смещается, целевая функция дрейфует. GAN-тренировка - другой случай: генератор и дискриминатор постоянно адаптируются друг к другу. Meta-learning, online-обучение на потоковых данных, fine-tuning с агрессивным learning rate - везде, где вчерашний градиент становится нерелевантным сегодня.
Механизм поломки выглядит так. β₂=0.999 означает экспоненциальное окно усреднения примерно в 1000 шагов. Второй момент v_t хранит память о квадратах градиентов почти тысячу итераций назад. Когда ландшафт резко меняется, v_t продолжает транслировать устаревшую оценку кривизны. Adam рассчитывает адаптивный learning rate на основе этой неверной оценки - и выдаёт обновления, которые либо зашкаливают, либо практически обнуляются. Loss взлетает, модель расходится.
Похожий эффект даёт β₁=0.9. Первый момент m_t сохраняет направление градиентов за последние ~10 шагов. В нестационарной среде этого слишком много: оптимизатор толкает параметры в направлении, которое было актуально десяток итераций назад, но теперь ведёт прямиком в область высокого loss.
Кейс, который мы разберём детально, иллюстрирует ровно эту картину. RL-агент обучался в среде со сменой динамики каждые несколько эпизодов. Стандартный Adam показал хаотические осцилляции loss и нулевой прогресс по reward. Снижение β₂ до 0.95 сократило окно памяти v_t до ~20 шагов, снижение β₁ до 0.5 - до ~2 шагов. Обучение стабилизировалось, агент вышел на оптимальную политику.
Перед тем как углубляться в детали, стоит зафиксировать: проблема не в Adam как алгоритме. Проблема в бездумном копировании параметров без понимания статистических свойств задачи. Дисциплина в условиях информационного шума - один из пяти факторов успеха в ML, и настройка оптимизатора под задачу - прямое следствие этой дисциплины.
Анатомия Adam: что на самом деле делают β₁, β₂ и ε
Adam объединяет идеи momentum и RMSProp. На каждом шаге он вычисляет экспоненциальное скользящее среднее градиента m_t и экспоненциальное скользящее среднее квадрата градиента v_t, корректирует смещение начальных нулевых оценок и делает шаг, масштабированный на sqrt(v_t) + ε. Три гиперпараметра управляют этим процессом - и каждый заслуживает отдельного разбора.
β₁: память о прошлых градиентах
β₁ определяет, насколько сильно текущий градиент смешивается с накопленной историей. При β₁=0.9 вклад градиента десятишаговой давности составляет примерно 0.9^10 ≈ 35%. Оптимизатор продолжает двигаться в направлении, которое было актуально десяток итераций назад, даже если ландшафт уже изменился.
В стационарной задаче это плюс: момент сглаживает шум батча и помогает преодолевать мелкие локальные минимумы. В нестационарной - прямой вред. Когда распределение данных или целевая функция дрейфуют, старые направления становятся не просто бесполезными, а деструктивными. Оптимизатор тратит несколько шагов на «торможение» старого момента, прежде чем начать двигаться в актуальном направлении.
Снижение β₁ до 0.5 радикально меняет картину. Вклад градиента двушаговой давности падает до 25%, трёхшаговой - до 12.5%. Оптимизатор реагирует на изменение ландшафта практически мгновенно. Плата за это - повышенная чувствительность к шуму батча, поэтому в стационарных задачах такой агрессивный β₁ обычно не нужен.
β₂: адаптивность learning rate и её подводные камни
β₂ контролирует окно усреднения квадратов градиентов v_t. При β₂=0.999 эффективный размер окна - около 1000 шагов. Это означает, что оценка «типичной» магнитуды градиента опирается на историю длиной в тысячу итераций.
В нестационарной среде это окно становится ловушкой. Представьте: первые 500 шагов агент исследовал одно подмножество состояний с небольшими градиентами. v_t накопил низкие значения. Затем агент перешёл в другое подмножество, где градиенты на порядок больше. v_t продолжает выдавать заниженную оценку, Adam делит на маленькое sqrt(v_t) - и делает гигантский шаг, улетая в область катастрофического loss. Либо обратная ситуация: v_t завышен относительно текущих градиентов, шаги становятся микроскопическими, обучение застревает.
Снижение β₂ до 0.95 сокращает окно до ~20 шагов. v_t быстрее адаптируется к новой кривизне ландшафта, learning rate остаётся адекватным текущей ситуации. Для RL это часто критично: политика агента меняется, распределение градиентов смещается, и оптимизатор должен успевать за этими изменениями.
Практическое правило: если задача стационарна - β₂ в диапазоне 0.99-0.999 работает хорошо. Если распределение градиентов дрейфует - начинайте с β₂=0.95 и корректируйте по результатам мониторинга v_t.
ε: не просто «чтобы не делить на ноль»
Распространено заблуждение: ε - техническая заглушка от деления на ноль, и её значение не влияет на обучение. Это верно для стандартного Adam со значением 1e-8, когда градиенты имеют умеренную магнитуду. Но в нестационарных задачах с высокой дисперсией градиентов ε становится полноценным гиперпараметром.
Механика здесь прямая. Обновление параметров в Adam пропорционально m_t / (sqrt(v_t) + ε). Когда v_t мал относительно шума, знаменатель почти равен ε, и обновление становится пропорциональным m_t / ε. Малый ε означает большие, дёрганые шаги при любом всплеске градиента. Большой ε сглаживает обновления, действуя как регуляризатор: даже при резких флуктуациях v_t шаг остаётся контролируемым.
В RL типичны ситуации, когда v_t падает на порядок после смены политики, а градиенты при этом остаются значительными. С ε=1e-8 это даёт взрывной шаг и расходимость. Увеличение ε до 1e-4 или 1e-3 предотвращает катастрофу ценой небольшого замедления сходимости в спокойных участках.
Эвристика: для задач с высокой дисперсией градиентов начинайте с ε=1e-4. Если обучение всё ещё нестабильно - поднимайте до 1e-3 или 1e-2. Сигнал к снижению ε - слишком медленная сходимость при стабильном loss. Управление конфигурациями и систематическое тестирование параметров - навык, который окупается на порядок быстрее, чем интуитивный перебор.
Практический кейс: как настройка β₁ и β₂ спасла RL-модель
Задача: обучение агента в среде с нестационарной динамикой. Каждые 50 эпизодов параметры среды менялись - смещались целевые состояния, корректировалась функция награды, появлялись новые препятствия. Агент на основе policy gradient с нейросетью из трёх полносвязных слоёв.
Запуск с дефолтным Adam (β₁=0.9, β₂=0.999, ε=1e-8, lr=3e-4) показал характерную картину патологии. Первые 100 эпизодов loss плавно снижался, агент нащупывал первую политику. Затем смена динамики среды - и loss взлетал с 0.3 до 15 за десяток шагов. v_t, накопивший низкие значения за стабильный период, не успевал адаптироваться к новым градиентам. Adam делал гигантские шаги, параметры улетали в область насыщения активаций, градиенты обнулялись - обучение останавливалось. Следующие 200 эпизодов loss хаотически осциллировал между 5 и 20 без тенденции к снижению.
Диагностика v_t подтвердила гипотезу: второй момент показывал плато на уровне 1e-6 в стабильный период, затем резкий скачок до 1e-2 при смене динамики и возврат к плато после расходимости. Оптимизатор «залипал» в неактуальной оценке кривизны.
Были протестированы три конфигурации:
- Конфиг A: β₁=0.7, β₂=0.95, ε=1e-8 - нестабильность снизилась, но скачки loss при смене динамики сохранялись.
- Конфиг B: β₁=0.5, β₂=0.95, ε=1e-8 - обучение стабилизировалось, loss плавно снижался после каждой смены среды.
- Конфиг C: β₁=0.5, β₂=0.95, ε=1e-4 - дополнительное сглаживание, самая стабильная кривая, небольшое замедление сходимости на первых 20 эпизодах после смены динамики.
Конфиг C был взят в production. Фрагмент кода на PyTorch:
import torch.optim as optim
optimizer = optim.Adam(
model.parameters(),
lr=3e-4,
betas=(0.5, 0.95),
eps=1e-4
)
Почему сработали именно эти значения. β₂=0.95 даёт окно адаптации ~20 шагов - достаточно, чтобы сгладить шум батча, но не настолько много, чтобы тащить устаревшую кривизну через смену динамики. β₁=0.5 позволяет оптимизатору перестроить направление движения за 2-3 шага после изменения ландшафта. ε=1e-4 страхует от взрывных обновлений в переходные моменты, когда v_t ещё не подстроился под новую магнитуду градиентов.
Результат: агент вышел на целевую политику через 400 эпизодов против отсутствия сходимости на дефолтном Adam за 1000 эпизодов. Reward стабилизировался на уровне 0.85±0.03 против хаотических 0.1-0.5 на стандартных настройках.
Этот кейс не уникален. Похожие проблемы возникают при обучении GAN, где дискриминатор и генератор постоянно смещают распределение градиентов друг другу, при meta-learning с быстрой адаптацией к новым задач и при online-обучении на потоковых данных с дрейфом распределения. Катастрофическое забывание при дообучении - смежная проблема, где неправильный выбор оптимизатора и его параметров усугубляет деградацию старых навыков.
Диагностика по v_t: как вовремя заметить, что Adam «сошёл с ума»
v_t - экспоненциальное скользящее среднее квадратов градиентов. Это главный диагностический сигнал оптимизатора. В здоровом обучении v_t плавно меняется, отражая текущую магнитуду градиентов. Патология проявляется в характерных паттернах, которые видны на графике за десятки итераций до того, как loss взлетит или застынет.
Типичные паттерны v_t и их интерпретация
Нормальный v_t. Плавное снижение на ранних этапах обучения (модель выходит из области больших градиентов инициализации), затем стабилизация около некоторого уровня с умеренными флуктуациями. При смене фазы обучения допустим плавный дрейф вверх или вниз на масштабе десятков шагов.
«Замороженный» v_t. Значение v_t выходит на плато и не меняется десятки или сотни шагов, несмотря на то что loss продолжает осциллировать. Причина: β₂ слишком высок для текущей скорости изменения ландшафта. v_t усредняет настолько длинную историю, что новые градиенты тонут в старых накоплениях. Решение: снизить β₂ до 0.95 или 0.9.
«Взрывной» v_t. Резкие пики v_t на порядок и более за несколько шагов. Часто предшествуют расходимости loss. Причина: сочетание высокого β₁ (сохраняет большое m_t) и малого ε (не демпфирует обновление). Оптимизатор делает шаг в область огромных градиентов, v_t взлетает, но из-за высокого β₂ не успевает опуститься - и следующие шаги тоже гигантские. Решение: увеличить ε до 1e-4 или выше, снизить β₁.
«Пилообразный» v_t. v_t осциллирует с большой амплитудой на масштабе нескольких шагов. Характерен для GAN-тренировки, когда дискриминатор и генератор по очереди создают большие градиенты. Решение: увеличить ε для сглаживания, возможно, снизить learning rate.
Мониторинг v_t настраивается в несколько строк. В PyTorch можно повесить hook на параметры оптимизатора и логировать среднее или максимальное значение v_t по слоям. В TensorBoard и WandB достаточно добавить кастомный скалярный metric, вычисляемый раз в N шагов. Тревожные пороги зависят от задачи, но универсальное правило: если v_t меняется более чем на порядок за 10 шагов - оптимизатор в нестабильном режиме.
Когда и как увеличивать ε: от 1e-8 до 1e-2 и выше
Увеличение ε - самый недооценённый приём стабилизации Adam. Стандартное значение 1e-8 подобрано для задач, где градиенты имеют магнитуду порядка 1e-3…1e-1. В нестационарных средах разброс магнитуд может составлять три-четыре порядка. Эффективный learning rate Adam равен приблизительно lr / (sqrt(v_t) + ε). Когда v_t мал, знаменатель определяется ε, и шаг становится lr / ε.
При ε=1e-8 и lr=3e-4 эффективный шаг в области малых v_t составляет 3e-4 / 1e-8 = 30000. Это не опечатка. Именно поэтому даже небольшой всплеск градиента в области с низким v_t приводит к катастрофическому обновлению.
Эвристика для выбора ε:
- Измерьте типичную магнитуду v_t в стабильной фазе обучения. Пусть это будет v_typical.
- Установите ε на уровне 0.01…0.1 от sqrt(v_typical).
- Если обучение нестабильно - увеличивайте ε на порядок.
- Если сходимость слишком медленная - уменьшайте ε вдвое.
Пример из RL-кейса: v_typical в стабильной фазе ~1e-4, sqrt(v_typical) = 1e-2. ε=1e-4 составляет 1% от sqrt(v_typical) - достаточно, чтобы сгладить всплески, но не настолько много, чтобы доминировать в знаменателе и замедлять обучение.
В задачах с экстремальной дисперсией градиентов - некоторые политики RL, GAN с агрессивным дискриминатором, training with noisy gradients - ε доходит до 1e-2 и даже 1e-1. Плата за такую стабилизацию: оптимизатор начинает вести себя ближе к SGD с momentum, теряя адаптивность. Баланс находится экспериментально под конкретную задачу.
Практический совет: никогда не тюньте ε изолированно. Изменение ε меняет эффективный learning rate, особенно в областях с малым v_t. После корректировки ε проверьте, не нужно ли скорректировать lr. Часто увеличение ε на порядок требует увеличения lr вдвое для сохранения темпа сходимости.
Общие рекомендации и границы применимости
Соберём рекомендации в таблицу. Это не догма, а стартовые точки для экспериментов под конкретную задачу.
| Тип задачи | β₁ | β₂ | ε | Примечания |
|---|---|---|---|---|
| Классификация, регрессия (стационарные данные) | 0.9 | 0.999 | 1e-8 | Дефолты работают хорошо. Тюнинг lr даст больше, чем трогание β и ε. |
| RL с умеренной нестационарностью | 0.7-0.9 | 0.95-0.99 | 1e-6…1e-4 | Начните с β₂=0.95, мониторьте v_t. |
| RL с частой сменой динамики | 0.5-0.7 | 0.9-0.95 | 1e-4…1e-2 | Кейс из статьи. β₁=0.5, β₂=0.95, ε=1e-4 - рабочая отправная точка. |
| GAN | 0.5-0.9 | 0.9-0.999 | 1e-7…1e-4 | Часто тюнят отдельно для генератора и дискриминатора. Дискриминатор может требовать более агрессивных настроек. |
| Meta-learning | 0.5-0.7 | 0.9-0.95 | 1e-4…1e-3 | Быстрая адаптация к новым задачам требует короткой памяти. |
| Fine-tuning LLM | 0.9 | 0.999 | 1e-8 | Дефолты обычно работают. Проблемы чаще связаны с learning rate scheduling. |
Границы применимости важно проговорить прямо. Все рекомендации этой статьи основаны на практическом опыте с нестационарными задачами, в первую очередь RL. В NLP с трансформерами дефолтный AdamW с β₁=0.9, β₂=0.999 остаётся разумным выбором - нестационарность там ниже, а батчи больше, что само по себе сглаживает шум. В компьютерном зрении на больших датасетах дефолты тоже редко подводят.
Тем не менее, мониторинг v_t полезен в любой задаче. Это дешёвый сигнал, который может указать на проблему за часы до того, как вы увидите её по графикам loss. Если v_t ведёт себя нестабильно - не игнорируйте. Снижайте β₂, увеличивайте ε, проверяйте гипотезу о нестационарности данных.
Отход от «священных» дефолтов Adam - не ересь, а инженерная необходимость в определённом классе задач. Терпение при отказах и готовность проверять базовые предположения - то, что отличает системный подход к ML от магического мышления. Параметры оптимизатора - такой же объект тюнинга, как архитектура сети или learning rate. Относитесь к ним соответственно.