12.9 Adadelta

Adadelta yra dar vienas AdaGrad (12.7 skyrelis) variantas. Pagrindinis skirtumas yra tas, kad jis sumažina dydį, kuriuo mokymosi greitis prisitaiko prie koordinačių. Be to, tradiciškai sakoma, kad jis neturi mokymosi greičio, nes pačią pokyčio apimtį naudoja kaip kalibravimą būsimam pokyčiui. Algoritmą pasiūlė (Zeiler, 2012). Jis gana paprastas, turint omenyje iki šiol aptartus ankstesnius algoritmus.

12.9.1 Algoritmas

Trumpai tariant, Adadelta naudoja du būsenos kintamuosius: st\mathbf{s}_t gradiento antrojo momento nutekėjusiam vidurkiui saugoti ir Δxt\Delta\mathbf{x}_t paties modelio parametrų pokyčio antrojo momento nutekėjusiam vidurkiui saugoti. Atkreipkite dėmesį, kad naudojame originalius autorių žymėjimus ir pavadinimus suderinamumui su kitomis publikacijomis ir įgyvendinimais (nėra jokios kitos tikros priežasties, kodėl reikėtų naudoti skirtingus graikiškus kintamuosius tą patį tikslą atliekančiam parametrui žymėti momentų, Adagrad, RMSProp ir Adadelta metoduose).

Štai techninės Adadelta detalės. Tarkime, kad dienos parametras yra ρ\rho; gauname šiuos nutekėjusius atnaujinimus, panašiai kaip 12.8 skyrelis:

st=ρst−1+(1−ρ)gt2.\begin{aligned} \mathbf{s}_t & = \rho \mathbf{s}_{t-1} + (1 - \rho) \mathbf{g}_t^2. \end{aligned}

Skirtumas nuo 12.8 skyrelis yra tas, kad atnaujinimus atliekame su pakartotinai sumastelintu gradientu gt′\mathbf{g}_t', t. y.

xt=xt−1−gt′.\begin{aligned} \mathbf{x}_t & = \mathbf{x}_{t-1} - \mathbf{g}_t'. \\ \end{aligned}

Taigi kas yra pakartotinai sumastelintas gradientas gt′\mathbf{g}_t'? Jį galime apskaičiuoti taip:

gt′=Δxt−1+ϵst+ϵ⊙gt,\begin{aligned} \mathbf{g}_t' & = \frac{\sqrt{\Delta\mathbf{x}_{t-1} + \epsilon}}{\sqrt{{\mathbf{s}_t + \epsilon}}} \odot \mathbf{g}_t, \\ \end{aligned}

kur Δxt−1\Delta \mathbf{x}_{t-1} yra pakartotinai sumastelintų gradientų gt′\mathbf{g}_t' kvadratų nutekėjęs vidurkis. Δx0\Delta \mathbf{x}_{0} inicializuojame 00 ir atnaujiname jį kiekviename žingsnyje su gt′\mathbf{g}_t', t. y.

Δxt=ρΔxt−1+(1−ρ)gt′2,\begin{aligned} \Delta \mathbf{x}_t & = \rho \Delta\mathbf{x}_{t-1} + (1 - \rho) {\mathbf{g}_t'}^2, \end{aligned}

o ϵ\epsilon (maža reikšmė, pavyzdžiui, 10−510^{-5}) pridedamas skaitiniam stabilumui išlaikyti.

12.9.2 Įgyvendinimas

Adadelta kiekvienam kintamajam turi išlaikyti du būsenos kintamuosius: st\mathbf{s}_t ir Δxt\Delta\mathbf{x}_t. Taip gaunamas toks įgyvendinimas.

%matplotlib inline
from d2l import mxnet as d2l
from mxnet import np, npx
npx.set_np()

def init_adadelta_states(feature_dim):
    s_w, s_b = d2l.zeros((feature_dim, 1)), d2l.zeros(1)
    delta_w, delta_b = d2l.zeros((feature_dim, 1)), d2l.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):
        # In-place updates via [:]
        s[:] = rho * s + (1 - rho) * np.square(p.grad)
        g = (np.sqrt(delta + eps) / np.sqrt(s + eps)) * p.grad
        p[:] -= g
        delta[:] = rho * delta + (1 - rho) * g * g
%matplotlib inline
from d2l import torch as d2l
import torch

def init_adadelta_states(feature_dim):
    s_w, s_b = d2l.zeros((feature_dim, 1)), d2l.zeros(1)
    delta_w, delta_b = d2l.zeros((feature_dim, 1)), d2l.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_()
%matplotlib inline
from d2l import tensorflow as d2l
import tensorflow as tf

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

def adadelta(params, grads, states, hyperparams):
    rho, eps = hyperparams['rho'], 1e-5
    for p, (s, delta), grad in zip(params, states, grads):
        s[:].assign(rho * s + (1 - rho) * tf.math.square(grad))
        g = (tf.math.sqrt(delta + eps) / tf.math.sqrt(s + eps)) * grad
        p[:].assign(p - g)
        delta[:].assign(rho * delta + (1 - rho) * g * g)

Pasirinkus ρ=0.9\rho = 0.9, kiekvieno parametro atnaujinimo pusėjimo trukmė yra 10. Tai paprastai veikia gana gerai. Gauname tokį elgesį.

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);

Glaustam įgyvendinimui tiesiog naudojame Adadelta algoritmą iš aukšto lygio API. Taip gauname šią vienos eilutės komandą daug kompaktiškesniam iškvietimui.

d2l.train_concise_ch11('adadelta', {'rho': 0.9}, data_iter)
trainer = torch.optim.Adadelta
d2l.train_concise_ch11(trainer, {'rho': 0.9}, data_iter)
# adadelta is not converging at default learning rate
# but it is converging at lr = 5.0
trainer = tf.keras.optimizers.Adadelta
d2l.train_concise_ch11(trainer, {'learning_rate':5.0, 'rho': 0.9}, data_iter)

12.9.3 Santrauka

  • Adadelta neturi mokymosi greičio parametro. Vietoj to mokymosi greičiui adaptuoti jis naudoja paties parametrų pokyčio spartą.
  • Adadelta reikia dviejų būsenos kintamųjų gradiento antriesiems momentams ir parametrų pokyčiui saugoti.
  • Adadelta naudoja nutekėjusiu vidurkius, kad palaikytų einamąjį atitinkamos statistikos įvertį.

12.9.4 Pratimai

  1. Keiskite ρ\rho reikšmę. Kas nutinka?
  2. Parodykite, kaip įgyvendinti algoritmą nenaudojant gt′\mathbf{g}_t'. Kodėl tai galėtų būti gera idėja?
  3. Ar Adadelta tikrai neturi mokymosi greičio? Ar galite rasti optimizavimo uždavinių, kurie sugadina Adadelta?
  4. Palyginkite Adadelta su Adagrad ir RMSProp ir aptarkite jų konvergavimo elgesį.

Diskusijos

Diskusijos

Diskusijos