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

Как работает стохастический градиентный спуск?

1.0 Junior🔥 221 комментариев
#Машинное обучение

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

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

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

Стохастический градиентный спуск (SGD)

Стохастический градиентный спуск — это фундаментальный оптимизационный алгоритм, который лежит в основе обучения нейронных сетей и многих ML моделей.

1. Базовый градиентный спуск (GD)

Сначала вспомню классический подход:

import numpy as np

# Стоимостная функция (Loss Function)
def loss_function(X, y, weights):
    predictions = X @ weights
    errors = predictions - y
    return np.sum(errors ** 2) / len(y)  # MSE

# Градиент — направление роста функции
def gradient(X, y, weights):
    predictions = X @ weights
    errors = predictions - y
    return (2 / len(y)) * (X.T @ errors)

# Классический градиентный спуск
weights = np.random.randn(n_features)
learning_rate = 0.01

for epoch in range(100):
    grad = gradient(X, y, weights)  # Считаем градиент на ВСЕХ данных
    weights -= learning_rate * grad

Проблема: при миллионах примеров это очень медленно. На каждой итерации нужно проходить все данные.

2. Суть стохастического подхода

SGD идея: обновляй веса на основе одного примера или небольшого batch, а не всего датасета:

# Стохастический градиентный спуск
weights = np.random.randn(n_features)
learning_rate = 0.01
batch_size = 32

for epoch in range(num_epochs):
    # Перемешиваем данные
    indices = np.random.permutation(len(X))
    
    for i in range(0, len(X), batch_size):
        batch_indices = indices[i:i + batch_size]
        X_batch = X[batch_indices]
        y_batch = y[batch_indices]
        
        # Вычисляем градиент ТОЛЬКО на batch
        grad = gradient(X_batch, y_batch, weights)
        weights -= learning_rate * grad

3. Три варианта gradient descent

ВариантРазмер данныхСкоростьСтабильностьИспользование памяти
Batch GDВесь датасетМедленноСтабильноМного
Stochastic GD1 примерБыстроШумныйМало
Mini-batch GD32-256 примеровБыстроХороший балансУмеренно

Мини-batch — это лучший выбор на практике:

# На PyTorch
from torch.utils.data import DataLoader

train_loader = DataLoader(dataset, batch_size=32, shuffle=True)

for epoch in range(num_epochs):
    for X_batch, y_batch in train_loader:  # автоматические batches
        optimizer.zero_grad()
        output = model(X_batch)
        loss = criterion(output, y_batch)
        loss.backward()
        optimizer.step()

4. Почему SGD работает

Математическое обоснование:

Полный градиент (на всех N примерах):
∇L = (1/N) * Σ ∇L_i

Ожидание SGD градиента (на одном примере):
E[∇L_i] = ∇L

То есть градиент на одном примере — это несмещенная оценка полного градиента! На среднем это работает правильно.

Практические преимущества:

  1. Скорость: обновления происходят чаще (после каждого batch)
  2. Память: не нужно загружать все данные в RAM
  3. Регуляризация: случайный шум помогает избежать локальных минимумов
  4. Online learning: можно обновлять модель по мере поступления новых данных

5. Проблемы базового SGD

Проблема 1: Высокая волатильность

Без сглаживания SGD может колебаться:

# Визуально: потеря функция
    |    *
    |   * *    <- шумные обновления
    |  *   *
    | *     *
    |*       *  <- общее направление к минимуму

Проблема 2: Медленная сходимость в конце обучения

6. Улучшенные алгоритмы

Momentum (Импульс)

Добавляю "инерцию" — копируем приземление мяча с горы:

# Momentum SGD
velocity = 0
momentum = 0.9

for epoch in range(num_epochs):
    grad = gradient(X_batch, y_batch, weights)
    velocity = momentum * velocity - learning_rate * grad
    weights += velocity

Это сглаживает шум и ускоряет сходимость в правильном направлении.

Adam (Adaptive Moment Estimation)

Самый популярный оптимизатор на практике:

from torch.optim import Adam

optimizer = Adam(model.parameters(), lr=0.001, beta1=0.9, beta2=0.999)

for epoch in range(num_epochs):
    for X_batch, y_batch in train_loader:
        output = model(X_batch)
        loss = criterion(output, y_batch)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()  # Adam автоматически

Adam комбинирует momentum и adaptive learning rates (RMSprop):

  • m_t = экспоненциальное среднее градиентов (первый момент)
  • v_t = экспоненциальное среднее квадратов градиентов (второй момент)
  • Adaptive LR = learning_rate / sqrt(v_t + epsilon)

7. Практические советы

# 1. Выбор batch size
batch_sizes = [16, 32, 64, 128]  # экспериментируй
# Больше → более стабильно, медленнее
# Меньше → шумнее, быстрее

# 2. Learning rate scheduling
from torch.optim.lr_scheduler import StepLR, ReduceLROnPlateau

scheduler = StepLR(optimizer, step_size=10, gamma=0.1)  # уменьшай LR

# 3. Нормализация данных
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# С нормализацией SGD работает стабильнее

# 4. Мониторинг
for epoch in range(num_epochs):
    for X_batch, y_batch in train_loader:
        loss = train_step(X_batch, y_batch)
        if (batch_idx + 1) % 100 == 0:
            print(f"Epoch {epoch}, Batch {batch_idx+1}: Loss {loss:.4f}")

Визуализация процесса

Gradient Descent сходимость:

Batch GD:     Stochastic GD:    Mini-batch GD:
    Loss          Loss              Loss
     |              |                |
 100 |*             |*               |*
  50 | *            | * * *          | *
  10 |  *--->       |  *  *  *       |  *
   1 |    ------>   |    ------>     |   ---->
     |______________|_______________|____________
              Iterations

Ключевые выводы

  1. SGD обновляет веса на основе mini-batch, а не всего датасета
  2. Импульс улучшает сходимость, сглаживая шум
  3. Adam — лучший выбор на практике для большинства задач
  4. Learning rate — критический гиперпараметр
  5. Batch size влияет на скорость и стабильность