| | | |

Introdução a PINNs

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

3.3 Problemas inversos

Talvez os problemas mais interessante que podemos resolver com PINNs sejam os problemas inversos, em que queremos determinar parâmetros desconhecidos de um modelo matemático a partir de dados experimentais. Ao contrario de redes baseadas em dados, as PINNs podem incorporar o conhecimento do modelo matemático, o que permite estimar parâmetros desconhecidos com menos dados e, em alguns casos, com maior precisão.

Vamos definir o seguinte problema inverso: dada uma equação diferencial

L(u;λ)=f,𝒙Dn, (3.54)

queremos estimar o parâmetro λ a partir de um conjunto de amostras

𝒟:={(𝒙(s),u(s))}s=1ns, (3.55)

da solução u=u(𝒙) da equação (3.54). Aqui, L é um operador diferencial, λ é o parâmetro a determinar e f é uma função fonte conhecida. Assumimos conhecidas condições iniciais ou de contorno, conforme o caso for estacionário ou transiente.

Denotemos a PINN com parâmetro a determinar por

u~=𝒩(𝒙;(𝜽,λ)), (3.56)

em que u~ é a solução estimada da equação diferencial (3.54), 𝜽 são os parâmetros da rede neural (pesos e biases) e λ é o parâmetro a determinar. Ou seja, uma rede neural em que λ é apenas mais um parâmetro a ser treinado juntamente com os pesos e biases da rede.

O treinamento da PINN é feito minimizando a função de perda que, agora, além de considerar o resíduo da equação e as condições inicial/contorno, também considera o erro entre a solução estimada u~ e as amostras 𝒟 da solução u. A função de perda é dada por

ελ:=1ns,ins=1ns,in|fL(uin(s);λ)|2resíduo+c.i./c.c.+amostras, (3.57)

em que o(s) termo(s) c.i. e c.c. são definidos conforme as condições inicial e de contorno. O termo das amostras é dado pelo erro médio quadrático entre a solução estimada u~ e as amostras 𝒟 da solução u

1nss=1ns|u~(s)u(s)|2. (3.58)

Observemos que a dependência do parâmetro λ na função de perda se dá pelo resíduo da equação diferencial, que é calculado em pontos de amostragem internos no domínio D.

Observação 3.3.1.(Outros tipos de problemas inversos)

A aplicação de PINNs não é restrita a problemas inversos de determinação de parâmetros. Podemos também estimar funções desconhecidas, como por exemplo, a função fonte f ou o operador L. Neste caso, podemos representar a função desconhecida por uma rede neural e treinar a rede juntamente com a rede que representa a solução u. Para mais detalhes, consultemos o E.LABEL:exer:problemas_inversos_funcao.

Exemplo 3.3.1.(Taxa populacional)

O modelo de Fisher666Ronald Aylmer Fisher, 1890-1962, biólogo inglês. Fonte: Wikipédia: Ronald Fisher. descreve a evolução de uma população com taxa de crescimento λ e difusão espacial. Trata-se de um modelo de reação-difusão, em que a população cresce logisticamente e se difunde no espaço. Aqui, vamos assumir a equação de Fisher com condições de contorno de Dirichlet não homogêneas:

ut=uxx+λu(1u),(t,x)(0,tf)×(0,1), (3.59)
u(0,x)=1/(1+eλ6x)2,x[0,1], (3.60)
u(t,0)=1/(1+e56λt)2, (3.61)
u(t,1)=1/(1+eλ656λt)2. (3.62)

Queremos determinar a taxa de crescimento populacional λ assumindo o seguinte conjunto de amostras da solução u

𝒟={((t(s),x(s)),u(s))}s=1ns, (3.63)

com (t(s),x(s)){0.1,0.2,0.3}×{0.25,0.5,0.75} e u(s)=ua(t(s),x(s)), sendo

ua(t,x)=1/(1+eλ6x56λt)2 (3.64)

a solução analítica do problema [1]. Consulte a Figura 3.4 para uma ilustração da solução e dos dados conhecidos para o valor esperado de λ=6.

Refer to caption
Figura 3.4: Solução PINN versus analítica para λ=6. Os pontos correspondem às amostras da solução conhecidas.

O Código 20 é uma implementação de uma PINN para resolver este problema inverso.

Código 20: ex_pinn_fisher.py
1import torch
2
3# dispositivo GPU/CPU ?
4device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
5print(f'device: {device}')
6
7# modelo
8
9n_h = 4 # num de camadas escondidas
10n_n = 50 # num de neurônios por camada escondida
11act_fun = torch.nn.Tanh() # função de ativação
12
13model = torch.nn.Sequential()
14# camada de entrada
15model.add_module('layer_1', torch.nn.Linear(2,n_n))
16model.add_module('fun_1', act_fun)
17# camadas escondidas
18for i in range(n_h-1):
19 model.add_module(f'layer_{i+2}', torch.nn.Linear(n_n,n_n))
20 model.add_module(f'fun_{i+2}', act_fun)
21# camada de saída
22model.add_module(f'layer_{n_h+1}', torch.nn.Linear(n_n,1))
23
24# adiciona param lambda
25model.lmbda = torch.nn.Parameter(12.0 * torch.rand(1, device=device))
26
27model.to(device) # envia modelo para o dispositivo
28
29# treinamento
30
31## optimizador
32optim = torch.optim.Adam(model.parameters(), lr=0.001)
33
34## num de amostras por época
35ns_t = 20 # num de amostras no tempo
36ns_x = 40 # num de amostras no espaço
37
38## num max épocas
39nepochs = 50_000
40
41## critério de parada (lambda)
42window_tol = 1e-3
43window_size = 100
44lmbda_window = [model.lmbda.item()]
45
46# dados
47ts_data = torch.tensor([0.1, 0.2, 0.3], device=device)
48xs_data = torch.tensor([0.25, 0.5, 0.75], device=device)
49Ts_data, Xs_data = torch.meshgrid(ts_data, xs_data, indexing='ij')
50S_data = torch.hstack((Ts_data.reshape(-1,1), Xs_data.reshape(-1,1)))
51lmbda_exp = torch.tensor(6.0)
52Us_data = 1.0/(1.0 + torch.exp(torch.sqrt(lmbda_exp/6.0)*S_data[:,1:2] \
53 - 5.0/6.0*lmbda_exp*S_data[:,0:1]))**2
54
55
56# laço de treinamento
57for epoch in range(nepochs):
58
59 # amostras de pontos internos
60 t = torch.rand(ns_t, 1, device=device)
61 x = torch.rand(ns_x, 1, device=device)
62 T, X = torch.meshgrid(t.flatten(), x.flatten(), indexing='ij')
63 S = torch.hstack((T.reshape(-1,1), X.reshape(-1,1)))
64 S.requires_grad = True
65
66 # propagação nos pontos internos
67 U = model(S)
68
69 # derivadas de primeira ordem
70 DU = torch.autograd.grad(U, S,
71 grad_outputs=torch.ones_like(U),
72 create_graph=True)[0]
73 DUDT = DU[:,0:1] # du/dt
74 DUDX = DU[:,1:2] # du/dx
75
76 # derivadas de segunda ordem
77 # d2u/dx^2
78 D2UDX = torch.autograd.grad(DUDX, S,
79 grad_outputs=torch.ones_like(DUDX),
80 create_graph=True)[0][:,1:2]
81
82 # param
83 lmbda = model.lmbda
84
85 # resíduo
86 R = DUDT - D2UDX - lmbda*U*(1.0 - U)
87
88 # função de perda: resíduo
89 loss_res = torch.mean(R**2)
90
91 # condição inicial
92 Sci = torch.hstack((torch.zeros_like(x), x))
93 Uci = model(Sci)
94 Uexp_ci = 1.0/(1.0 + torch.exp(torch.sqrt(lmbda/6.0)*x))**2
95 loss_ci = torch.mean((Uci - Uexp_ci)**2)
96
97 # condições de contorno
98
99 # contorno x = 0
100 Scc1 = torch.hstack((t, torch.zeros_like(t)))
101 Ucc1 = model(Scc1)
102 Ucc1_exp = 1.0/(1.0 + torch.exp(-5.0/6.0*lmbda*t))**2
103 loss_cc1 = torch.mean((Ucc1 - Ucc1_exp)**2)
104
105 # contorno x = 1
106 Scc2 = torch.hstack((t, torch.ones_like(t)))
107 Ucc2 = model(Scc2)
108 Ucc2_exp = 1.0/(1.0 + torch.exp(torch.sqrt(lmbda/6.0) \
109 - 5.0/6.0*lmbda*t))**2
110 loss_cc2 = torch.mean((Ucc2 - Ucc2_exp)**2)
111
112 # dados
113 Udata_est = model(S_data)
114 loss_data = torch.mean((Udata_est - Us_data)**2)
115
116 # função de perda total
117 loss = loss_res + loss_ci + loss_cc1 + loss_cc2 + loss_data
118
119
120 if (epoch % 100 == 0):
121 print(f'{epoch}: lambda: {model.lmbda.item():.2e} loss_res: {loss.item():.2e} ci: {loss_ci.item():.1e} cc1: {loss_cc1.item():.1e} cc2: {loss_cc2.item():.1e}')
122 torch.save(model, f'model.pt')
123
124 # critério de parada
125 if (epoch < window_size):
126 lmbda_window.append(model.lmbda.item())
127 else:
128 lmbda_window.append(model.lmbda.item())
129 lmbda_window.pop(0)
130 lmbda_var = max(lmbda_window) - min(lmbda_window)
131
132 if lmbda_var < window_tol and epoch > 5000:
133 print(f'Critério de parada atingido na época {epoch}.')
134 print(f'\tlambda: {model.lmbda.item():.2e}')
135 break
136
137 # backward
138 optim.zero_grad()
139 loss.backward()
140 optim.step()

3.3.1 Teste de sensibilidade

Uma característica importante de todo o método para problemas inversos é sua sensibilidade em relação aos dados. Mais precisamente, queremos saber como a estimativa do parâmetro λ muda quando adicionamos ruídos aos dados.

Exemplo 3.3.2.

Vamos considerar o mesmo problema do Exemplo 3.3.1, mas agora vamos adicionar ruídos aos dados. Para isso, vamos gerar amostras da solução u com ruído gaussiano aditivo

u(s)=ua(t(s),x(s))+σϵ(s),s=1,,ns, (3.65)

em que ϵ(s)𝒩(0,1) é uma variável aleatória com distribuição normal padrão e σ>0 é o desvio padrão do ruído. Vamos considerar diferentes valores de σ e verificar como a estimativa do parâmetro λ muda com o aumento do ruído.

Para isso, vamos fazer a seguinte alteração no Código 20:

Código 21: ex_pinn_fisher_ruido.py
1# dados com ruído
2sigma = 0.1 # desvio padrão do ruído
3Us_data = 1.0/(1.0 + torch.exp(torch.sqrt(lmbda_exp/6.0)*S_data[:,1:2] \
4 - 5.0/6.0*lmbda_exp*S_data[:,0:1]))**2
5Us_data += sigma * torch.randn_like(Us_data)
Tabela 3.1: Estimativa do parâmetro λ (média de 5 tentativas) para diferentes valores de ruído σ.
σ λ¯ Erro
0.00 5.994 0.1%
0.01 %
0.05 %
0.1
0.2
0.5 6.1234
1.0 6.5678

3.3.2 Exercícios

E. 3.3.1.

Crie uma PINN para estimar o parâmetro λ>0 do seguinte problema de Poisson

λu′′=π2sen(πx), 0<x<1, (3.66)
u(0)=u(1)=0, (3.67)

dado o conjunto de amostras D={(x(s),u(s))}s=13

D={(16,2),(14,2),(13,3)}. (3.68)

Faça uma análise de sensibilidade adicionando ruídos aos dados e verificando como a estimativa do parâmetro λ varia com o aumento do ruído.

E. 3.3.2.

Crie uma PINN para resolver o seguinte problema inverso de Poisson

u=λ(2x2y2),(x,y)D=(1,1)2, (3.69)
u=0,(x,y)D, (3.70)

para estimar o parâmetro λ>0 dado que u(0,0)=25. Faça uma análise de sensibilidade adicionando ruído ao dado. O aumento do conjunto de dados melhora a estimativa do parâmetro λ? Justifique sua resposta.

E. 3.3.3.

Crie uma PINN para resolver o problema inverso de estimar a fonte de calor para o modelo físico

ut=uxx+etf(x),(t,x)(0,1)2, (3.71)
u(0,x)=sen(πx),x[0,1], (3.72)
u(t,0)=u(t,1)=0,t[0,1], (3.73)

em que f(x) é a função a ser estimada dado que a solução no tempo final t=1 é

u(1,x)=e1sen(πx),x[0,1]. (3.74)
E. 3.3.4.

Crie uma PINN para estimar os parâmetros escalares λ1,λ2>0 do seguinte sistema de EDPs

Δu+λ1v=f1,(x,y)D=(1,1)2, (3.75)
Δv+λ2u=f2,(x,y)D=(1,1)2, (3.76)
u=v=0,(x,y)D, (3.77)

com fontes

f1(x,y)=42x22y2+sen(πx)sen(πy), (3.78)
f2(x,y)=2π2sen(πx)sen(πy)+2(1x2)(1y2). (3.79)

tendo, ainda, o conjunto de amostras da solução

𝒟={((0,0),1,0),((0.5,0.5),0.5625,1),((0.5,0.5),0.5625,1)}, (3.80)

em que cada elemento é da forma ((x(s),y(s)),u(s),v(s)). Faça uma análise de sensibilidade adicionando ruído aos dados e verifique como as estimativas de λ1 e λ2 variam com o aumento do ruído.

E. 3.3.5.

FALTA UM EXERCÍCIO AQUI!!!


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