Optimização e Deep Learning
===========================
Nesta seção, discutiremos a relação entre a otimização e o aprendizado
profundo, bem como os desafios de usar a otimização no aprendizado
profundo. Para um problema de aprendizado profundo, geralmente
definiremos uma *função de perda* primeiro. Uma vez que temos a função
de perda, podemos usar um algoritmo de otimização na tentativa de
minimizar a perda. Na otimização, uma função de perda é frequentemente
referida como a *função objetivo* do problema de otimização. Por
tradição e convenção, a maioria dos algoritmos de otimização se preocupa
com a *minimização*. Se alguma vez precisarmos maximizar um objetivo, há
uma solução simples: basta virar o sinal no objetivo.
Objetivos da Optimização
------------------------
Embora a otimização forneça uma maneira de minimizar a função de perda
para profundas aprendizagem, em essência, as metas de otimização e
aprendizagem profunda são fundamentalmente diferente. O primeiro se
preocupa principalmente em minimizar um objetivo, enquanto o último está
preocupado em encontrar um modelo adequado, dado um quantidade finita de
dados. Em :numref: ``sec_model_selection``, discutimos a diferença entre
esses dois objetivos em detalhes. Por exemplo, erro de treinamento e
erro de generalização geralmente diferem: uma vez que o objetivo função
do algoritmo de otimização é geralmente uma função de perda com base no
conjunto de dados de treinamento, o objetivo da otimização é reduzir o
erro de treinamento. No entanto, o objetivo do aprendizado profundo (ou
mais amplamente, inferência estatística) é reduzir o erro de
generalização. Para realizar o último, precisamos pagar atenção ao
*overfitting*, além de usar o algoritmo de otimização para reduzir o
erro de treinamento.
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
from mpl_toolkits import mplot3d
from mxnet import np, npx
from d2l import mxnet as d2l
npx.set_np()
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import numpy as np
import torch
from mpl_toolkits import mplot3d
from d2l import torch as d2l
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import numpy as np
import tensorflow as tf
from mpl_toolkits import mplot3d
from d2l import tensorflow as d2l
.. raw:: html
.. raw:: html
Para ilustrar os diferentes objetivos mencionados acima, deixe-nos
considerar o risco empírico e o risco. Conforme descrito in :numref:
``subsec_empirical-risk-and-risk``, o risco empírico é uma perda média
no conjunto de dados de treinamento enquanto o risco é a perda esperada
em toda a população de dados. Abaixo, definimos duas funções: a função
de risco ``f`` e a função de risco empírica ``g``. Suponha que tenhamos
apenas uma quantidade finita de dados de treinamento. Como resultado,
aqui ``g`` é menos suave do que ``f``.
.. raw:: html
.. raw:: html
.. code:: python
def f(x):
return x * np.cos(np.pi * x)
def g(x):
return f(x) + 0.2 * np.cos(5 * np.pi * x)
.. raw:: html
.. raw:: html
.. code:: python
def f(x):
return x * torch.cos(np.pi * x)
def g(x):
return f(x) + 0.2 * torch.cos(5 * np.pi * x)
.. raw:: html
.. raw:: html
.. code:: python
def f(x):
return x * tf.cos(np.pi * x)
def g(x):
return f(x) + 0.2 * tf.cos(5 * np.pi * x)
.. raw:: html
.. raw:: html
O gráfico abaixo ilustra que o mínimo do risco empírico em um conjunto
de dados de treinamento pode estar em um local diferente do mínimo do
risco (erro de generalização).
.. raw:: html
.. raw:: html
.. code:: python
def annotate(text, xy, xytext): #@save
d2l.plt.gca().annotate(text, xy=xy, xytext=xytext,
arrowprops=dict(arrowstyle='->'))
x = np.arange(0.5, 1.5, 0.01)
d2l.set_figsize((4.5, 2.5))
d2l.plot(x, [f(x), g(x)], 'x', 'risk')
annotate('min of\nempirical risk', (1.0, -1.2), (0.5, -1.1))
annotate('min of risk', (1.1, -1.05), (0.95, -0.5))
.. figure:: output_optimization-intro_70d214_27_0.svg
.. raw:: html
.. raw:: html
.. code:: python
def annotate(text, xy, xytext): #@save
d2l.plt.gca().annotate(text, xy=xy, xytext=xytext,
arrowprops=dict(arrowstyle='->'))
x = torch.arange(0.5, 1.5, 0.01)
d2l.set_figsize((4.5, 2.5))
d2l.plot(x, [f(x), g(x)], 'x', 'risk')
annotate('min of\nempirical risk', (1.0, -1.2), (0.5, -1.1))
annotate('min of risk', (1.1, -1.05), (0.95, -0.5))
.. figure:: output_optimization-intro_70d214_30_0.svg
.. raw:: html
.. raw:: html
.. code:: python
def annotate(text, xy, xytext): #@save
d2l.plt.gca().annotate(text, xy=xy, xytext=xytext,
arrowprops=dict(arrowstyle='->'))
x = tf.range(0.5, 1.5, 0.01)
d2l.set_figsize((4.5, 2.5))
d2l.plot(x, [f(x), g(x)], 'x', 'risk')
annotate('min of\nempirical risk', (1.0, -1.2), (0.5, -1.1))
annotate('min of risk', (1.1, -1.05), (0.95, -0.5))
.. figure:: output_optimization-intro_70d214_33_0.svg
.. raw:: html
.. raw:: html
Desafios de otimização em Deep Learning
---------------------------------------
Neste capítulo, vamos nos concentrar especificamente no desempenho dos
algoritmos de otimização para minimizar a função objetivo, ao invés de
um erro de generalização do modelo. Em :numref:
``sec_linear_regression`` distinguimos entre soluções analíticas e
soluções numéricas em problemas de otimização. No aprendizado profundo,
a maioria das funções objetivas são complicadas e não possuem soluções
analíticas. Em vez disso, devemos usar algoritmos de otimização. Os
algoritmos de otimização neste capítulo todos caem nisso categoria.
Existem muitos desafios na otimização do aprendizado profundo. Alguns
dos mais irritantes são mínimos locais, pontos de sela e gradientes de
desaparecimento. Vamos dar uma olhada neles.
Minimo Local
~~~~~~~~~~~~
Para qualquer função objetivo :math:`f(x)`, se o valor de :math:`f(x)`
em :math:`x` for menor do que os valores de :math:`f(x)` em quaisquer
outros pontos nas proximidades de :math:`x`, então :math:`f(x)` poderia
ser um mínimo local . Se o valor de :math:`f(x)` em :math:`x` é o mínimo
da função objetivo em todo o domínio, então :math:`f(x)` é o mínimo
global.
Por exemplo, dada a função
.. math:: f(x) = x \cdot \text{cos}(\pi x) \text{ for } -1.0 \leq x \leq 2.0,
podemos aproximar o mínimo local e o mínimo global desta função.
.. raw:: html
.. raw:: html
.. code:: python
x = np.arange(-1.0, 2.0, 0.01)
d2l.plot(x, [f(x), ], 'x', 'f(x)')
annotate('local minimum', (-0.3, -0.25), (-0.77, -1.0))
annotate('global minimum', (1.1, -0.95), (0.6, 0.8))
.. figure:: output_optimization-intro_70d214_39_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x = torch.arange(-1.0, 2.0, 0.01)
d2l.plot(x, [f(x), ], 'x', 'f(x)')
annotate('local minimum', (-0.3, -0.25), (-0.77, -1.0))
annotate('global minimum', (1.1, -0.95), (0.6, 0.8))
.. figure:: output_optimization-intro_70d214_42_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x = tf.range(-1.0, 2.0, 0.01)
d2l.plot(x, [f(x), ], 'x', 'f(x)')
annotate('local minimum', (-0.3, -0.25), (-0.77, -1.0))
annotate('global minimum', (1.1, -0.95), (0.6, 0.8))
.. figure:: output_optimization-intro_70d214_45_0.svg
.. raw:: html
.. raw:: html
A função objetivo dos modelos de aprendizado profundo geralmente tem
muitos ótimos locais. Quando a solução numérica de um problema de
otimização está próxima do ótimo local, a solução numérica obtida pela
iteração final pode apenas minimizar a função objetivo *localmente*, ao
invés de *globalmente*, conforme o gradiente das soluções da função
objetivo se aproxima ou torna-se zero . Apenas algum grau de ruído pode
derrubar o parâmetro do mínimo local. Na verdade, esta é uma das
propriedades benéficas de descida gradiente estocástica do minibatch
onde a variação natural dos gradientes sobre os minibatches é capaz de
deslocar os parâmetros dos mínimos locais.
Pontos de Sela
~~~~~~~~~~~~~~
Além dos mínimos locais, os pontos de sela são outra razão para o
desaparecimento dos gradientes. Um \* ponto de sela \* é qualquer local
onde todos os gradientes de uma função desaparecem, mas que não é um
mínimo global nem local. Considere a função :math:`f(x) = x^3`. Sua
primeira e segunda derivadas desaparecem para :math:`x=0`. A otimização
pode parar neste ponto, embora não seja o mínimo.
.. raw:: html
.. raw:: html
.. code:: python
x = np.arange(-2.0, 2.0, 0.01)
d2l.plot(x, [x**3], 'x', 'f(x)')
annotate('saddle point', (0, -0.2), (-0.52, -5.0))
.. figure:: output_optimization-intro_70d214_51_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x = torch.arange(-2.0, 2.0, 0.01)
d2l.plot(x, [x**3], 'x', 'f(x)')
annotate('saddle point', (0, -0.2), (-0.52, -5.0))
.. figure:: output_optimization-intro_70d214_54_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x = tf.range(-2.0, 2.0, 0.01)
d2l.plot(x, [x**3], 'x', 'f(x)')
annotate('saddle point', (0, -0.2), (-0.52, -5.0))
.. figure:: output_optimization-intro_70d214_57_0.svg
.. raw:: html
.. raw:: html
Os pontos de sela em dimensões mais altas são ainda mais insidiosos,
como mostra o exemplo abaixo. Considere a função
:math:`f(x, y) = x^2 - y^2`. Ele tem seu ponto de sela em
:math:`(0, 0)`. Este é um máximo em relação a :math:`y` e um mínimo em
relação a :math:`x`. Além disso, *se parece* com uma sela, que é onde
essa propriedade matemática recebeu seu nome.
.. raw:: html
.. raw:: html
.. code:: python
x, y = np.meshgrid(
np.linspace(-1.0, 1.0, 101), np.linspace(-1.0, 1.0, 101))
z = x**2 - y**2
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z, **{'rstride': 10, 'cstride': 10})
ax.plot([0], [0], [0], 'rx')
ticks = [-1, 0, 1]
d2l.plt.xticks(ticks)
d2l.plt.yticks(ticks)
ax.set_zticks(ticks)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y');
.. figure:: output_optimization-intro_70d214_63_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x, y = torch.meshgrid(
torch.linspace(-1.0, 1.0, 101), torch.linspace(-1.0, 1.0, 101))
z = x**2 - y**2
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z, **{'rstride': 10, 'cstride': 10})
ax.plot([0], [0], [0], 'rx')
ticks = [-1, 0, 1]
d2l.plt.xticks(ticks)
d2l.plt.yticks(ticks)
ax.set_zticks(ticks)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y');
.. figure:: output_optimization-intro_70d214_66_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x, y = tf.meshgrid(
tf.linspace(-1.0, 1.0, 101), tf.linspace(-1.0, 1.0, 101))
z = x**2 - y**2
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z, **{'rstride': 10, 'cstride': 10})
ax.plot([0], [0], [0], 'rx')
ticks = [-1, 0, 1]
d2l.plt.xticks(ticks)
d2l.plt.yticks(ticks)
ax.set_zticks(ticks)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y');
.. figure:: output_optimization-intro_70d214_69_0.svg
.. raw:: html
.. raw:: html
Assumimos que a entrada de uma função é um vetor :math:`k` -dimensional
e seu a saída é um escalar, então sua matriz Hessiana terá :math:`k`
autovalores (consulte o `apêndice online sobre
eigendecompositions `__).
A solução do função pode ser um mínimo local, um máximo local ou um
ponto de sela em um posição onde o gradiente da função é zero:
- Quando os autovalores da matriz Hessiana da função na posição do
gradiente zero são todos positivos, temos um mínimo local para a
função.
- Quando os valores próprios da matriz Hessiana da função na posição do
gradiente zero são todos negativos, temos um máximo local para a
função.
- Quando os valores próprios da matriz Hessiana da função na posição do
gradiente zero são negativos e positivos, temos um ponto de sela para
a função.
Para problemas de alta dimensão, a probabilidade de que pelo menos \*
alguns \* dos autovalores sejam negativos é bastante alta. Isso torna os
pontos de sela mais prováveis do que os mínimos locais. Discutiremos
algumas exceções a essa situação na próxima seção, ao introduzir a
convexidade. Em suma, funções convexas são aquelas em que os autovalores
do Hessiano nunca são negativos. Infelizmente, porém, a maioria dos
problemas de aprendizado profundo não se enquadra nessa categoria. No
entanto, é uma ótima ferramenta para estudar algoritmos de otimização.
Gradiente de Desaparecimento
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provavelmente, o problema mais insidioso a ser encontrado é o gradiente
de desaparecimento. Lembre-se de nossas funções de ativação comumente
usadas e seus derivados em: numref: ``subsec_activation-functions``. Por
exemplo, suponha que queremos minimizar a função :math:`f(x) = \tanh(x)`
e começamos em :math:`x = 4`. Como podemos ver, o gradiente de :math:`f`
é quase nulo. Mais especificamente, :math:`f'(x) = 1 - \tanh^2(x)` e
portanto :math:`f'(4) = 0.0013`. Conseqüentemente, a otimização ficará
parada por um longo tempo antes de progredirmos. Isso acabou sendo um
dos motivos pelos quais treinar modelos de aprendizado profundo era
bastante complicado antes da introdução da função de ativação ReLU.
.. raw:: html
.. raw:: html
.. code:: python
x = np.arange(-2.0, 5.0, 0.01)
d2l.plot(x, [np.tanh(x)], 'x', 'f(x)')
annotate('vanishing gradient', (4, 1), (2, 0.0))
.. figure:: output_optimization-intro_70d214_75_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x = torch.arange(-2.0, 5.0, 0.01)
d2l.plot(x, [torch.tanh(x)], 'x', 'f(x)')
annotate('vanishing gradient', (4, 1), (2, 0.0))
.. figure:: output_optimization-intro_70d214_78_0.svg
.. raw:: html
.. raw:: html
.. code:: python
x = tf.range(-2.0, 5.0, 0.01)
d2l.plot(x, [tf.tanh(x)], 'x', 'f(x)')
annotate('vanishing gradient', (4, 1), (2, 0.0))
.. figure:: output_optimization-intro_70d214_81_0.svg
.. raw:: html
.. raw:: html
Como vimos, a otimização para aprendizado profundo está cheia de
desafios. Felizmente, existe uma gama robusta de algoritmos que
funcionam bem e são fáceis de usar, mesmo para iniciantes. Além disso,
não é realmente necessário encontrar a melhor solução. Ótimos locais ou
mesmo soluções aproximadas deles ainda são muito úteis.
Sumário
-------
- Minimizar o erro de treinamento *não* garante que encontraremos o
melhor conjunto de parâmetros para minimizar o erro de generalização.
- Os problemas de otimização podem ter muitos mínimos locais.
- O problema pode ter ainda mais pontos de sela, pois geralmente os
problemas não são convexos.
- O desaparecimento de gradientes pode causar o travamento da
otimização. Frequentemente, uma reparametrização do problema ajuda.
Uma boa inicialização dos parâmetros também pode ser benéfica.
Exercícios
----------
1. Considere um MLP simples com uma única camada oculta de, digamos,
:math:`d` dimensões na camada oculta e uma única saída. Mostre que,
para qualquer mínimo local, existem pelo menos :math:`d!` Soluções
equivalentes que se comportam de forma idêntica.
2. Suponha que temos uma matriz aleatória simétrica :math:`\mathbf{M}`
onde as entradas :math:`M_{ij} = M_{ji}` são, cada um, extraídos de
alguma distribuição de probabilidade :math:`p_{ij}`. Além disso,
assuma que :math:`p_{ij}(x) = p_{ij}(-x)`, ou seja, que o a
distribuição é simétrica (consulte, por exemplo :cite:`Wigner.1958`
para obter detalhes).
1. Prove que a distribuição sobre os autovalores também é simétrica.
Ou seja, para qualquer autovetor :math:`\mathbf{v}` a
probabilidade de que o autovalor associado :math:`\lambda`
satisfaça :math:`P(\lambda > 0) = P(\lambda < 0)`.
2. Por que o acima *não* implica :math:`P(\lambda > 0) = 0.5`?
3. Em que outros desafios envolvidos na otimização do aprendizado
profundo você consegue pensar?
4. Suponha que você deseja equilibrar uma bola (real) em uma sela
(real).
1. Por que isso é difícil?
2. Você pode explorar esse efeito também para algoritmos de
otimização?
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
`Discussão `__
.. raw:: html
.. raw:: html
.. raw:: html