← Назад к вопросам

Что такое momentum в оптимизации?

1.7 Middle🔥 121 комментариев
#Глубокое обучение#Машинное обучение

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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 │
└──────────────────┴────────────┴──────────────┴──────────────┘

Лучшие практики

  1. Начни с SGD + Momentum (0.9) — простой, эффективный, интерпретируемый
  2. Используй Nesterov, если нужна небольшая оптимизация
  3. Переходи на Adam, если SGD не сходится
  4. Не смешивай high momentum с high learning rate — может привести к дивергенции
  5. Мониторь норму градиентов — огромные градиенты + momentum = взрыв

Заключение

Momentum — это простая, но мощная техника, которая ускоряет сходимость градиентного спуска благодаря добавлению инерции. Это основной компонент всех современных оптимизаторов (Adam, RMSprop, Adagrad) и критичен для успешного обучения глубоких нейронных сетей.

Что такое momentum в оптимизации? | PrepBro