Что такое momentum в оптимизации?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Momentum в оптимизации
Momentum (Импульс) — это техника оптимизации, которая ускоряет сходимость градиентного спуска, добавляя «инерцию» к обновлениям весов. Вместо того чтобы на каждом шаге просто двигаться в направлении градиента, momentum запоминает предыдущие направления движения и добавляет их к текущему обновлению. Это помогает быстрее спускаться в долины и преодолевать плато.
Проблема обычного градиентного спуска
Обычный стохастический градиентный спуск (SGD) часто ходит туда-сюда, особенно если функция потерь имеет разные кривизны по разным направлениям:
┌─────────────────────────────────┐
│ Поверхность потерь │
│ │
│ * (начало) │
│ | │
│ | <- Прямой SGD может │
│ | часто колебаться │
│ | │
│ * (минимум) │
│ │
└─────────────────────────────────┘
Это приводит к:
- Медленной сходимости
- Колебаниям около минимума
- Застреванию в плоских областях
Основная идея Momentum
Momentum похож на катящийся мяч, который набирает скорость при скатывании вниз. Мяч не останавливается сразу, когда наклон становится меньше, а продолжает двигаться благодаря инерции.
Формула обновления с momentum:
v_t = beta * v_{t-1} + grad_t
theta_t = theta_{t-1} - learning_rate * v_t
Где:
- v_t — скорость (накопленный импульс)
- beta — коэффициент momentum (обычно 0.9)
- grad_t — градиент на шаге t
- theta_t — параметры модели
Пошаговый пример
import numpy as np
import matplotlib.pyplot as plt
# Функция для оптимизации: y = x^2
def f(x):
return x ** 2
def grad_f(x):
return 2 * x
# SGD без momentum
def sgd_no_momentum(x_start, learning_rate=0.1, steps=20):
x = x_start
history = [x]
for _ in range(steps):
grad = grad_f(x)
x = x - learning_rate * grad
history.append(x)
return np.array(history)
# SGD с momentum
def sgd_with_momentum(x_start, learning_rate=0.1, beta=0.9, steps=20):
x = x_start
v = 0 # Начальная скорость
history = [x]
for _ in range(steps):
grad = grad_f(x)
v = beta * v + grad # Обновляем скорость
x = x - learning_rate * v # Обновляем параметр
history.append(x)
return np.array(history)
# Сравнение
x_start = 5.0
history_no_momentum = sgd_no_momentum(x_start)
history_with_momentum = sgd_with_momentum(x_start)
print(f"Шаги без momentum: {len(history_no_momentum) - 1}")
print(f"Шаги с momentum: {len(history_with_momentum) - 1}")
print(f"Финальное значение без momentum: {history_no_momentum[-1]:.6f}")
print(f"Финальное значение с momentum: {history_with_momentum[-1]:.6f}")
# Визуализация
plt.figure(figsize=(12, 5))
# График 1: Траектория
plt.subplot(1, 2, 1)
x = np.linspace(-5, 5, 100)
y = f(x)
plt.plot(x, y, 'b-', label='f(x) = x^2')
plt.plot(history_no_momentum, f(history_no_momentum), 'r.-', label='SGD без momentum')
plt.plot(history_with_momentum, f(history_with_momentum), 'g.-', label='SGD с momentum')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.legend()
plt.title('Траектория оптимизации')
# График 2: Сходимость
plt.subplot(1, 2, 2)
plt.plot(f(history_no_momentum), 'r-', label='SGD без momentum')
plt.plot(f(history_with_momentum), 'g-', label='SGD с momentum')
plt.xlabel('Итерация')
plt.ylabel('Потери')
plt.yscale('log')
plt.legend()
plt.title('Сходимость')
plt.tight_layout()
plt.show()
Реализация в PyTorch
import torch
import torch.nn as nn
import torch.optim as optim
# Создаём простую модель
model = nn.Sequential(
nn.Linear(10, 50),
nn.ReLU(),
nn.Linear(50, 1)
)
# Метод 1: Встроенный SGD с momentum
optimizer_sgd = optim.SGD(
model.parameters(),
lr=0.01,
momentum=0.9, # Коэффициент momentum
nesterov=False
)
# Метод 2: Adam (содержит momentum-подобный механизм)
optimizer_adam = optim.Adam(
model.parameters(),
lr=0.001,
betas=(0.9, 0.999) # beta1 контролирует momentum
)
# Пример обучения
criterion = nn.MSELoss()
X = torch.randn(100, 10)
y = torch.randn(100, 1)
for epoch in range(10):
optimizer_sgd.zero_grad()
output = model(X)
loss = criterion(output, y)
loss.backward()
optimizer_sgd.step() # Использует momentum
if (epoch + 1) % 2 == 0:
print(f"Epoch {epoch + 1}, Loss: {loss.item():.4f}")
Математика Momentum
Экспоненциальное взвешивание средних:
Momentum эквивалентен экспоненциально взвешенному среднему предыдущих градиентов:
v_t = beta * v_{t-1} + (1-beta) * grad_t / (1-beta)
= beta * v_{t-1} + grad_t
Это эквивалентно:
v_t = (1-beta) * (grad_t + beta*grad_{t-1} + beta^2*grad_{t-2} + ...)
Посвежие градиенты имеют больший вес, старые — меньший.
Эффективность Momentum:
# В долине с переменной кривизной:
# Без momentum: много колебаний
# С momentum: инерция помогает преодолеть шум
# Время сходимости:
# SGD: ~1/learning_rate итерации
# SGD + Momentum: ~1/sqrt(learning_rate) итерации
# При beta близком к 1 ускорение может быть в 10x раз
Nesterov Momentum
Nesterov Accelerated Gradient (NAG) — улучшенная версия momentum, которая смотрит на шаг вперёд перед вычислением градиента:
# Обычный momentum
v_t = beta * v_{t-1} + grad(theta_{t-1})
theta_t = theta_{t-1} - lr * v_t
# Nesterov momentum
v_t = beta * v_{t-1} + grad(theta_{t-1} - lr * beta * v_{t-1})
theta_t = theta_{t-1} - lr * v_t
Визуализация разницы:
def sgd_nesterov(x_start, learning_rate=0.1, beta=0.9, steps=20):
x = x_start
v = 0
history = [x]
for _ in range(steps):
# Смотрим вперёд на beta * v
x_lookahead = x - learning_rate * beta * v
grad = grad_f(x_lookahead)
v = beta * v + grad
x = x - learning_rate * v
history.append(x)
return np.array(history)
# Nesterov часто сходится быстрее
history_nesterov = sgd_nesterov(x_start)
print(f"Финальное значение (Nesterov): {history_nesterov[-1]:.6f}")
В PyTorch:
optimizer_sgd = optim.SGD(
model.parameters(),
lr=0.01,
momentum=0.9,
nesterov=True # Включаем Nesterov
)
Выбор коэффициента Beta
| Beta | Эффект | Применение |
|---|---|---|
| 0.0 | Без momentum, обычный SGD | Редко используется |
| 0.5 | Слабый momentum | Очень чувствительные модели |
| 0.9 | Стандартный momentum | Большинство случаев |
| 0.95 | Сильный momentum | Оптимизация на больших датасетах |
| 0.99 | Очень сильный momentum | Редко, может привести к нестабильности |
Momentum в разных оптимизаторах
1. SGD с Momentum
optimizer = optim.SGD(
params,
lr=0.01,
momentum=0.9,
weight_decay=0.0001
)
2. Adam (содержит momentum в обе стороны)
optimizer = optim.Adam(
params,
lr=0.001,
betas=(0.9, 0.999), # Первый beta — как momentum
eps=1e-8
)
# Adam = Momentum + RMSprop
3. AdaGrad (адаптивный learning rate)
optimizer = optim.AdaGrad(
params,
lr=0.01
)
# Не использует momentum явно, но адаптирует lr
4. RMSprop (улучшенный AdaGrad)
optimizer = optim.RMSprop(
params,
lr=0.01,
alpha=0.99,
momentum=0.9
)
Практический пример: Обучение CNN
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# Модель
model = nn.Sequential(
nn.Conv2d(1, 32, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64 * 7 * 7, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
# Оптимизаторы для сравнения
optimizer_sgd = optim.SGD(model.parameters(), lr=0.01, momentum=0.0)
optimizer_sgd_momentum = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
optimizer_sgd_nesterov = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)
optimizer_adam = optim.Adam(model.parameters(), lr=0.001)
optimizers = [
('SGD', optimizer_sgd),
('SGD + Momentum', optimizer_sgd_momentum),
('SGD + Nesterov', optimizer_sgd_nesterov),
('Adam', optimizer_adam)
]
for name, optimizer in optimizers:
print(f"\nОптимизатор: {name}")
# Обучение здесь...
Сравнение с другими методами
┌──────────────────┬────────────┬──────────────┬──────────────┐
│ Оптимизатор │ Скорость │ Память │ Типичное lr │
├──────────────────┼────────────┼──────────────┼──────────────┤
│ SGD │ Медленно │ Низкая │ 0.01-0.1 │
│ SGD + Momentum │ Быстро │ Низкая │ 0.01-0.1 │
│ SGD + Nesterov │ Очень │ Низкая │ 0.01-0.1 │
│ Adagrad │ Быстро │ Средняя │ 0.01-0.1 │
│ RMSprop │ Быстро │ Средняя │ 0.001-0.01 │
│ Adam │ Очень │ Средняя │ 0.0001-0.001 │
│ AdamW │ Очень │ Средняя │ 0.0001-0.001 │
└──────────────────┴────────────┴──────────────┴──────────────┘
Лучшие практики
- Начни с SGD + Momentum (0.9) — простой, эффективный, интерпретируемый
- Используй Nesterov, если нужна небольшая оптимизация
- Переходи на Adam, если SGD не сходится
- Не смешивай high momentum с high learning rate — может привести к дивергенции
- Мониторь норму градиентов — огромные градиенты + momentum = взрыв
Заключение
Momentum — это простая, но мощная техника, которая ускоряет сходимость градиентного спуска благодаря добавлению инерции. Это основной компонент всех современных оптимизаторов (Adam, RMSprop, Adagrad) и критичен для успешного обучения глубоких нейронных сетей.