11.8 Алгоритм RMSProp
Одна из ключевых проблем алгоритма AdaGrad из раздела 11.7 состоит в том, что скорость обучения уменьшается по заранее заданному графику . Хотя так обычно и делают для выпуклых задач, для невыпуклых задач, с которыми мы сталкиваемся в глубоком обучении, такое поведение может быть не идеальным. Однако как предобусловливатель покоординатная адаптивность AdaGrad очень желательна.
Тилеман и Хинтон (Tieleman and Hinton, 2012) предложили алгоритм RMSProp как простое исправление, которое отделяет расписание скорости обучения от покоординатной адаптации скорости обучения. Проблема в том, что AdaGrad суммирует квадраты градиентов в векторе состояния . Поэтому из-за отсутствия нормировки продолжает расти, примерно линейно, и это происходит и тогда, когда алгоритм уже сходится.
Один из способов решить эту проблему — использовать . Для разумных распределений это значение будет сходиться. К сожалению, эффект ограничения может проявиться не скоро, поскольку эта процедура помнит всю траекторию значений. Другой способ — использовать экспоненциально затухающее среднее, как в методе импульса: , где параметр . Если сохранить всё остальное без изменений, получится алгоритм RMSProp.
11.8.1 Алгоритм
Запишем эти уравнения подробно.
Константа обычно выбирается равной , чтобы не допустить деления на ноль или слишком большого шага. С учётом этого расширения мы теперь можем свободно управлять скоростью обучения , не обращая внимания на масштабирование, применяемое к каждой координате. Что касается экспоненциально затухающего среднего, можно использовать те же рассуждения, что и для метода импульса. Раскроем определение :
Как и в разделе 11.6.1.3 о методе импульса, мы используем . Поэтому сумма весов нормирована к , а период полураспада наблюдения равен . Изобразим веса для разных значений за последние 40 шагов по времени.
MXNet:
%matplotlib inline
from d2l import mxnet as d2l
import math
from mxnet import np, npx
npx.set_np()
PyTorch:
from d2l import torch as d2l
import torch
import math
TensorFlow:
from d2l import tensorflow as d2l
import tensorflow as tf
import math
PaddlePaddle:
from d2l import paddle as d2l
import warnings
warnings.filterwarnings("ignore")
import paddle
import math
d2l.set_figsize()
gammas = [0.95, 0.9, 0.8, 0.7]
for gamma in gammas:
x = d2l.numpy(d2l.arange(40))
d2l.plt.plot(x, (1-gamma) * gamma ** x, label=f'γ = {gamma:.2f}')
d2l.plt.xlabel('время');
11.8.2 Реализация с нуля
Как и раньше, мы используем квадратичную функцию , чтобы наблюдать за траекторией алгоритма RMSProp. Вспомним, что в разделе 11.7 при использовании AdaGrad с коэффициентом скорости обучения 0.4 переменная на поздних стадиях алгоритма двигалась очень медленно, поскольку скорость обучения быстро убывала. В RMSProp этого не происходит, потому что задаётся отдельно.
def rmsprop_2d(x1, x2, s1, s2):
g1, g2, eps = 0.2 * x1, 4 * x2, 1e-6
s1 = gamma * s1 + (1 - gamma) * g1 ** 2
s2 = gamma * s2 + (1 - gamma) * g2 ** 2
x1 -= eta / math.sqrt(s1 + eps) * g1
x2 -= eta / math.sqrt(s2 + eps) * g2
return x1, x2, s1, s2
def f_2d(x1, x2):
return 0.1 * x1 ** 2 + 2 * x2 ** 2
eta, gamma = 0.4, 0.9
d2l.show_trace_2d(f_2d, d2l.train_2d(rmsprop_2d))
Далее реализуем алгоритм RMSProp для глубокой сети.
MXNet, PyTorch:
def init_rmsprop_states(feature_dim):
s_w = d2l.zeros((feature_dim, 1))
s_b = d2l.zeros(1)
return (s_w, s_b)
PaddlePaddle:
def init_rmsprop_states(feature_dim):
s_w = d2l.zeros((feature_dim, 1))
s_b = d2l.zeros([1])
return (s_w, s_b)
TensorFlow:
def init_rmsprop_states(feature_dim):
s_w = tf.Variable(d2l.zeros((feature_dim, 1)))
s_b = tf.Variable(d2l.zeros(1))
return (s_w, s_b)
MXNet:
def rmsprop(params, states, hyperparams):
gamma, eps = hyperparams['gamma'], 1e-6
for p, s in zip(params, states):
s[:] = gamma * s + (1 - gamma) * np.square(p.grad)
p[:] -= hyperparams['lr'] * p.grad / np.sqrt(s + eps)
PyTorch:
def rmsprop(params, states, hyperparams):
gamma, eps = hyperparams['gamma'], 1e-6
for p, s in zip(params, states):
with torch.no_grad():
s[:] = gamma * s + (1 - gamma) * torch.square(p.grad)
p[:] -= hyperparams['lr'] * p.grad / torch.sqrt(s + eps)
p.grad.data.zero_()
TensorFlow:
def rmsprop(params, grads, states, hyperparams):
gamma, eps = hyperparams['gamma'], 1e-6
for p, s, g in zip(params, states, grads):
s[:].assign(gamma * s + (1 - gamma) * tf.math.square(g))
p[:].assign(p - hyperparams['lr'] * g / tf.math.sqrt(s + eps))
PaddlePaddle:
def rmsprop(params, states, hyperparams):
a = []
gamma, eps = hyperparams['gamma'], 1e-6
for p, s in zip(params, states):
with paddle.no_grad():
s[:] = gamma * s + (1 - gamma) * paddle.square(p.grad)
p[:] -= hyperparams['lr'] * p.grad / paddle.sqrt(s + eps)
p.grad.zero_()
a.append(p)
return a
Начальную скорость обучения установим равной 0.01, а весовой коэффициент — 0.9. То есть накапливает среднее значение последних наблюдений квадратов градиентов.
data_iter, feature_dim = d2l.get_data_ch11(batch_size=10)
d2l.train_ch11(rmsprop, init_rmsprop_states(feature_dim),
{'lr': 0.01, 'gamma': 0.9}, data_iter, feature_dim);
11.8.3 Краткая реализация
Мы можем использовать алгоритм RMSProp, уже реализованный во фреймворке глубокого обучения, для обучения модели.
d2l.train_concise_ch11('rmsprop', {'learning_rate': 0.01, 'gamma1': 0.9},
data_iter)
PyTorch:
trainer = torch.optim.RMSprop
d2l.train_concise_ch11(trainer, {'lr': 0.01, 'alpha': 0.9},
data_iter)
TensorFlow:
trainer = tf.keras.optimizers.RMSprop
d2l.train_concise_ch11(trainer, {'learning_rate': 0.01, 'rho': 0.9},
data_iter)
PaddlePaddle:
trainer = paddle.optimizer.RMSProp
d2l.train_concise_ch11(trainer, {'learning_rate': 0.01, 'rho': 0.9},
data_iter)
Резюме
- Алгоритм RMSProp очень похож на AdaGrad, поскольку оба используют квадраты градиентов для масштабирования коэффициентов.
- RMSProp и метод импульса используют экспоненциально затухающее среднее. Однако RMSProp применяет этот приём, чтобы настроить покоординатный предобусловливатель.
- В экспериментах скорость обучения должна подбираться экспериментатором.
- Коэффициент определяет, насколько долго учитывается история при настройке масштаба для каждой координаты.
Упражнения
- Что произойдёт в эксперименте, если задать ? Почему?
- Поверните задачу оптимизации так, чтобы минимизировать . Что произойдёт со сходимостью?
- Попробуйте применить RMSProp к настоящей задаче машинного обучения, например, к обучению на Fashion-MNIST. Подберите скорость обучения, перебирая разные значения.
- Нужно ли корректировать по ходу оптимизации? Насколько чувствителен RMSProp к этому параметру?
MXNet: Обсуждение
PyTorch: Обсуждение
TensorFlow: Обсуждение
PaddlePaddle: Обсуждение
Литература
- Tieleman, T., & Hinton, G. (2012). Lecture 6.5-RMSProp: Divide the gradient by a running average of its recent magnitude. COURSERA: Neural Networks for Machine Learning, 4, 26–31.