Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

В предыдущих разделах мы использовали стохастический градиентный спуск в процессе обучения, но не объясняли, почему он работает. Чтобы прояснить это, в разделе 11.3 мы описали основные принципы градиентного спуска. В этом разделе подробнее рассмотрим стохастический градиентный спуск (stochastic gradient descent, SGD).

MXNet:

%matplotlib inline
from d2l import mxnet as d2l
import math
from mxnet import np, npx
npx.set_np()

PyTorch:

%matplotlib inline
from d2l import torch as d2l
import math
import torch

TensorFlow:

%matplotlib inline
from d2l import tensorflow as d2l
import math
import tensorflow as tf

PaddlePaddle:

%matplotlib inline
from d2l import paddle as d2l
import warnings
warnings.filterwarnings("ignore")
import math
import paddle

11.4.1 Обновление по случайному градиенту

В глубоком обучении целевая функция обычно является средним значением функций потерь по каждому примеру обучающего набора данных. Пусть задан обучающий набор из примеров, и — функция потерь для обучающего примера с индексом , где — вектор параметров. Тогда мы получаем целевую функцию

Градиент целевой функции в точке вычисляется как

Если использовать градиентный спуск, то вычислительная стоимость каждой итерации обновления параметров составляет и линейно растёт с ростом . Поэтому при большом обучающем наборе данных каждая итерация градиентного спуска обходится дорого.

Стохастический градиентный спуск (SGD) снижает вычислительную стоимость каждой итерации. На каждой итерации SGD равномерно и случайно выбирает индекс из и вычисляет градиент , чтобы обновить :

где — скорость обучения. Видно, что стоимость каждой итерации снижается с у градиентного спуска до константы . Кроме того, стоит подчеркнуть, что стохастический градиент является несмещённой оценкой полного градиента , поскольку

Это означает, что в среднем стохастический градиент — хорошая оценка градиента.

Теперь сравним его с градиентным спуском, добавив к градиенту случайный шум со средним и дисперсией , чтобы смоделировать стохастический градиентный спуск.

def f(x1, x2):  # Целевая функция
    return x1 ** 2 + 2 * x2 ** 2

def f_grad(x1, x2):  # Градиент целевой функции
    return 2 * x1, 4 * x2

MXNet, PyTorch, PaddlePaddle:

def sgd(x1, x2, s1, s2, f_grad):
    g1, g2 = f_grad(x1, x2)
    # Имитация зашумлённого градиента
    g1 += d2l.normal(0.0, 1, (1,)).item()
    g2 += d2l.normal(0.0, 1, (1,)).item()
    eta_t = eta * lr()
    return (x1 - eta_t * g1, x2 - eta_t * g2, 0, 0)

TensorFlow:

def sgd(x1, x2, s1, s2, f_grad):
    g1, g2 = f_grad(x1, x2)
    # Имитация зашумлённого градиента
    g1 += d2l.normal([1], 0.0, 1)
    g2 += d2l.normal([1], 0.0, 1)
    eta_t = eta * lr()
    return (x1 - eta_t * g1, x2 - eta_t * g2, 0, 0)
def constant_lr():
    return 1

eta = 0.1
lr = constant_lr  # Постоянная скорость обучения
d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))

Как видно, траектория переменных при стохастическом градиентном спуске гораздо более шумная, чем траектория при градиентном спуске, которую мы наблюдали в разделе 11.3. Это следствие случайной природы градиента. Иными словами, даже вблизи минимума мы всё ещё подвержены неопределённости, которую вносит мгновенный градиент . Даже после 50 итераций качество решения остаётся невысоким. Хуже того, с дальнейшими шагами оно не улучшается. Остаётся единственный выход — менять скорость обучения . Но если выбрать её слишком маленькой, то в самом начале мы не добьёмся никакого заметного прогресса; если же выбрать её слишком большой, мы не получим хорошего решения, как показано выше. Единственный способ примирить эти противоречивые цели — динамически уменьшать скорость обучения в процессе оптимизации.

Именно поэтому в функцию шага sgd добавлена функция скорости обучения lr. В приведённом выше примере функция планирования скорости обучения «спит», поскольку мы задали для lr постоянное значение.

11.4.2 Динамическая скорость обучения

Замена на зависящую от времени скорость обучения усложняет управление сходимостью алгоритма оптимизации. В частности, нужно понять, с какой скоростью убывает . Если убывание слишком быстрое, оптимизация остановится преждевременно; если слишком медленное — мы потратим на оптимизацию слишком много времени. Ниже приведены несколько базовых стратегий изменения во времени (более продвинутые стратегии мы обсудим позже):

В первом, кусочно-постоянном (piecewise constant), сценарии мы уменьшаем скорость обучения, например, каждый раз, когда прогресс оптимизации останавливается. Это распространённая стратегия при обучении глубоких сетей. Или же можно уменьшать её агрессивнее с помощью экспоненциального затухания (exponential decay). К сожалению, это часто приводит к преждевременной остановке алгоритма до его сходимости. Популярный выбор — полиномиальное затухание (polynomial decay) с . Для выпуклой оптимизации есть много свидетельств того, что такой темп убывания работает хорошо.

Посмотрим, как выглядит экспоненциальное затухание на практике.

def exponential_lr():
    # Глобальная переменная, которая определена вне функции и обновляется внутри неё
    global t
    t += 1
    return math.exp(-0.1 * t)

t = 1
lr = exponential_lr
d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=1000, f_grad=f_grad))

Как и ожидалось, дисперсия параметров значительно уменьшилась. Однако это произошло ценой того, что алгоритм не сошёлся к оптимальному решению . Даже после 1000 итераций мы всё ещё далеки от оптимума. По сути, алгоритм вообще не сходится. С другой стороны, если использовать полиномиальное затухание, при котором скорость обучения убывает обратно пропорционально квадратному корню из числа итераций, то уже после 50 итераций сходимость окажется лучше.

def polynomial_lr():
    # Глобальная переменная, которая определена вне функции и обновляется внутри неё
    global t
    t += 1
    return (1 + 0.1 * t) ** (-0.5)

t = 1
lr = polynomial_lr
d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))

Существует и много других вариантов выбора скорости обучения. Например, можно начать с небольшой скорости, затем быстро поднять её, а потом снова уменьшать, хотя это будет происходить медленнее. Можно даже переключаться между маленькими и большими значениями. Однако сейчас мы сосредоточимся на схемах скорости обучения, для которых можно провести полный теоретический анализ, то есть на скорости обучения в выпуклом случае. Для общих невыпуклых задач трудно получить содержательные гарантии сходимости, поскольку в целом минимизация нелинейных невыпуклых задач является NP-трудной. Обзор соответствующих исследований можно найти, например, в превосходных лекционных заметках Тибширани (Tibshirani) за 2015 год.

11.4.3 Анализ сходимости для выпуклых целей

Следующий анализ сходимости стохастического градиентного спуска для выпуклых целевых функций является необязательным и в основном служит для того, чтобы передать больше интуиции об этой задаче. Мы ограничимся одним из самых простых доказательств (Nesterov and Vial, 2000). Существуют заметно более продвинутые методы доказательства, например, для случаев, когда целевая функция ведёт себя особенно хорошо.

Предположим, что для всех целевая функция выпукла по . Более конкретно, рассмотрим обновление стохастического градиентного спуска:

где — целевая функция обучающего примера , который извлекается из некоторого распределения на шаге , а — параметры модели. Обозначим через

ожидаемый риск, а через — минимальный риск для . Наконец, пусть — точка минимума (мы предполагаем, что она лежит в области определения ). В этом случае мы можем отслеживать расстояние между текущими параметрами в момент времени и минимизатором риска , чтобы проверить, улучшается ли оно со временем:

Предположим, что -норма стохастического градиента ограничена некоторой константой , поэтому

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

Подставив неравенства (11.4.2) и (11.4.3) в (11.4.1), мы получим оценку расстояния между параметрами на шаге :

Это означает, что прогресс мы получаем всякий раз, когда разница между текущими потерями и оптимальными потерями превышает . Поскольку эта разница обязательно стремится к нулю, скорость обучения тоже должна стремиться к нулю.

Далее возьмём математическое ожидание от (11.4.4). Получаем

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

Заметим, что поскольку задано, математическое ожидание можно опустить. Наконец, определим

Поскольку

то по неравенству Йенсена (уравнение (11.2.1) при , ) и из выпуклости , которая даёт , мы получаем

Подставив это в (11.4.5), получаем оценку

где — оценка расстояния между начальным выбором параметров и конечным результатом. Коротко говоря, скорость сходимости зависит от того, как ограничена норма стохастического градиента (), и от расстояния между начальным значением параметров и оптимальным решением (). Обратите внимание, что оценка выражена через , а не через , потому что — это сглаженная версия траектории оптимизации. Зная , и , можно выбрать скорость обучения . Тогда верхняя оценка равна . То есть мы сходимся к оптимальному решению со скоростью .

11.4.4 Стохастический градиент и конечные выборки

До сих пор, говоря о стохастическом градиентном спуске, мы действовали несколько поверхностно. Мы предполагали, что примеры (обычно вместе с метками ) выбираются из распределения , и использовали их для обновления параметров модели определённым образом. В частности, для конечного числа примеров мы рассматривали только дискретное распределение , состоящее из функций и , которые позволяют выполнять по нему стохастический градиентный спуск.

Однако на самом деле мы так не поступали. В простом примере этого раздела мы просто добавляли шум к другому, нестохастическому градиенту, то есть делали вид, будто у нас есть пары . Оказывается, для данного случая такой подход оправдан (подробное обсуждение см. в упражнениях). Но во всех предыдущих рассуждениях мы явно так не поступали. Вместо этого мы проходили по всем примерам ровно один раз. Чтобы понять, почему это предпочтительнее, рассмотрим обратную ситуацию: мы с возвращением выбираем наблюдений из дискретного распределения. Вероятность выбрать элемент при каждом выборе равна . Поэтому вероятность выбрать его хотя бы один раз равна

Аналогичные рассуждения показывают, что вероятность выбрать какой-то пример ровно один раз равна

Это приводит к увеличению дисперсии и снижению эффективности использования данных по сравнению с выборкой без возвращения. Поэтому на практике мы используем последнюю (это выбор по умолчанию в этой книге). И последнее замечание: при повторном проходе по обучающему набору данных его обходят в другом случайном порядке.

Резюме

  • Для выпуклых задач можно доказать, что при широком выборе скорости обучения стохастический градиентный спуск сходится к оптимальному решению.
  • В глубоком обучении это обычно не так. Однако анализ выпуклых задач даёт представление о том, как следует проводить оптимизацию: постепенно уменьшать скорость обучения, но не слишком быстро.
  • Проблемы возникают, если скорость обучения слишком мала или слишком велика. На практике подходящую скорость обучения обычно находят только после многочисленных экспериментов.
  • Когда в обучающем наборе данных много примеров, каждая итерация градиентного спуска обходится дороже, поэтому в таких случаях предпочтителен стохастический градиентный спуск.
  • Гарантии оптимальности стохастического градиентного спуска в невыпуклом случае, как правило, недоступны, поскольку число локальных минимумов, которые нужно проверить, может быть экспоненциальным.

Упражнения

  1. Поэкспериментируйте с различными графиками скорости обучения для стохастического градиентного спуска и с разным числом итераций. В частности, постройте график зависимости расстояния до оптимального решения от числа итераций.
  2. Докажите, что для функции добавление к градиенту нормального шума эквивалентно минимизации функции потерь , где извлекается из нормального распределения.
  3. Сравните сходимость стохастического градиентного спуска при выборке из с возвращением и без возвращения.
  4. Как изменится решатель стохастического градиентного спуска, если некоторые градиенты (точнее, связанные с ними координаты) всегда больше всех остальных?
  5. Пусть . Сколько у локальных минимумов? Попробуйте изменить так, чтобы для минимизации потребовалось вычислить все локальные минимумы.

MXNet: Обсуждение

PyTorch: Обсуждение

TensorFlow: Обсуждение

PaddlePaddle: Обсуждение

Литература

  • Nesterov, Y., & Vial, J.-P. (2000). Confidence level solutions for stochastic programming. Stochastic Programming E-Print Series.