Skip to main content

Neural network based control charting for multiple stream processes

Project description

NN4MSP

This repository contains Python code and data of the paper of Lepore, Palumbo and Sposito Neural network based control charting for multiple stream processes with an application to HVAC systems in passenger railway vehicles. Note that this work has been done in the framework of the R&D project of the multiregional investment programme REINForce:REsearch to INspire the Future (CDS000609) with Hitachi Rail STS (https://www.hitachirail.com/), supported by the Italian Ministry for Economic Development (MISE) through the Invitalia agency.

This repository contains the following files:

  • NN4MSP/data contains the HVAC data set
  • NN4MSP/dataset.py allows the user to access the HVAC data set from the NN4MSPpackage
  • NN4MSP/functions.py is the source code of the Python package NN4MSP
  • NN4MSP_tutorial.ipynb is the Jupyter Notebook performing all the analysis shown in the Section "A real-case study" of the paper

Moreover, in the following Section we provide a tutorial to show how to implement in Python the proposed methodology used in the paper to the real-case study.

Neural network based control charting for multiple stream processes with an application to HVAC systems in passenger railway vehicles

Introduction

This tutorial shows how to implement in Python the proposed methodology to the real-case study to monitor the HVAC systems installed on board of passenger railway vehicles. The operational data were acquired and made available by the rail transport company Hitachi Rail STS based in Italy. HVAC data set contains the data analyzed in the paper and can be loaded by using the function load_HVAC_data(). Alternatively, one can use another data set and apply this methodology to any multiple stream process.

You can install the development version of the Python package NN4MSP from GitHub with

pip install git+https://github.com/unina-sfere/NN4MSP#egg=NN4MSP

You can install the Python package NN4MSP using pip

pip install NN4MSP
# Import libraries

import pandas as pd
import numpy as np

from sklearn import preprocessing
from sklearn.metrics import roc_curve, auc
from sklearn.model_selection import train_test_split
from itertools import combinations

import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
from matplotlib.ticker import ScalarFormatter, AutoMinorLocator

from keras import Sequential
from keras.layers import Dense

from NN4MSP.functions import *
import NN4MSP.dataset

Neural Network training

Set the simulation parameters to properly generate the data set to train the Neaural Network (NN).

# Simulation parameters

s = 6 # number of streams
k = 5 # subgroup size
num_neg_samples = 55800 # number of negative samples of k observations
num_pos_samples = 300 # number of positive samples of k observations for each OC scenario

loc_res = 0 # Mean of the distribution of the residuals
scale_res = 1 # Standard deviation of the distribution of the residuals

Then, call the function dataset_generator from the MSPforNN package to generate the data set, simulated according to the procedure described in the simulation section of the paper, and the corresponding vector of classes 0 (negative sample) and 1 (positive sample)

X, y = dataset_generator(s = s, k = k, num_neg_samples = num_neg_samples, num_pos_samples = num_pos_samples, loc_res = loc_res, 
                scale_res = scale_res, set_seed = 0)

Split the simulated data set into 70% training set and 30% validation set.

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.3, stratify = y ,random_state=27)

Standardize the features by removing the mean and scaling to unit variance.

scaler = preprocessing.StandardScaler().fit(X_train)
X_train = scaler.transform(X_train)
X_val = scaler.transform(X_val)

Set the NN hyperparameters and train the NN with the function NN_model from the MSPforNN package.

# NN hyperparameters

num_hidden_layer = 1 # Number of hidden layers
hidden_activation_function = ['relu'] # activation function in the hidden layer
number_hidden_neuron = [5] # number of neurons in the hidden layer

epochs = 10 # Number of epochs to train the model. An epoch is an iteration over the entire data set provided
batch_size = 256 # Number of samples per gradient update

# NN Training 

classifier = NN_model(hidden_activation_function = hidden_activation_function,
                   num_hidden_layer = num_hidden_layer, num_hidden_neuron = number_hidden_neuron) 

# Compiling the neural network

classifier.compile(optimizer ='adam', loss='binary_crossentropy', metrics = ['accuracy']) # Configures the model for training

# Fitting 

history = classifier.fit(X_train, y_train, batch_size = batch_size, epochs = epochs, validation_data=(X_val, y_val)) # Trains the model

# History of training and validation accuracy

plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='lower right')
plt.show()

Plot the Receiver Operating Characteristic (ROC) curve and compute the Area Under the Curve (AUC) as performance measure to manually tune the typical NN hyperparameters. Use the function ROC_AUC_plot from the MSPforNN package.

fig_size = (5, 5)
f = plt.figure(figsize=fig_size)
f = ROC_AUC_plot(classifier, X_val, y_val, f, xlabel = 'False Positive Rate', ylabel = 'True Positive Rate')

Set the cut-off value of the neuron in the output layer

To allow fair comparison with the traditional statistical control charting procedures, the cut-off value (CV) of the neuron in the output layer must be set and can be regarded as the key threshold to set the Type-I and Type-II errors. A table of the CVs of the proposed NN corresponding to typical false alarm rate values is provided in the paper. Additionally, using the function set_cv_alpha from the MSPforNN package, you can compute the Type-I error corresponding to any CV.

set_seed = 0
cv = 0.940 # cut-off value
n = 100000 # number of samples of 5 observations

alpha = set_cv_alpha(n = n, s = s, k = k, loc_res = loc_res, scale_res = scale_res , scaler = scaler, classifier = classifier, cv = cv, set_seed = set_seed)
print(alpha)

0.0027 \ which is the default value for a Shewhart control chart in the 6-sigma quality approach.

Real-case study: HVAC systems in passenger railway vehicles

Import the HVAC datavset. Data have already been cleaned to remove unsteady working conditions and sensor measurement errors and validated by domain expert.

HVAC_data = NN4MSP.dataset.load_HVAC_data()

Phase I

Filter Vehicle by Train_1 and consider 10 days of operational data from "07-27" to "08-08" for mean and variance estimations.

train_1_data = HVAC_data[HVAC_data["Vehicle"] == "Train_1"]
train_1_data = train_1_data.loc[(train_1_data['Timestamp'] >= '07-27')
                     & (train_1_data['Timestamp'] < '08-08')]
                     

Select only the DeltaTemp variables and compute the mean every 5 rows.

train_1_data = train_1_data.iloc[:,-6:]
train_1_data = train_1_data.to_numpy() # Convert pandas dataframe to NumPy array
train_1_data_mean = train_1_data.transpose().reshape(-1,k).mean(1).reshape(s,-1).transpose() 

Plot the average value of the DeltaTemp signals of all of the coaches of train 1 over 50 subgroup (10 minutes worth of data).

fig = plt.figure(figsize=(12, 6))

x = np.arange(1,51,1)

plt.plot(x,train_1_data_mean[210:260,0], label = 'Coach 1', color='black', ls='-', marker='*')
plt.plot(x,train_1_data_mean[210:260,1], label = 'Coach 2', color='blue', ls='-', marker='.')
plt.plot(x,train_1_data_mean[210:260,2], label = 'Coach 3', color='red', ls='-.', marker= 's')
plt.plot(x,train_1_data_mean[210:260,3], label = 'Coach 4', color='green', ls='-', marker='D')
plt.plot(x,train_1_data_mean[210:260,4], label = 'Coach 5', color='orange', ls='-', marker='+')
plt.plot(x,train_1_data_mean[210:260,5], label = 'Coach 6', color='violet', ls='-', marker='P')
plt.xlabel('Subgroup', fontsize=12)
plt.ylabel('$ \Delta$T', fontsize=12)
plt.legend(fontsize=10)

plt.xlim([0,51])
plt.tick_params(axis='both', which='major', size = 7, width = 1 , direction = 'out', labelsize = 10)

plt.show()

Compute the residuals from each train coach and then calculate the mean and the variance.

train_1_residual = train_1_data_mean - np.mean(train_1_data_mean, axis = 1, keepdims= True) 

mean_res = np.mean(train_1_residual)
std_res = np.std(train_1_residual)

Phase II

Train 2

The following figure shows a MSP data in which we clearly see that the process is out of control and that an assignable cause affects the output from one stream

train_2_data = HVAC_data[HVAC_data["Vehicle"] == "Train_2"] # Filter Vehicle by Train 2 
train_2_data = train_2_data.iloc[0:-4,-6:] # Select the DeltaTemp variables 
train_2_data = train_2_data.to_numpy()
train_2_data_mean = train_2_data.transpose().reshape(-1,k).mean(1).reshape(s,-1).transpose() # Average every 5 rows 

# Plot the ΔT signals from the six train coaches 

fig = plt.figure(figsize=(12, 6))

x = np.arange(1,31,1)

plt.plot(x,train_2_data_mean[235:265,0], label = 'Coach 1', color='black', ls='-', marker='*')
plt.plot(x,train_2_data_mean[235:265,1], label = 'Coach 2', color='blue', ls='-', marker='.')
plt.plot(x,train_2_data_mean[235:265,2], label = 'Coach 3', color='red', ls='-.', marker= 's')
plt.plot(x,train_2_data_mean[235:265,3], label = 'Coach 4', color='green', ls='-', marker='D')
plt.plot(x,train_2_data_mean[235:265,4], label = 'Coach 5', color='orange', ls='-', marker='+')
plt.plot(x,train_2_data_mean[235:265,5], label = 'Coach 6', color='violet', ls='-', marker='P')
plt.xlabel('Subgroup', fontsize=12)
plt.ylabel('$ \Delta$T', fontsize=12)
plt.legend(fontsize=10)

plt.xlim([0,31])
plt.tick_params(axis='both', which='major', size = 7, width = 1 , direction = 'out', labelsize = 10)

plt.show()

After computing and standardizing the residuals from each coach, the range of the subgroup means of the residuals and the overall mean at each sample time are calculated.

# Definton of the input vector

train_2_residual = train_2_data_mean - np.mean(train_2_data_mean, axis = 1, keepdims= True)
train_2_mean_std = (train_2_residual - mean_res)/std_res
overall_mean = train_2_mean_std.mean(axis=1) 
sample_range = train_2_mean_std.max(axis=1) - train_2_mean_std.min(axis=1) 
train_2_mean_std = np.c_[train_2_mean_std,overall_mean,sample_range]

Then the input vector is given as input to the NN.

train_2_mean_std = scaler.transform(train_2_mean_std)
train_2_mean_std_pred = classifier.predict(train_2_mean_std)

Finally, you can plot the control chart based on the NN predicted probability by calling the function control_chart from the MSPforNN package.

fig_size = (12, 6)
fig_control_chart = plt.figure(figsize=fig_size)
fig_control_chart = control_chart(NN_pred = train_2_mean_std_pred[235:265], fig_control_chart = fig_control_chart, 
                                  CV = cv, xlabel = "Subgroup", ylabel = "Probability")

The time-series plot of the residuals for each coach is displayed to help the practioner to identify how many and which stream(s) have shifted.

fig = plt.figure(figsize=(12, 6))

x = np.arange(1,31,1)

plt.plot(x,train_2_mean_std[235:265,0], label = 'Coach 1', color='black', ls='-', marker='*')
plt.plot(x,train_2_mean_std[235:265,1], label = 'Coach 2', color='blue', ls='-', marker='.')
plt.plot(x,train_2_mean_std[235:265,2], label = 'Coach 3', color='red', ls='-.', marker= 's')
plt.plot(x,train_2_mean_std[235:265,3], label = 'Coach 4', color='green', ls='-', marker='D')
plt.plot(x,train_2_mean_std[235:265,4], label = 'Coach 5', color='orange', ls='-', marker='+')
plt.plot(x,train_2_mean_std[235:265,5], label = 'Coach 6', color='violet', ls='-', marker='P')
plt.xlabel('Subgroup', fontsize=12)
plt.ylabel('$ X_{tj} $', fontsize=12)
plt.legend(fontsize=10)

plt.xlim([0,31])
plt.tick_params(axis='both', which='major', size = 7, width = 1 , direction = 'out', labelsize = 10)

plt.show()

We can see that the coach 5 residuals are significantly higher than the other residuals, thus the HVAC system installed on-board coach 5 perform badly and is not able to meet the required European regulations and ensure passenger thermal comfort.

Train 3

The following figure shows a MSP data in which we clearly see that the process is out of control and that an assignable cause affects the output from four streams

train_3_data = HVAC_data[HVAC_data["Vehicle"] == "Train_3"]

train_3_data = train_3_data.loc[(train_3_data['Timestamp'] >= '07-25')
                     & (train_3_data['Timestamp'] < '07-26')]

train_3_data = train_3_data.iloc[0:-3,-6:]

train_3_data = train_3_data.to_numpy()
train_3_data_mean = train_3_data.transpose().reshape(-1,k).mean(1).reshape(s,-1).transpose() 

# Plot the ΔT signals from the six train coaches

fig = plt.figure(figsize=(12, 6))

x = np.arange(1,41,1)

plt.plot(x,train_3_data_mean[15:55,0], label = 'Coach 1', color='black', ls='-', marker='*')
plt.plot(x,train_3_data_mean[15:55,1], label = 'Coach 2', color='blue', ls='-', marker='.')
plt.plot(x,train_3_data_mean[15:55,2], label = 'Coach 3', color='red', ls='-.', marker= 's')
plt.plot(x,train_3_data_mean[15:55,3], label = 'Coach 4', color='green', ls='-', marker='D')
plt.plot(x,train_3_data_mean[15:55,4], label = 'Coach 5', color='orange', ls='-', marker='+')
plt.plot(x,train_3_data_mean[15:55,5], label = 'Coach 6', color='violet', ls='-', marker='P')
plt.xlabel('Subgroup', fontsize=12)
plt.ylabel('$ \Delta$T', fontsize=12)
plt.legend(fontsize=10)

plt.xlim([0,41])
plt.tick_params(axis='both', which='major', size = 7, width = 1 , direction = 'out', labelsize = 10)

plt.show()

After computing and standardizing the residuals from each coach, the range of the subgroup means of the residuals and the overall mean at each sample time are calculated.

train_3_data_mean = train_3_data_mean - np.mean(train_3_data_mean, axis = 1, keepdims= True)
train_3_mean_std = (train_3_data_mean - mean_res)/std_res

overall_mean = train_3_mean_std.mean(axis=1) 
sample_range = train_3_mean_std.max(axis=1) - train_3_mean_std.min(axis=1) 
train_3_mean_std = np.c_[train_3_mean_std,overall_mean,sample_range]

Then the range, the overall mean and the six residuals for each coach are given as input to the NN.

train_3_mean_std = scaler.transform(train_3_mean_std)
train_3_mean_std_pred = classifier.predict(train_3_mean_std)

Finally, you can plot the control chart by calling the function control_chart from the MSPforNN package

fig_size = (12, 6)
fig_control_chart = plt.figure(figsize=fig_size)
fig_control_chart = control_chart(NN_pred = train_3_mean_std_pred[15:55], fig_control_chart = fig_control_chart, 
                                  CV = cv, xlabel = "Subgroup", ylabel = "Probability")

We can plot the residuals from each coach of the train 3.

fig = plt.figure(figsize=(12, 6))

x = np.arange(1,41,1)

plt.plot(x,train_3_mean_std[15:55,0], label = 'Coach 1', color='black', ls='-', marker='*')
plt.plot(x,train_3_mean_std[15:55,1], label = 'Coach 2', color='blue', ls='-', marker='.')
plt.plot(x,train_3_mean_std[15:55,2], label = 'Coach 3', color='red', ls='-.', marker= 's')
plt.plot(x,train_3_mean_std[15:55,3], label = 'Coach 4', color='green', ls='-', marker='D')
plt.plot(x,train_3_mean_std[15:55,4], label = 'Coach 5', color='orange', ls='-', marker='+')
plt.plot(x,train_3_mean_std[15:55,5], label = 'Coach 6', color='violet', ls='-', marker='P')
plt.xlabel('Subgroup', fontsize=12)
plt.ylabel('$ X_{tj} $', fontsize=12)
plt.legend(fontsize=10)

plt.xlim([0,41])
plt.tick_params(axis='both', which='major', size = 7, width = 1 , direction = 'out', labelsize = 10)

plt.show()

The above plot shows that coaches 1,2,4,5 of the train 3 perform differently from the other two coaches and helps the practitioner to obtain a correct interpretation of the OC situation.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

NN4MSP-2.28.tar.gz (22.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

NN4MSP-2.28-py3-none-any.whl (1.6 MB view details)

Uploaded Python 3

File details

Details for the file NN4MSP-2.28.tar.gz.

File metadata

  • Download URL: NN4MSP-2.28.tar.gz
  • Upload date:
  • Size: 22.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.1 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.11

File hashes

Hashes for NN4MSP-2.28.tar.gz
Algorithm Hash digest
SHA256 fe5e181b1fc5ac5f31f6af4c1b3fa67032bcd4a63bfa7bcaa2386b2c2f284a35
MD5 9932af6b0edeb7a230388b8e8a4c82fe
BLAKE2b-256 a58bd2ddca6875bda061647db0fe6c6cd9154d00fc4dc7be3a0ada8b753a3465

See more details on using hashes here.

File details

Details for the file NN4MSP-2.28-py3-none-any.whl.

File metadata

  • Download URL: NN4MSP-2.28-py3-none-any.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.8.1 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.11

File hashes

Hashes for NN4MSP-2.28-py3-none-any.whl
Algorithm Hash digest
SHA256 c8996b5a3715f139c0bd564272e9c3ebe7b432d94c7c538138ee8b814d4b4566
MD5 6058131c7bf7ab9fd2a0b7a8006f6c89
BLAKE2b-256 45cabefe638814a2ddc077d534874a98708e9715f2b4779ff57a6e0161227753

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page