export tutorials changed in db9df8b

This commit is contained in:
dario-coscia
2025-05-05 08:59:15 +00:00
committed by Dario Coscia
parent a94791f0ff
commit e3d4c2fc1a
23 changed files with 737 additions and 727 deletions

View File

@@ -5,7 +5,7 @@
# [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mathLab/PINA/blob/master/tutorials/tutorial4/tutorial.ipynb)
# In this tutorial, we will show how to use the Continuous Convolutional Filter, and how to build common Deep Learning architectures with it. The implementation of the filter follows the original work [*A Continuous Convolutional Trainable Filter for Modelling Unstructured Data*](https://arxiv.org/abs/2210.13416).
#
#
# First of all we import the modules needed for the tutorial:
# In[ ]:
@@ -37,72 +37,72 @@ warnings.filterwarnings("ignore")
# ## Tutorial Structure
#
#
# The tutorial is structured as follows:
#
# - [🔹 Continuous Filter Background](#continuous-filter-background):
#
# - [🔹 Continuous Filter Background](#continuous-filter-background):
# Understand how the convolutional filter works and how to use it.
#
# - [🔹 Building a MNIST Classifier](#building-a-mnist-classifier):
#
# - [🔹 Building a MNIST Classifier](#building-a-mnist-classifier):
# Learn how to build a simple classifier using the MNIST dataset, and how to combine a continuous convolutional layer with a feedforward neural network.
#
# - [🔹 Building a Continuous Convolutional Autoencoder](#building-a-continuous-convolutional-autoencoder):
#
# - [🔹 Building a Continuous Convolutional Autoencoder](#building-a-continuous-convolutional-autoencoder):
# Explore how to use the continuous filter to work with unstructured data for autoencoding and up-sampling.
#
#
# ## Continuous Filter Background
#
#
# As reported by the authors in the original paper, in contrast to discrete convolution, **continuous convolution** is mathematically defined as:
#
#
# $$
# \mathcal{I}_{\rm{out}}(\mathbf{x}) = \int_{\mathcal{X}} \mathcal{I}(\mathbf{x} + \mathbf{\tau}) \cdot \mathcal{K}(\mathbf{\tau}) d\mathbf{\tau},
# $$
#
#
# where:
# - $\mathcal{K} : \mathcal{X} \rightarrow \mathbb{R}$ is the **continuous filter** function,
# - $\mathcal{I} : \Omega \subset \mathbb{R}^N \rightarrow \mathbb{R}$ is the input function.
#
#
# The **continuous filter function** is approximated using a **FeedForward Neural Network**, which is **trainable** during the training phase. The way in which the integral is approximated can vary. In the **PINA** framework, we approximate it using a simple sum, as suggested by the authors. Thus, given the points $\{\mathbf{x}_i\}_{i=1}^{n}$ in $\mathbb{R}^N$ mapped onto the filter domain $\mathcal{X}$, we approximate the equation as:
#
#
# $$
# \mathcal{I}_{\rm{out}}(\mathbf{\tilde{x}}_i) = \sum_{{\mathbf{x}_i}\in\mathcal{X}} \mathcal{I}(\mathbf{x}_i + \mathbf{\tau}) \cdot \mathcal{K}(\mathbf{x}_i),
# $$
#
#
# where $\mathbf{\tau} \in \mathcal{S}$, with $\mathcal{S}$ being the set of available strides, represents the current stride position of the filter. The $\mathbf{\tilde{x}}_i$ points are obtained by taking the **centroid** of the filter position mapped onto the domain $\Omega$.
#
#
# ### Working with the Continuous Filter
#
#
# From the above definition, what is needed is:
# 1. A **domain** and a **function** defined on that domain (the input),
# 2. A **stride**, corresponding to the positions where the filter needs to be applied (this is the `stride` variable in `ContinuousConv`),
# 3. The **filter's rectangular domain**, which corresponds to the `filter_dim` variable in `ContinuousConv`.
#
#
# ### Input Function
#
#
# The input function for the continuous filter is defined as a tensor of shape:
#
#
# $$[B \times N_{\text{in}} \times N \times D]$$
#
#
# where:
# - $B$ is the **batch size**,
# - $N_{\text{in}}$ is the number of input fields,
# - $N$ is the number of points in the mesh,
# - $D$ is the dimension of the problem.
#
# - $D$ is the dimension of the problem.
#
# In particular:
# - $D$ represents the **number of spatial variables** + 1. The last column must contain the field value. For example, for 2D problems, $D=3$ and the tensor will look like `[first coordinate, second coordinate, field value]`.
# - $N_{\text{in}}$ represents the number of vectorial functions presented. For example, a vectorial function $f = [f_1, f_2]$ will have $N_{\text{in}}=2$.
#
#
# #### Example: Input Function for a Vectorial Field
#
#
# Lets see an example to clarify the idea. Suppose we wish to create the function:
#
#
# $$
# f(x, y) = [\sin(\pi x) \sin(\pi y), -\sin(\pi x) \sin(\pi y)] \quad (x,y)\in[0,1]\times[0,1]
# $$
#
#
# We can do this with a **batch size** equal to 1. This function consists of two components (vectorial field), so $N_{\text{in}}=2$. For each $(x,y)$ pair in the domain $[0,1] \times [0,1]$, we will compute the corresponding field values:
#
#
# 1. $\sin(\pi x) \sin(\pi y)$
# 2. $-\sin(\pi x) \sin(\pi y)$
@@ -139,9 +139,9 @@ print(f"Filter input data has shape: {data.shape}")
# ### Stride
#
#
# The **stride** is passed as a dictionary `stride` that dictates where the filter should move. Here's an example for the domain $[0,1] \times [0,5]$:
#
#
# ```python
# # stride definition
# stride = {"domain": [1, 5],
@@ -155,9 +155,9 @@ print(f"Filter input data has shape: {data.shape}")
# 2. `start`: The starting position of the filter's centroid. In this example, the filter starts at the position $(0, 0)$.
# 3. `jump`: The steps or jumps of the filters centroid to the next position. In this example, the filter moves by $(0.1, 0.3)$ along the x and y axes respectively.
# 4. `direction`: The directions of the jumps for each coordinate. A value of 1 indicates the filter moves right, 0 means no movement, and -1 indicates the filter moves left with respect to its current position.
#
#
# ### Filter definition
#
#
# Now that we have defined the stride, we can move on to construct the continuous filter.
# Lets assume we want the output to contain only one field, and we will set the filter dimension to be $[0.1, 0.1]$.
@@ -185,7 +185,7 @@ cConv = ContinuousConvBlock(
# That's it! In just one line of code, we have successfully created the continuous convolutional filter. By default, the `pina.model.FeedForward` neural network is initialized, which can be further customized according to your needs.
#
#
# Additionally, if the mesh does not change during training, we can set the `optimize` flag to `True` to leverage optimizations for efficiently finding the points to convolve. This feature helps in improving the performance by reducing redundant calculations when the mesh remains constant.
# In[4]:
@@ -244,10 +244,10 @@ cConv = ContinuousConvBlock(
)
# Notice that we pass the **class** of the model and not an already built object! This is important because the `ContinuousConv` filter will automatically instantiate the model class when needed during training.
#
# Notice that we pass the **class** of the model and not an already built object! This is important because the `ContinuousConv` filter will automatically instantiate the model class when needed during training.
#
# ## Building a MNIST Classifier
#
#
# Let's see how we can build a MNIST classifier using a continuous convolutional filter. We will use the MNIST dataset from PyTorch. In order to keep small training times we use only 6000 samples for training and 1000 samples for testing.
# In[7]:
@@ -276,7 +276,7 @@ train_data = torchvision.datasets.MNIST(
# Now, let's proceed to build a simple classifier for the MNIST dataset. The MNIST dataset consists of vectors with the shape `[batch, 1, 28, 28]`, but we can treat them as field functions where each pixel at coordinates $i,j$ corresponds to a point in a $[0, 27] \times [0, 27]$ domain. The pixel values represent the field values.
#
#
# To use the continuous convolutional filter, we need to transform the regular tensor into a format compatible with the filter. Here's a function that will help with this transformation:
# In[8]:
@@ -401,9 +401,9 @@ print(f"Accuracy of the network on the test images: {(correct / total):.3%}")
# As we can see we have very good performance for having trained only for 1 epoch! Nevertheless, we are still using structured data... Let's see how we can build an autoencoder for unstructured data now.
#
#
# ## Building a Continuous Convolutional Autoencoder
#
#
# As a toy problem, we will now build an autoencoder for the function \( f(x, y) = \sin(\pi x) \sin(\pi y) \) on the unit circle domain centered at \( (0.5, 0.5) \). We will also explore the ability to up-sample the results (once trained) without needing to retrain the model. To begin, we'll generate the input data for the function. First, we will use a mesh of 100 points and visualize the input function. Heres how to proceed:
# In[12]:
@@ -451,7 +451,7 @@ plt.show()
# Now, let's create a simple autoencoder using the continuous convolutional filter. Since the data is inherently unstructured, a standard convolutional filter may not be effective without some form of projection or interpolation. We'll begin by building an `Encoder` and `Decoder` class, and then combine them into a unified `Autoencoder` class.
#
#
# In[13]:
@@ -608,9 +608,9 @@ print(f"l2 error: {l2_error(input_data[0, 0, :, -1], output[0, 0, :, -1]):.2%}")
# The $l_2$ error is approximately $4\%$, which is quite low considering that we only use **one** convolutional layer and a simple feedforward network to reduce the dimension. Now, let's explore some of the unique features of the filter.
#
#
# ### Upsampling with the Filter
#
#
# Suppose we have a hidden representation and we want to upsample it on a different grid with more points. Let's see how we can achieve that:
# In[18]:
@@ -656,15 +656,15 @@ print(
# ## What's Next?
#
#
# Congratulations on completing the tutorial on using the Continuous Convolutional Filter in **PINA**! Now that you have the basics, there are several exciting directions you can explore:
#
#
# 1. **Train using Physics-Informed strategies**: Leverage physics-based knowledge to improve model performance for solving real-world problems.
#
#
# 2. **Use the filter to build an unstructured convolutional autoencoder**: Explore reduced-order modeling by implementing unstructured convolutional autoencoders.
#
#
# 3. **Experiment with upsampling at different resolutions**: Try encoding or upsampling on different grids to see how the model generalizes across multiple resolutions.
#
#
# 4. **...and many more!**: There are endless possibilities, from improving model architecture to testing with more complex datasets.
#
#
# For more resources and tutorials, check out the [PINA Documentation](https://mathlab.github.io/PINA/).