Machine learning model of maximum stresses in a gas pipeline branch arising from erosive wear#
The how-to guide provides a detailed description of the creation of a machine learning model for approximating the maximum stresses acting in a bend and arising from erosive wear of the inner surface of the bend during the operation of a gas pipeline.
As you read this guide, you will learn:
Where and why is a gas pipeline loop needed?
The purpose and objectives of modeling the process of operating gas pipelines;
Basic steps to create a machine learning model (ML model) using Python to evaluate the erosion process;
How to use the ML model with a gas-dynamic model of a gas pipeline on the REPEAT VISION platform.
1. General information about gas pipelines#
Gas pipelines (see figure below) are used at gas fields and are designed to transport gas from wells to an integrated gas treatment unit (IGTU). After undergoing the necessary preparation for transport to the IGTU, the gas enters the main gas pipeline.
A gas pipeline loop on a cluster of two gas wells.#
The gas pipeline (№1 in the figure above) is subject to the following influences during operation:
The inner surface of the gas pipeline is loaded with gas pressure and temperature;
The outer surface of the gas pipeline is subject to variable temperatures of the external environment.
1.1. The problem of erosive wear of the gas pipeline#
The process of operating a gas well (№2 in the figure above) may be accompanied by the removal of water containing mechanical impurities, which causes erosive wear of the inner surface of the gas pipeline.
The removal of water containing a small amount of mechanical impurities can occur continuously, which makes it possible to create simple semi-empirical analytical methods for predicting erosive wear.
Erosive wear can also be cyclical in nature with periodic removal of water and mechanical impurities.
There are also very dangerous sudden releases of mechanical impurities with large quantities of water, which can lead to unexpected and significant wear of gas pipeline components.
The most vulnerable element of a gas pipeline is the first branch, if you count from the wellhead of the gas well (№2 in the figure above).
1.2. Ways to reduce risk#
To reduce the risk of incidents and accidents due to erosive wear of the gas pipeline, a comprehensive solution is required, including:
Application of physical and mathematical modeling of the process of operation of a pipeline system and its control system;
Application of automated diagnostic tools for the technical condition of gas pipeline elements;
Implementation of automated control systems for gas well operation processes using the results of mathematical modeling of the current technical condition of the gas pipeline.
2. Diagnostic tools for the technical condition of gas pipelines#
Automated diagnostic tools for the technical condition of gas pipeline components required to monitor the erosion process include the following systems:
Flow control systems;
External temperature sensors and gas pressure and temperature control systems;
Intrusive erosion measurement sensors and acoustic sand monitoring systems;
Point and distributed thickness measurement systems.
3. The purpose and objectives of modeling the operation process of gas pipelines#
Objective: To improve the company’s production efficiency and prevent accidents at industrial facilities.
Modeling tasks:
Calculation of strength parameters based on current data on the technical condition of gas pipelines;
Calculation of the parameters of the collection system for compliance with the technical project for field development;
Ensuring maximum gas production during peak seasonal periods (winter, demand) and minimum production (summer);
Ensuring optimal operating parameters of the collection system during unplanned shutdown of wells or when well operating modes do not correspond to the design ones;
Determination of parameters of the collection system when changing the input parameters of the gas and condensate treatment system.
The solution to problem #1 is presented in the next section. The remaining problems are solved by modeling pipeline systems using REPEAT VISION and solving global and local optimization problems.
4. Description of the task#
Perform an assessment of the stress-strain state (SSS) of the branch based on the initial data and boundary conditions.
The machine learning model is created based on a data table of the static strength calculation results for the branch model. Each row in the data table represents a separate design case. Each design case includes:
Initial data and boundary conditions:
Minimum wall thickness,
Gas pressure
Gas temperature,
Ambient temperature,
Calculation result SSS: Maximum stresses according to von Mises.
The calculation cases are based on the technological regulations of a specific field:
Gas pressure varies in the range: from 8.5 to 6.0 \([MPa]\),
The gas temperature varies in the range: from 7 to 20 \([^{\circ}C]\),
The ambient temperature varies in the range: from -55 to 40 \([^{\circ}C]\),
The design cases include minimum branch wall thicknesses from 6.45 to 4.9 \([mm]\).
The result of the calculation of the static strength of one of the design cases is the von Mises stress distribution shown in the figure below.
An example of von Mises stress distribution in a bend.#
For each result of the calculation of stresses acting in the branch, shown in the figure above, the maximum value is selected and entered into the table of calculation results.
5. Preparing a table of calculation cases#
The calculation of the static strength of the bend is performed in a software package that implements the finite element method (FEM).
5.1. Static strength model#
To determine the SSS of the branch, the influence of the entire gas pipeline-loop and the associated support structures a cluster of two gas wells was taken into account.
Linear sections of the gas pipeline loop and supporting structures were modeled in the beam approximation.
The bends and tees were modeled using volumetric FE.
5.2. The main stages of data preparation#
The preparation of the table of design cases is reduced to an automated process of parametric studies of the static strength of the bend, which in itself is a separate engineering task.
Hint
If you need more information and skills in performing parametric studies, please check out our how-to guide on this topic.
The main stages of the parametric study process are shown in the figure below.
The process of data preparation based on the modeling of the static strength of the bend.#
This process was repeated approximately 6000 times in the parametric study workflow to obtain the required table of design cases for subsequent creation of the machine learning model.
6. Building a neural network model#
Note
You can download the Jupyter notebook file and the requirements.txt file to create the necessary virtual environment if you want to quickly run the code in this tutorial on your personal computer.
Additionally, you will need data for training the model and (optionally) data for visualization of one calculation result.
You can also download the model file and immediately use it in a project on the REPEAT VISION platform using the Approximation model block.
The model will be written using the Python language and the PyTorch framework, so basic knowledge of this language and skills in working with PyTorch are required.
Important
Please note that to install a GPU-enabled version of PyTorch that is compatible with your CUDA version, you need to visit the official website.
In addition, to solve the problem, many libraries that are generally accepted for the corresponding subtasks are used:
NumPy and Pandas — working with arrays, linear algebra, and processing tabular data, respectively.
Matplotlib, Seaborn — visualization of static graphs to monitor the quality of model performance.
Plotly - interactive graphs for visual representation of the object of study.
scikit-learn — metrics calculation and data partitioning.
Перед началом подготовки ML модели необходимо установить Python, а также требуется установить пакеты для работы с данными и создания ML модели. Для этого необходимо выполнить в командной строке вашего компьютера следующие команды и продолжить работу в Jupyter ноутбуке.
mkdir ml_branch_stress
cd ml_branch_stress
python -m venv repeat_ml.
source repeat_ml/bin/activate
python -m pip install numpy pandas scikit-learn onnx onnxscript
python -m pip install seaborn matplotlib plotly
python -m pip install jupyterlab tqdm ipywidgets netron
python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
jupyter lab
mkdir ml_branch_stress
cd ml_branch_stress
python -m venv repeat_ml.
repeat_ml\Scripts\Activate.ps1
python -m pip install numpy pandas scikit-learn onnx onnxscript
python -m pip install seaborn matplotlib plotly
python -m pip install jupyterlab tqdm ipywidgets netron
python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
jupyter lab
First of all, we import the necessary libraries for creating neural networks, building visualizations, and subsequent import into the ONNX format.
1import numpy as np
2import pandas as pd
3
4import seaborn as sns
5import matplotlib.pyplot as plt
6import plotly.graph_objects as go
7
8import torch
9import torch.nn as nn
10import torch.optim as optim
11from torch.utils.data import Dataset, DataLoader
12
13from sklearn.model_selection import train_test_split
14from sklearn.metrics import (mean_squared_error,
15 mean_absolute_error,
16 mean_absolute_percentage_error,
17 r2_score)
18
19from tqdm.notebook import tqdm
20from typing import Tuple, List, Dict, Union
To improve reproducibility and utilization of the available device, it is necessary to fix random number generation and graphics accelerator availability.
1SEED = 42
2torch.manual_seed(SEED)
3np.random.seed(SEED)
4
5device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
6print(device)
6.1. Data exploration#
To visualize the stress distribution in the branch, we visualize the three-dimensional distribution of points using interactive 3D visualization with Plotly.
1def branch_plot(df: pd.DataFrame) -> go.Figure:
2 """
3 Создает 3D визуализацию распределения напряжений.
4
5 Параметры:
6 df: pd.DataFrame
7 DataFrame с данными о координатах и напряжениях
8 Возвращает:
9 go.Figure: Объект графика Plotly
10 """
11 marker_config = dict(
12 size=3,
13 color=df['Stress'],
14 colorscale='Jet',
15 opacity=0.8,
16 colorbar=dict(title="Напряжение (МПа)")
17 )
18 hover_template = (
19 '<b>Узел</b>: %{customdata}<br>'
20 '<b>X</b>: %{x:.2f} мм<br>'
21 '<b>Y</b>: %{y:.2f} мм<br>'
22 '<b>Z</b>: %{z:.2f} мм<br>'
23 '<b>Напряжение</b>: %{marker.color:.2f} МПа'
24 )
25 fig = go.Figure(
26 data=[
27 go.Scatter3d(
28 x=df['X'],
29 y=df['Y'],
30 z=df['Z'],
31 mode='markers',
32 marker=marker_config,
33 hovertemplate=hover_template,
34 customdata=df['Node']
35 )
36 ]
37 )
38 scene_config = dict(
39 xaxis_title='X (мм)',
40 yaxis_title='Y (мм)',
41 zaxis_title='Z (мм)',
42 aspectmode='data',
43 camera=dict(
44 eye=dict(x=0, y=0, z=2.5),
45 center=dict(x=0, y=0, z=0),
46 up=dict(x=0, y=1, z=0)
47 )
48 )
49 margin_config = dict(l=0, r=0, b=0, t=30)
50 fig.update_layout(
51 title='Распределение напряжений',
52 scene=scene_config,
53 margin=margin_config
54 )
55
56 return fig
As a result, for a randomly selected file containing the results of a numerical experiment, a set of points will be plotted. The color of the points will represent the voltage magnitude, and the familiar \(X, Y, Z\) axes will be used. We’ll create the visualization by executing the following code:
1df = pd.read_csv('stress_branch.csv')
2fig = branch_plot(df)
3fig.show()
Stress distribution in the branch.#
After processing these types of files, the data needed for training was aggregated into a table. Of all the von Mises stress values calculated in the bend, the maximum value was recorded in the table.
The final table for setting the regression problem has the following parameters:
Input parameters (features):
Pressure varies in the range: 6.0 - 8.5 \([MPa]\).
Gas temperature (gas_temperature) varies in the range: 7.0 - 20.0 \([^{\circ}C]\).
Ambient temperature (ambient_temperature) varies in the range: -55.0 - 40.0 \([^{\circ}C]\).
Minimum wall thickness (wall_thickness_min), varies in the range: 4.90 - 6.45 \([mm]\).
Target variable:
The maximum von Mises stress in the structure (stress_max) varies in the range: 45.31 - 140.40 \([MPa]\).
1df = pd.read_csv('branch_data.csv')
2df.describe()
To display data directly in the form of a table, with columns divided into features and a target variable, you can use the following code:
1features = ['pressure', 'gas_temp', 'ambient_temp', 'wall_thickness']
2target = 'stress_max'
3
4df[features + [target]].head()
6.2. Data preparation#
To efficiently work with data, PyTorch uses a special class, StressDataset, which inherits from torch.utils.data.Dataset. The parent class is abstract and requires the implementation of two main methods: __len__() and __getitem__(). Ultimately, this class performs several important functions:
Encapsulates data loading and preprocessing logic. In this case, it converts data to a
torch.Tensorand transfers it to the required device.Provides a consistent data access interface that simplifies working with a model that meets the requirements for integration into REPEAT VISION.
Allows you to work efficiently with large data sets by loading them in portions, which is implemented in this case using features as an example.
Simplifies integration with DataLoader for batching and shuffling data.
StressDataset class# 1class StressDataset(Dataset):
2 """
3 Класс датасета для обучения модели.
4 """
5 def __init__(
6 self,
7 X: np.ndarray,
8 y: np.ndarray,
9 device: torch.device = torch.device('cpu')
10 ) -> None:
11 """
12 Инициализация набора данных.
13
14 Параметры:
15 X: np.ndarray
16 Массив входных признаков
17 y: np.ndarray
18 Массив целевых значений
19 device: torch.device
20 Устройство для вычислений
21 """
22 super().__init__()
23 self.device = device
24 self.X = X
25 self.y = torch.FloatTensor(y, device=self.device).reshape(-1, 1)
26
27 def __len__(self) -> int:
28 """
29 Методы получения количества элементов.
30
31 Возвращает:
32 int: Количество элементов
33 """
34 return len(self.X)
35
36 def __getitem__(self, idx: int) -> Tuple[Tuple[torch.Tensor, ...], torch.Tensor]:
37 """
38 Возвращает пару (признаки, целевое значение) для заданного индекса.
39
40 Параметры:
41 idx: int
42 Индекс образца
43
44 Возвращает:
45 Tuple[Tuple[torch.Tensor, ...], torch.Tensor]:
46 Кортеж из кортежа признаков и целевого значения
47 """
48 return (
49 tuple(torch.Tensor([feature], device=self.device)
50 for feature in self.X[idx]),
51 self.y[idx]
52 )
Important
It is important to note that each feature is returned as a separate tensor, which is important for export to ONNX and integration with REPEAT VISION.
Let’s use the created class to prepare data for training.
1X = df[features].values
2y = df[target].values
3
4X_train, X_test, y_train, y_test = train_test_split(
5 X, y,
6 test_size=0.2,
7 random_state=42
8)
9
10train_dataset = StressDataset(X_train, y_train, device)
11test_dataset = StressDataset(X_test, y_test, device)
12
13batch_size = 64
14train_loader = DataLoader(
15 train_dataset,
16 batch_size=batch_size,
17 shuffle=True
18)
19test_loader = DataLoader(
20 test_dataset,
21 batch_size=batch_size,
22 shuffle=False
23)
The result of executing this code is:
Lines 1-2. Separation of numerical values of features and the target variable.
Lines 4-8. Splitting into training and test samples.
Line 10-11. Creating instances of the
StressDatasetclass for training and testingLine 13-23. Creating instances of the
DataLoaderclass to efficiently load data in batches.
6.3. Neural network architecture#
The problem contains four input features: pressure (pressure), gas temperature (gas_temp), ambient temperature (ambient_temp), and wall thickness (wall_thickness). These are all combined into a single vector of size 4. The output is a single numerical value (the predicted maximum stress), making the problem an example of a regression problem:
where:
where:
\(p\) - давление;
\(t_g\) - gas temperature;
\(t_a\) - ambient temperature;
\(w\) - wall thickness;
\(\sigma_{max}\) - maximum voltage.
Various models can be applied to tabular data, from classical machine learning to deep neural networks. In this case, the MLP (Multi Layer Perceptron) model is used.
The model’s architecture is a three-layer fixed-depth MLP, where the size of the hidden layers is configured during initialization. This structure provides sufficient capacity to approximate nonlinear relationships in the data.
Matrix representation of a three-layer MLP model with 4 input channels, 1 output channel and a hidden state \( h \):
where:
\(x \in \mathbb{R}^4\) - input feature vector
\(W_1 \in \mathbb{R}^{h \times 4}, b_1 \in \mathbb{R}^{h}\) - weights and biases of the first layer
\(W_2 \in \mathbb{R}^{h \times h}, b_2 \in \mathbb{R}^{h}\) - weights and biases of the second layer
\(W_3 \in \mathbb{R}^{h \times h}, b_3 \in \mathbb{R}^{h}\) - weights and biases of the third layer
\(W_4 \in \mathbb{R}^{1 \times h}, b_4 \in \mathbb{R}\) - weights and biases of the output layer
\(y \in \mathbb{R}\) is the output value (predicted voltage)
The model uses several ideas:
To stabilize training, the model uses pre-computed statistics (
meanandstd) from the training dataset. Normalizing the input features in theforwardmethod allows each example to be brought to a single distribution without requiring a separate normalizer, which would complicate import into REPEAT VISION.A combination of
Dropout, L2 regularization (weight decay) and early stopping (early stopping) is used as regularization. The ReLU activation function was chosen based on data analysis, although smoother variants could be used alternatively. The Kaiming method is used to initialize the weights, which stabilizes the variance of the layer outputs and prevents gradient problems. The biases are initialized to zeros since they learn themselves quickly.Instead of the popular
BatchNorm, preference is given toLayerNorm, which ensures more stable training on consistent data with significant statistical differences between batches.
All the described ideas are implemented in the StressNet class. This architecture provides a good balance between model capacity and regularization, which is especially important when working with small datasets, as in this problem.
StressNet class# 1class StressNet(nn.Module):
2 """
3 Нейронная сеть.
4 """
5 def __init__(
6 self,
7 input_size: int = 4,
8 hidden_size: int = 32,
9 mean: torch.Tensor = torch.zeros(4),
10 std: torch.Tensor = torch.ones(4),
11 dropout_rate: float = 0.2
12 ) -> None:
13 """
14 Инициализация модели.
15
16 Параметры:
17 input_size: int
18 Размер входного слоя
19 hidden_size: int
20 Размер скрытых слоев
21 mean: torch.Tensor
22 Средние значения для нормализации
23 std: torch.Tensor
24 Стандартные отклонения для нормализации
25 dropout_rate: float
26 Вероятность отключения нейронов
27 """
28 super().__init__()
29
30 self.fc1 = nn.Linear(input_size, hidden_size)
31 self.norm1 = nn.LayerNorm(hidden_size)
32 self.fc2 = nn.Linear(hidden_size, hidden_size)
33 self.norm2 = nn.LayerNorm(hidden_size)
34 self.fc3 = nn.Linear(hidden_size, hidden_size)
35 self.norm3 = nn.LayerNorm(hidden_size)
36 self.out = nn.Linear(hidden_size, 1)
37
38 self.act = nn.ReLU()
39 self.dropout = nn.Dropout(dropout_rate)
40
41 self.mean = mean
42 self.std = std
43
44 self.apply(self._init_weights)
45
46 def _init_weights(self, module: nn.Module) -> None:
47 """
48 Инициализация весов слоев.
49
50 Параметры:
51 module: nn.Module
52 Модуль для инициализации весов
53 """
54 if isinstance(module, nn.Linear):
55 nn.init.kaiming_uniform_(
56 module.weight,
57 nonlinearity='relu'
58 )
59 nn.init.zeros_(module.bias)
60
61 def forward(
62 self,
63 pressure: torch.Tensor,
64 gas_temp: torch.Tensor,
65 ambient_temp: torch.Tensor,
66 wall_thickness: torch.Tensor
67 ) -> torch.Tensor:
68 """
69 Прямой проход через сеть.
70
71 Параметры:
72 pressure: torch.Tensor
73 Давление
74 gas_temp: torch.Tensor
75 Температура газа
76 ambient_temp: torch.Tensor
77 Температура окружения
78 wall_thickness: torch.Tensor
79 Толщина стенки
80
81 Возвращает:
82 torch.Tensor: Предсказанное значение максимального напряжения
83 """
84 batch_size = pressure.shape[0]
85
86 x = torch.cat([
87 pressure.view(batch_size, -1),
88 gas_temp.view(batch_size, -1),
89 ambient_temp.view(batch_size, -1),
90 wall_thickness.view(batch_size, -1)
91 ], dim=1)
92
93 x = (x - self.mean) / self.std
94
95 x = self.fc1(x)
96 x = self.norm1(x)
97 x = self.act(x)
98 x = self.dropout(x)
99
100 x = self.fc2(x)
101 x = self.norm2(x)
102 x = self.act(x)
103 x = self.dropout(x)
104
105 x = self.fc3(x)
106 x = self.norm3(x)
107 x = self.act(x)
108 x = self.dropout(x)
109
110 return self.out(x)
Lines 30-39. Declaring three layers with LayerNorm and Dropout for regularization.
Lines 41-42 Since the input data is normalized within the model,
meanandstdare passed to the constructor.Lines 44. The initialization of the weights is applied (lines 46-59).
Lines 61-110 The main method,
forward, takes the required input parameters, combines them into a single tensor, and then passes it through all the layers in the required order.
After defining the model architecture, it is necessary to initialize the model itself and configure the optimizer and criterion (loss function).
In this case, the following configuration is used:
The hidden layer size (
hidden_size) is set to 512 neurons.The
dropout rateis 0.2Statistics from the training set are used to normalize the input data
The mean square error (MSE) is chosen as the loss function.
AdamW optimizer with learning rate 5e-4 and L2 regularization 1e-4
1hidden_size = 512
2dropout_rate = 0.2
3
4stats = pd.DataFrame(df[features])
5mean = torch.tensor(stats.mean().values, dtype=torch.float32)
6std = torch.tensor(stats.std().values, dtype=torch.float32)
7
8model = StressNet(
9 input_size=len(features),
10 mean=mean,
11 std=std,
12 hidden_size=hidden_size,
13 dropout_rate=dropout_rate
14)
15
16criterion = nn.MSELoss()
17optimizer = optim.AdamW(model.parameters(), lr=5e-4, weight_decay=1e-4)
6.4. Model training#
The train_model function is designed to train a neural network using the gradient descent method. Here are the main components and steps of the function:
Preparation:
Transfer the model to the specified device.
Initializing lists for tracking losses.
Setting up early stopping.
Learning cycle:
Learning from training data.
Validation on test data.
Tracking the best model.
Checking early stopping conditions.
Monitoring:
Tracking the value of the loss function.
Saving the best model.
Conclusion of learning progress.
This implementation provides stable model training with early stopping capability to prevent overfitting.
train_model function# 1def train_model(
2 model: nn.Module,
3 train_loader: DataLoader,
4 val_loader: DataLoader,
5 criterion: nn.Module,
6 optimizer: optim.Optimizer,
7 epochs: int,
8 device: torch.device = torch.device('cpu'),
9 patience: int = 20
10) -> Tuple[nn.Module, List[float], List[float]]:
11 """
12 Обучает модель нейронной сети.
13
14 Параметры:
15 model: nn.Module
16 Модель нейронной сети для обучения
17 train_loader: DataLoader
18 Загрузчик данных для обучающей выборки
19 val_loader: DataLoader
20 Загрузчик данных для валидационной выборки
21 criterion: nn.Module
22 Функция потерь
23 optimizer: optim.Optimizer
24 Оптимизатор для обновления весов
25 epochs: int
26 Количество эпох обучения
27 device: torch.device
28 Устройство для вычислений (CPU/GPU)
29 patience: int
30 Количество проведенных эпох без улучшения, достаточное для ранней остановки обучения
31
32 Возвращает:
33 Tuple[nn.Module, List[float], List[float]]: Обученная модель,
34 список потерь на обучающей выборке, список потерь на валидационной выборке
35 """
36 model.to(device)
37
38 train_losses = []
39 val_losses = []
40
41 best_val_loss = float('inf')
42 no_improvement = 0
43 best_model_state = None
44
45 for epoch in tqdm(range(epochs), desc='Training'):
46 model.train()
47 train_loss = 0.0
48
49 for inputs, targets in train_loader:
50 optimizer.zero_grad()
51
52 outputs = model(*inputs)
53 loss = criterion(outputs, targets)
54
55 loss.backward()
56 optimizer.step()
57
58 train_loss += loss.item() * targets.size(0)
59
60 train_loss = train_loss / len(train_loader.dataset)
61 train_losses.append(train_loss)
62
63 model.eval()
64 val_loss = 0.0
65
66 with torch.no_grad():
67 for inputs, targets in val_loader:
68 outputs = model(*inputs)
69 loss = criterion(outputs, targets)
70
71 val_loss += loss.item() * targets.size(0)
72
73 val_loss = val_loss / len(val_loader.dataset)
74 val_losses.append(val_loss)
75
76 if epoch % 10 == 0:
77 print(
78 f'Epoch {epoch+1}/{epochs}, '
79 f'Train Loss: {train_loss:.6f}, '
80 f'Val Loss: {val_loss:.6f}'
81 )
82
83 if val_loss < best_val_loss:
84 best_val_loss = val_loss
85 no_improvement = 0
86 best_model_state = model.state_dict().copy()
87 else:
88 no_improvement += 1
89 if no_improvement >= patience:
90 print(f'Early stopping at epoch {epoch+1}')
91 model.load_state_dict(best_model_state)
92 break
93
94 return model, train_losses, val_losses
Lines 49-58. The main training loop with loss calculation on the training dataset.
Lines 66-71. Model validation and loss calculation on the validation set.
Lines 83-92. Early stopping and best model retention logic.
To train the model, you need to call the train_model function, passing it the previously created parameters and entities correctly.
train_model function# 1model, train_losses, val_losses = train_model(
2 model=model,
3 train_loader=train_loader,
4 val_loader=test_loader,
5 criterion=criterion,
6 optimizer=optimizer,
7 epochs=500,
8 device=device,
9 patience=10
10)
6.5. Model evaluation#
The ‘evaluate_model’ function is used to evaluate the performance of a trained neural network on a test dataset. It performs the following steps:
Preparing the model:
Puts the model into evaluation mode (
model.eval()).Disables gradient calculation to optimize memory.
Data processing:
Iterates through all batches in DataLoader.
Gets the model’s predictions.
Collects real values and predictions into lists.
Metrics calculation:
MSE (Mean Squared Error) - root mean square error.
RMSE (Root Mean Squared Error) is the root of the root mean square error.
MAPE (Mean Absolute Percentage Error) - mean absolute percentage error.
MAE (Mean Absolute Error) - mean absolute error.
R² (R-squared) - coefficient of determination.
evaluate_model function# 1def evaluate_model(
2 model: nn.Module,
3 data_loader: DataLoader,
4 criterion: nn.Module,
5 device: torch.device = torch.device('cpu')
6) -> Dict[str, Union[float, np.ndarray]]:
7 """
8 Оценивает производительность модели на заданном наборе данных.
9
10 Параметры:
11 model: nn.Module
12 Модель PyTorch для оценки
13 data_loader: DataLoader
14 DataLoader с данными для оценки
15 criterion: nn.Module
16 Функция потерь
17 device: torch.device
18 Устройство для вычислений
19
20 Возвращает:
21 Dict[str, Union[float, np.ndarray]]: Словарь с метриками оценки
22 и результатами предсказаний
23 """
24 model.to(device)
25 model.eval()
26
27 all_targets = []
28 all_predictions = []
29 total_loss = 0.0
30
31 with torch.no_grad():
32 for inputs, targets in data_loader:
33 outputs = model(*inputs)
34 loss = criterion(outputs, targets)
35 total_loss += loss.item() * targets.size(0)
36
37 all_targets.extend(targets.cpu().numpy())
38 all_predictions.extend(outputs.cpu().numpy())
39
40 all_targets = np.array(all_targets).flatten()
41 all_predictions = np.array(all_predictions).flatten()
42
43 mse = mean_squared_error(all_targets, all_predictions)
44 rmse = np.sqrt(mse)
45 mape = mean_absolute_percentage_error(all_targets, all_predictions)
46 mae = mean_absolute_error(all_targets, all_predictions)
47 r2 = r2_score(all_targets, all_predictions)
48
49 avg_loss = total_loss / len(data_loader.dataset)
50
51 return {
52 'loss': avg_loss,
53 'mse': mse,
54 'rmse': rmse,
55 'mape': mape,
56 'mae': mae,
57 'r2': r2,
58 'targets': all_targets,
59 'predictions': all_predictions
60 }
In this implementation, many different metrics of the regression problem are considered in order to obtain the most complete understanding of the quality and accuracy of the problem solution.
To get all the metrics, simply run the following code:
evaluate_model function#1test_results = evaluate_model(model, test_loader, criterion, device)
2
3print("Test metrics:")
4print(f"MSE: {test_results['loss']:.3f}")
5print(f"RMSE: {test_results['rmse']:.3f}")
6print(f"MAPE: {test_results['mape']*100:.3f}%")
7print(f"MAE: {test_results['mae']:.3f}")
8print(f"R^2: {test_results['r2']:.3f}")
This code will output the results of the model testing.
Metrics |
Meaning |
Interpretation |
|---|---|---|
MSE |
5.281 |
Root mean square error |
RMSE |
2.298 |
Root of the mean square error |
MAPE |
1.066% |
Mean absolute percentage error |
MAE |
0.814 |
Mean absolute error |
R² |
0.987 |
Coefficient of determination |
6.6. Visualization of results#
The visualize_results function is designed to visualize the dynamics of the change in the loss function on the training and validation samples and to construct an error histogram.
visualize_results function# 1def visualize_results(
2 train_losses: List[float],
3 val_losses: List[float],
4 test_results: Dict[str, np.ndarray]
5) -> None:
6 """
7 Визуализирует результаты обучения модели.
8
9 Параметры:
10 train_losses: List[float]
11 Список значений функции потерь на обучающей выборке
12 val_losses: List[float]
13 Список значений функции потерь на валидационной выборке
14 test_results: Dict[str, np.ndarray]
15 Словарь с результатами тестирования модели
16 """
17 _, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
18
19 ax1.plot(train_losses, label='Train Loss')
20 ax1.plot(val_losses, label='Val Loss')
21 ax1.set_xlabel('Epoch')
22 ax1.set_ylabel('Loss')
23 ax1.legend()
24 ax1.grid(True)
25
26 errors: np.ndarray = test_results['predictions'] - test_results['targets']
27 sns.histplot(errors, bins=30)
28 ax2.set_xlabel('Error')
29 ax2.set_ylabel('Frequency')
30 ax2.grid(True)
31
32 plt.tight_layout()
33 plt.show()
Let’s call this function.
visualize_results function#1visualize_results(train_losses, val_losses, test_results)
As a result, we have two graphs:
The first is the dynamics of the loss function during training and validation.
The second is a histogram of the distribution of prediction errors.
Graphs of changes in the value of the loss function and the distribution of errors.#
6.7. Exporting a model to ONNX#
The export_to_onnx function is designed to export a trained PyTorch model to the ONNX format. The function takes into account the need to convert to double mode and takes into account the names of the input and output parameters.
export_to_onnx function# 1def export_to_onnx(
2 model: nn.Module,
3 features: List[str],
4 target: str,
5 output_path: str = "stress_model.onnx"
6) -> None:
7 """
8 Экспортирует PyTorch модель в формат ONNX.
9
10 Args:
11 model: nn.Module
12 PyTorch модель для экспорта
13 features: List[str]
14 Список имен входных признаков
15 target: str
16 Имя целевой переменной
17 output_path: str
18 Путь для сохранения ONNX модели
19 """
20 model.eval()
21 model.double()
22
23 dummy_inputs: Dict[str, torch.Tensor] = {
24 feature: torch.zeros(1, 1, dtype=torch.float64)
25 for feature in features
26 }
27
28 torch.onnx.export(
29 model,
30 dummy_inputs,
31 output_path,
32 input_names=features,
33 output_names=[target],
34 opset_version=12,
35 dynamo = False
36 )
37
38 print(f"Model exported to {output_path}.")
To configure a previously trained model, it is necessary to pass it to the previously declared model and the names of the features and target variable for correct display in REPEAT VISION.
export_to_onnx function#1export_to_onnx(model, features, target)
In the next step, the saved model will be imported into the REPEAT VISION platform in the gas pipeline project to perform interdisciplinary modeling.
7. The REPEAT VISION platform’s gas pipeline project#
The figure below shows a project on the REPEAT VISION platform for modeling a gas pipeline using blocks from the Pneumatics, Automation, and External models libraries. This model of gas pipeline-loop allows:
Calculate the parameters of the collection system to ensure compliance with the technical design for the field development;
Determine the parameters of the collection system when changing the input parameters of the gas and condensate treatment system.
Gas pipeline project on the REPEAT VISION platform.#
Within the framework of a single project, based on current data from diagnostics of the technical condition of the gas pipeline-loop, the calculation of von Mises stresses of the branch is also performed using the block №12 Approximation model and the machine learning model imported into this block.
The project shown above allows:
Perform computational experiments on machine learning models,
In a general interdisciplinary model develop a control system for the technological process of gas production, taking into account the assessment of the maximum von Mises stresses in the branch;
Using Modbus blocks, it is possible to create digital twins of gas pipelines for the stage of their operation, connecting thickness measurement systems, pressure, temperature and gas flow measurement systems to the model.
7.1. Results of computational experiments#
The dynamics of the decrease in the wall thickness of the bend is shown in the figure below.
Dynamics of reduction of minimum wall thickness of the bend.#
A decrease in the thickness of the branch wall, as well as changes in the ambient temperature while maintaining a constant gas pressure, lead to an increase in stress in the branch, the approximate results of which are shown in the next figure.
Dynamics of increase of maximum von Mises stresses in a bend.#
8. Conclusions#
Integration of machine learning models into the REPEAT VISION platform:
Expands the range of tasks to be solved in system modeling and in the creation of digital twins of complex systems;
Provides the use of FVM and FEM models of large dimensions, as well as models based on the results of diagnostics of operating systems;
Compared to direct integration with external engineering software: eliminates the risk of errors due to problems with the convergence of the FEM (FVM) model calculation, significantly increases the speed of structural design calculation.