.. _sec_calculus:
Cálculo
=======
Encontrar a área de um polígono permaneceu um mistério até pelo menos
2.500 anos atrás, quando os gregos antigos dividiam um polígono em
triângulos e somavam suas áreas. Para encontrar a área de formas curvas,
como um círculo, os gregos antigos inscreviam polígonos nessas formas.
Conforme mostrado em :numref: ``fig_circle_area``, um polígono inscrito
com mais lados de igual comprimento se aproxima melhor o circulo. Este
processo também é conhecido como *método de exaustão*.
.. _fig_circle_area:
.. figure:: ../img/polygon-circle.svg
Buscando a área de um círculo através do método de exaustão.
Na verdade, o método de exaustão é de onde o *cálculo integral* (será
descrito em :numref: ``sec_integral_calculus``) se origina. Mais de
2.000 anos depois, o outro ramo do cálculo, *cálculo diferencial*, foi
inventado. Entre as aplicações mais críticas do cálculo diferencial,
problemas de otimização consideram como fazer algo *o melhor*. Conforme
discutido em :numref:`subsec_norms_and_objectives`, tais problemas são
onipresentes em *Deep Learning*.
Em *Deep Learning*, nós *treinamos* modelos, atualizando-os
sucessivamente para que fiquem cada vez melhores à medida que veem mais
e mais dados. Normalmente, melhorar significa minimizar uma *função de
perda*, uma pontuação que responde à pergunta “quão *ruim* é o nosso
modelo?” Esta pergunta é mais sutil do que parece. Em última análise, o
que realmente nos preocupa está produzindo um modelo com bom desempenho
em dados que nunca vimos antes. Mas só podemos ajustar o modelo aos
dados que podemos realmente ver. Assim, podemos decompor a tarefa de
ajustar os modelos em duas preocupações principais: i) *otimização*:
processo de adequação de nossos modelos aos dados observados; ii)
*generalização*: os princípios matemáticos e a sabedoria dos
profissionais que guia sobre como produzir modelos cuja validade se
estende além do conjunto exato de exemplos de dados usados para
treiná-los.
Para te ajudar a entender problemas e métodos de otimização em capítulos
posteriores, aqui, damos uma breve introdução ao cálculo diferencial que
é comumente usado no *Deep Learning*.
Derivadas e Diferenciação
-------------------------
Começamos abordando o cálculo de derivadas, uma etapa crucial em quase
todos os algoritmos de otimização de *Deep Learning*. No *Deep
Learning*, normalmente escolhemos funções de perda que são
diferenciáveis em relação aos parâmetros do nosso modelo. Simplificando,
isso significa que para cada parâmetro, podemos determinar a rapidez com
que a perda aumentaria ou diminuiria, deveríamos *aumentar* ou
*diminuir* esse parâmetro por uma quantidade infinitesimalmente pequena.
Suponha que temos uma função
:math:`f: \mathbb{R} \rightarrow \mathbb{R}`, cuja entrada e saída são
escalares. A *derivada* de :math:`f` é definida como
.. math:: f'(x) = \lim_{h \rightarrow 0} \frac{f(x+h) - f(x)}{h},
:label: eq_derivative
se este limite existe. Se :math:`f'(a)` existe, Diz-se que :math:`f` é
*diferenciável* em :math:`a`. Se :math:`f` é diferenciável a cada número
de um intervalo, então esta função é diferenciável neste intervalo.
Podemos interpretar a derivada :math:`f '(x)` em
:eq:`eq_derivative` como a taxa de variação *instantânea* de
:math:`f (x)` em relação a :math:`x`. A chamada taxa instantânea de
mudança é baseada em a variação :math:`h` em :math:`x`, que se aproxima
de :math:`0`.
Para ilustrar derivadas, vamos experimentar com um exemplo. Define-se
:math:`u = f(x) = 3x^2-4x`.
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
from IPython import display
from mxnet import np, npx
from d2l import mxnet as d2l
npx.set_np()
def f(x):
return 3 * x ** 2 - 4 * x
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import numpy as np
from IPython import display
from d2l import torch as d2l
def f(x):
return 3 * x ** 2 - 4 * x
.. raw:: html
.. raw:: html
.. code:: python
%matplotlib inline
import numpy as np
from IPython import display
from d2l import tensorflow as d2l
def f(x):
return 3 * x ** 2 - 4 * x
.. raw:: html
.. raw:: html
Definindo :math:`x = 1` e deixando :math:`h` se aproximar de :math:`0`,
o resultado numérico de :math:`\frac{f(x+h) - f(x)}{h}` in: eqref:
``eq_derivative`` aproxima-se de :math:`2`. Embora este experimento não
seja uma prova matemática, veremos mais tarde que a derivada :math:`u '`
é :math:`2` quando :math:`x = 1`.
.. raw:: html
.. raw:: html
.. code:: python
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
h *= 0.1
.. parsed-literal::
:class: output
h=0.10000, numerical limit=2.30000
h=0.01000, numerical limit=2.03000
h=0.00100, numerical limit=2.00300
h=0.00010, numerical limit=2.00030
h=0.00001, numerical limit=2.00003
.. raw:: html
.. raw:: html
.. code:: python
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
h *= 0.1
.. parsed-literal::
:class: output
h=0.10000, numerical limit=2.30000
h=0.01000, numerical limit=2.03000
h=0.00100, numerical limit=2.00300
h=0.00010, numerical limit=2.00030
h=0.00001, numerical limit=2.00003
.. raw:: html
.. raw:: html
.. code:: python
def numerical_lim(f, x, h):
return (f(x + h) - f(x)) / h
h = 0.1
for i in range(5):
print(f'h={h:.5f}, numerical limit={numerical_lim(f, 1, h):.5f}')
h *= 0.1
.. parsed-literal::
:class: output
h=0.10000, numerical limit=2.30000
h=0.01000, numerical limit=2.03000
h=0.00100, numerical limit=2.00300
h=0.00010, numerical limit=2.00030
h=0.00001, numerical limit=2.00003
.. raw:: html
.. raw:: html
Vamos nos familiarizar com algumas notações equivalentes para derivadas.
Dado :math:`y = f (x)`, onde :math:`x` e :math:`y` são a variável
independente e a variável dependente da função :math:`f`,
respectivamente. As seguintes expressões são equivalentes:
.. math:: f'(x) = y' = \frac{dy}{dx} = \frac{df}{dx} = \frac{d}{dx} f(x) = Df(x) = D_x f(x),
onde os símbolos :math:`\frac{d}{dx}` e :math:`D` são *operadores de
diferenciação* que indicam operação de *diferenciação*. Podemos usar as
seguintes regras para diferenciar funções comuns:
- :math:`DC = 0` (:math:`C` é uma constante),
- :math:`Dx^n = nx^{n-1}` (uma exponenciação, :math:`n` é qualquer
valor real),
- :math:`De^x = e^x`,
- :math:`D\ln(x) = 1/x.`
Para diferenciar uma função que é formada de algumas funções mais
simples, como as funções comuns acima, as regras a seguir podem ser
úteis para nós. Suponha que as funções :math:`f` e :math:`g` sejam
diferenciáveis e :math:`C` seja uma constante, temos a *regra múltipla
constante*
.. math:: \frac{d}{dx} [Cf(x)] = C \frac{d}{dx} f(x),
a *regra da soma*
.. math:: \frac{d}{dx} [f(x) + g(x)] = \frac{d}{dx} f(x) + \frac{d}{dx} g(x),
a *regra do produto*
.. math:: \frac{d}{dx} [f(x)g(x)] = f(x) \frac{d}{dx} [g(x)] + g(x) \frac{d}{dx} [f(x)],
e a *regra do quociente*
.. math:: \frac{d}{dx} \left[\frac{f(x)}{g(x)}\right] = \frac{g(x) \frac{d}{dx} [f(x)] - f(x) \frac{d}{dx} [g(x)]}{[g(x)]^2}.
Agora podemos aplicar algumas das regras acima para encontrar
:math:`u' = f'(x) = 3 \frac{d}{dx} x^2-4\frac{d}{dx}x = 6x-4`. Assim,
definindo :math:`x = 1`, temos :math:`u '= 2`: isso é apoiado por nosso
experimento anterior nesta seção onde o resultado numérico se aproxima
de :math:`2`. Esta derivada também é a inclinação da linha tangente para
a curva :math:`u = f (x)` quando :math:`x = 1`.
Para visualizar tal interpretação das derivadas, usaremos
``matplotlib``, uma biblioteca de plotagem popular em Python. Para
configurar as propriedades das figuras produzidas por ``matplotlib``,
precisamos definir algumas funções. Na sequência, a função
``use_svg_display`` especifica o pacote\ ``matplotlib`` para produzir os
números SVG para imagens mais nítidas. Observe que o comentário
``# @ save`` é uma marca especial onde a seguinte função, classe, ou
instruções são salvas no pacote ``d2l`` então, mais tarde, eles podem
ser chamados diretamente (por exemplo, ``d2l.use_svg_display ()``) sem
serem redefinidos.
.. code:: python
def use_svg_display(): #@save
"""Use the svg format to display a plot in Jupyter."""
display.set_matplotlib_formats('svg')
Definimos a função ``set_figsize`` para especificar o tamanho das
figuras. Observe que aqui usamos diretamente ``d2l.plt``, uma vez que a
instrução import\ ``from matplotlib import pyplot as plt`` foi marcada
para ser salva no pacote ``d2l`` no prefácio.
.. code:: python
def set_figsize(figsize=(3.5, 2.5)): #@save
"""Set the figure size for matplotlib."""
use_svg_display()
d2l.plt.rcParams['figure.figsize'] = figsize
A seguinte função ``set_axes`` define as propriedades dos eixos das
figuras produzidas por\ ``matplotlib``.
.. code:: python
#@save
def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
"""Set the axes for matplotlib."""
axes.set_xlabel(xlabel)
axes.set_ylabel(ylabel)
axes.set_xscale(xscale)
axes.set_yscale(yscale)
axes.set_xlim(xlim)
axes.set_ylim(ylim)
if legend:
axes.legend(legend)
axes.grid()
Com essas três funções para configurações de figura, nós definimos a
função ``plot`` para traçar várias curvas sucintamente uma vez que
precisaremos visualizar muitas curvas ao longo do livro.
.. code:: python
#@save
def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None,
ylim=None, xscale='linear', yscale='linear',
fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None):
"""Plot data points."""
if legend is None:
legend = []
set_figsize(figsize)
axes = axes if axes else d2l.plt.gca()
# Return True if `X` (tensor or list) has 1 axis
def has_one_axis(X):
return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list)
and not hasattr(X[0], "__len__"))
if has_one_axis(X):
X = [X]
if Y is None:
X, Y = [[]] * len(X), X
elif has_one_axis(Y):
Y = [Y]
if len(X) != len(Y):
X = X * len(Y)
axes.cla()
for x, y, fmt in zip(X, Y, fmts):
if len(x):
axes.plot(x, y, fmt)
else:
axes.plot(y, fmt)
set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
Agora podemos plotar a função :math:`u = f (x)` e sua linha tangente
:math:`y = 2x - 3` em :math:`x = 1`, onde o coeficiente :math:`2` é a
inclinação da linha tangente .
.. raw:: html
.. raw:: html
.. code:: python
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
.. parsed-literal::
:class: output
/tmp/ipykernel_122506/77894834.py:3: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
display.set_matplotlib_formats('svg')
.. figure:: output_calculus_7e7694_35_1.svg
.. raw:: html
.. raw:: html
.. code:: python
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
.. parsed-literal::
:class: output
/tmp/ipykernel_104167/77894834.py:3: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
display.set_matplotlib_formats('svg')
.. figure:: output_calculus_7e7694_38_1.svg
.. raw:: html
.. raw:: html
.. code:: python
x = np.arange(0, 3, 0.1)
plot(x, [f(x), 2 * x - 3], 'x', 'f(x)', legend=['f(x)', 'Tangent line (x=1)'])
.. parsed-literal::
:class: output
/tmp/ipykernel_105316/77894834.py:3: DeprecationWarning: `set_matplotlib_formats` is deprecated since IPython 7.23, directly use `matplotlib_inline.backend_inline.set_matplotlib_formats()`
display.set_matplotlib_formats('svg')
.. figure:: output_calculus_7e7694_41_1.svg
.. raw:: html
.. raw:: html
Derivadas Parciais
------------------
Até agora, lidamos com a diferenciação de funções de apenas uma
variável. No *Deep Learning*, as funções geralmente dependem de *muitas*
variáveis. Portanto, precisamos estender as idéias de diferenciação para
essas funções *multivariadas*.
Seja :math:`y = f(x_1, x_2, \ldots, x_n)` uma função com :math:`n`
variáveis. A *derivada parcial* de :math:`y` em relação ao seu
:math:`i ^ \mathrm {th}` parâmetro :math:`x_i` é
.. math:: \frac{\partial y}{\partial x_i} = \lim_{h \rightarrow 0} \frac{f(x_1, \ldots, x_{i-1}, x_i+h, x_{i+1}, \ldots, x_n) - f(x_1, \ldots, x_i, \ldots, x_n)}{h}.
Para calcular :math:`\frac{\partial y}{\partial x_i}`, podemos
simplesmente tratar :math:`x_1, \ldots, x_{i-1}, x_{i+1}, \ldots, x_n`
como constantes e calcular a derivada de :math:`y` com respeito a
:math:`x_i`. Para notação de derivadas parciais, os seguintes são
equivalentes:
.. math:: \frac{\partial y}{\partial x_i} = \frac{\partial f}{\partial x_i} = f_{x_i} = f_i = D_i f = D_{x_i} f.
Gradientes
----------
Podemos concatenar derivadas parciais de uma função multivariada com
respeito a todas as suas variáveis para obter o vetor *gradiente* da
função. Suponha que a entrada da função
:math:`f: \mathbb{R}^n \rightarrow \mathbb{R}` seja um vetor :math:`n`
-dimensional :math:`\mathbf{x} = [x_1, x_2, \ldots, x_n]^\top` e a saída
é um escalar. O gradiente da função :math:`f(\mathbf{x})` em relação a
:math:`\mathbf {x}` é um vetor de :math:`n` derivadas parciais:
.. math:: \nabla_{\mathbf{x}} f(\mathbf{x}) = \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_n}\bigg]^\top,
onde :math:`\nabla_{\mathbf{x}} f(\mathbf{x})` é frequentemente
substituído por :math:`\nabla f(\mathbf{x})` quando não há ambiguidade.
Seja :math:`\mathbf {x}` um vetor :math:`n` -dimensional, as seguintes
regras são freqüentemente usadas ao diferenciar funções multivariadas:
- For all :math:`\mathbf{A} \in \mathbb{R}^{m \times n}`,
:math:`\nabla_{\mathbf{x}} \mathbf{A} \mathbf{x} = \mathbf{A}^\top`,
- For all :math:`\mathbf{A} \in \mathbb{R}^{n \times m}`,
:math:`\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} = \mathbf{A}`,
- For all :math:`\mathbf{A} \in \mathbb{R}^{n \times n}`,
:math:`\nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{A} \mathbf{x} = (\mathbf{A} + \mathbf{A}^\top)\mathbf{x}`,
- :math:`\nabla_{\mathbf{x}} \|\mathbf{x} \|^2 = \nabla_{\mathbf{x}} \mathbf{x}^\top \mathbf{x} = 2\mathbf{x}`.
Da mesma forma, para qualquer matriz :math:`\mathbf {X}`, temos
:math:`\nabla_{\mathbf{X}} \|\mathbf{X} \|_F^2 = 2\mathbf{X}`. Como
veremos mais tarde, os gradientes são úteis para projetar algoritmos de
otimização em *deep learning*.
Regra da Cadeia
---------------
No entanto, esses gradientes podem ser difíceis de encontrar. Isso
ocorre porque as funções multivariadas no *deep learning* são
frequentemente *compostas*, portanto, não podemos aplicar nenhuma das
regras mencionadas acima para diferenciar essas funções. Felizmente, a
*regra da cadeia* nos permite diferenciar funções compostas.
Vamos primeiro considerar as funções de uma única variável. Suponha que
as funções :math:`y = f (u)` e :math:`u = g (x)` sejam diferenciáveis,
então a regra da cadeia afirma que
.. math:: \frac{dy}{dx} = \frac{dy}{du} \frac{du}{dx}.
Agora, vamos voltar nossa atenção para um cenário mais geral onde as
funções têm um número arbitrário de variáveis. Suponha que a função
diferenciável :math:`y` tenha variáveis :math:`u_1, u_2, \ldots, u_m`,
onde cada função diferenciável :math:`u_i` tem variáveis
:math:`x_1, x_2, \ldots, x_n`. Observe que :math:`y` é uma função de
:math:`x_1, x_2, \ldots, x_n`. Então a regra da cadeia será
.. math:: \frac{dy}{dx_i} = \frac{dy}{du_1} \frac{du_1}{dx_i} + \frac{dy}{du_2} \frac{du_2}{dx_i} + \cdots + \frac{dy}{du_m} \frac{du_m}{dx_i}
para qualquer :math:`i = 1, 2, \ldots, n`.
Sumário
-------
- Cálculo diferencial e cálculo integral são dois ramos do cálculo,
onde o primeiro pode ser aplicado aos problemas de otimização
onipresentes no *deep learning*.
- Uma derivada pode ser interpretada como a taxa instantânea de mudança
de uma função em relação à sua variável. É também a inclinação da
linha tangente à curva da função.
- Um gradiente é um vetor cujos componentes são as derivadas parciais
de uma função multivariada com respeito a todas as suas variáveis.
- A regra da cadeia nos permite diferenciar funções compostas.
Exercícios
----------
1. Trace a função :math:`y = f(x) = x^3 - \frac{1}{x}` e sua linha
tangente quando :math:`x = 1`.
2. Encontre o gradiente da função
:math:`f(\mathbf{x}) = 3x_1^2 + 5e^{x_2}`.
3. Qual é o gradiente da função
:math:`f(\mathbf{x}) = \|\mathbf{x}\|_2`?
4. Você pode escrever a regra da cadeia para o caso em que
:math:`u = f(x, y, z)` e :math:`x = x(a, b)`, :math:`y = y(a, b)`, e
:math:`z = z(a, b)`?
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
`Discussions `__
.. raw:: html
.. raw:: html
.. raw:: html