AdaGrad

Описание

Info

Родительский класс: Optimizer

Производные классы: -

Данный модуль реализует принцип работы адаптивного градиентного спуска (adaptive gradient - AdaGrad).

Обычный стохастический градиентый спуск и его инерционные вариации (MomentumSGD, NesterovSGD) не учитывают тот момент, что некоторые признаки могут быть крайне информативными, но в то же время редко встречаться (например, в несбалансированных выборках). Хотя стоит уточнить, что речь идёт не только о входных параметрах - такие же редкие признаки могут встретиться и в глубинных представлениях свёрточной сети, когда входные параметры были "переварены" несколькими слоями.

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

Перед тем, как представить изложенную информацию в аналитическом виде, напомним формулу для SGD:

$$ \theta_{t+1} = \theta_t - \eta \cdot \frac{1}{l}\sum_{i=0}^{l} \nabla_{\theta}{J_i(\theta_t)} $$

Далее для краткости опустим усреднение суммы и произведём замену:

g_t = \nabla_{\theta}{J_i(\theta_t)}

Тогда для i-го параметра \theta обновление будет выглядеть следующим образом:

\theta_{t + 1, i} = \theta_{t, i} - \eta \cdot g_{t, i}

Для адаптивного градиентного спуска вводится сумма квадратов обновлений G_t для каждого параметра модели:

G_t = G_t + g_t^2

В данном случае G_t - это диагональная матрица, где каждый элемент на позиции i,i - сумма квадратов градиентов для i-го параметра.

Перепишем формулу для обновления i-го параметра \theta:

\theta_{t + 1, i} = \theta_{t, i} - \frac {\eta}{\sqrt{G_{t, ii} + \epsilon}} \cdot g_{t, i}

где

\epsilon - сглаживающий параметр, необходимый, чтобы избежать деления на 0 (обычно принимается 1e-8).

В векторной форме (с использованием операции матричного умножения \odot):

\theta_{t + 1} = \theta_t - \frac {\eta}{\sqrt{G_t + \epsilon}} \odot g_t

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

Инициализация

def __init__(self, learnRate=1e-3, epsilon=1e-8, nodeinfo=None):

Параметры

Параметр Возможные типы Описание По умолчанию
learnRate float Скорость обучения 1e-3
epsilon float Сглаживающий параметр 1e-8
nodeinfo NodeInfo Объект, содержащий информацию о вычислительном узле None

Пояснения

-

Примеры


Необходимые импорты:

import numpy as np
from PuzzleLib.Optimizers import AdaGrad
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 = AdaGrad(learnRate=0.01)

Пусть уже есть некоторая сеть 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))