AdaGrad¶
Описание¶
Данный модуль реализует принцип работы адаптивного градиентного спуска (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)} $$
Далее для краткости опустим усреднение суммы и произведём замену:
Тогда для i-го параметра \theta обновление будет выглядеть следующим образом:
Для адаптивного градиентного спуска вводится сумма квадратов обновлений G_t для каждого параметра модели:
В данном случае G_t - это диагональная матрица, где каждый элемент на позиции i,i - сумма квадратов градиентов для i-го параметра.
Перепишем формулу для обновления i-го параметра \theta:
где
\epsilon - сглаживающий параметр, необходимый, чтобы избежать деления на 0 (обычно принимается 1e-8).
В векторной форме (с использованием операции матричного умножения \odot):
Главный недостаток алгоритма заключается в том, что при его работе происходит постоянное накопление квадратов градиентов в знаменателе, т.к. каждый новый добавляющийся член положителен. Это, в свою очередь, приводит к тому, что коэффициент обучения для некоторых признаков становится настолько малым, что алгоритм больше не способен продолжать дополнительные исследования поверхности целевой функции. В качестве решения проблемы были предложены 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))