| | | |

Introdução a PINNs

Ajude a manter o site livre, gratuito e sem propagandas. Colabore!

2.6 Diferenciação automática

Diferenciação automática é um conjunto de técnicas para a computação de derivadas numéricas em um programa de computador. Explora-se o fato de que um programa computacional executa uma sequência de operações aritméticas e funções elementares, podendo-se computar a derivada por aplicações da regra da cadeia.

PyTorch computa o gradiente (derivada) de uma função f:ℝn→ℝ a partir de seu grafo computacional. Os gradientes são computados por retropropagação. Por exemplo, para a computação do gradiente

∇𝒙f⁢(𝒙0)=d⁢fd⁢𝒙|𝒙=𝒙0, (2.79)

primeiramente, propaga-se a entrada 𝒙0 pela função computacional f, obtendo-se y0=f⁢(𝒙0). Então, o gradiente é computado por retropropagação.

Exemplo 2.6.1.

Seja f⁢(x)=sen⁡(π⁢x) e vamos computar

f′⁢(0)=d⁢fd⁢x|x=0 (2.80)

por diferenciação automática.

Refer to caption
Figura 2.14: Grafo computacional da diferenciação automática de f⁢(x)=sen⁡(π⁢x).

A Figura 2.14 mostra o grafo computacional da função f⁢(x)=sen⁡(π⁢x). A entrada x é propagada formando o grafo de propagação. Ao grafo de propagação, folhas são adicionadas contendo as derivadas de cada operação. Para a computação do gradiente, adicionamos uma variável fictícia z=y e, então, o gradiente d⁢y/d⁢x=d⁢z/d⁢x é computado por retropropagação. A retropropagação é feita da seguinte forma

d⁢yd⁢x|x=x0=d⁢zd⁢x|x=x0 (2.81)
=d⁢zd⁢u|x=x0d⁢ud⁢x|x=x0 (2.82)
=d⁢zd⁢y|x=x0d⁢yd⁢u|x=x0d⁢ud⁢x|x=x0 (2.83)
=1⋅π⋅cos(πx0)=πcos(πx0). (2.84)
Refer to caption
Figura 2.15: Comparação entre as diferenciações analítica (f′) e automática (autograd).

O Código 13 contém uma implementação PyTorch da diferenciação automática da função f⁢(x)=sen⁡(π⁢x) para x∈(0,1/4,1/2,3/4,1). Observe que para a computação do gradiente, a entrada x deve ser marcada com o atributo requires_grad=True. O comando y.backward() computa o gradiente de y em relação a x. O argumento gradient=ones_like(y) é usado para especificar o vetor de gradientes iniciais para a retropropagação. Este é o d⁢z/d⁢y neste exemplo aqui. O gradiente d⁢y/d⁢x é armazenado no atributo x.grad. Ainda, a Figura 2.15 mostra a comparação entre as diferenciações analítica (f′) e automática (autograd). Observemos que os resultados são idênticos, com diferenças da ordem de 10−7, que é a precisão numérica do PyTorch141414Por padrão, o PyTorch usa pontos flutuantes de 32-bits, com precisão aproximada de 10−⁢7..

Código 13: ex_autograd.py
1from torch import tensor, sin, pi, ones_like
2
3# entrada
4x = tensor([0.,0.25,0.5,0.75,1.])
5x = x.reshape(-1, 1)
6x.requires_grad_(True)
7
8# propagação
9y = sin(pi * x)
10
11# retropropagação (cálculo do gradiente)
12y.backward(gradient=ones_like(y))
13
14# print gradiente
15print('dydx =', x.grad)
dydx = tensor([[ 3.1416e+00],
[ 2.2214e+00],
[-1.3732e-07],
[-2.2214e+00],
[-3.1416e+00]])

O grafo computacional da retropropagação pode ser recuperado no PyTorch com o seguinte código (Código 14).

Código 14: ex_autograd.py (continuação)
1def print_backward_graph(node, depth=0, seen=None):
2 seen = set() if seen is None else seen
3 if node is None or node in seen:
4 return
5 seen.add(node)
6 print(" " * depth + type(node).__name__)
7 for parent, _ in node.next_functions:
8 print_backward_graph(parent, depth + 1, seen)
9
10print_backward_graph(y.grad_fn)
SinBackward0
MulBackward0
AccumulateGrad

Observemos que a computação do gradiente de primeira ordem também acaba por construir um novo grafo. Este, por sua vez, pode ser usado para a computação da diferenciação automática de segunda ordem, i.e. para a derivação de segunda ordem.

Exemplo 2.6.2.

Vamos computar a segunda derivada da função f⁢(x)=sen⁡(π⁢x) por diferenciação automática. O primeiro passo é propagar a entrada x pela função f, obtendo-se y=f⁢(x). Em seguida, computamos o gradiente de primeira ordem d⁢y/d⁢x por retropropagação. O gradiente de segunda ordem d2⁢y/d⁢x2 é computado propagando-se o gradiente de primeira ordem d⁢y/d⁢x pelo grafo computacional da retropropagação, obtendo-se d2⁢y/d⁢x2.

Refer to caption
Figura 2.16: Comparação entre as diferenciações analítica (f′, f′′) e automática (dydx, d2ydx2).

O Código 15 contém uma implementação PyTorch da diferenciação automática de segunda ordem dessa função para x∈(0,1/4,1/2,3/4,1). A Figura 2.16 mostra a comparação entre as diferenciações analítica (f′, f′′) e automática (dydx, d2ydx2). Observemos que os resultados são idênticos, com diferenças na ordem da precisão computacional do PyTorch, que é da ordem de 10−7. Verifique!

Código 15: ex_autograd_d2f1d.py
1from torch import tensor, sin, pi, ones_like
2from torch.autograd import grad
3
4# entrada
5x = tensor([0.,0.25,0.5,0.75,1.])
6x = x.reshape(-1, 1)
7x.requires_grad_(True)
8
9# propagação
10y = sin(pi * x)
11
12# retropropagação y (gradiente de primeira ordem)
13dydx = grad(y, x, grad_outputs=ones_like(y),
14 create_graph=True)[0]
15
16# propagação dydx (gradiente de segunda ordem)
17d2ydx2 = grad(dydx, x, grad_outputs=ones_like(dydx),
18 create_graph=True)[0]
19
20# print gradiente
21print('d2ydx2 =', d2ydx2)
d2ydx2 = tensor([[-0.0000e+00],
[-6.9789e+00],
[-9.8696e+00],
[-6.9789e+00],
[ 8.6283e-07]], grad_fn=<MulBackward0>)

2.6.1 Autograd MLP

Os conceitos de diferenciação automática (autograd) são diretamente estendidos para redes do tipo Perceptron Multicamadas (MLP, do inglês, Multilayer Perceptron). Uma MLP é uma composição de funções definidas por parâmetros (pesos e biases). Como estudamos na Subseção 2.2, seu treinamento também pode ser feito por retropropagação em duas etapas:

  1. 1.

    Propagação (forward): os dados de entrada são propagados para todas as funções da rede, produzindo a saída estimada.

  2. 2.

    Retropropagação (backward): a computação do gradiente do erro151515Medida da diferença entre o valor estimado e o valor esperado. em relação aos parâmetros da rede é realizada coletando as derivadas (gradientes) das funções da rede. Pela regra da cadeia, essa coleta é feita a partir da camada de saída em direção à camada de entrada da rede.

No seguinte exemplo, exploramos o fato de MLPs serem aproximadoras universais e avaliamos as aproximações das derivadas de primeira e segunda ordem de uma MLP na aproximação de uma função.

Exemplo 2.6.3.

Na Subseção 2.5.2, criamos uma MLP para aproximar a função f⁢(x)=sen⁡(π⁢x) para x∈[−1,1]. Aqui, vamos computar as derivadas de primeira e segunda ordem da MLP por diferenciação automática e comparar com as derivadas analíticas da função f⁢(x)=sen⁡(π⁢x), que são

f′⁢(x)=π⁢cos⁡(π⁢x), (2.85)
f′′⁢(x)=−π2⁢sen⁡(π⁢x). (2.86)
Refer to caption
Figura 2.17: Comparação da diferenciação automática da MLP com a derivada analítica f′⁢(x)=π⁢cos⁡(π⁢x).

Antes de computarmos os gradientes da MLP, voltemos ao Código 11 para adicionarmos uma linha de código ao final do treinamento da MLP, que armazena o modelo treinado em um arquivo. A linha de código é

1torch.save(model, 'modelo.pt')

Armazenado o modelo treinado, podemos carregá-lo em outro código para computarmos os gradientes de primeira e segunda ordem da MLP. O Código 16 contém uma implementação PyTorch da diferenciação automática da MLP para alguns valores de x∈[−1,1]. A Figura 2.17 mostra a comparação entre os gradientes analíticos e automáticos de primeira e segunda ordem da MLP. Observemos que as aproximações das derivadas são boas, mas dependem da qualidade da aproximação da função pela MLP e as diferenças aumentam com a ordem da derivada. Verifique!

Código 16: mlp_autograd_apfun1d.py
1import torch
2
3# modelo treinado
4model = torch.load('model.pt',
5 weights_only=False).to('cpu')
6
7# autograd MLP
8X = torch.tensor([-1.,-0.5,0.,0.5,1.]).reshape(-1,1)
9X.requires_grad = True
10
11Y = model(X)
12
13# gradiente de primeira ordem
14DYDX = torch.autograd.grad(
15 Y, X,
16 grad_outputs=torch.ones_like(Y),
17 create_graph=True)[0]
18
19# gradiente de segunda ordem
20D2YDX2 = torch.autograd.grad(
21 DYDX, X,
22 grad_outputs=torch.ones_like(DYDX))[0]

2.6.2 Exercícios

E. 2.6.1.

Por diferenciação automática, compute o gradiente (a derivada) das seguintes funções:

  1. a)

    g⁢(x)=cos2⁡(x) para valores x∈[0,2⁢π].

  2. b)

    f⁢(x)=x2−2⁢x+1 para valores x∈[−2,2].

  3. c)

    h⁢(x)=ln⁡(x−1) para valores x∈(1,2].

  4. d)

    u⁢(t)=e−t2⁢sen⁡(t) para valores t∈[−π,π].

Em cada caso, monte os grafos computacionais da propagação e da retropropagação na computação dos gradientes. Compare os valores computados com os valores esperados.

E. 2.6.2.

Para cada função no E.2.6.1, crie uma MLP para aproximar a função e, por diferenciação automática, compute as derivadas de primeira e segunda ordem. Compare os valores computados com os valores esperados.

E. 2.6.3.

Por diferenciação automática, compute os gradientes das seguintes funções:

  1. a)

    f⁢(x,y)=x2+y2 para valores (x,y)∈[−1,1]2.

  2. b)

    g⁢(x,y)=ex⁢sen⁡(x⁢y) para valores (x,y)∈(−1,2)×(0,π).

Em cada caso, monte os grafos computacionais da propagação e da retropropagação na computação dos gradientes. Compare os valores computados com os valores esperados.

E. 2.6.4.

Para cada função no E.2.6.3, crie uma MLP para aproximar a função e compute as derivadas ∂/∂x, ∂/∂y, ∂2/∂x2, ∂2/∂x⁢∂y e ∂2/∂y2 pela MLP. Compare os valores computados com os valores esperados.

E. 2.6.5.

Para cada função no E.2.6.3, crie uma MLP para aproximar a função e compute o laplacino Δ=(∂2/∂x2+∂2/∂y2) pela MLP. Compare os valores computados com os valores esperados.


Envie seu comentário

Aproveito para agradecer a todas/os que de forma assídua ou esporádica contribuem enviando correções, sugestões e críticas!

Opcional. Preencha seu nome para que eu possa lhe contatar.
Opcional. Preencha seu e-mail para que eu possa lhe contatar.
As informações preenchidas são enviadas por e-mail para o desenvolvedor do site e tratadas de forma privada. Consulte a política de uso de dados para mais informações.

Licença Creative Commons
Este texto é disponibilizado nos termos da Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional. Ícones e elementos gráficos podem estar sujeitos a condições adicionais.

Pedro H A Konzen
| | | |