NesterovSGD¶
Описание¶
Данный модуль реализует принцип работы модификации стохастического градиентного спуска, предложенной Ю.Е. Нестеровым.
По сути, данный метод является улучшенной версией инерционного метода, или по-другому стохастического градиентного спуска с моментом. По Нестерову в инерционный метод добавляется известная в вычислительной математике идея: заглядывать вперёд по вектору обновления.
Напомним формулу для инерционного метода (расшифровку параметров можете посмотреть в документации к MomentumSGD):
\begin{equation} g_t = \gamma{g_{t-1}} + (1 - \gamma)\eta\nabla_\theta J(\theta_t) \end{equation} \begin{equation} \theta_{t+1} = \theta_t - g_t \end{equation}
Идея заключается в том, что раз мы всё равно собираемся смещаться на \gamma{g_{t-1}}, то имеет смысл посчитать градиент функции потерь не в точке \theta_t, а в точке \theta_t - \gamma{g_{t-1}}:
\begin{equation} g_t = \gamma{g_{t-1}} + (1 - \gamma)\eta\nabla_\theta J(\theta_t - \gamma{g_{t-1}}) \end{equation} \begin{equation} \theta_{t+1} = \theta_t - g_t \end{equation}
Такое изменение позволяет быстрее "катиться", если в стороне, куда мы направляемся, производная увеличивается, и медленнее, если наоборот.
"Заглядывание" вперёд может сыграть с нами злую шутку, если установлены слишком большие \gamma и \eta: мы заглядываем настолько далеко, что промахиваемся мимо областей с противоположным знаком градиента.
Инициализация¶
def __init__(self, learnRate=1e-3, momRate=0.9, nodeinfo=None):
Параметры
Параметр | Возможные типы | Описание | По умолчанию |
---|---|---|---|
learnRate | float | Скорость обучения | 1e-3 |
momRate | float | Коэффициент сохранения | 0.9 |
nodeinfo | NodeInfo | Объект, содержащий информацию о вычислительном узле | None |
Пояснения
-
Примеры¶
Необходимые импорты:
import numpy as np
from PuzzleLib.Optimizers import NesterovSGD
from PuzzleLib.Backend import gpuarray
Info
gpuarray
необходим для правильного размещения тензора на GPU.
Создадим синтетическую обучающую выборку:
data = gpuarray.to_gpu(np.random.randn(16, 128).astype(np.float32))
target = gpuarray.to_gpu(np.random.randn(16, 1).astype(np.float32))
Объявляем оптимизатор:
optimizer = NesterovSGD(learnRate=0.01, momRate=0.85)
Пусть уже есть некоторая сеть net
, определённая, например, через Graph, тогда, чтобы установить оптимизатор на сеть, требуется следующее:
optimizer.setupOn(net, useGlobalState=True)
Info
Подробнее про методы оптимизаторов и их параметры читайте в описании родительского класса Optimizer
Также пусть есть некая функция ошибки loss
, наследованная от Cost, рассчитывающая в т.ч. её градиент. Тогда получаем реализацию процесса оптимизации:
for i in range(100):
... predictions = net(data)
... error, grad = loss(predictions, target)
... optimizer.zeroGradParams()
... net.backward(grad)
... optimizer.update()
... if (i + 1) % 5 == 0:
... print("Iteration #%d error: %s" % (i + 1, error))