Projekt 6

5. Przykłady

W przykładzie wykorzystano sieć neuronową typu Sequencial z biblioteki Keras, o strukturze podobnej jak w projekcie 4.

Do wygenerowania danych uczących wykorzystano generator SP – skoki sygnału SP w całym zakresie zmian. Testy przeprowadzono dla skoków wokół punktu pracy. Dane przygotowano w ten sposób, że dodano do nich dodatkowe kolumny dla sygnałów wejść i wyjścia z modelu opóźnionych o 1 krok (k-1):


dataset_train['L2_1'] = dataset_train['L2'].shift(1)
dataset_train.loc[0, "L2_1"] = dataset_train['L2_1'][1]

dataset_train['L3_1'] = dataset_train['L3'].shift(1)
dataset_train.loc[0, "L3_1"] = dataset_train['L3_1'][1]

dataset_test['L2_1'] = dataset_test['L2'].shift(1)
dataset_test.loc[0, "L2_1"] = dataset_test['L2_1'][1]

dataset_test['L3_1'] = dataset_test['L3'].shift(1)
dataset_test.loc[0, "L3_1"] = dataset_test['L3_1'][1]
    
Dane uczące wykorzystane do identyfikacji modelu poziomu w zbiorniku 3
Dane uczące wykorzystane do identyfikacji modelu poziomu w zbiorniku 3
Dane uczące wykorzystane do identyfikacji modelu poziomu w zbiorniku 3
Dane testowe wykorzystane do identyfikacji modelu poziomu w zbiorniku 3
Dane testowe wykorzystane do identyfikacji modelu poziomu w zbiorniku 3
Dane testowe wykorzystane do identyfikacji modelu poziomu w zbiorniku 3

Do modelowania wybrano sieć typu Sequential z biblioteki Keras. Wybrano bardzo prostą strukturę sieci, z jedną warstwą ukrytą i funkcjami aktywacji w postaci tanh oraz optymalizatorem Adam. Wykorzystano normalizację sygnałów wejściowych.

Listing: Normalizacja i konfiguracja modelu sieci neuronowych

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

# Narmalizacja
normalizer = tf.keras.layers.Normalization(axis=-1)
normalizer.adapt(np.array(train_features))

# Warstwa wejść i konstrukcja modelu
input_layer = keras.layers.Input(train_features.shape[1:])
model = keras.Sequential([
      input_layer,
      layers.Dense(2, activation='tanh'),
      layers.Dense(1)
  ])

# Kompilacja modelu
model.compile(loss='mean_absolute_error',
              optimizer=tf.keras.optimizers.Adam(0.02))
    

Na rysunkach poniżej przedstawiono podsumowanie jakości modelowania, zarówno w trybie STD jak i MRO. Jest ona zadawalająca, chociaż widać, że w trybie MRO dokładność modelu spada.

Jakość odtwarzania wielkości L3 na podstawie modelu neuronowego w trybie STD
Jakość odtwarzania wielkości L3 na podstawie modelu neuronowego w trybie STD
Jakość odtwarzania wielkości L3 na podstawie modelu neuronowego w trybie MRO
Jakość odtwarzania wielkości L3 na podstawie modelu neuronowego w trybie MRO

Aby osadzić model neuronowy w symulatorze przygotowano dedykowany blok funkcyjny KerasModelFirstOrder (modelling.py), przygotowany do przetwarzania opóźnionych o 1 krok sygnałów we/wy.

Listing: Implementacja modelu neuronowego w postaci dedykowanego bloku funkcyjnego

from tensorflow import keras
from common.block import Block
from numpy import array

class KerasModelFirstOrder(Block):
    def __init__(self, _name, _model, _mode):
        super().__init__(_name)
        self.add_output(None, 1, 0.018)

        self.mode = _mode
        self.model = keras.models.load_model('../output/'+_model+'.keras')

    def add_input(self, _input, _del=1):
        super().add_input(_input, _del)

    def calculate(self):
        match self.mode:
            case 'std':
                features = array([[self.input_val_del(0, 1), self.input_val_del(1, 1)]])
            case 'mro':
                features = array([[self.input_val_del(0, 1), self.output_val_del(0, 1)]])
            case _:
                features = 0
        self.output_val(0, self.model.predict(features, verbose=None)[0][0])
    

W ostatnim kroku dodano „czujnik programowy” do konfiguracji głównej symulatora (konstruktor klasy TtsPidSim). Dodano także dodatkowy parametr konstruktora _use_L3_modelling, według którego model wykorzystywany jest tylko do odtwarzania wielkości L3 (monit) lub jest także wykorzystywany w pętli sprzężenia zwrotnego jako wielkość regulowana (control):


# Process-Controller feadback: use L3 measurement
if _use_L3_modelling!='control':
    pv_measure.add_input(three_tanks.output(2))
# Soft-sensor: modelling L3
if _use_L3_modelling=='monit' or _use_L3_modelling=='control' :
    l3_pred = monit_sim.add_block( KerasModelFirstOrder('l3_pred', 'L3_L2_1_L3_1', 'std'))
    l3_pred.add_input(l2_dist.output(0))
    l3_pred.add_input(l3_dist.output(0))

# Process-Controller feadback: use L3 model output
if _use_L3_modelling=='control':
    pv_measure.add_input(l3_pred.output(0))
    

Pojawiła się także nowa zmienna wyjściowa symulatora L3_pred:


if _use_L3_modelling=='monit' or _use_L3_modelling=='control' :
    self.add_out_var('L3_pred', l3_pred.output(0))
    

Jak widać na rysunkach poniżej osiągnięto zadowalającą, ale daleką od optymalnej, jakość regulacji, zarówno w trybie STD jak i MRO.

Jakość regulacji ze sprzężeniem od wyjścia modelu działającego w trybie STD
Jakość regulacji ze sprzężeniem od wyjścia modelu działającego w trybie STD
Jakość regulacji ze sprzężeniem od wyjścia modelu działającego w trybie MRO
Jakość regulacji ze sprzężeniem od wyjścia modelu działającego w trybie MRO