.. _sec_adadelta: Adadelta ======== Adadelta é outra variante do AdaGrad (:numref:`sec_adagrad`). A principal diferença reside no fato de que diminui a quantidade pela qual a taxa de aprendizagem é adaptável às coordenadas. Além disso, tradicionalmente é referido como não tendo uma taxa de aprendizagem, uma vez que usa a quantidade de mudança em si como calibração para mudanças futuras. O algoritmo foi proposto em :cite:`Zeiler.2012`. É bastante simples, dada a discussão de algoritmos anteriores até agora. O Algoritmo ----------- Em poucas palavras, Adadelta usa duas variáveis de estado, :math:`\mathbf{s}_t` para armazenar uma média de vazamento do segundo momento do gradiente e :math:`\Delta\mathbf{x}_t` para armazenar uma média de vazamento do segundo momento da mudança de parâmetros no próprio modelo. Observe que usamos a notação original e a nomenclatura dos autores para compatibilidade com outras publicações e implementações (não há outra razão real para usar variáveis gregas diferentes para indicar um parâmetro que serve ao mesmo propósito em momentum, Adagrad, RMSProp e Adadelta) Aqui estão os detalhes técnicos do Adadelta. Dado que o parâmetro du jour é :math:`\rho`, obtemos as seguintes atualizações vazadas de forma semelhante a :numref:`sec_rmsprop`: .. math:: \begin{aligned} \mathbf{s}_t & = \rho \mathbf{s}_{t-1} + (1 - \rho) \mathbf{g}_t^2. \end{aligned} A diferença para :numref:`sec_rmsprop` é que realizamos atualizações com o gradiente redimensionado :math:`\mathbf{g}_t'`, ou seja, .. math:: \begin{aligned} \mathbf{x}_t & = \mathbf{x}_{t-1} - \mathbf{g}_t'. \\ \end{aligned} Então, qual é o gradiente redimensionado :math:`\mathbf{g}_t'`? Podemos calculá-lo da seguinte maneira: .. math:: \begin{aligned} \mathbf{g}_t' & = \frac{\sqrt{\Delta\mathbf{x}_{t-1} + \epsilon}}{\sqrt{{\mathbf{s}_t + \epsilon}}} \odot \mathbf{g}_t, \\ \end{aligned} onde :math:`\Delta \mathbf{x}_{t-1}` é a média de vazamento dos gradientes redimensionados ao quadrado :math:`\mathbf{g}_t'`. Inicializamos :math:`\Delta \mathbf{x}_{0}` para ser :math:`0` e atualizamos em cada etapa com :math:`\mathbf{g}_t'`, ou seja, .. math:: \begin{aligned} \Delta \mathbf{x}_t & = \rho \Delta\mathbf{x}_{t-1} + (1 - \rho) {\mathbf{g}_t'}^2, \end{aligned} e :math:`\epsilon` (um pequeno valor como :math:`10^{-5}`) é adicionado para manter a estabilidade numérica. Implementação ------------- Adadelta precisa manter duas variáveis de estado para cada variável, :math:`\mathbf{s}_t` e :math:`\Delta\mathbf{x}_t`. Isso produz a seguinte implementação. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python %matplotlib inline from mxnet import np, npx from d2l import mxnet as d2l npx.set_np() def init_adadelta_states(feature_dim): s_w, s_b = np.zeros((feature_dim, 1)), np.zeros(1) delta_w, delta_b = np.zeros((feature_dim, 1)), np.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 .. raw:: html
.. raw:: html
.. code:: python %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_() .. raw:: html
.. raw:: html
.. code:: python %matplotlib inline import tensorflow as tf from d2l import tensorflow as d2l def init_adadelta_states(feature_dim): s_w = tf.Variable(tf.zeros((feature_dim, 1))) s_b = tf.Variable(tf.zeros(1)) delta_w = tf.Variable(tf.zeros((feature_dim, 1))) delta_b = tf.Variable(tf.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) .. raw:: html
.. raw:: html
Escolher :math:`\rho = 0,9` equivale a um tempo de meia-vida de 10 para cada atualização de parâmetro. Isso tende a funcionar muito bem. Obtemos o seguinte comportamento. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python 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); .. parsed-literal:: :class: output loss: 0.244, 0.219 sec/epoch .. figure:: output_adadelta_0b41cb_15_1.svg .. raw:: html
.. raw:: html
.. code:: python 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); .. parsed-literal:: :class: output loss: 0.244, 0.015 sec/epoch .. figure:: output_adadelta_0b41cb_18_1.svg .. raw:: html
.. raw:: html
.. code:: python 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); .. parsed-literal:: :class: output loss: 0.244, 0.107 sec/epoch .. figure:: output_adadelta_0b41cb_21_1.svg .. raw:: html
.. raw:: html
Para uma implementação concisa, simplesmente usamos o algoritmo ``adadelta`` da classe ``Trainer``. Isso produz o seguinte one-liner para uma invocação muito mais compacta. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
.. code:: python d2l.train_concise_ch11('adadelta', {'rho': 0.9}, data_iter) .. parsed-literal:: :class: output loss: 0.245, 0.216 sec/epoch .. figure:: output_adadelta_0b41cb_27_1.svg .. raw:: html
.. raw:: html
.. code:: python trainer = torch.optim.Adadelta d2l.train_concise_ch11(trainer, {'rho': 0.9}, data_iter) .. parsed-literal:: :class: output loss: 0.243, 0.013 sec/epoch .. figure:: output_adadelta_0b41cb_30_1.svg .. raw:: html
.. raw:: html
.. code:: python # adadelta is not converging at default learning rate # but it's converging at lr = 5.0 trainer = tf.keras.optimizers.Adadelta d2l.train_concise_ch11(trainer, {'learning_rate':5.0, 'rho': 0.9}, data_iter) .. parsed-literal:: :class: output loss: 0.244, 0.076 sec/epoch .. figure:: output_adadelta_0b41cb_33_1.svg .. raw:: html
.. raw:: html
Sumário ------- - Adadelta não tem parâmetro de taxa de aprendizagem. Em vez disso, ele usa a taxa de mudança nos próprios parâmetros para adaptar a taxa de aprendizado. - Adadelta requer duas variáveis de estado para armazenar os segundos momentos de gradiente e a mudança nos parâmetros. - Adadelta usa médias vazadas para manter uma estimativa contínua das estatísticas apropriadas. Exercícios ---------- 1. Ajuste o valor de :math:`\rho`. O que acontece? 2. Mostre como implementar o algoritmo sem o uso de :math:`\mathbf{g}_t'`. Por que isso pode ser uma boa ideia? 3. A taxa de aprendizagem Adadelta é realmente gratuita? Você conseguiu encontrar problemas de otimização que quebram o Adadelta? 4. Compare Adadelta com Adagrad e RMS prop para discutir seu comportamento de convergência. .. raw:: html
mxnetpytorchtensorflow
.. raw:: html
`Discussão `__ .. raw:: html
.. raw:: html
`Discussão `__ .. raw:: html
.. raw:: html
`Discussão `__ .. raw:: html
.. raw:: html
.. raw:: html