12.9. Adadelta

Adadelta — это ещё один вариант AdaGrad (Section 12.7). Главное отличие заключается в том, что он уменьшает величину, на которую скорость обучения адаптируется к координатам. Кроме того, традиционно считается, что этот алгоритм не имеет скорости обучения, поскольку он использует величину изменения в качестве калибровки для будущих изменений. Алгоритм был предложен в работе Zeiler (2012). Он достаточно прост, учитывая обсуждение предыдущих алгоритмов.

12.9.1. Алгоритм

Короче говоря, Adadelta использует две переменные состояния: \(\mathbf{s}_t\) для хранения затухающего среднего второго момента градиента и \(\Delta\mathbf{x}_t\) для хранения затухающего среднего второго момента изменения параметров в самой модели. Обратите внимание, что мы используем оригинальную нотацию и обозначения авторов для совместимости с другими публикациями и реализациями (нет никаких иных причин использовать разные греческие буквы для обозначения параметров, выполняющих одну и ту же функцию в импульсе, Adagrad, RMSProp и Adadelta).

Ниже приведены технические детали алгоритма Adadelta. Если текущим параметром является \(\rho\), мы получаем следующие частичные обновления, аналогично Section 12.8:

(12.9.1)\[\begin{aligned} \mathbf{s}_t & = \rho \mathbf{s}_{t-1} + (1 - \rho) \mathbf{g}_t^2. \end{aligned}\]

Отличие от Section 12.8 заключается в том, что мы выполняем обновления с помощью перемасштабированного градиента \(\mathbf{g}_t'\), то есть

(12.9.2)\[\begin{split}\begin{aligned} \mathbf{x}_t & = \mathbf{x}_{t-1} - \mathbf{g}_t'. \\ \end{aligned}\end{split}\]

Так что же такое пересчитанный градиент \(\mathbf{g}_t'\)? Мы можем вычислить его следующим образом:

(12.9.3)\[\begin{split}\begin{aligned} \mathbf{g}_t' & = \frac{\sqrt{\Delta\mathbf{x}_{t-1} + \epsilon}}{\sqrt{{\mathbf{s}_t + \epsilon}}} \odot \mathbf{g}_t, \\ \end{aligned}\end{split}\]

где \(\Delta \mathbf{x}_{t-1}\) — это затухающее среднее квадратов пересчитанных градиентов \(\mathbf{g}_t'\). Мы инициализируем \(\Delta \mathbf{x}_{0}\) значением \(0\) и обновляем его на каждом шаге с помощью \(\mathbf{g}_t'\), то есть

(12.9.4)\[\begin{aligned} \Delta \mathbf{x}_t & = \rho \Delta\mathbf{x}_{t-1} + (1 - \rho) {\mathbf{g}_t'}^2, \end{aligned}\]

и \(\epsilon\) (небольшое значение, например, \(10^{-5}\)) добавляется для обеспечения численной устойчивости.

12.9.2. Реализация

Adadelta требует хранения двух переменных состояния для каждой переменной, \(\mathbf{s}_t\) и \(\Delta\mathbf{x}_t\). Это приводит к следующей реализации.

%matplotlib inline
import torch
from d2l import torch as d2l


def init_adadelta_states(feature_dim):
    s_w, s_b = torch.zeros((feature_dim, 1)), torch.zeros(1)
    delta_w, delta_b = torch.zeros((feature_dim, 1)), torch.zeros(1)
    return ((s_w, delta_w), (s_b, delta_b))

def adadelta(params, states, hyperparams):
    rho, eps = hyperparams['rho'], 1e-5
    for p, (s, delta) in zip(params, states):
        with torch.no_grad():
            # In-place updates via [:]
            s[:] = rho * s + (1 - rho) * torch.square(p.grad)
            g = (torch.sqrt(delta + eps) / torch.sqrt(s + eps)) * p.grad
            p[:] -= g
            delta[:] = rho * delta + (1 - rho) * g * g
        p.grad.data.zero_()

Выбор \(\rho = 0.9\) соответствует времени полураспада, равному 10, для каждого обновления параметра. Это обычно работает довольно хорошо. Мы получаем следующее поведение.

data_iter, feature_dim = d2l.get_data_ch11(batch_size=10)
d2l.train_ch11(adadelta, init_adadelta_states(feature_dim),
               {'rho': 0.9}, data_iter, feature_dim);

Для лаконичной реализации мы просто используем алгоритм Adadelta из высокоуровневых API. Это позволяет получить следующую однострочную запись, которая значительно компактнее.

trainer = torch.optim.Adadelta
d2l.train_concise_ch11(trainer, {'rho': 0.9}, data_iter)

12.9.3. Итоги

  • У Adadelta отсутствует параметр скорости обучения. Вместо этого он использует скорость изменения самих параметров для адаптации скорости обучения.

  • Adadelta требует две переменные состояния для хранения вторых моментов градиента и изменения параметров.

  • Adadelta использует затухающие средние для поддержания текущей оценки соответствующих статистик.

12.9.4. Упражнения

  1. Измените значение \(\rho\). Что произойдет?

  2. Покажите, как реализовать алгоритм без использования \(\mathbf{g}_t'\). Почему это может быть хорошей идеей?

  3. Действительно ли Adadelta не зависит от скорости обучения? Можете ли вы найти задачи оптимизации, в которых Adadelta дает сбой?

  4. Сравните Adadelta с Adagrad и RMSprop, чтобы обсудить их поведение сходимости.

Обсуждения