| | | |

Introdução a PINNs

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

2.3 Rede perceptron multicamadas

Uma rede perceptron multicamadas (MLP, do inglês, multilayer perceptron) é um modelo de rede neural feedforward formada por composições de camadas de perceptrons. A Figura 2.6 contém um esquema de uma MLP. Observemos que a rede é formada por uma camada de entrada, nh camadas escondidas e uma camada de saída. Cada camada é formada por um conjunto de neurônios, cada um com sua própria função de ativação. A saída da rede é calculada por composições das camadas, i.e. a saída da primeira camada é a entrada da segunda camada, e assim sucessivamente.

Refer to caption
Figura 2.6: Arquitetura de uma rede do tipo perceptron multicamadas (MLP).

Denotamos uma MLP de nl camadas por

𝒚=𝒩(𝒙;𝜽), (2.47)

onde 𝜽 são os parâmetros do modelo, sendo 𝜽l=(W(l),𝒃(l)) os parâmetros (pesos W(l) matriz de nn(l1)×nn(l) números reais e biases 𝒃(l) vetor de nn(l) números reais) da camada l=1,2,,nh+1, onde nh denota o número de camadas escondidas. É assumido conhecidos a priori o número de entradas nI, o número de neurônios de cada camada escondida nn(l), o número de camadas escondidas nh e o número de saídas nO. Também, são conhecidos a priori as funções de ativação de cada camada 𝒂(l)=𝝋(l)(𝒛(l)), onde 𝒛(l) é a pré-ativação da camada l, i.e.,

𝒛(l)=W(l)𝒂(l1)+𝒃(l), (2.48)

para l=1,2,,nh+1, denotando a entrada por 𝒙=:𝒂(0) e a saída por 𝒚=:𝒂(nh+1).

De forma mais precisa, o cálculo da inferência de uma entrada 𝒙 na rede é feito por iteradas composições das camadas, i.e., a saída da rede é calculada pela iteração

𝒂(l)=𝝋(l)(W(l)𝒂(l1)+𝒃(l))𝒛(l), (2.49)

para l=1,2,,nh+1, lembrando que a entrada 𝒙=:𝒂(0) é vetor de nI números reais e 𝒚=:𝒂(nh+1) é vetor de nO números reais.

Exemplo 2.3.1.(Modelo xor)

No E.2.1.5, vimos que não é possível criar um perceptron para emular a operação lógica xor. Por outro lado, esta operação é equivalente a seguinte combinação de operações lógicas

A xor B=(A or B) and not(A and B). (2.50)

Com isso, podemos criar uma MLP com duas entradas, uma camada escondida com dois neurônios e uma camada de saída com um neurônio, como ilustrado na Figura 2.7.

Refer to caption
Figura 2.7: Arquitetura de uma rede MLP para emular a operação lógica xor.

Seguindo esta arquitetura de rede, o Código 5 contém a implementação de uma MLP para emular a operação lógica xor. Verifique! No código, as entradas são representadas por 1 (verdadeiro) e 1 (falso), e a saída da rede é interpretada como verdadeiro se for maior que zero e falso caso contrário. As funções de ativação são funções sinal, i.e. φ(x)=sign(x). Na primeira camada escondia, o primeiro neurônio emula a operação lógica or e o segundo neurônio emula a operação lógica not and. A saída da rede é a operação lógica and entre as saídas dos dois neurônios da camada escondida.

Código 5: mlp_xor.py
1from torch import sign, tensor, no_grad
2from torch.nn import (
3 Module, Sequential,
4 Linear, Parameter
5 )
6
7# função sinal
8class Sign(Module):
9 def forward(self, x):
10 return sign(x)
11
12# modelo
13model = Sequential()
14model.add_module("layer_1", Linear(2, 2))
15model.add_module("fun_1", Sign())
16model.add_module("layer_2", Linear(2, 1))
17model.add_module("fun_2", Sign())
18
19# parâmetros
20with no_grad():
21 model.layer_1.weight = Parameter(tensor([[1., 1.],
22 [-1., -1.]]))
23 model.layer_1.bias = Parameter(tensor([1., 1.]))
24 model.layer_2.weight = Parameter(tensor([[1., 1.]]))
25 model.layer_2.bias = Parameter(tensor([-1.]))
26
27# dados de entrada
28X = tensor([[1., 1.],
29 [1., -1.],
30 [-1., 1.],
31 [-1., -1.]])
32
33# inferência (propagação)
34y_est = model(X)
35print('Saída:')
36print(y_est)

2.3.1 Treinamento por retropropagação

A ideia do treinamento por retropropagação que estudamos na Seção 2.3.1 para o perceptron, pode ser diretamente estendida para o treinamento de uma MLP. A diferença é que, agora, temos mais parâmetros a serem ajustados e, portanto, mais gradientes a serem calculados.

Vamos assumir uma MLP

𝒚~=𝒩(𝒙;𝜽), (2.51)

Para o treinamento da rede, assumimos uma função de perda da forma

ε(𝜽~):=1nss=1nsε(s)(𝜽,𝒚~(s)(𝜽)) (2.52)

onde ε(s):nθ, ε(s)=ε(s)(𝜽,𝒚~) é a função de perda da s-ésima amostra, i.e. uma função dos parâmetros do modelo 𝜽, onde nθ é o número de parâmetros da rede.

O treinamento da rede consiste em resolver o problema de minimização

argmin𝜽ε(𝜽) (2.53)

Aplicando um método baseado em gradiente, temos o seguinte algoritmo:

  1. 1.

    𝜽 aproximações iniciais.

  2. 2.

    Para e1,,ne:

    1. (a)

      𝜽𝜽lr𝒅(𝜽ε)

onde, ne é o número de épocas, lr é a taxa de aprendizagem e 𝒅=𝒅(𝜽ε) é o vetor direção da atualização dos parâmetros, com

𝜽ε:=(εW,ε𝒃) (2.54)
=1nss=1ns(ε(s)W,ε(s)𝒃) (2.55)
Observação 2.3.1.(Otimizadores)

O método de atualização dos parâmetros da rede pode ser feito por diferentes otimizadores baseados em gradiente. Em acada método há uma forma diferente de calcular o vetor direção 𝒅, que pode depender do gradiente atual e de gradientes anteriores. Na Seção 2.3.1, estudamos o método do gradiente descendente (GD) e o método do gradiente descendente estocástico (GDE), que são métodos de atualização dos parâmetros da rede com base no gradiente atual. Ambos os métodos podem ser aplicados para o treinamento de uma MLP e mais adiante, vamos estudar outras variantes de otimizadores baseados em gradiente.

O cálculo dos gradientes pode ser feito por retropropagação como uma aplicação da regra da cadeia. Para os pesos da última camada, temos888Com um certo abuso de linguagem devido à álgebra matricial envolvida.

ε(s)W(nh+1)=ε(s)𝒚~𝒚~𝒛(nh+1)𝒛(nh+1)W(nh+1) (2.56)
=ε(s)𝒚~[𝝋(nh+1)](W(nh+1)𝒂(nh)+𝒃(nh+1))𝒂(nh). (2.57)

Para os pesos da penúltima camada, temos

ε(s)W(nh)=ε(s)𝒚~𝒚~𝒛(nh+1)𝒛(nh+1)W(nh), (2.58)
=ε(s)𝒚[𝝋(nh+1)](𝒛(nh+1))𝒛(nh+1)𝒂(nh)𝒂(nh)𝒛(nh)𝒛(nh)W(nh) (2.59)
=ε(s)𝒚[𝝋(nh+1)](𝒛(nh+1))W(nh+1)[𝝋(nh)](𝒛(nh))𝒂(nh1) (2.60)

e assim, sucessivamente para as demais camadas da rede. Os gradientes em relação aos biases podem ser calculados de forma análoga (consulte o E.2.3.1).

Em todos os casos, notemos que o cálculo do gradiente da função de perda em relação aos parâmetros da rede é feito de forma retroativa, ou seja, a partir da saída da rede e voltando-se para os parâmetros da rede. Ainda, as derivadas envolvidas, usualmente, podem ser calculadas de forma analítica e armazenadas durante a etapa de propagação, evitando-se a repetição de cálculos. Por fim, o cálculo do gradiente da função de perda pode ser feito por um processo de acumulação, tendo em vista que a função de perda é uma soma de funções de perda das amostras.

Exemplo 2.3.2.(Modelo xor com treinamento)

No E.2.3.1, estudamos que é possível criar uma MLP para emular a operação lógica xor. No entanto, os parâmetros da rede foram escolhidos de forma manual. Aqui, vamos criar uma MLP para este mesmo problema, mas agora vamos treinar a rede para ajustar os parâmetros de forma automática.

Vamos assumir uma rede com duas entradas, uma camada escondida com dois neurônios e uma camada de saída com um neurônio. Para garantir a diferenciabilidade, vamos assumir que todas as funções de ativação são funções tangente hiperbólica, i.e. φ(x)=tanh(x). A função de perda é a função erro quadrático médio

ε:=1nss=1ns|y~(s)y(s)|2, (2.61)

onde y~(s)=𝒩(𝒙(s)) são os valores estimados e {𝒙(s),y(s)}s=1ns, ns=4, o conjunto de treinamento conforme na Tabela 2.4.

Tabela 2.4: Conjunto de treinamento para a operação lógica xor. Assumindo que 1 representa verdadeiro e 1 representa falso.
x1 x2 y
1 1 1
1 1 1
1 1 1
1 1 1

O Código 6 contém a implementação do treinamento da MLP com otimizador GD. Verifique!

Código 6: mlp_xor_gd.py
1import torch
2
3# modelo
4model = torch.nn.Sequential()
5model.add_module('layer_1', torch.nn.Linear(2,2))
6model.add_module('fun_1', torch.nn.Tanh())
7model.add_module('layer_2', torch.nn.Linear(2,1))
8model.add_module('fun_2', torch.nn.Tanh())
9
10
11# treinamento
12
13# optimizador
14optim = torch.optim.SGD(model.parameters(), lr=0.1)
15
16# dados de treinamento
17X_train = torch.tensor([[1., 1.],
18 [1., -1.],
19 [-1., 1.],
20 [-1., -1.]])
21y_train = torch.tensor([-1., 1., 1., -1.]).reshape(-1,1)
22
23print("\nDados de treinamento")
24print("X_train =")
25print(X_train)
26print("y_train = ")
27print(y_train)
28
29# num max épocas
30nepochs = 500
31
32# tolerância
33tol = 5e-2
34
35for epoch in range(nepochs):
36
37 # propagação
38 y_est = model(X_train)
39
40 # função erro
41 loss = torch.mean((y_est - y_train)**2)
42
43 print(f'{epoch}: {loss.item():.4e}')
44
45 # critério de parada
46 if (loss.item() < tol):
47 break
48
49 # retropropagação
50 optim.zero_grad()
51 loss.backward()
52 optim.step()
53
54
55# verificação
56y = model(X_train)
57print(f'y_est = {y}')

2.3.2 Exercícios

E. 2.3.1.

Para uma MLP com nl=nh+1 camadas com função de perda da forma (2.52), calcule os gradientes:

  1. a)

    ε(s)W(1).

  2. b)

    ε(s)𝒃(nh+1).

  3. c)

    ε(s)𝒃(nh).

  4. d)

    ε(s)𝒃(1).

E. 2.3.2.

Faça uma nova versão do Código 6, substituindo as linhas

1# retropropagação
2optim.zero_grad()
3loss.backward()
4optim.step()

por uma implementação manual, em que os gradientes sejam calculados explicitamente e os parâmetros atualizados manualmente.

E. 2.3.3.

Faça uma nova versão do Código 6, substituindo o otimizador GD pelo otimizador GDE. Faça uma análise comparativa entre os dois métodos de treinamento, considerando o número de épocas e a taxa de aprendizagem.

E. 2.3.4.

No exercício anterior (E.2.3.3), você deve ter observado que ambos os otimizadores GD e GDE podem cair em mínimos locais, dependendo da inicialização dos parâmetros da rede. Modifique seus códigos de forma que eles busquem identificar quando estão presos em mínimos locais e, nesse caso, parem o treinamento.

E. 2.3.5.

No Exemplo 2.3.1, criamos uma MLP para emular a operação lógica xor com base na combinação de operações

A xor B=(A or B) and not(A and B). (2.62)

Esta não é a única forma de combinar as operações lógicas or, and e not para calcular a xor. De fato, observe que o Código 6 treina uma nova MLP a cada execução do código, com diferentes parâmetros iniciais e acaba convergindo para diferentes soluções. Faça a análise das redes treinadas e, através da análise dos parâmetros treinados, descubra que outras combinações de operações lógicas podem ser utilizadas para emular a operação lógica xor.


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