Стохастический спуск, мини-батчи и локальные минимумы

На больших данных считать градиент по всей выборке дорого — спасают стохастический и мини-батч спуск, а их шум ещё и помогает не застрять.

Стохастический градиентный спуск (SGD) обновляет параметры по одному случайному объекту за раз, а мини-батч — по небольшой случайной порции, вместо всей выборки сразу.

Проблема полного (batch) спуска

В уроках выше мы на каждом шаге считали градиент по всем данным. Это называется полный (batch) градиентный спуск. Он точен, но если объектов миллионы, один шаг требует пройти весь датасет — обучение ползёт. Идея SGD: зачем смотреть на все данные, чтобы понять направление вниз? Достаточно оценки градиента по части данных — она шумная, но в среднем верная, зато шагов в секунду в сотни раз больше.

SGD: один объект за шаг

Стохастический спуск берёт случайный объект, считает градиент только по нему и сразу делает шаг. Каждый отдельный шаг «дёрганый», но направление в среднем правильное, и за то же время мы делаем гораздо больше обновлений. Реализуем SGD для той же линейной регрессии.

import random
random.seed(0)

xs = [1, 2, 3, 4, 5]
ys = [3.1, 4.9, 7.2, 8.8, 11.1]   # около y = 2x + 1

w, b = 0.0, 0.0
lr = 0.01

for epoch in range(300):
    # перемешиваем и идём по одному объекту
    order = list(range(len(xs)))
    random.shuffle(order)
    for i in order:
        x, y = xs[i], ys[i]
        err = w * x + b - y          # ошибка на ОДНОМ объекте
        w -= lr * 2 * err * x        # градиент по одной точке
        b -= lr * 2 * err

print("SGD нашёл: w =", round(w, 3), ", b =", round(b, 3))

Вывод:

SGD нашёл: w = 1.995 , b = 1.04

SGD пришёл примерно туда же (w ≈ 2, b ≈ 1), что и полный спуск, но обновлялся по одному объекту за раз — на огромных данных это решающее преимущество.

Мини-батч — золотая середина

На практике берут мини-батч: не один объект и не всю выборку, а порцию из 16–256 объектов. Это компромисс — меньше шума, чем у чистого SGD, но всё ещё быстрые шаги и эффективная работа на видеокартах. Почти всё современное глубокое обучение — это мини-батч спуск.

import random
random.seed(1)

xs = list(range(1, 21))
ys = [3 * x + 2 for x in xs]      # точная зависимость y = 3x + 2

def batches(indices, size):
    for start in range(0, len(indices), size):
        yield indices[start:start + size]

w, b = 0.0, 0.0
lr = 0.001
n = len(xs)

for epoch in range(2000):
    order = list(range(n))
    random.shuffle(order)
    for batch in batches(order, 4):       # мини-батч из 4 объектов
        dw = sum(2 * (w * xs[i] + b - ys[i]) * xs[i] for i in batch) / len(batch)
        db = sum(2 * (w * xs[i] + b - ys[i])         for i in batch) / len(batch)
        w -= lr * dw
        b -= lr * db

print("Мини-батч спуск: w =", round(w, 3), ", b =", round(b, 3), " (цель 3 и 2)")

Вывод:

Мини-батч спуск: w = 3.001 , b = 1.983  (цель 3 и 2)

Шум помогает выбираться из ловушек

У стохастичности есть бонус. Полный спуск, скатившись в локальный минимум невыпуклой функции, застревает там навсегда: градиент равен нулю, шагать некуда. А шумные шаги SGD иногда «выталкивают» точку из мелкой ямки, давая шанс найти яму поглубже. Поэтому случайность — не только про скорость, но и про качество решения на сложных, невыпуклых ландшафтах нейросетей.

МетодДанных на шагКогда применять
Полный (batch)вся выборкамаленькие данные, нужна точность
SGDодин объекточень большие данные, онлайн-обучение
Мини-батч16–256 объектовстандарт глубокого обучения

Итог

  • Полный спуск точен, но считает градиент по всем данным — медленно на больших выборках.
  • SGD обновляет параметры по одному объекту: шумно, зато много шагов.
  • Мини-батч (16–256 объектов) — компромисс и стандарт глубокого обучения.
  • Шум стохастичности помогает выбираться из локальных минимумов невыпуклых функций.
Проверьте себя
1. Чем стохастический градиентный спуск (SGD) отличается от полного?
AОн обновляет параметры по одному случайному объекту, а не по всем данным сразу
BОн считает градиент по всей выборке точнее
CОн не использует learning rate
DОн работает только с выпуклыми функциями
2. Почему мини-батч спуск — стандарт глубокого обучения?
AОн единственный даёт точный результат
BЭто компромисс: меньше шума, чем у SGD, быстрые шаги и эффективность на видеокартах
CОн не требует данных
DОн не использует градиент
3. Как шум стохастического спуска связан с локальными минимумами?
AОн гарантирует попадание в локальный минимум
BОн полностью убирает локальные минимумы
CСлучайные шаги иногда выталкивают точку из мелкой ямки, помогая найти минимум поглубже
DШум не влияет на минимумы