From 48446407276691dffa69a47e67fc143b07b27630 Mon Sep 17 00:00:00 2001 From: Dario Coscia Date: Thu, 9 Nov 2023 11:24:00 +0100 Subject: [PATCH] fix Supervised/PINN solvers forward + fix tut5 --- .../_rst/tutorials/tutorial5/tutorial.rst | 32 ++++--- .../tutorial5/tutorial_files/tutorial_6_0.png | Bin 14293 -> 15473 bytes pina/solvers/pinn.py | 8 +- pina/solvers/supervised.py | 46 ++++++++- tutorials/tutorial5/tutorial.ipynb | 90 +++++++++--------- tutorials/tutorial5/tutorial.py | 26 ++--- 6 files changed, 123 insertions(+), 79 deletions(-) diff --git a/docs/source/_rst/tutorials/tutorial5/tutorial.rst b/docs/source/_rst/tutorials/tutorial5/tutorial.rst index 4a7c4eb..e58e9b2 100644 --- a/docs/source/_rst/tutorials/tutorial5/tutorial.rst +++ b/docs/source/_rst/tutorials/tutorial5/tutorial.rst @@ -44,8 +44,8 @@ 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)[:100, ...] - u_train = torch.tensor(data['u_train'], dtype=torch.float).unsqueeze(-1)[:100, ...] + 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) x = torch.tensor(data['x'], dtype=torch.float)[0] @@ -77,7 +77,7 @@ inheriting from ``AbstractProblem``. input_variables = ['u_0'] output_variables = ['u'] conditions = {'data' : Condition(input_points=LabelTensor(k_train, input_variables), - output_points=LabelTensor(u_train, input_variables))} + output_points=LabelTensor(u_train, output_variables))} # make problem problem = NeuralOperatorSolver() @@ -99,7 +99,7 @@ training using supervised learning. solver = SupervisedSolver(problem=problem, model=model) # make the trainer and train - trainer = Trainer(solver=solver, max_epochs=100, accelerator='cpu', enable_model_summary=False) # we train on CPU and avoid model summary at beginning of training (optional) + trainer = Trainer(solver=solver, max_epochs=10, accelerator='cpu', enable_model_summary=False, batch_size=10) # we train on CPU and avoid model summary at beginning of training (optional) trainer.train() @@ -112,15 +112,18 @@ training using supervised learning. HPU available: False, using: 0 HPUs +.. parsed-literal:: + + Epoch 9: : 100it [00:00, 383.36it/s, v_num=36, mean_loss=0.108] .. parsed-literal:: - Training: 0it [00:00, ?it/s] + `Trainer.fit` stopped: `max_epochs=10` reached. .. parsed-literal:: - `Trainer.fit` stopped: `max_epochs=100` reached. + Epoch 9: : 100it [00:00, 380.57it/s, v_num=36, mean_loss=0.108] The final loss is pretty high… We can calculate the error by importing @@ -143,8 +146,8 @@ The final loss is pretty high… We can calculate the error by importing .. parsed-literal:: - Final error training 56.24% - Final error testing 55.95% + Final error training 56.04% + Final error testing 56.01% Solving the problem with a Fuorier Neural Operator (FNO) @@ -170,7 +173,7 @@ operator this approach is better suited, as we shall see. solver = SupervisedSolver(problem=problem, model=model) # make the trainer and train - trainer = Trainer(solver=solver, max_epochs=100, accelerator='cpu', enable_model_summary=False) # we train on CPU and avoid model summary at beginning of training (optional) + trainer = Trainer(solver=solver, max_epochs=10, accelerator='cpu', enable_model_summary=False, batch_size=10) # we train on CPU and avoid model summary at beginning of training (optional) trainer.train() @@ -183,15 +186,18 @@ operator this approach is better suited, as we shall see. HPU available: False, using: 0 HPUs +.. parsed-literal:: + + Epoch 9: : 100it [00:04, 22.13it/s, v_num=37, mean_loss=0.000952] .. parsed-literal:: - Training: 0it [00:00, ?it/s] + `Trainer.fit` stopped: `max_epochs=10` reached. .. parsed-literal:: - `Trainer.fit` stopped: `max_epochs=100` reached. + Epoch 9: : 100it [00:04, 22.07it/s, v_num=37, mean_loss=0.000952] We can clearly see that the final loss is lower. Let’s see in testing.. @@ -210,8 +216,8 @@ training, when many data samples are used. .. parsed-literal:: - Final error training 10.86% - Final error testing 12.77% + Final error training 4.45% + Final error testing 4.91% As we can see the loss is way lower! diff --git a/docs/source/_rst/tutorials/tutorial5/tutorial_files/tutorial_6_0.png b/docs/source/_rst/tutorials/tutorial5/tutorial_files/tutorial_6_0.png index f62bd0d6391035b3fbab93c234c15aee2904e0b5..fec83e2c20352675a80410dd35fe20a419bc6386 100644 GIT binary patch literal 15473 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 literal 14293 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 B57Gbt diff --git a/pina/solvers/pinn.py b/pina/solvers/pinn.py index 828b36d..746cae7 100644 --- a/pina/solvers/pinn.py +++ b/pina/solvers/pinn.py @@ -83,11 +83,11 @@ class PINN(SolverInterface): :return: PINN solution. :rtype: torch.Tensor """ - # extract labels - x = x.extract(self.problem.input_variables) - # perform forward pass + # extract torch.Tensor from corresponding label + x = x.extract(self.problem.input_variables).as_subclass(torch.Tensor) + # perform forward pass (using torch.Tensor) + converting to LabelTensor output = self.neural_net(x).as_subclass(LabelTensor) - # set the labels + # set the labels for LabelTensor output.labels = self.problem.output_variables return output diff --git a/pina/solvers/supervised.py b/pina/solvers/supervised.py index 2b33405..fb3df56 100644 --- a/pina/solvers/supervised.py +++ b/pina/solvers/supervised.py @@ -72,11 +72,11 @@ class SupervisedSolver(SolverInterface): :return: Solver solution. :rtype: torch.Tensor """ - # extract labels - x = x.extract(self.problem.input_variables) - # perform forward pass + # extract torch.Tensor from corresponding label + x = x.extract(self.problem.input_variables).as_subclass(torch.Tensor) + # perform forward pass (using torch.Tensor) + converting to LabelTensor output = self.neural_net(x).as_subclass(LabelTensor) - # set the labels + # set the labels for LabelTensor output.labels = self.problem.output_variables return output @@ -99,6 +99,44 @@ class SupervisedSolver(SolverInterface): :rtype: LabelTensor """ + dataloader = self.trainer.train_dataloader + condition_idx = batch['condition'] + + for condition_id in range(condition_idx.min(), condition_idx.max()+1): + + condition_name = dataloader.condition_names[condition_id] + condition = self.problem.conditions[condition_name] + pts = batch['pts'] + out = batch['output'] + + if condition_name not in self.problem.conditions: + raise RuntimeError('Something wrong happened.') + + # for data driven mode + if not hasattr(condition, 'output_points'): + raise NotImplementedError('Supervised solver works only in data-driven mode.') + + output_pts = out[condition_idx == condition_id] + input_pts = pts[condition_idx == condition_id] + + loss = self.loss(self.forward(input_pts), output_pts) * condition.data_weight + loss = loss.as_subclass(torch.Tensor) + + self.log('mean_loss', float(loss), prog_bar=True, logger=True) + return loss + + + def training_step_(self, batch, batch_idx): + """Solver training step. + + :param batch: The batch element in the dataloader. + :type batch: tuple + :param batch_idx: The batch index. + :type batch_idx: int + :return: The sum of the loss functions. + :rtype: LabelTensor + """ + for condition_name, samples in batch.items(): if condition_name not in self.problem.conditions: diff --git a/tutorials/tutorial5/tutorial.ipynb b/tutorials/tutorial5/tutorial.ipynb index e5c79f8..9c4dbeb 100644 --- a/tutorials/tutorial5/tutorial.ipynb +++ b/tutorials/tutorial5/tutorial.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 11, "id": "5f2744dc", "metadata": {}, "outputs": [], @@ -54,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 12, "id": "2ffb8a4c", "metadata": {}, "outputs": [], @@ -63,8 +63,8 @@ "data = io.loadmat(\"Data_Darcy.mat\")\n", "\n", "# extract data (we use only 100 data for train)\n", - "k_train = torch.tensor(data['k_train'], dtype=torch.float).unsqueeze(-1)[:100, ...]\n", - "u_train = torch.tensor(data['u_train'], dtype=torch.float).unsqueeze(-1)[:100, ...]\n", + "k_train = torch.tensor(data['k_train'], dtype=torch.float).unsqueeze(-1)\n", + "u_train = torch.tensor(data['u_train'], dtype=torch.float).unsqueeze(-1)\n", "k_test = torch.tensor(data['k_test'], dtype=torch.float).unsqueeze(-1)\n", "u_test= torch.tensor(data['u_test'], dtype=torch.float).unsqueeze(-1)\n", "x = torch.tensor(data['x'], dtype=torch.float)[0]\n", @@ -81,13 +81,13 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "id": "c8501b6f", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiwAAAEjCAYAAAARyVqhAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA73klEQVR4nO3de3RTZbo/8O9O2qaFtilI6QW5FbkI2KIonSpXQUv1IKDDpaPSMoIzHhid1YWjdQkUbz3iURmBA+oMFAdUdAbhzBEZsQocB1AB63Xk0NrSIm1pC73T5rLf3x/+Gg295H1pQ3bD97NWFmTnyc67k+ynT5K9n1cTQggQERERGZjJ1wMgIiIi8oQFCxERERkeCxYiIiIyPBYsREREZHgsWIiIiMjwWLAQERGR4bFgISIiIsNjwUJERESGx4KFiIiIDI8FC3VbWVlZ0DQNlZWVHmMHDRqE9PR01/V9+/ZB0zTs27fPtSw9PR2DBg3q+oESGcxnn32GG2+8ET179oSmacjLy3PtTxdDdt8pKiqCpmnIycm5qMe5GDk5OdA0DUVFRV263smTJ2Py5Mlduk7qGAsWonY0NjYiKyvLragh6u7sdjvmzJmDs2fP4sUXX8Rf/vIXDBw40NfDMqRvv/0WWVlZXV7s0MUJ8PUAiC6F48ePw2TquD5/9dVXoeu663pjYyNWrVoFAPwkRX6joKAAJ0+exKuvvopFixa5lj/++ON49NFHfTgy4/n222+xatUqTJ48udU3SO+//75vBnUZY8FCHjU2NqJHjx6+HkanWCwWjzGBgYGXYCREvnXmzBkAQEREhNvygIAABATwT4KsoKAgXw/hssOfhLq5lt+dv/vuO8ydOxfh4eG44oor8NBDD6GpqcktduvWrRg7dixCQkLQu3dvzJ8/HyUlJW4xkydPxujRo3H06FFMnDgRPXr0wGOPPeb67fk///M/sX79esTFxaFHjx649dZbUVJSAiEEnnzySVx55ZUICQnBzJkzcfbs2Vbjfe+99zBhwgT07NkTYWFhuP322/HNN9+4xXz55ZdIT09HXFwcgoODER0djV//+teoqqpq8zmorKz0uO0XHsPSlp//Dl9UVITIyEgAwKpVq6BpGjRNQ1ZWFjZv3gxN0/D555+3WsczzzwDs9mMH374ocPHIvKF9PR0TJo0CQAwZ84caJrm+vawvWNYZPJGW6qrq5Geng6r1YqIiAikpaWhurpaapx2ux2rVq3C0KFDERwcjCuuuALjx4/H3r173eI+/PBDVz6JiIjAzJkz8a9//cvj+lv25Qv9PE/k5ORgzpw5AIApU6a4ckDLT8RtHcNy5swZ3HfffYiKikJwcDASEhKwZcsWt5if59JXXnkFQ4YMgcViwQ033IDPPvtM6vm5XLGc9hNz587FoEGDkJ2djcOHD+Oll17CuXPn8NprrwEAnn76aSxfvhxz587FokWLUFFRgbVr12LixIn4/PPP3T5tVVVVISUlBfPnz8c999yDqKgo123btm2DzWbD7373O5w9exarV6/G3LlzcfPNN2Pfvn145JFHkJ+fj7Vr12LZsmXYtGmT675/+ctfkJaWhuTkZDz77LNobGzEhg0bMH78eHz++eeuYmHv3r34/vvvsXDhQkRHR+Obb77BK6+8gm+++QaHDx9ulVQ9bfvFiIyMxIYNG/DAAw9g9uzZuPPOOwEA8fHxGDx4MJYsWYJt27bh2muvdbvftm3bMHnyZPTr1++iH5vIW37zm9+gX79+eOaZZ/Dggw/ihhtucNu/L6SSN35OCIGZM2fi448/xm9/+1tcffXVeOedd5CWliY1zqysLGRnZ2PRokUYN24camtrceTIERw7dgy33HILAOCDDz5ASkoK4uLikJWVhfPnz2Pt2rW46aabcOzYsU4fQD9x4kQ8+OCDeOmll/DYY4/h6quvBgDXvxc6f/48Jk+ejPz8fCxduhSDBw/G22+/jfT0dFRXV+Ohhx5yi3/99ddRV1eH3/zmN9A0DatXr8add96J77//nt/2tkdQt7Zy5UoBQNxxxx1uy//93/9dABBffPGFKCoqEmazWTz99NNuMV999ZUICAhwWz5p0iQBQGzcuNEttrCwUAAQkZGRorq62rU8MzNTABAJCQnCbre7lqempoqgoCDR1NQkhBCirq5OREREiMWLF7utt6ysTFitVrfljY2NrbbzjTfeEADEgQMHlLa9xcCBA0VaWprr+kcffSQAiI8++si1LC0tTQwcONB1vaKiQgAQK1eubDWe1NRUERsbK5xOp2vZsWPHBACxefPmVvFERtHy3n/77bfdlrfsTy1U8saF+87OnTsFALF69WrXMofDISZMmCC1jyQkJIjbb7+9w5gxY8aIvn37iqqqKteyL774QphM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", "text/plain": [ "
" ] @@ -116,7 +116,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 14, "id": "8b27d283", "metadata": {}, "outputs": [], @@ -125,7 +125,7 @@ " input_variables = ['u_0']\n", " output_variables = ['u']\n", " conditions = {'data' : Condition(input_points=LabelTensor(k_train, input_variables), \n", - " output_points=LabelTensor(u_train, input_variables))}\n", + " output_points=LabelTensor(u_train, output_variables))}\n", "\n", "# make problem\n", "problem = NeuralOperatorSolver()" @@ -143,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 15, "id": "e34f18b0", "metadata": {}, "outputs": [ @@ -158,24 +158,24 @@ ] }, { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "40f63403b97248a88e49755e8cb096fc", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Training: 0it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 9: : 100it [00:00, 383.36it/s, v_num=36, mean_loss=0.108]" + ] }, { "name": "stderr", "output_type": "stream", "text": [ - "`Trainer.fit` stopped: `max_epochs=100` reached.\n" + "`Trainer.fit` stopped: `max_epochs=10` reached.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 9: : 100it [00:00, 380.57it/s, v_num=36, mean_loss=0.108]\n" ] } ], @@ -188,7 +188,7 @@ "solver = SupervisedSolver(problem=problem, model=model)\n", "\n", "# make the trainer and train\n", - "trainer = Trainer(solver=solver, max_epochs=100, accelerator='cpu', enable_model_summary=False) # we train on CPU and avoid model summary at beginning of training (optional)\n", + "trainer = Trainer(solver=solver, max_epochs=10, accelerator='cpu', enable_model_summary=False, batch_size=10) # we train on CPU and avoid model summary at beginning of training (optional)\n", "trainer.train()\n" ] }, @@ -202,7 +202,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 16, "id": "0e2a6aa4", "metadata": {}, "outputs": [ @@ -210,8 +210,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Final error training 56.24%\n", - "Final error testing 55.95%\n" + "Final error training 56.04%\n", + "Final error testing 56.01%\n" ] } ], @@ -241,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 17, "id": "9af523a5", "metadata": {}, "outputs": [ @@ -256,24 +256,24 @@ ] }, { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "5328859a5d9344ddb818622fd058d2a5", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Training: 0it [00:00, ?it/s]" - ] - }, - "metadata": {}, - "output_type": "display_data" + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 9: : 100it [00:04, 22.13it/s, v_num=37, mean_loss=0.000952]" + ] }, { "name": "stderr", "output_type": "stream", "text": [ - "`Trainer.fit` stopped: `max_epochs=100` reached.\n" + "`Trainer.fit` stopped: `max_epochs=10` reached.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 9: : 100it [00:04, 22.07it/s, v_num=37, mean_loss=0.000952]\n" ] } ], @@ -293,7 +293,7 @@ "solver = SupervisedSolver(problem=problem, model=model)\n", "\n", "# make the trainer and train\n", - "trainer = Trainer(solver=solver, max_epochs=100, accelerator='cpu', enable_model_summary=False) # we train on CPU and avoid model summary at beginning of training (optional)\n", + "trainer = Trainer(solver=solver, max_epochs=10, accelerator='cpu', enable_model_summary=False, batch_size=10) # we train on CPU and avoid model summary at beginning of training (optional)\n", "trainer.train()\n" ] }, @@ -307,7 +307,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 18, "id": "58e2db89", "metadata": {}, "outputs": [ @@ -315,8 +315,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Final error training 10.86%\n", - "Final error testing 12.77%\n" + "Final error training 4.45%\n", + "Final error testing 4.91%\n" ] } ], @@ -366,7 +366,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.9.16" } }, "nbformat": 4, diff --git a/tutorials/tutorial5/tutorial.py b/tutorials/tutorial5/tutorial.py index 82509d8..5dd9406 100644 --- a/tutorials/tutorial5/tutorial.py +++ b/tutorials/tutorial5/tutorial.py @@ -6,7 +6,7 @@ # In this tutorial we are going to solve the Darcy flow problem in two dimensions, presented in [*Fourier Neural Operator for # Parametric Partial Differential Equation*](https://openreview.net/pdf?id=c8P9NQVtmnO). First of all we import the modules needed for the tutorial. Importing `scipy` is needed for input output operations. -# In[1]: +# In[11]: # !pip install scipy # install scipy @@ -32,15 +32,15 @@ import matplotlib.pyplot as plt # Specifically, $u$ is the flow pressure, $k$ is the permeability field and $f$ is the forcing function. The Darcy flow can parameterize a variety of systems including flow through porous media, elastic materials and heat conduction. Here you will define the domain as a 2D unit square Dirichlet boundary conditions. The dataset is taken from the authors original reference. # -# In[17]: +# In[12]: # download the dataset 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)[:100, ...] -u_train = torch.tensor(data['u_train'], dtype=torch.float).unsqueeze(-1)[:100, ...] +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) x = torch.tensor(data['x'], dtype=torch.float)[0] @@ -49,7 +49,7 @@ y = torch.tensor(data['y'], dtype=torch.float)[0] # Let's visualize some data -# In[18]: +# In[13]: plt.subplot(1, 2, 1) @@ -63,14 +63,14 @@ plt.show() # We now create the neural operator class. It is a very simple class, inheriting from `AbstractProblem`. -# In[19]: +# In[14]: 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, input_variables))} + output_points=LabelTensor(u_train, output_variables))} # make problem problem = NeuralOperatorSolver() @@ -80,7 +80,7 @@ problem = NeuralOperatorSolver() # # We will first solve the problem using a Feedforward neural network. We will use the `SupervisedSolver` for solving the problem, since we are training using supervised learning. -# In[20]: +# In[15]: # make model @@ -91,13 +91,13 @@ model = FeedForward(input_dimensions=1, output_dimensions=1) solver = SupervisedSolver(problem=problem, model=model) # make the trainer and train -trainer = Trainer(solver=solver, max_epochs=100, accelerator='cpu', enable_model_summary=False) # we train on CPU and avoid model summary at beginning of training (optional) +trainer = Trainer(solver=solver, max_epochs=10, accelerator='cpu', enable_model_summary=False, batch_size=10) # we train on CPU and avoid model summary at beginning of training (optional) trainer.train() # The final loss is pretty high... We can calculate the error by importing `LpLoss`. -# In[21]: +# In[16]: from pina.loss import LpLoss @@ -117,7 +117,7 @@ print(f'Final error testing {err:.2f}%') # # We will now move to solve the problem using a FNO. Since we are learning operator this approach is better suited, as we shall see. -# In[22]: +# In[17]: # make model @@ -135,13 +135,13 @@ model = FNO(lifting_net=lifting_net, solver = SupervisedSolver(problem=problem, model=model) # make the trainer and train -trainer = Trainer(solver=solver, max_epochs=100, accelerator='cpu', enable_model_summary=False) # we train on CPU and avoid model summary at beginning of training (optional) +trainer = Trainer(solver=solver, max_epochs=10, accelerator='cpu', enable_model_summary=False, batch_size=10) # we train on CPU and avoid model summary at beginning of training (optional) trainer.train() # We can clearly see that the final loss is lower. Let's see in testing.. Notice that the number of parameters is way higher than a `FeedForward` network. We suggest to use GPU or TPU for a speed up in training, when many data samples are used. -# In[23]: +# In[18]: err = float(metric_err(u_train.squeeze(-1), solver.models[0](k_train).squeeze(-1)).mean())*100