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:
Отличие от Section 12.8 заключается в том, что мы выполняем обновления с помощью перемасштабированного градиента \(\mathbf{g}_t'\), то есть
Так что же такое пересчитанный градиент \(\mathbf{g}_t'\)? Мы можем вычислить его следующим образом:
где \(\Delta \mathbf{x}_{t-1}\) — это затухающее среднее квадратов пересчитанных градиентов \(\mathbf{g}_t'\). Мы инициализируем \(\Delta \mathbf{x}_{0}\) значением \(0\) и обновляем его на каждом шаге с помощью \(\mathbf{g}_t'\), то есть
и \(\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. Упражнения¶
Измените значение \(\rho\). Что произойдет?
Покажите, как реализовать алгоритм без использования \(\mathbf{g}_t'\). Почему это может быть хорошей идеей?
Действительно ли Adadelta не зависит от скорости обучения? Можете ли вы найти задачи оптимизации, в которых Adadelta дает сбой?
Сравните Adadelta с Adagrad и RMSprop, чтобы обсудить их поведение сходимости.