Оптимизаторы: SGD и Adam
Разбираем оптимизаторы — алгоритмы, которые решают, как именно двигать веса по градиентам.
Оптимизатор — объект из
torch.optim, который обновляет веса модели на основе их градиентов и заданного правила.
Что делает оптимизатор
backward даёт нам градиенты — направление, куда веса нужно двигать, чтобы уменьшить loss. Но насколько двигать и по какому правилу — решает оптимизатор. Самый простой — SGD (стохастический градиентный спуск): он вычитает из каждого веса его градиент, умноженный на шаг обучения lr.
import torch
# создаём оптимизатор, передав параметры модели и шаг
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# внутри цикла:
optimizer.zero_grad()
loss.backward()
optimizer.step() # w = w - 0.01 * w.grad для каждого веса
Заметьте: оптимизатору при создании передают model.parameters() — так он узнаёт, какие именно тензоры ему поручено обновлять.
Learning rate — самый важный гиперпараметр
Шаг обучения lr определяет размер шага по градиенту. Это, пожалуй, самая чувствительная настройка во всём обучении:
| lr | Что происходит |
| слишком большой | шаги перелетают минимум, loss скачет или растёт (расходится) |
| слишком маленький | обучение идёт мучительно медленно |
| в самый раз | loss плавно и стабильно снижается |
Типичные стартовые значения: 0.1–0.01 для SGD, 0.001 для Adam. Если loss «взрывается» в NaN — первое подозрение всегда на слишком большой lr.
Adam — умный оптимизатор по умолчанию
SGD прост, но требует аккуратной настройки lr и сходится медленно. Adam адаптирует размер шага для каждого веса автоматически (запоминая историю градиентов) и работает «из коробки» почти везде. Поэтому для большинства задач Adam — разумный выбор по умолчанию.
# SGD с инерцией (momentum помогает быстрее проходить пологие участки)
opt_sgd = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# Adam — адаптивный, хорош по умолчанию
opt_adam = torch.optim.Adam(model.parameters(), lr=0.001)
API у всех оптимизаторов одинаковый: создал → в цикле zero_grad() / step(). Сменить SGD на Adam — это поменять одну строку, остальной цикл не трогаете. Поэтому оптимизатор удобно подбирать экспериментом.
Руками воспроизводим шаг SGD
Чтобы оптимизатор перестал быть «чёрным ящиком», повторим шаг SGD в чистом Python. Минимизируем f(w) = (w - 5)² — минимум в точке w = 5. Градиент f'(w) = 2(w - 5). Этот код запускается:
# ручной градиентный спуск: ищем минимум (w - 5)^2
w = 0.0 # начальное значение веса
lr = 0.1 # learning rate
for step in range(20):
grad = 2 * (w - 5) # производная f по w
w = w - lr * grad # ровно то, что делает optimizer.step() для SGD
print("итоговый w:", round(w, 4))
print("минимум должен быть в w = 5")
Вывод:
итоговый w: 4.9424 минимум должен быть в w = 5
За 20 шагов вес уверенно подполз к 5. Это в точности то, что optimizer.step() делает в PyTorch — только сразу для миллионов весов и с автоматически посчитанными autograd градиентами. Попробуйте поставить lr = 1.5 и увидите, как шаги начнут «перелетать» минимум и расходиться — наглядная иллюстрация слишком большого learning rate.
Итог
- Оптимизатор из
torch.optimрешает, как двигать веса по их градиентам; ему передаютmodel.parameters(). lr(learning rate) — критичный гиперпараметр: велик — расходится, мал — медленно.SGDпрост;Adamадаптивен и хорош как выбор по умолчанию.- Шаг SGD — это
w = w - lr * grad; сменить оптимизатор — одна строка.