В чем разница между SGD и обычным градиентным спуском?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между SGD и обычным градиентным спуском
Оба метода используют один и тот же принцип — движение в направлении антиградиента для минимизации функции потерь. Но они отличаются размером батча и, следовательно, скоростью, точностью и поведением оптимизации.
Основное отличие
Batch Gradient Descent (BGD) — обычный градиентный спуск
- Обновляет веса на основе всего датасета
- Один шаг = один проход по всем данным
- Математика: θ := θ - α * ∇J(θ) [все примеры сразу]
Stochastic Gradient Descent (SGD)
- Обновляет веса на основе одного примера (или mini-batch)
- Один шаг = обновление на одной (или нескольких) точке
- Математика: θ := θ - α * ∇J(θ; x_i, y_i) [один пример]
Визуальное сравнение
Batch Gradient Descent:
┌─────────────────────────────┐
│ Гладкая кривая │
│ Медленно сходится │
│ Может застрять в локальных │
│ минимумах │
└─────────────────────────────┘
Stochastic Gradient Descent:
┌─────────────────────────────┐
│ Шумная, прерывистая кривая │
│ Быстро движется │
│ Может выскочить из локального│
│ минимума благодаря шуму │
└─────────────────────────────┘
Код: практическое сравнение
import numpy as np
import matplotlib.pyplot as plt
# Простая квадратичная функция потерь
def loss_function(x):
return x**2
def gradient(x):
return 2*x
# Batch Gradient Descent
def batch_gd(x_init, learning_rate=0.1, iterations=50):
x = x_init
history = [x]
for i in range(iterations):
grad = gradient(x) # Градиент всех данных (тут симуляция)
x = x - learning_rate * grad
history.append(x)
return np.array(history)
# Stochastic Gradient Descent
def sgd(x_init, learning_rate=0.1, iterations=50):
x = x_init
history = [x]
for i in range(iterations):
# Случайно возмущаем градиент (симуляция шума от одного примера)
grad = gradient(x) + np.random.randn() * 0.5
x = x - learning_rate * grad
history.append(x)
return np.array(history)
bgd_path = batch_gd(x_init=5.0)
sgd_path = sgd(x_init=5.0)
plt.plot(bgd_path, label='BGD', marker='o', alpha=0.7)
plt.plot(sgd_path, label='SGD', marker='.', alpha=0.5)
plt.xlabel('Итерация')
plt.ylabel('Значение x')
plt.legend()
plt.grid(True)
plt.show()
Сравнение характеристик
| Параметр | BGD | SGD |
|---|---|---|
| Батч | Все данные | 1 пример |
| Память | Высокая (весь датасет) | Низкая (один пример) |
| Скорость сходимости | Медленная | Быстрая (много шагов) |
| Гладкость траектории | Гладкая | Шумная |
| Локальные минимумы | Может застрять | Может выскочить (шум помогает) |
| Масштабируемость | Плохая (большие датасеты) | Хорошая |
| Вычисления | Много за раз | Часто обновлять |
Mini-batch Gradient Descent
На практике используют золотую середину:
from torch.utils.data import DataLoader
import torch
# Mini-batch SGD
batch_size = 32 # Обновляем на 32 примерах за раз
learning_rate = 0.01
train_loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
for epoch in range(num_epochs):
for batch_x, batch_y in train_loader: # Mini-batch размер 32
# Forward pass
predictions = model(batch_x)
loss = criterion(predictions, batch_y)
# Backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step() # Обновляем на 32 примерах
Почему mini-batch лучше?
- Лучше использует GPU (параллелизм)
- Гладче сходится, чем pure SGD
- Быстрее, чем BGD
- Можно добавить регуляризацию
Практический пример на PyTorch
import torch
import torch.nn as nn
from torch.optim import SGD
# Модель
model = nn.Linear(10, 1)
# Оптимизатор SGD (по умолчанию mini-batch)
optimizer = SGD(model.parameters(), lr=0.01) # Это mini-batch SGD!
# Данные
for epoch in range(100):
for batch_x, batch_y in data_loader: # batch_size = 32 (по умолчанию)
# Предсказание
pred = model(batch_x)
loss = criterion(pred, batch_y)
# Обновление
optimizer.zero_grad()
loss.backward()
optimizer.step() # Обновляет один раз за batch
Математическое описание
BGD (Batch Gradient Descent):
θ := θ - α * (1/m) * Σ(∇J(θ; x_i, y_i)) for i=1 to m
where m = количество всех примеров
SGD (Stochastic Gradient Descent):
θ := θ - α * ∇J(θ; x_i, y_i) for random (x_i, y_i)
Обновляется на каждом примере
Mini-batch SGD:
θ := θ - α * (1/b) * Σ(∇J(θ; x_i, y_i)) for i=1 to b
where b = размер мини-батча (32, 64, 128)
Когда что использовать
BGD:
- Очень маленькие датасеты (< 1000 примеров)
- Хочется гарантированной сходимости
- Вычисления дёшевы
SGD (на практике mini-batch):
- Большие датасеты (> 10000 примеров)
- Ограничена память на GPU
- Нужна скорость
- Работа на реальных задачах
Усовершенствования SGD
from torch.optim import Adam, RMSprop, Momentum
# Momentum SGD — помнит предыдущие градиенты
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# Adam — адаптивный learning rate
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# RMSprop — масштабирует learning rate по координатам
optimizer = torch.optim.RMSprop(model.parameters(), lr=0.01)
Эти методы решают проблемы pure SGD (шумность, медленная сходимость).
Итого: BGD гарантирует сходимость на гладких функциях, но медленный. SGD (mini-batch) быстрее и масштабируется на больших данных, но более шумный. На практике используют mini-batch SGD с улучшениями (Momentum, Adam и т.п.).