Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 применяет этот приём, чтобы настроить покоординатный предобусловливатель.
  • В экспериментах скорость обучения должна подбираться экспериментатором.
  • Коэффициент определяет, насколько долго учитывается история при настройке масштаба для каждой координаты.

Упражнения

  1. Что произойдёт в эксперименте, если задать ? Почему?
  2. Поверните задачу оптимизации так, чтобы минимизировать . Что произойдёт со сходимостью?
  3. Попробуйте применить RMSProp к настоящей задаче машинного обучения, например, к обучению на Fashion-MNIST. Подберите скорость обучения, перебирая разные значения.
  4. Нужно ли корректировать по ходу оптимизации? Насколько чувствителен 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.