Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug network fix #208

Merged
merged 1 commit into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 21 additions & 22 deletions docs/source/_rst/tutorials/tutorial5/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ First of all we import the modules needed for the tutorial. Importing
from scipy import io
import torch
from pina.model import FNO, FeedForward # let's import some models
from pina import Condition
from pina import LabelTensor
from pina import Condition, LabelTensor
from pina.solvers import SupervisedSolver
from pina.trainer import Trainer
from pina.problem import AbstractProblem
Expand Down Expand Up @@ -44,10 +43,10 @@ taken from the authors original reference.
data = io.loadmat("Data_Darcy.mat")

# extract data (we use only 100 data for train)
k_train = torch.tensor(data['k_train'], dtype=torch.float).unsqueeze(-1)
u_train = torch.tensor(data['u_train'], dtype=torch.float).unsqueeze(-1)
k_test = torch.tensor(data['k_test'], dtype=torch.float).unsqueeze(-1)
u_test= torch.tensor(data['u_test'], dtype=torch.float).unsqueeze(-1)
k_train = LabelTensor(torch.tensor(data['k_train'], dtype=torch.float).unsqueeze(-1), ['u0'])
u_train = LabelTensor(torch.tensor(data['u_train'], dtype=torch.float).unsqueeze(-1), ['u'])
k_test = LabelTensor(torch.tensor(data['k_test'], dtype=torch.float).unsqueeze(-1), ['u0'])
u_test= LabelTensor(torch.tensor(data['u_test'], dtype=torch.float).unsqueeze(-1), ['u'])
x = torch.tensor(data['x'], dtype=torch.float)[0]
y = torch.tensor(data['y'], dtype=torch.float)[0]

Expand All @@ -74,10 +73,10 @@ inheriting from ``AbstractProblem``.
.. code:: ipython3

class NeuralOperatorSolver(AbstractProblem):
input_variables = ['u_0']
output_variables = ['u']
conditions = {'data' : Condition(input_points=LabelTensor(k_train, input_variables),
output_points=LabelTensor(u_train, output_variables))}
input_variables = k_train.labels
output_variables = u_train.labels
conditions = {'data' : Condition(input_points=k_train,
output_points=u_train)}

# make problem
problem = NeuralOperatorSolver()
Expand Down Expand Up @@ -114,7 +113,7 @@ training using supervised learning.

.. parsed-literal::

Epoch 9: : 100it [00:00, 383.36it/s, v_num=36, mean_loss=0.108]
Epoch 9: : 100it [00:00, 357.28it/s, v_num=1, mean_loss=0.108]

.. parsed-literal::

Expand All @@ -123,7 +122,7 @@ training using supervised learning.

.. parsed-literal::

Epoch 9: : 100it [00:00, 380.57it/s, v_num=36, mean_loss=0.108]
Epoch 9: : 100it [00:00, 354.81it/s, v_num=1, mean_loss=0.108]


The final loss is pretty high… We can calculate the error by importing
Expand All @@ -137,10 +136,10 @@ The final loss is pretty high… We can calculate the error by importing
metric_err = LpLoss(relative=True)


err = float(metric_err(u_train.squeeze(-1), solver.models[0](k_train).squeeze(-1)).mean())*100
err = float(metric_err(u_train.squeeze(-1), solver.neural_net(k_train).squeeze(-1)).mean())*100
print(f'Final error training {err:.2f}%')

err = float(metric_err(u_test.squeeze(-1), solver.models[0](k_test).squeeze(-1)).mean())*100
err = float(metric_err(u_test.squeeze(-1), solver.neural_net(k_test).squeeze(-1)).mean())*100
print(f'Final error testing {err:.2f}%')


Expand All @@ -163,10 +162,10 @@ operator this approach is better suited, as we shall see.
projecting_net = torch.nn.Linear(24, 1)
model = FNO(lifting_net=lifting_net,
projecting_net=projecting_net,
n_modes=16,
n_modes=8,
dimensions=2,
inner_size=24,
padding=11)
padding=8)


# make solver
Expand All @@ -188,7 +187,7 @@ operator this approach is better suited, as we shall see.

.. parsed-literal::

Epoch 9: : 100it [00:04, 22.13it/s, v_num=37, mean_loss=0.000952]
Epoch 0: : 0it [00:00, ?it/s]Epoch 9: : 100it [00:02, 47.76it/s, v_num=4, mean_loss=0.00106]

.. parsed-literal::

Expand All @@ -197,7 +196,7 @@ operator this approach is better suited, as we shall see.

.. parsed-literal::

Epoch 9: : 100it [00:04, 22.07it/s, v_num=37, mean_loss=0.000952]
Epoch 9: : 100it [00:02, 47.65it/s, v_num=4, mean_loss=0.00106]


We can clearly see that the final loss is lower. Let’s see in testing..
Expand All @@ -207,17 +206,17 @@ training, when many data samples are used.

.. code:: ipython3

err = float(metric_err(u_train.squeeze(-1), solver.models[0](k_train).squeeze(-1)).mean())*100
err = float(metric_err(u_train.squeeze(-1), solver.neural_net(k_train).squeeze(-1)).mean())*100
print(f'Final error training {err:.2f}%')

err = float(metric_err(u_test.squeeze(-1), solver.models[0](k_test).squeeze(-1)).mean())*100
err = float(metric_err(u_test.squeeze(-1), solver.neural_net(k_test).squeeze(-1)).mean())*100
print(f'Final error testing {err:.2f}%')


.. parsed-literal::

Final error training 4.45%
Final error testing 4.91%
Final error training 4.83%
Final error testing 5.16%


As we can see the loss is way lower!
Expand Down
7 changes: 6 additions & 1 deletion pina/model/fno.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import torch.nn as nn
from ..utils import check_consistency
from .layers.fourier import FourierBlock1D, FourierBlock2D, FourierBlock3D
from pina import LabelTensor
import warnings


class FNO(torch.nn.Module):
Expand Down Expand Up @@ -69,7 +71,7 @@ def __init__(self,
elif dimensions == 3:
fourier_layer = FourierBlock3D
else:
NotImplementedError('FNO implemented only for 1D/2D/3D data.')
raise NotImplementedError('FNO implemented only for 1D/2D/3D data.')

# Here we build the FNO by stacking Fourier Blocks

Expand Down Expand Up @@ -137,6 +139,9 @@ def forward(self, x):
:return: The output tensor obtained from the FNO.
:rtype: torch.Tensor
"""
if isinstance(x, LabelTensor): #TODO remove when Network is fixed
warnings.warn('LabelTensor passed as input is not allowed, casting LabelTensor to Torch.Tensor')
x = x.as_subclass(torch.Tensor)

# lifting the input in higher dimensional space
x = self._lifting_net(x)
Expand Down
15 changes: 8 additions & 7 deletions pina/model/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def forward(self, x):
:param torch.Tensor x: Input of the network.
:return torch.Tensor: Output of the network.
"""
# only labeltensors as input
assert isinstance(x, LabelTensor), "Expected LabelTensor as input to the model."

# extract torch.Tensor from corresponding label
# in case `input_variables = []` all points are used
if self._input_variables:
Expand All @@ -65,22 +68,20 @@ def forward(self, x):
for feature in self._extra_features:
x = x.append(feature(x))

# convert LabelTensor to torch.Tensor
x = x.as_subclass(torch.Tensor)

# perform forward pass (using torch.Tensor) + converting to LabelTensor
# perform forward pass + converting to LabelTensor
output = self._model(x).as_subclass(LabelTensor)

# set the labels for LabelTensor
output.labels = self._output_variables

return output


# TODO to remove in next releases (only used in GAROM solver)
def forward_map(self, x):
"""
Forward method for Network class when the input is
a tuple. This class implements the standard forward method,
and it adds the possibility to pass extra features.
a tuple. This class is simply a forward with the input casted as a
tuple or list :class`torch.Tensor`.
All the PINA models ``forward`` s are overriden
by this class, to enable :class:`pina.label_tensor.LabelTensor` labels
extraction.
Expand Down
37 changes: 37 additions & 0 deletions tests/test_model/test_network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import torch
import pytest

from pina.model.network import Network
from pina.model import FeedForward
from pina import LabelTensor

data = torch.rand((20, 3))
data_lt = LabelTensor(data, ['x', 'y', 'z'])
input_dim = 3
output_dim = 4
torchmodel = FeedForward(input_dim, output_dim)
extra_feat = []


def test_constructor():
Network(model=torchmodel,
input_variables=['x', 'y', 'z'],
output_variables=['a', 'b', 'c', 'd'],
extra_features=None)

def test_forward():
net = Network(model=torchmodel,
input_variables=['x', 'y', 'z'],
output_variables=['a', 'b', 'c', 'd'],
extra_features=None)
out = net.torchmodel(data)
out_lt = net(data_lt)
assert isinstance(out, torch.Tensor)
assert isinstance(out_lt, LabelTensor)
assert out.shape == (20, 4)
assert out_lt.shape == (20, 4)
assert torch.allclose(out_lt, out)
assert out_lt.labels == ['a', 'b', 'c', 'd']

with pytest.raises(AssertionError):
net(data)
Loading
Loading