By Randley Morales

Problem Statement¶

Business Context¶

Renewable energy sources play an increasingly important role in the global energy mix, as the effort to reduce the environmental impact of energy production increases.

Out of all the renewable energy alternatives, wind energy is one of the most developed technologies worldwide. The U.S Department of Energy has put together a guide to achieving operational efficiency using predictive maintenance practices.

Predictive maintenance uses sensor information and analysis methods to measure and predict degradation and future component capability. The idea behind predictive maintenance is that failure patterns are predictable and if component failure can be predicted accurately and the component is replaced before it fails, the costs of operation and maintenance will be much lower.

The sensors fitted across different machines involved in the process of energy generation collect data related to various environmental factors (temperature, humidity, wind speed, etc.) and additional features related to various parts of the wind turbine (gearbox, tower, blades, break, etc.).

Objective¶

“ReneWind” is a company working on improving the machinery/processes involved in the production of wind energy using machine learning and has collected data of generator failure of wind turbines using sensors. They have shared a ciphered version of the data, as the data collected through sensors is confidential (the type of data collected varies with companies). Data has 40 predictors, 20000 observations in the training set and 5000 in the test set.

The objective is to build various classification models, tune them, and find the best one that will help identify failures so that the generators could be repaired before failing/breaking to reduce the overall maintenance cost. The nature of predictions made by the classification model will translate as follows:

  • True positives (TP) are failures correctly predicted by the model. These will result in repairing costs.
  • False negatives (FN) are real failures where there is no detection by the model. These will result in replacement costs.
  • False positives (FP) are detections where there is no failure. These will result in inspection costs.

It is given that the cost of repairing a generator is much less than the cost of replacing it, and the cost of inspection is less than the cost of repair.

“1” in the target variables should be considered as “failure” and “0” represents “No failure”.

Data Description¶

The data provided is a transformed version of the original data which was collected using sensors.

  • Train.csv - To be used for training and tuning of models.
  • Test.csv - To be used only for testing the performance of the final best model.

Both the datasets consist of 40 predictor variables and 1 target variable.

Installing and Importing the necessary libraries¶

In [1]:
# Installing the libraries with the specified version
!pip install --no-deps tensorflow==2.18.0 scikit-learn==1.3.2 matplotlib===3.8.3 seaborn==0.13.2 numpy==1.26.4 pandas==2.2.2 -q --user --no-warn-script-location
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.0/61.0 kB 1.4 MB/s eta 0:00:00
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 615.5/615.5 MB 852.3 kB/s eta 0:00:00
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 10.8/10.8 MB 89.7 MB/s eta 0:00:00
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 11.6/11.6 MB 83.3 MB/s eta 0:00:00
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 18.0/18.0 MB 70.4 MB/s eta 0:00:00

Note:

  • After running the above cell, kindly restart the runtime (for Google Colab) or notebook kernel (for Jupyter Notebook), and run all cells sequentially from the next cell.
  • On executing the above line of code, you might see a warning regarding package dependencies. This error message can be ignored as the above code ensures that all necessary libraries and their dependencies are maintained to successfully execute the code in *this notebook*.
In [2]:
# Import libraries for data manipulation
import numpy as np
import pandas as pd

# Import libraries for data visualization
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px

# Set default Seaborn theme for plots
sns.set()

# Suppress warnings to prevent them from being displayed during code execution
import warnings
warnings.filterwarnings('ignore')

# Enable the inline plotting of matplotlib figures directly within the notebook
%matplotlib inline

# Set the display format for float values to three decimal places
pd.set_option('display.float_format', lambda x: '%.4f' % x)

# Removes the limit for the number of displayed columns
pd.set_option("display.max_columns", None)
# Sets the limit for the number of displayed rows
pd.set_option("display.max_rows", 100)

# Distribution Libraries for statistical tests
import scipy.stats as stats
from scipy.stats import norm, uniform, binom, expon, t
from scipy.stats import ttest_1samp, ttest_ind

# Libraries for model building and evaluation
import statsmodels.api as sm
from sklearn.model_selection import train_test_split, GridSearchCV, RandomizedSearchCV, StratifiedKFold, cross_val_score

# Libraries to get different metric scores
from sklearn import metrics
from sklearn.metrics import (
    confusion_matrix,
    accuracy_score,
    precision_score,
    recall_score,
    f1_score,
    roc_auc_score,
    ConfusionMatrixDisplay,
    classification_report
)

# Libraries for model building (Logistic Regression, Decision Tree, etc.)
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor

# Libraries for ensemble methods and boosting
from sklearn.ensemble import (
    BaggingClassifier,
    RandomForestClassifier,
    AdaBoostClassifier,
    GradientBoostingClassifier,
    StackingClassifier,
)

# Libraries for working with XGBoost
from xgboost import XGBClassifier

# Libraries for oversampling and undersampling (imbalanced data handling)
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler

# Libraries for scaling and transforming data
from sklearn.preprocessing import LabelEncoder, StandardScaler, MinMaxScaler, OneHotEncoder

# Libraries for missing value imputation
from sklearn.impute import SimpleImputer

# Libraries for creating and managing pipelines
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer

# Libraries to help with model tuning and hyperparameter optimization
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

# Time related functions.
import time

#Imports the tensorflow,keras and layers.
import tensorflow
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dense, Input, Dropout,BatchNormalization
from tensorflow.keras import backend

Loading the Data¶

In [3]:
# Google Colab
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
In [4]:
# Load the Train and Test dataset
train_data = pd.read_csv("/content/drive/MyDrive/Introduction to Neural Networks/ReneWind/Train.csv")
test_data = pd.read_csv("/content/drive/MyDrive/Introduction to Neural Networks/ReneWind/Test.csv")

# Copying Train and Test data to another variable to avoid any changes to original data
df = train_data.copy()
df_test = test_data.copy()

Data Overview¶

In [5]:
# Display the first few rows of the dataset
print("First few rows of train dataset")
df.head()
First few rows of train dataset
Out[5]:
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40 Target
0 -4.4646 -4.6791 3.1015 0.5061 -0.2211 -2.0325 -2.9109 0.0507 -1.5224 3.7619 -5.7147 0.7359 0.9813 1.4179 -3.3758 -3.0473 0.3062 2.9141 2.2700 4.3949 -2.3883 0.6464 -1.1905 3.1330 0.6653 -2.5108 -0.0367 0.7262 -3.9822 -1.0726 1.6671 3.0597 -1.6904 2.8463 2.2352 6.6675 0.4438 -2.3692 2.9506 -3.4803 0
1 3.3659 3.6534 0.9097 -1.3675 0.3320 2.3589 0.7326 -4.3321 0.5657 -0.1011 1.9145 -0.9515 -1.2553 -2.7065 0.1932 -4.7694 -2.2053 0.9077 0.7569 -5.8337 -3.0651 1.5966 -1.7573 1.7664 -0.2671 3.6250 1.5003 -0.5857 0.7830 -0.2012 0.0249 -1.7955 3.0328 -2.4675 1.8946 -2.2978 -1.7310 5.9088 -0.3863 0.6162 0
2 -3.8318 -5.8244 0.6340 -2.4188 -1.7738 1.0168 -2.0989 -3.1732 -2.0819 5.3926 -0.7707 1.1067 1.1443 0.9433 -3.1638 -4.2478 -4.0389 3.6885 3.3112 1.0590 -2.1430 1.6501 -1.6606 1.6799 -0.4508 -4.5507 3.7388 1.1344 -2.0335 0.8408 -1.6004 -0.2571 0.8035 4.0862 2.2921 5.3608 0.3520 2.9400 3.8392 -4.3094 0
3 1.6181 1.8883 7.0461 -1.1473 0.0831 -1.5298 0.2073 -2.4936 0.3449 2.1186 -3.0530 0.4597 2.7045 -0.6361 -0.4537 -3.1740 -3.4043 -1.2815 1.5821 -1.9518 -3.5166 -1.2060 -5.6279 -1.8177 2.1241 5.2946 4.7481 -2.3085 -3.9630 -6.0287 4.9488 -3.5844 -2.5775 1.3638 0.6227 5.5501 -1.5268 0.1389 3.1014 -1.2774 0
4 -0.1114 3.8725 -3.7584 -2.9829 3.7927 0.5450 0.2054 4.8490 -1.8549 -6.2200 1.9983 4.7238 0.7091 -1.9894 -2.6327 4.1844 2.2454 3.7345 -6.3128 -5.3799 -0.8867 2.0617 9.4456 4.4900 -3.9451 4.5821 -8.7804 -3.3830 5.1065 6.7875 2.0442 8.2659 6.6292 -10.0687 1.2230 -3.2298 1.6869 -2.1639 -3.6446 6.5103 0
In [6]:
# Display the last few rows of the dataset
print("Last few rows of train dataset")
df.tail()
Last few rows of train dataset
Out[6]:
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40 Target
19995 -2.0713 -1.0883 -0.7962 -3.0117 -2.2875 2.8073 0.4814 0.1052 -0.5866 -2.8994 8.8684 1.7172 1.3578 -1.7771 0.7098 4.9449 -3.1005 -1.1992 -1.0846 -0.3650 3.1312 -3.9481 -3.5785 -8.1391 -1.9369 -1.3277 -0.4027 -1.7348 9.9965 6.9554 -3.9385 -8.2740 5.7450 0.5890 -0.6500 -3.0432 2.2165 0.6087 0.1782 2.9278 1
19996 2.8903 2.4831 5.6439 0.9371 -1.3809 0.4121 -1.5934 -5.7625 2.1501 0.2723 -2.0948 -1.5258 0.0716 -3.5401 -2.7620 -10.6322 -0.4952 1.7201 3.8716 -1.2096 -8.2221 2.1209 -5.4918 1.4523 1.4500 3.6847 1.0768 -0.3842 -0.8386 -0.7483 -1.0886 -4.1591 1.1815 -0.7424 5.3690 -0.6930 -1.6690 3.6600 0.8199 -1.9873 0
19997 -3.8970 -3.9424 -0.3514 -2.4175 1.1075 -1.5276 -3.5199 2.0548 -0.2340 -0.3577 -3.7820 2.1800 6.1118 1.9847 -8.3300 -1.6392 -0.9150 5.6723 -3.9242 2.1332 -4.5020 2.7772 5.7279 1.6198 -1.6997 -0.0419 -2.9231 -2.7602 -2.2538 2.5520 0.9819 7.1122 1.4761 -3.9537 1.8556 5.0292 2.0826 -6.4093 1.4771 -0.8741 0
19998 -3.1873 -10.0517 5.6960 -4.3701 -5.3548 -1.8730 -3.9472 0.6794 -2.3893 5.4568 1.5830 3.5715 9.2266 2.5536 -7.0391 -0.9936 -9.6649 1.1552 3.8769 3.5236 -7.0153 -0.1320 -3.4462 -4.8014 -0.8757 -3.8119 5.4221 -3.7323 0.6088 5.2565 1.9148 0.4028 3.1637 3.7521 8.5299 8.4506 0.2040 -7.1299 4.2494 -6.1123 0
19999 -2.6869 1.9612 6.1371 2.6001 2.6572 -4.2909 -2.3443 0.9740 -1.0275 0.4974 -9.5891 3.1766 1.0545 -1.4159 -4.6686 -5.4054 3.7198 2.8929 2.3286 1.4577 -6.4285 1.8182 0.8059 7.7860 0.3309 5.2574 -4.8674 -0.8189 -5.6674 -2.8610 4.6743 6.6208 -1.9888 -1.3489 3.9518 5.4497 -0.4554 -2.2021 1.6782 -1.9744 0
In [7]:
# Display the first few rows of the test dataset
print("First few rows of test dataset")
df_test.head()
First few rows of test dataset
Out[7]:
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40 Target
0 -0.6135 -3.8196 2.2023 1.3004 -1.1849 -4.4960 -1.8358 4.7230 1.2061 -0.3419 -5.1229 1.0170 4.8185 3.2690 -2.9843 1.3874 2.0320 -0.5116 -1.0231 7.3387 -2.2422 0.1555 2.0538 -2.7723 1.8514 -1.7887 -0.2773 -1.2551 -3.8329 -1.5045 1.5868 2.2912 -5.4114 0.8701 0.5745 4.1572 1.4281 -10.5113 0.4547 -1.4484 0
1 0.3896 -0.5123 0.5271 -2.5768 -1.0168 2.2351 -0.4413 -4.4057 -0.3329 1.9668 1.7965 0.4105 0.6383 -1.3896 -1.8834 -5.0179 -3.8272 2.4181 1.7623 -3.2423 -3.1930 1.8575 -1.7080 0.6334 -0.5879 0.0837 3.0139 -0.1823 0.2239 0.8652 -1.7822 -2.4749 2.4936 0.3152 2.0593 0.6839 -0.4855 5.1284 1.7207 -1.4882 0
2 -0.8749 -0.6406 4.0842 -1.5905 0.5259 -1.9576 -0.6954 1.3473 -1.7323 0.4665 -4.9282 3.5651 -0.4493 -0.6562 -0.1665 -1.6302 2.2919 2.3965 0.6013 1.7935 -2.1202 0.4820 -0.8407 1.7902 1.8744 0.3639 -0.1691 -0.4838 -2.1190 -2.1566 2.9073 -1.3189 -2.9975 0.4597 0.6198 5.6315 1.3235 -1.7522 1.8083 1.6757 0
3 0.2384 1.4586 4.0145 2.5345 1.1970 -3.1173 -0.9240 0.2695 1.3224 0.7023 -5.5783 -0.8507 2.5905 0.7674 -2.3908 -2.3420 0.5719 -0.9338 0.5087 1.2107 -3.2595 0.1046 -0.6589 1.4981 1.1003 4.1430 -0.2484 -1.1365 -5.3558 -4.5459 3.8087 3.5179 -3.0741 -0.2842 0.9546 3.0293 -1.3672 -3.4121 0.9060 -2.4509 0
4 5.8282 2.7683 -1.2345 2.8093 -1.6416 -1.4067 0.5686 0.9650 1.9184 -2.7749 -0.5300 1.3745 -0.6509 -1.6795 -0.3792 -4.4431 3.8939 -0.6076 2.9449 0.3672 -5.7891 4.5975 4.4503 3.2249 0.3967 0.2478 -2.3620 1.0794 -0.4731 2.2428 -3.5914 1.7738 -1.5016 -2.2267 4.7768 -6.5597 -0.8056 -0.2760 -3.8582 -0.5377 0
In [8]:
# Display the last few rows of the test dataset
print("Last few rows of test dataset")
df_test.tail()
Last few rows of test dataset
Out[8]:
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40 Target
4995 -5.1205 1.6348 1.2513 4.0359 3.2912 -2.9322 -1.3287 1.7541 -2.9846 1.2486 -6.8777 3.7152 -2.5118 -1.3950 -2.5541 -2.1974 4.7717 2.4029 3.7918 0.4868 -2.0281 1.7779 3.6680 11.3749 -1.9771 2.2516 -7.3185 1.9070 -3.7337 -0.0125 2.1205 9.9791 0.0634 0.2173 3.0364 2.1093 -0.5574 1.9387 0.5127 -2.6942 0
4996 -5.1725 1.1717 1.5791 1.2199 2.5296 -0.6686 -2.6183 -2.0005 0.6338 -0.5789 -3.6712 0.4602 3.3206 -1.0750 -7.1125 -4.3559 -0.0011 3.6984 -0.8464 -0.2223 -3.6450 0.7360 0.9257 3.2777 -2.2768 4.4576 -4.5429 -1.3480 -1.7794 0.3522 -0.2143 4.4239 2.6038 -2.1522 0.9174 2.1566 0.4670 0.4701 2.1968 -2.3765 0
4997 -1.1141 -0.4036 -1.7649 -5.8795 3.5716 3.7108 -2.4830 -0.3076 -0.9219 -2.9991 -0.1117 -1.9769 -1.6230 -0.9455 -2.7351 -0.8130 0.6098 8.1488 -9.1992 -3.8725 -0.2957 1.4684 2.8843 2.7921 -1.1357 1.1979 -4.3417 -2.8694 4.1241 4.1967 3.4707 3.7918 7.4815 -10.0614 -0.3872 1.8485 1.8182 -1.2456 -1.2609 7.4747 0
4998 -1.7032 0.6146 6.2205 -0.1041 0.9559 -3.2787 -1.6339 -0.1039 1.3882 -1.0656 -7.9698 2.2621 3.1340 -0.4858 -3.4983 -4.5617 3.1358 2.5364 -0.7922 4.3984 -4.0730 -0.0376 -2.3713 -1.5420 2.9079 3.2145 -0.1687 -1.5413 -4.7244 -5.5250 1.6680 -4.1004 -5.9493 0.5504 -1.5736 6.8239 2.1393 -4.0362 3.4361 0.5792 0
4999 -0.6037 0.9595 -0.7210 8.2296 -1.8156 -2.2755 -2.5745 -1.0415 4.1296 -2.7313 -3.2924 -1.6738 0.4645 -1.6459 -5.2634 -7.9876 6.4805 0.2263 4.9633 6.7520 -6.3058 3.2705 1.8972 3.2708 -0.6371 -0.9250 -6.7589 2.9902 -0.8138 3.4990 -8.4347 2.3698 -1.0624 0.7908 4.9520 -7.4408 -0.0695 -0.9181 -2.2912 -5.3629 0
In [9]:
# Display the shape of the train dataset
df.shape
print("There are", df.shape[0], "rows and", df.shape[1], "columns. In the train dataset")
There are 20000 rows and 41 columns. In the train dataset
In [10]:
# Display the data types of the columns in the train dataset
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20000 entries, 0 to 19999
Data columns (total 41 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   V1      19982 non-null  float64
 1   V2      19982 non-null  float64
 2   V3      20000 non-null  float64
 3   V4      20000 non-null  float64
 4   V5      20000 non-null  float64
 5   V6      20000 non-null  float64
 6   V7      20000 non-null  float64
 7   V8      20000 non-null  float64
 8   V9      20000 non-null  float64
 9   V10     20000 non-null  float64
 10  V11     20000 non-null  float64
 11  V12     20000 non-null  float64
 12  V13     20000 non-null  float64
 13  V14     20000 non-null  float64
 14  V15     20000 non-null  float64
 15  V16     20000 non-null  float64
 16  V17     20000 non-null  float64
 17  V18     20000 non-null  float64
 18  V19     20000 non-null  float64
 19  V20     20000 non-null  float64
 20  V21     20000 non-null  float64
 21  V22     20000 non-null  float64
 22  V23     20000 non-null  float64
 23  V24     20000 non-null  float64
 24  V25     20000 non-null  float64
 25  V26     20000 non-null  float64
 26  V27     20000 non-null  float64
 27  V28     20000 non-null  float64
 28  V29     20000 non-null  float64
 29  V30     20000 non-null  float64
 30  V31     20000 non-null  float64
 31  V32     20000 non-null  float64
 32  V33     20000 non-null  float64
 33  V34     20000 non-null  float64
 34  V35     20000 non-null  float64
 35  V36     20000 non-null  float64
 36  V37     20000 non-null  float64
 37  V38     20000 non-null  float64
 38  V39     20000 non-null  float64
 39  V40     20000 non-null  float64
 40  Target  20000 non-null  int64  
dtypes: float64(40), int64(1)
memory usage: 6.3 MB
In [11]:
# Display the shape of the test dataset
df_test.shape
print("There are", df_test.shape[0], "rows and", df_test.shape[1], "columns. In the test dataset")
There are 5000 rows and 41 columns. In the test dataset
In [12]:
# Display the data types of the columns in the test dataset
df_test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5000 entries, 0 to 4999
Data columns (total 41 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   V1      4995 non-null   float64
 1   V2      4994 non-null   float64
 2   V3      5000 non-null   float64
 3   V4      5000 non-null   float64
 4   V5      5000 non-null   float64
 5   V6      5000 non-null   float64
 6   V7      5000 non-null   float64
 7   V8      5000 non-null   float64
 8   V9      5000 non-null   float64
 9   V10     5000 non-null   float64
 10  V11     5000 non-null   float64
 11  V12     5000 non-null   float64
 12  V13     5000 non-null   float64
 13  V14     5000 non-null   float64
 14  V15     5000 non-null   float64
 15  V16     5000 non-null   float64
 16  V17     5000 non-null   float64
 17  V18     5000 non-null   float64
 18  V19     5000 non-null   float64
 19  V20     5000 non-null   float64
 20  V21     5000 non-null   float64
 21  V22     5000 non-null   float64
 22  V23     5000 non-null   float64
 23  V24     5000 non-null   float64
 24  V25     5000 non-null   float64
 25  V26     5000 non-null   float64
 26  V27     5000 non-null   float64
 27  V28     5000 non-null   float64
 28  V29     5000 non-null   float64
 29  V30     5000 non-null   float64
 30  V31     5000 non-null   float64
 31  V32     5000 non-null   float64
 32  V33     5000 non-null   float64
 33  V34     5000 non-null   float64
 34  V35     5000 non-null   float64
 35  V36     5000 non-null   float64
 36  V37     5000 non-null   float64
 37  V38     5000 non-null   float64
 38  V39     5000 non-null   float64
 39  V40     5000 non-null   float64
 40  Target  5000 non-null   int64  
dtypes: float64(40), int64(1)
memory usage: 1.6 MB
In [13]:
# Checking missing values across each column of train Data Set
missing_values = df.isnull().sum()
print("The number of missing values on each column of the train data set is:")
missing_values
The number of missing values on each column of the train data set is:
Out[13]:
0
V1 18
V2 18
V3 0
V4 0
V5 0
V6 0
V7 0
V8 0
V9 0
V10 0
V11 0
V12 0
V13 0
V14 0
V15 0
V16 0
V17 0
V18 0
V19 0
V20 0
V21 0
V22 0
V23 0
V24 0
V25 0
V26 0
V27 0
V28 0
V29 0
V30 0
V31 0
V32 0
V33 0
V34 0
V35 0
V36 0
V37 0
V38 0
V39 0
V40 0
Target 0

In [14]:
# Checking missing values across each column of test Data Set
missing_values = df_test.isnull().sum()
print("The number of missing values on each column of the test data set is:")
missing_values
The number of missing values on each column of the test data set is:
Out[14]:
0
V1 5
V2 6
V3 0
V4 0
V5 0
V6 0
V7 0
V8 0
V9 0
V10 0
V11 0
V12 0
V13 0
V14 0
V15 0
V16 0
V17 0
V18 0
V19 0
V20 0
V21 0
V22 0
V23 0
V24 0
V25 0
V26 0
V27 0
V28 0
V29 0
V30 0
V31 0
V32 0
V33 0
V34 0
V35 0
V36 0
V37 0
V38 0
V39 0
V40 0
Target 0

Observations:¶
  • Dataset Shapes:

    • Training set: 20,000 rows × 41 columns (40 predictors + 1 target variable)
    • Test set: 5,000 rows × 41 columns
  • Missing Values:

    • Train: 36 missing values across the dataset
    • Test: 11 missing values
In [15]:
# Check for complete duplicate records of the train
duplicate_records = df.duplicated().sum()
print("The number of duplicate values on the train data set is:", duplicate_records)
The number of duplicate values on the train data set is: 0
In [16]:
# Check for complete duplicate records of the test
duplicate_records = df_test.duplicated().sum()
print("The number of duplicate values on the test data set is:", duplicate_records)
The number of duplicate values on the test data set is: 0
In [17]:
print("Target Value Counts for train dataset:")
print(df['Target'].value_counts())
print("\nTarget Distribution (%):")
print(df['Target'].value_counts(normalize=True) * 100)
Target Value Counts for train dataset:
Target
0    18890
1     1110
Name: count, dtype: int64

Target Distribution (%):
Target
0   94.4500
1    5.5500
Name: proportion, dtype: float64
In [18]:
print("Target Value Counts for test dataset:")
print(df_test['Target'].value_counts())
print("\nTarget Distribution (%):")
print(df_test['Target'].value_counts(normalize=True) * 100)
Target Value Counts for test dataset:
Target
0    4718
1     282
Name: count, dtype: int64

Target Distribution (%):
Target
0   94.3600
1    5.6400
Name: proportion, dtype: float64
Observations on Target Distribution:¶
  • Training Dataset

    • Class 0 (No Failure): 18,890 samples (~94.45%)
    • Class 1 (Failure): 1,110 samples (~5.55%)
  • Test Dataset

    • Class 0 (No Failure): 4,718 samples (~94.36%)

    • Class 1 (Failure): 282 samples (~5.64%)

  • Highly Imbalanced Classes

    • Both train and test datasets show a similar imbalance (~94% non-failures vs ~6% failures).
    • This imbalance is expected in predictive maintenance (failures are rare events).
  • Consistency Across Train & Test

    • The failure rate in train (5.55%) and test (5.64%) are almost identical.
    • This suggests the datasets are well-sampled and representative of real-world conditions.
  • Business Implication of Imbalance

    • Since failures are rare but costly (replacement > repair > inspection), the model should prioritize recall (catching failures) over plain accuracy.

    • A naïve model predicting all 0s (no failures) would already achieve ~94% accuracy, but it would miss all failures → disastrous in terms of business costs.

In [19]:
# Display the complete statistical summary for the train dataset
print("Complete Statistical Summary for train dataset")
df.describe(include="all").T
Complete Statistical Summary for train dataset
Out[19]:
count mean std min 25% 50% 75% max
V1 19982.0000 -0.2720 3.4416 -11.8765 -2.7371 -0.7479 1.8401 15.4930
V2 19982.0000 0.4404 3.1508 -12.3200 -1.6407 0.4715 2.5440 13.0893
V3 20000.0000 2.4847 3.3890 -10.7081 0.2069 2.2558 4.5662 17.0909
V4 20000.0000 -0.0832 3.4316 -15.0821 -2.3477 -0.1352 2.1306 13.2364
V5 20000.0000 -0.0538 2.1048 -8.6034 -1.5356 -0.1020 1.3405 8.1338
V6 20000.0000 -0.9954 2.0410 -10.2271 -2.3472 -1.0005 0.3803 6.9758
V7 20000.0000 -0.8793 1.7616 -7.9497 -2.0309 -0.9172 0.2237 8.0061
V8 20000.0000 -0.5482 3.2958 -15.6576 -2.6427 -0.3891 1.7230 11.6795
V9 20000.0000 -0.0168 2.1606 -8.5963 -1.4950 -0.0676 1.4092 8.1376
V10 20000.0000 -0.0130 2.1932 -9.8540 -1.4112 0.1010 1.4770 8.1085
V11 20000.0000 -1.8954 3.1243 -14.8321 -3.9224 -1.9212 0.1189 11.8264
V12 20000.0000 1.6048 2.9305 -12.9480 -0.3965 1.5078 3.5715 15.0807
V13 20000.0000 1.5805 2.8747 -13.2282 -0.2235 1.6372 3.4599 15.4196
V14 20000.0000 -0.9506 1.7897 -7.7386 -2.1707 -0.9572 0.2707 5.6707
V15 20000.0000 -2.4150 3.3550 -16.4166 -4.4153 -2.3826 -0.3591 12.2465
V16 20000.0000 -2.9252 4.2217 -20.3742 -5.6342 -2.6827 -0.0950 13.5832
V17 20000.0000 -0.1343 3.3455 -14.0912 -2.2156 -0.0146 2.0688 16.7564
V18 20000.0000 1.1893 2.5923 -11.6440 -0.4039 0.8834 2.5718 13.1799
V19 20000.0000 1.1818 3.3969 -13.4918 -1.0502 1.2791 3.4933 13.2377
V20 20000.0000 0.0236 3.6695 -13.9227 -2.4330 0.0334 2.5124 16.0523
V21 20000.0000 -3.6113 3.5677 -17.9562 -5.9304 -3.5329 -1.2659 13.8405
V22 20000.0000 0.9518 1.6515 -10.1221 -0.1181 0.9747 2.0256 7.4099
V23 20000.0000 -0.3661 4.0319 -14.8661 -3.0988 -0.2621 2.4517 14.4587
V24 20000.0000 1.1344 3.9121 -16.3871 -1.4681 0.9690 3.5460 17.1633
V25 20000.0000 -0.0022 2.0167 -8.2283 -1.3652 0.0251 1.3971 8.2234
V26 20000.0000 1.8738 3.4351 -11.8343 -0.3379 1.9505 4.1300 16.8364
V27 20000.0000 -0.6124 4.3688 -14.9049 -3.6523 -0.8849 2.1892 17.5604
V28 20000.0000 -0.8832 1.9177 -9.2695 -2.1712 -0.8911 0.3759 6.5276
V29 20000.0000 -0.9856 2.6844 -12.5795 -2.7874 -1.1762 0.6298 10.7221
V30 20000.0000 -0.0155 3.0053 -14.7960 -1.8671 0.1843 2.0362 12.5058
V31 20000.0000 0.4868 3.4614 -13.7228 -1.8178 0.4903 2.7307 17.2551
V32 20000.0000 0.3038 5.5004 -19.8765 -3.4205 0.0521 3.7617 23.6332
V33 20000.0000 0.0498 3.5753 -16.8984 -2.2429 -0.0662 2.2551 16.6925
V34 20000.0000 -0.4627 3.1838 -17.9851 -2.1370 -0.2550 1.4369 14.3582
V35 20000.0000 2.2296 2.9371 -15.3498 0.3362 2.0986 4.0644 15.2911
V36 20000.0000 1.5148 3.8009 -14.8332 -0.9438 1.5665 3.9839 19.3296
V37 20000.0000 0.0113 1.7882 -5.4784 -1.2558 -0.1284 1.1755 7.4670
V38 20000.0000 -0.3440 3.9481 -17.3750 -2.9876 -0.3168 2.2794 15.2899
V39 20000.0000 0.8907 1.7531 -6.4389 -0.2723 0.9193 2.0575 7.7599
V40 20000.0000 -0.8756 3.0122 -11.0239 -2.9402 -0.9208 1.1199 10.6543
Target 20000.0000 0.0555 0.2290 0.0000 0.0000 0.0000 0.0000 1.0000
Observations:¶
  • Central Tendency & Scale

    • All 40 predictor variables (V1–V40) are continuous and numeric.
    • Means are close to 0, and medians are also near 0, confirming that the dataset was standardized or z-score transformed.
    • Standard deviations vary between ~2 and ~3.5, so the features are roughly on the same scale.
  • Spread & Ranges

    • Min and max values range approximately from -15 to +17 across features.
    • Most values fall within [-3, +3], which is consistent with standardized distributions.
    • The interquartile ranges (IQR: 25%–75%) are compact, suggesting that the majority of sensor readings are well-clustered around the center.
  • Outliers

    • Almost every variable has extreme values beyond ±10, seen in min/max ranges.
    • These are statistical outliers but could represent abnormal sensor readings linked to failures.
    • Outlier handling should be cautious—don’t remove them blindly, since they may contain the failure signals you need.
  • Symmetry & Skewness

    • Skewness is generally low, meaning most distributions are fairly symmetric.
    • A few features (e.g., V1, V29, V37, V3, V27) show moderate positive skew, with a long right tail.
    • Heavy tails suggest rare extreme values which may correspond to operational stress before failure.

    The statistical summary suggests the dataset is well-prepared, standardized, and balanced in feature scale, but it is heavily imbalanced in target outcomes. The outliers and skewed features could be key indicators of impending failures, making them valuable for predictive modeling.

In [20]:
# Display the complete statistical summary for the test dataset
print("Complete Statistical Summary for test dataset")
df_test.describe(include="all").T
Complete Statistical Summary for test dataset
Out[20]:
count mean std min 25% 50% 75% max
V1 4995.0000 -0.2776 3.4663 -12.3817 -2.7437 -0.7648 1.8313 13.5044
V2 4994.0000 0.3979 3.1396 -10.7162 -1.6492 0.4274 2.4445 14.0791
V3 5000.0000 2.5518 3.3266 -9.2379 0.3149 2.2604 4.5870 15.3145
V4 5000.0000 -0.0489 3.4139 -14.6824 -2.2927 -0.1458 2.1665 12.1402
V5 5000.0000 -0.0801 2.1109 -7.7116 -1.6152 -0.1319 1.3412 7.6728
V6 5000.0000 -1.0421 2.0054 -8.9242 -2.3689 -1.0486 0.3076 5.0677
V7 5000.0000 -0.9079 1.7690 -8.1242 -2.0543 -0.9397 0.2122 7.6162
V8 5000.0000 -0.5746 3.3319 -12.2527 -2.6421 -0.3579 1.7129 10.4147
V9 5000.0000 0.0301 2.1741 -6.7855 -1.4557 -0.0799 1.4495 8.8507
V10 5000.0000 0.0185 2.1454 -8.1710 -1.3533 0.1663 1.5112 6.5987
V11 5000.0000 -2.0086 3.1122 -13.1518 -4.0504 -2.0431 0.0441 9.9564
V12 5000.0000 1.5764 2.9074 -8.1640 -0.4497 1.4883 3.5626 12.9836
V13 5000.0000 1.6225 2.8829 -11.5482 -0.1260 1.7186 3.4646 12.6200
V14 5000.0000 -0.9211 1.8035 -7.8139 -2.1110 -0.8960 0.2723 5.7341
V15 5000.0000 -2.4522 3.3870 -15.2858 -4.4791 -2.4171 -0.4329 11.6734
V16 5000.0000 -3.0185 4.2644 -20.9858 -5.6483 -2.7738 -0.1781 13.9758
V17 5000.0000 -0.1037 3.3365 -13.4183 -2.2277 0.0475 2.1119 19.7766
V18 5000.0000 1.1956 2.5864 -12.2140 -0.4089 0.8814 2.6040 13.6422
V19 5000.0000 1.2105 3.3847 -14.1696 -1.0264 1.2959 3.5263 12.4280
V20 5000.0000 0.1384 3.6572 -13.7196 -2.3255 0.1934 2.5396 13.8706
V21 5000.0000 -3.6644 3.5778 -16.3407 -5.9444 -3.6629 -1.3296 11.0469
V22 5000.0000 0.9620 1.6404 -6.7402 -0.0477 0.9860 2.0293 7.5053
V23 5000.0000 -0.4222 4.0567 -14.4223 -3.1627 -0.2792 2.4259 13.1809
V24 5000.0000 1.0888 3.9682 -12.3155 -1.6232 0.9128 3.5372 17.8060
V25 5000.0000 0.0612 2.0102 -6.7701 -1.2984 0.0767 1.4285 6.5569
V26 5000.0000 1.8473 3.4003 -11.4140 -0.2425 1.9170 4.1561 17.5282
V27 5000.0000 -0.5524 4.4029 -13.1770 -3.6626 -0.8720 2.2473 17.2902
V28 5000.0000 -0.8677 1.9262 -7.9334 -2.1598 -0.9307 0.4206 7.4157
V29 5000.0000 -1.0958 2.6555 -9.9878 -2.8614 -1.3405 0.5218 14.0395
V30 5000.0000 -0.1187 3.0233 -12.4384 -1.9967 0.1125 1.9465 10.3150
V31 5000.0000 0.4688 3.4463 -11.2633 -1.8224 0.4857 2.7790 12.5589
V32 5000.0000 0.2326 5.5856 -17.2442 -3.5563 -0.0767 3.7519 26.5394
V33 5000.0000 -0.0801 3.5386 -14.9038 -2.3481 -0.1597 2.0992 13.3235
V34 5000.0000 -0.3927 3.1661 -14.6997 -2.0096 -0.1717 1.4654 12.1463
V35 5000.0000 2.2112 2.9484 -12.2606 0.3218 2.1117 4.0316 13.4892
V36 5000.0000 1.5948 3.7750 -12.7356 -0.8661 1.7030 4.1044 17.1161
V37 5000.0000 0.0229 1.7853 -5.0791 -1.2405 -0.1104 1.2375 6.8099
V38 5000.0000 -0.4057 3.9689 -15.3345 -2.9845 -0.3812 2.2880 13.0650
V39 5000.0000 0.9388 1.7165 -5.4510 -0.2080 0.9592 2.1308 7.1822
V40 5000.0000 -0.9324 2.9782 -10.0762 -2.9866 -1.0028 1.0797 8.6985
Target 5000.0000 0.0564 0.2307 0.0000 0.0000 0.0000 0.0000 1.0000
Observations:¶
  • Central Tendency & Scale

    • Similar to the training set, all 40 predictors are numeric and appear to be standardized/normalized.
    • Means are very close to 0 (e.g., V1 = -0.28, V2 = 0.39, V3 = 2.55).
    • Medians also hover near 0, confirming symmetry and standardization.
  • Spread & Ranges

    • Standard deviations range roughly 2.0 to 3.5, similar to the training dataset.
    • Min/Max values fall within -15 to +15, which aligns with the training dataset ranges.
    • Most values are concentrated between -3 and +3 (based on quartiles).
  • Outliers

    • Several features (e.g., V1, V2, V3, V4) include extreme values (< -10 or > 10).
    • These outliers are consistent with the training dataset and may represent rare sensor spikes linked to failure events.
  • Symmetry & Skewness

    • Features show low to moderate skewness.
    • A few variables (like V1, V2, V3) exhibit slightly longer tails, but the overall distribution remains roughly symmetric.
    • This suggests distributions are stable across train and test.
  • Consistency With Training Dataset

    • The statistical properties (mean, std, ranges, skewness) are highly similar between train and test datasets.
    • This indicates that the test set is a representative sample, not drawn from a different distribution.
    • Ensures models trained on the training set will generalize well.

    The test dataset mirrors the training dataset in distribution, scale, and variability. This confirms good dataset preparation, ensuring that models trained on the training set will likely generalize effectively when evaluated on the test set.

Exploratory Data Analysis¶

In [21]:
# Function to plot a boxplot and a histogram along the same scale.
def histogram_boxplot(data, feature, figsize=(12, 7), kde=False, bins=None):
    """
    Boxplot and histogram combined with a title

    data: dataframe
    feature: dataframe column
    figsize: size of figure (default (12,7))
    kde: whether to show the density curve (default False)
    bins: number of bins for histogram (default None)
    """
    f2, (ax_box2, ax_hist2) = plt.subplots(
        nrows=2,  # Number of rows of the subplot grid= 2
        sharex=True,  # x-axis will be shared among all subplots
        gridspec_kw={"height_ratios": (0.25, 0.75)},
        figsize=figsize,
    )  # creating the 2 subplots

    # Add a title to the figure
    f2.suptitle(f"Distribution of {feature}", fontsize=16)

    # Boxplot creation
    sns.boxplot(
        data=data,
        x=feature,
        ax=ax_box2,
        showmeans=True,
        palette="Oranges",
    )

    # Histogram creation
    sns.histplot(
        data=data,
        x=feature,
        kde=kde,
        ax=ax_hist2,
        bins=bins if bins else 50,
        color="orange",
    )

    # Add mean and median lines
    ax_hist2.axvline(data[feature].mean(), color="green", linestyle="--", label="Mean")
    ax_hist2.axvline(
        data[feature].median(), color="black", linestyle="-", label="Median"
    )

    # Add a legend for the mean and median lines
    ax_hist2.legend()

    plt.show()  # Show the plot
In [22]:
# Copying data to another variable to avoid any changes to original data
df_eda = df.copy()

Univariate analysis¶

In [23]:
# Selecting all numerical columns ('int64', 'float64')
num_cols = df_eda.select_dtypes(include=["int64", "float64"]).columns

# Iterate through each numerical column and plot the histogram and boxplot combined
for column in num_cols:
    print(f"Distribution of '{column}'")
    print(df_eda[column].describe())
    histogram_boxplot(df_eda, column, bins=50, kde=True)
    print("-" * 100)
Distribution of 'V1'
count   19982.0000
mean       -0.2720
std         3.4416
min       -11.8765
25%        -2.7371
50%        -0.7479
75%         1.8401
max        15.4930
Name: V1, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V2'
count   19982.0000
mean        0.4404
std         3.1508
min       -12.3200
25%        -1.6407
50%         0.4715
75%         2.5440
max        13.0893
Name: V2, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V3'
count   20000.0000
mean        2.4847
std         3.3890
min       -10.7081
25%         0.2069
50%         2.2558
75%         4.5662
max        17.0909
Name: V3, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V4'
count   20000.0000
mean       -0.0832
std         3.4316
min       -15.0821
25%        -2.3477
50%        -0.1352
75%         2.1306
max        13.2364
Name: V4, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V5'
count   20000.0000
mean       -0.0538
std         2.1048
min        -8.6034
25%        -1.5356
50%        -0.1020
75%         1.3405
max         8.1338
Name: V5, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V6'
count   20000.0000
mean       -0.9954
std         2.0410
min       -10.2271
25%        -2.3472
50%        -1.0005
75%         0.3803
max         6.9758
Name: V6, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V7'
count   20000.0000
mean       -0.8793
std         1.7616
min        -7.9497
25%        -2.0309
50%        -0.9172
75%         0.2237
max         8.0061
Name: V7, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V8'
count   20000.0000
mean       -0.5482
std         3.2958
min       -15.6576
25%        -2.6427
50%        -0.3891
75%         1.7230
max        11.6795
Name: V8, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V9'
count   20000.0000
mean       -0.0168
std         2.1606
min        -8.5963
25%        -1.4950
50%        -0.0676
75%         1.4092
max         8.1376
Name: V9, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V10'
count   20000.0000
mean       -0.0130
std         2.1932
min        -9.8540
25%        -1.4112
50%         0.1010
75%         1.4770
max         8.1085
Name: V10, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V11'
count   20000.0000
mean       -1.8954
std         3.1243
min       -14.8321
25%        -3.9224
50%        -1.9212
75%         0.1189
max        11.8264
Name: V11, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V12'
count   20000.0000
mean        1.6048
std         2.9305
min       -12.9480
25%        -0.3965
50%         1.5078
75%         3.5715
max        15.0807
Name: V12, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V13'
count   20000.0000
mean        1.5805
std         2.8747
min       -13.2282
25%        -0.2235
50%         1.6372
75%         3.4599
max        15.4196
Name: V13, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V14'
count   20000.0000
mean       -0.9506
std         1.7897
min        -7.7386
25%        -2.1707
50%        -0.9572
75%         0.2707
max         5.6707
Name: V14, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V15'
count   20000.0000
mean       -2.4150
std         3.3550
min       -16.4166
25%        -4.4153
50%        -2.3826
75%        -0.3591
max        12.2465
Name: V15, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V16'
count   20000.0000
mean       -2.9252
std         4.2217
min       -20.3742
25%        -5.6342
50%        -2.6827
75%        -0.0950
max        13.5832
Name: V16, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V17'
count   20000.0000
mean       -0.1343
std         3.3455
min       -14.0912
25%        -2.2156
50%        -0.0146
75%         2.0688
max        16.7564
Name: V17, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V18'
count   20000.0000
mean        1.1893
std         2.5923
min       -11.6440
25%        -0.4039
50%         0.8834
75%         2.5718
max        13.1799
Name: V18, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V19'
count   20000.0000
mean        1.1818
std         3.3969
min       -13.4918
25%        -1.0502
50%         1.2791
75%         3.4933
max        13.2377
Name: V19, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V20'
count   20000.0000
mean        0.0236
std         3.6695
min       -13.9227
25%        -2.4330
50%         0.0334
75%         2.5124
max        16.0523
Name: V20, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V21'
count   20000.0000
mean       -3.6113
std         3.5677
min       -17.9562
25%        -5.9304
50%        -3.5329
75%        -1.2659
max        13.8405
Name: V21, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V22'
count   20000.0000
mean        0.9518
std         1.6515
min       -10.1221
25%        -0.1181
50%         0.9747
75%         2.0256
max         7.4099
Name: V22, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V23'
count   20000.0000
mean       -0.3661
std         4.0319
min       -14.8661
25%        -3.0988
50%        -0.2621
75%         2.4517
max        14.4587
Name: V23, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V24'
count   20000.0000
mean        1.1344
std         3.9121
min       -16.3871
25%        -1.4681
50%         0.9690
75%         3.5460
max        17.1633
Name: V24, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V25'
count   20000.0000
mean       -0.0022
std         2.0167
min        -8.2283
25%        -1.3652
50%         0.0251
75%         1.3971
max         8.2234
Name: V25, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V26'
count   20000.0000
mean        1.8738
std         3.4351
min       -11.8343
25%        -0.3379
50%         1.9505
75%         4.1300
max        16.8364
Name: V26, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V27'
count   20000.0000
mean       -0.6124
std         4.3688
min       -14.9049
25%        -3.6523
50%        -0.8849
75%         2.1892
max        17.5604
Name: V27, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V28'
count   20000.0000
mean       -0.8832
std         1.9177
min        -9.2695
25%        -2.1712
50%        -0.8911
75%         0.3759
max         6.5276
Name: V28, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V29'
count   20000.0000
mean       -0.9856
std         2.6844
min       -12.5795
25%        -2.7874
50%        -1.1762
75%         0.6298
max        10.7221
Name: V29, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V30'
count   20000.0000
mean       -0.0155
std         3.0053
min       -14.7960
25%        -1.8671
50%         0.1843
75%         2.0362
max        12.5058
Name: V30, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V31'
count   20000.0000
mean        0.4868
std         3.4614
min       -13.7228
25%        -1.8178
50%         0.4903
75%         2.7307
max        17.2551
Name: V31, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V32'
count   20000.0000
mean        0.3038
std         5.5004
min       -19.8765
25%        -3.4205
50%         0.0521
75%         3.7617
max        23.6332
Name: V32, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V33'
count   20000.0000
mean        0.0498
std         3.5753
min       -16.8984
25%        -2.2429
50%        -0.0662
75%         2.2551
max        16.6925
Name: V33, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V34'
count   20000.0000
mean       -0.4627
std         3.1838
min       -17.9851
25%        -2.1370
50%        -0.2550
75%         1.4369
max        14.3582
Name: V34, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V35'
count   20000.0000
mean        2.2296
std         2.9371
min       -15.3498
25%         0.3362
50%         2.0986
75%         4.0644
max        15.2911
Name: V35, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V36'
count   20000.0000
mean        1.5148
std         3.8009
min       -14.8332
25%        -0.9438
50%         1.5665
75%         3.9839
max        19.3296
Name: V36, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V37'
count   20000.0000
mean        0.0113
std         1.7882
min        -5.4784
25%        -1.2558
50%        -0.1284
75%         1.1755
max         7.4670
Name: V37, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V38'
count   20000.0000
mean       -0.3440
std         3.9481
min       -17.3750
25%        -2.9876
50%        -0.3168
75%         2.2794
max        15.2899
Name: V38, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V39'
count   20000.0000
mean        0.8907
std         1.7531
min        -6.4389
25%        -0.2723
50%         0.9193
75%         2.0575
max         7.7599
Name: V39, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'V40'
count   20000.0000
mean       -0.8756
std         3.0122
min       -11.0239
25%        -2.9402
50%        -0.9208
75%         1.1199
max        10.6543
Name: V40, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Distribution of 'Target'
count   20000.0000
mean        0.0555
std         0.2290
min         0.0000
25%         0.0000
50%         0.0000
75%         0.0000
max         1.0000
Name: Target, dtype: float64
No description has been provided for this image
----------------------------------------------------------------------------------------------------
Obserations:¶
  • General Characteristics

    • All 40 predictors are continuous numeric (float).
    • Most features are centered around 0 (mean ~0), consistent with standardized or ciphered transformation.
    • Standard deviations range mostly between 2.0 – 3.5, indicating moderate variability.
  • Skewness

    • None of the 40 predictors show high skewness (|skew| > 1).
    • Most distributions are approximately symmetric or mildly skewed.
    • This is consistent with transformed data where distributions were normalized.
  • Outliers

    • Many predictors show values beyond 3× standard deviation (e.g., V1–V15 confirmed).
    • This suggests sensor anomalies or rare extreme readings.
    • Outliers are scattered rather than concentrated, indicating natural system variability rather than data corruption.
  • Spread

    • Example ranges:
      • V1: -11.9 to +15.5
      • V3: -10.7 to +17.1
      • V5: -8.6 to +8.1
    • Some variables cover wide ranges, others are more compact.
    • This mix of wide/narrow distributions may influence model interpretability.
  • Key Observations (Across All Predictors)

    • Balanced Feature Distributions → Most predictors resemble bell-shaped curves with moderate variance.
      • No heavily skewed features → Good for algorithms assuming normality (e.g., Logistic Regression).
    • Outliers are common across features (esp. V1–V15 tested) → May require robust scaling or outlier-tolerant models (Random Forest, XGBoost).
    • No missing values issue → Only 36 missing values in total, negligible at dataset scale.

In summary:

The dataset is well-prepared, symmetric, and standardized, but has rare outliers and a severe class imbalance. These will be the two main EDA findings guiding preprocessing and model design.

Bivariate Analysis¶

In [24]:
# Calculate the correlation matrix
corr_matrix = df_eda[num_cols].corr()

# Visual representation of the correlation matrix
plt.figure(figsize=(25, 25))
sns.heatmap(corr_matrix, annot=True, fmt='.2f', cbar=False, cmap='Spectral')
plt.title("Correlation Heatmap")
plt.xlabel("Features")
plt.ylabel("Features")
plt.show()
No description has been provided for this image
Observations:¶
  • Correlation with Target

    • Most predictors (V1–V40) show low-to-moderate correlation with Target (values are generally within ±0.1 to ±0.3).
    • No single variable stands out as highly correlated with failures (no |corr| > 0.5).
    • Observation → Failures are multi-factorial and likely need combinations of features (non-linear models will be helpful).
  • Inter-feature Correlation

    • Several predictors are strongly correlated with each other (|corr| > 0.7).
      • Example clusters visible in the heatmap:
        • V1–V4 group shows moderate inter-correlation.
        • V13–V16 are highly correlated.
        • V26–V29 show notable correlation.
    • Observation → There is multicollinearity in the dataset.
      • Linear models may suffer (unstable coefficients).
      • Tree-based models handle this better.
  • Overall Patterns

    • The correlation matrix is dense, with many weak-to-moderate correlations spread across variables.
    • This suggests that the data is not sparse — most features carry some information.
    • No single dominant predictor → classification may benefit from ensemble learning rather than simple univariate thresholds.
  • Modeling Implications

    • Feature Selection / Dimensionality Reduction
      • Since many features are correlated, PCA or feature importance methods (Random Forest/XGBoost) can help reduce redundancy.
      • Alternatively, L1 regularization (Lasso) can help shrink correlated features.
    • Model Choice
      • Tree-based methods (Random Forest, XGBoost, LightGBM) will handle correlated predictors and interactions effectively.
      • Logistic Regression/SVM may need regularization to avoid instability.
    • Failure Detection Difficulty
      • Low correlation with target → linear separability is weak.
      • Models that capture non-linear boundaries and feature interactions will perform better.

Summary:

  • No single “hero feature” strongly predicts failures.
  • Several feature clusters are inter-correlated, introducing multicollinearity.
  • Failures depend on complex feature interactions, requiring non-linear, ensemble-based models with feature importance ranking.
In [25]:
# Function to create distribution plot
def distribution_plot_wrt_target(data, predictor, target):

    fig, axs = plt.subplots(2, 2, figsize=(12, 10))

    target_uniq = data[target].unique()

    axs[0, 0].set_title(f"Distribution of target for target={str(target_uniq[0])}")
    sns.histplot(
        data=data[data[target] == target_uniq[0]],
        x=predictor,
        kde=True,
        ax=axs[0, 0],
        color="green",
        stat="density",
    )

    axs[0, 1].set_title(f"Distribution of target for target={str(target_uniq[1])}")
    sns.histplot(
        data=data[data[target] == target_uniq[1]],
        x=predictor,
        kde=True,
        ax=axs[0, 1],
        color="blue",
        stat="density",
    )

    axs[1, 0].set_title("Boxplot w.r.t target")
    sns.boxplot(data=data, x=target, y=predictor, ax=axs[1, 0], palette="gist_rainbow")

    axs[1, 1].set_title("Boxplot (without outliers) w.r.t target")
    sns.boxplot(
        data=data,
        x=target,
        y=predictor,
        ax=axs[1, 1],
        showfliers=False,
        palette="gist_rainbow",
    )

    plt.tight_layout()
    plt.show()
In [26]:
# Selecting all numerical columns ('int64', 'float64')
num_cols = df_eda.select_dtypes(include=["int64", "float64"]).columns[:-1]

# Iterate through each numerical column and distribution plot
for column in num_cols:
    distribution_plot_wrt_target(df_eda, column, "Target")
    print("-" * 150)
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
No description has been provided for this image
------------------------------------------------------------------------------------------------------------------------------------------------------
Observations:¶
  • The target variable shows only weak correlations with the predictors overall. The features with the strongest associations to the target are:

    • V7: 0.24
    • V15: 0.25
    • V16: 0.23
    • V21: 0.26

Observations of V7 and Target

Higher values of V7 appear to be strongly associated with failures. For Target = 1, the distribution is roughly symmetric but shifted to the right, with a mean closer to 2. In addition, the median value of V7 under Target = 1 is clearly higher than under Target = 0.

These patterns suggest that V7 is an effective discriminator between failures and non-failures, particularly given its positive correlation with the target.

Observations of V15 and Target

Similar to V7, higher values of V15 are linked to failures. This feature may serve as another strong predictor for distinguishing between failures and non-failures, given that failures consistently occur at higher V15 values.

Observations of V15 and Target

Failures are characterized by higher values of V16, continuing the pattern observed in the previous variables. This feature may also contribute to the model’s ability to effectively identify failure events.

Observations of V21 and Target

As with the other variables, higher values of V21 are associated with failure events. The separation between the two groups is evident, with failures displaying both higher and more dispersed values, making V21 a useful feature for distinguishing between failure and non-failure cases.

Data Preprocessing¶

In [27]:
# Copying data to another variable to avoid any changes to original data
df_model = df_eda.copy()
In [28]:
# Outlier detection using boxplot
# Creating a list of numerical columns
numeric_columns = df_model.select_dtypes(include=np.number).columns.tolist()

# Calculate the number of rows needed for the subplots
num_plots = len(num_cols)
num_rows = (num_plots // 3) + (num_plots % 3 > 0)

# Create subplots
plt.figure(figsize=(15, num_rows * 5))
for i, variable in enumerate(num_cols):
    plt.subplot(num_rows, 3, i + 1)
    sns.boxplot(data=df_model, x=variable)
    plt.tight_layout(pad=2)
plt.show()

# Check for outliers in continuous variables
outliers = df_model[num_cols].describe()
print("\nSummary of the numerical features, including outliers:")
display(outliers)
No description has been provided for this image
Summary of the numerical features, including outliers:
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40
count 19982.0000 19982.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000 20000.0000
mean -0.2720 0.4404 2.4847 -0.0832 -0.0538 -0.9954 -0.8793 -0.5482 -0.0168 -0.0130 -1.8954 1.6048 1.5805 -0.9506 -2.4150 -2.9252 -0.1343 1.1893 1.1818 0.0236 -3.6113 0.9518 -0.3661 1.1344 -0.0022 1.8738 -0.6124 -0.8832 -0.9856 -0.0155 0.4868 0.3038 0.0498 -0.4627 2.2296 1.5148 0.0113 -0.3440 0.8907 -0.8756
std 3.4416 3.1508 3.3890 3.4316 2.1048 2.0410 1.7616 3.2958 2.1606 2.1932 3.1243 2.9305 2.8747 1.7897 3.3550 4.2217 3.3455 2.5923 3.3969 3.6695 3.5677 1.6515 4.0319 3.9121 2.0167 3.4351 4.3688 1.9177 2.6844 3.0053 3.4614 5.5004 3.5753 3.1838 2.9371 3.8009 1.7882 3.9481 1.7531 3.0122
min -11.8765 -12.3200 -10.7081 -15.0821 -8.6034 -10.2271 -7.9497 -15.6576 -8.5963 -9.8540 -14.8321 -12.9480 -13.2282 -7.7386 -16.4166 -20.3742 -14.0912 -11.6440 -13.4918 -13.9227 -17.9562 -10.1221 -14.8661 -16.3871 -8.2283 -11.8343 -14.9049 -9.2695 -12.5795 -14.7960 -13.7228 -19.8765 -16.8984 -17.9851 -15.3498 -14.8332 -5.4784 -17.3750 -6.4389 -11.0239
25% -2.7371 -1.6407 0.2069 -2.3477 -1.5356 -2.3472 -2.0309 -2.6427 -1.4950 -1.4112 -3.9224 -0.3965 -0.2235 -2.1707 -4.4153 -5.6342 -2.2156 -0.4039 -1.0502 -2.4330 -5.9304 -0.1181 -3.0988 -1.4681 -1.3652 -0.3379 -3.6523 -2.1712 -2.7874 -1.8671 -1.8178 -3.4205 -2.2429 -2.1370 0.3362 -0.9438 -1.2558 -2.9876 -0.2723 -2.9402
50% -0.7479 0.4715 2.2558 -0.1352 -0.1020 -1.0005 -0.9172 -0.3891 -0.0676 0.1010 -1.9212 1.5078 1.6372 -0.9572 -2.3826 -2.6827 -0.0146 0.8834 1.2791 0.0334 -3.5329 0.9747 -0.2621 0.9690 0.0251 1.9505 -0.8849 -0.8911 -1.1762 0.1843 0.4903 0.0521 -0.0662 -0.2550 2.0986 1.5665 -0.1284 -0.3168 0.9193 -0.9208
75% 1.8401 2.5440 4.5662 2.1306 1.3405 0.3803 0.2237 1.7230 1.4092 1.4770 0.1189 3.5715 3.4599 0.2707 -0.3591 -0.0950 2.0688 2.5718 3.4933 2.5124 -1.2659 2.0256 2.4517 3.5460 1.3971 4.1300 2.1892 0.3759 0.6298 2.0362 2.7307 3.7617 2.2551 1.4369 4.0644 3.9839 1.1755 2.2794 2.0575 1.1199
max 15.4930 13.0893 17.0909 13.2364 8.1338 6.9758 8.0061 11.6795 8.1376 8.1085 11.8264 15.0807 15.4196 5.6707 12.2465 13.5832 16.7564 13.1799 13.2377 16.0523 13.8405 7.4099 14.4587 17.1633 8.2234 16.8364 17.5604 6.5276 10.7221 12.5058 17.2551 23.6332 16.6925 14.3582 15.2911 19.3296 7.4670 15.2899 7.7599 10.6543
In [29]:
# Step 1: Define and Prepare the Data
X = df_model.drop("Target", axis=1)
y = df_model["Target"]

display(X.head())
display(y.head())
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40
0 -4.4646 -4.6791 3.1015 0.5061 -0.2211 -2.0325 -2.9109 0.0507 -1.5224 3.7619 -5.7147 0.7359 0.9813 1.4179 -3.3758 -3.0473 0.3062 2.9141 2.2700 4.3949 -2.3883 0.6464 -1.1905 3.1330 0.6653 -2.5108 -0.0367 0.7262 -3.9822 -1.0726 1.6671 3.0597 -1.6904 2.8463 2.2352 6.6675 0.4438 -2.3692 2.9506 -3.4803
1 3.3659 3.6534 0.9097 -1.3675 0.3320 2.3589 0.7326 -4.3321 0.5657 -0.1011 1.9145 -0.9515 -1.2553 -2.7065 0.1932 -4.7694 -2.2053 0.9077 0.7569 -5.8337 -3.0651 1.5966 -1.7573 1.7664 -0.2671 3.6250 1.5003 -0.5857 0.7830 -0.2012 0.0249 -1.7955 3.0328 -2.4675 1.8946 -2.2978 -1.7310 5.9088 -0.3863 0.6162
2 -3.8318 -5.8244 0.6340 -2.4188 -1.7738 1.0168 -2.0989 -3.1732 -2.0819 5.3926 -0.7707 1.1067 1.1443 0.9433 -3.1638 -4.2478 -4.0389 3.6885 3.3112 1.0590 -2.1430 1.6501 -1.6606 1.6799 -0.4508 -4.5507 3.7388 1.1344 -2.0335 0.8408 -1.6004 -0.2571 0.8035 4.0862 2.2921 5.3608 0.3520 2.9400 3.8392 -4.3094
3 1.6181 1.8883 7.0461 -1.1473 0.0831 -1.5298 0.2073 -2.4936 0.3449 2.1186 -3.0530 0.4597 2.7045 -0.6361 -0.4537 -3.1740 -3.4043 -1.2815 1.5821 -1.9518 -3.5166 -1.2060 -5.6279 -1.8177 2.1241 5.2946 4.7481 -2.3085 -3.9630 -6.0287 4.9488 -3.5844 -2.5775 1.3638 0.6227 5.5501 -1.5268 0.1389 3.1014 -1.2774
4 -0.1114 3.8725 -3.7584 -2.9829 3.7927 0.5450 0.2054 4.8490 -1.8549 -6.2200 1.9983 4.7238 0.7091 -1.9894 -2.6327 4.1844 2.2454 3.7345 -6.3128 -5.3799 -0.8867 2.0617 9.4456 4.4900 -3.9451 4.5821 -8.7804 -3.3830 5.1065 6.7875 2.0442 8.2659 6.6292 -10.0687 1.2230 -3.2298 1.6869 -2.1639 -3.6446 6.5103
Target
0 0
1 0
2 0
3 0
4 0

Observations:¶

In classification problems with imbalanced target classes, it is recommended to use stratified sampling to maintain the relative class frequencies across the training and test sets. This can be achieved by setting the stratify parameter in the train_test_split function.

In [30]:
# Splitting train dataset into training and validation set (75% train, 25% validation), using stratify to maintain target distribution
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.25, random_state=1, stratify=y)
In [31]:
# Checking the number of rows and columns in the X_train data
print("There are", X_train.shape[0], "rows and", X_train.shape[1], "columns in the Training set.", X_train.shape,)

# Checking the number of rows and columns in the X_val data
print("There are", X_val.shape[0], "rows and", X_val.shape[1], "columns in the Test set.", X_val.shape,)
There are 15000 rows and 40 columns in the Training set. (15000, 40)
There are 5000 rows and 40 columns in the Test set. (5000, 40)
In [32]:
print("Percentage of classes in training set:")
print(y_train.value_counts(normalize=True) * 100)

print("")

print("Percentage of classes in validation set:")
print(y_val.value_counts(normalize=True) * 100)
Percentage of classes in training set:
Target
0   94.4533
1    5.5467
Name: proportion, dtype: float64

Percentage of classes in validation set:
Target
0   94.4400
1    5.5600
Name: proportion, dtype: float64
In [33]:
# Dividing test data into X_test and y_test
X_test = df_test.drop(["Target"], axis=1)
y_test = df_test["Target"]

display(X_test.head())
display(y_test.head())
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40
0 -0.6135 -3.8196 2.2023 1.3004 -1.1849 -4.4960 -1.8358 4.7230 1.2061 -0.3419 -5.1229 1.0170 4.8185 3.2690 -2.9843 1.3874 2.0320 -0.5116 -1.0231 7.3387 -2.2422 0.1555 2.0538 -2.7723 1.8514 -1.7887 -0.2773 -1.2551 -3.8329 -1.5045 1.5868 2.2912 -5.4114 0.8701 0.5745 4.1572 1.4281 -10.5113 0.4547 -1.4484
1 0.3896 -0.5123 0.5271 -2.5768 -1.0168 2.2351 -0.4413 -4.4057 -0.3329 1.9668 1.7965 0.4105 0.6383 -1.3896 -1.8834 -5.0179 -3.8272 2.4181 1.7623 -3.2423 -3.1930 1.8575 -1.7080 0.6334 -0.5879 0.0837 3.0139 -0.1823 0.2239 0.8652 -1.7822 -2.4749 2.4936 0.3152 2.0593 0.6839 -0.4855 5.1284 1.7207 -1.4882
2 -0.8749 -0.6406 4.0842 -1.5905 0.5259 -1.9576 -0.6954 1.3473 -1.7323 0.4665 -4.9282 3.5651 -0.4493 -0.6562 -0.1665 -1.6302 2.2919 2.3965 0.6013 1.7935 -2.1202 0.4820 -0.8407 1.7902 1.8744 0.3639 -0.1691 -0.4838 -2.1190 -2.1566 2.9073 -1.3189 -2.9975 0.4597 0.6198 5.6315 1.3235 -1.7522 1.8083 1.6757
3 0.2384 1.4586 4.0145 2.5345 1.1970 -3.1173 -0.9240 0.2695 1.3224 0.7023 -5.5783 -0.8507 2.5905 0.7674 -2.3908 -2.3420 0.5719 -0.9338 0.5087 1.2107 -3.2595 0.1046 -0.6589 1.4981 1.1003 4.1430 -0.2484 -1.1365 -5.3558 -4.5459 3.8087 3.5179 -3.0741 -0.2842 0.9546 3.0293 -1.3672 -3.4121 0.9060 -2.4509
4 5.8282 2.7683 -1.2345 2.8093 -1.6416 -1.4067 0.5686 0.9650 1.9184 -2.7749 -0.5300 1.3745 -0.6509 -1.6795 -0.3792 -4.4431 3.8939 -0.6076 2.9449 0.3672 -5.7891 4.5975 4.4503 3.2249 0.3967 0.2478 -2.3620 1.0794 -0.4731 2.2428 -3.5914 1.7738 -1.5016 -2.2267 4.7768 -6.5597 -0.8056 -0.2760 -3.8582 -0.5377
Target
0 0
1 0
2 0
3 0
4 0

In [34]:
# Checking the number of rows and columns in the X_test data
print("There are", X_test.shape[0], "rows and", X_test.shape[1], "columns in the Test set.", X_test.shape,)
There are 5000 rows and 40 columns in the Test set. (5000, 40)
In [35]:
print("Percentage of classes in test set:")
print(y_test.value_counts(normalize=True) * 100)
Percentage of classes in test set:
Target
0   94.3600
1    5.6400
Name: proportion, dtype: float64
In [36]:
# Creating an instance of the imputer to be used
imputer = SimpleImputer(strategy="median")
In [37]:
# Fit and transform the train data
X_train = pd.DataFrame(imputer.fit_transform(X_train), columns=X_train.columns)

# Transform the validation data without data leakage (using the same imputer trained on X_train)
X_val = pd.DataFrame(imputer.transform(X_val), columns=X_train.columns)

# Transform the test data without data leakage
X_test = pd.DataFrame(imputer.transform(X_test), columns=X_train.columns)
In [38]:
# Checking that no column has missing values in train, validation, or test sets
print("Missing values on the Train dataset")
print(X_train.isna().sum())
print("-" * 50)

print("Missing values on the Validation dataset")
print(X_val.isna().sum())  # Checking missing values in validation set
print("-" * 50)

print("Missing values on the Test dataset")
print(X_test.isna().sum())  # Checking missing values in test set
Missing values on the Train dataset
V1     0
V2     0
V3     0
V4     0
V5     0
V6     0
V7     0
V8     0
V9     0
V10    0
V11    0
V12    0
V13    0
V14    0
V15    0
V16    0
V17    0
V18    0
V19    0
V20    0
V21    0
V22    0
V23    0
V24    0
V25    0
V26    0
V27    0
V28    0
V29    0
V30    0
V31    0
V32    0
V33    0
V34    0
V35    0
V36    0
V37    0
V38    0
V39    0
V40    0
dtype: int64
--------------------------------------------------
Missing values on the Validation dataset
V1     0
V2     0
V3     0
V4     0
V5     0
V6     0
V7     0
V8     0
V9     0
V10    0
V11    0
V12    0
V13    0
V14    0
V15    0
V16    0
V17    0
V18    0
V19    0
V20    0
V21    0
V22    0
V23    0
V24    0
V25    0
V26    0
V27    0
V28    0
V29    0
V30    0
V31    0
V32    0
V33    0
V34    0
V35    0
V36    0
V37    0
V38    0
V39    0
V40    0
dtype: int64
--------------------------------------------------
Missing values on the Test dataset
V1     0
V2     0
V3     0
V4     0
V5     0
V6     0
V7     0
V8     0
V9     0
V10    0
V11    0
V12    0
V13    0
V14    0
V15    0
V16    0
V17    0
V18    0
V19    0
V20    0
V21    0
V22    0
V23    0
V24    0
V25    0
V26    0
V27    0
V28    0
V29    0
V30    0
V31    0
V32    0
V33    0
V34    0
V35    0
V36    0
V37    0
V38    0
V39    0
V40    0
dtype: int64

Model Building¶

Model Evaluation Criterion¶

Write down the model evaluation criterion with rationale

  • Interpretation of Predictions

    • True Positives (TP): Actual failures that the model correctly identifies.
    • False Negatives (FN): Actual failures that the model fails to detect (most costly).
    • False Positives (FP): Cases where the model predicts a failure, but the generator is actually fine.

Metric We Should Optimize

Since missed failures (FN) are far more expensive than unnecessary inspections (FP), our priority is to correctly capture as many real failures as possible.

  • Recall is the most appropriate metric to optimize.
  • Maximizing Recall reduces false negatives, ensuring that potential failures are flagged in advance.
  • This aligns with the business objective of minimizing costly generator replacements by enabling timely repairs.
In [39]:
def plot(history, name):
    """
    Function to plot loss/accuracy

    history: an object which stores the metrics and losses.
    name: can be one of Loss or Accuracy
    """
    fig, ax = plt.subplots() #Creating a subplot with figure and axes.
    plt.plot(history.history[name]) #Plotting the train accuracy or train loss
    plt.plot(history.history['val_'+name]) #Plotting the validation accuracy or validation loss

    plt.title('Model ' + name.capitalize()) #Defining the title of the plot.
    plt.ylabel(name.capitalize()) #Capitalizing the first letter.
    plt.xlabel('Epoch') #Defining the label for the x-axis.
    fig.legend(['Train', 'Validation'], loc="outside right upper") #Defining the legend, loc controls the position of the legend.
In [40]:
# defining a function to compute different metrics to check performance of a classification model built using statsmodels
def model_performance_classification(
    model, predictors, target, threshold=0.5
):
    """
    Function to compute different metrics to check classification model performance

    model: classifier
    predictors: independent variables
    target: dependent variable
    threshold: threshold for classifying the observation as class 1
    """

    # checking which probabilities are greater than threshold
    pred = model.predict(predictors) > threshold
    # pred_temp = model.predict(predictors) > threshold
    # # rounding off the above values to get classes
    # pred = np.round(pred_temp)

    acc = accuracy_score(target, pred)  # to compute Accuracy
    recall = recall_score(target, pred, average='macro')  # to compute Recall
    precision = precision_score(target, pred, average='macro')  # to compute Precision
    f1 = f1_score(target, pred, average='macro')  # to compute F1-score

    # creating a dataframe of metrics
    df_perf = pd.DataFrame(
        {"Accuracy": acc, "Recall": recall, "Precision": precision, "F1 Score": f1,}, index = [0]
    )

    return df_perf
In [41]:
#Defining the columns of the dataframe which are nothing but the hyper parameters and the metrics.
columns = ["# hidden layers","# neurons - hidden layer","activation function - hidden layer","# epochs","batch size","train loss","validation loss","train accuracy","validation accuracy","time (secs)"]

#Creating a pandas dataframe.
results = pd.DataFrame(columns=columns)

Initial Model Building (Model 0)¶

  • Let's start with a neural network consisting of
    • just one hidden layer
    • activation function of ReLU
    • SGD as the optimizer
In [42]:
# clears the current Keras session, resetting all layers and models previously created, freeing up memory and resources.
tf.keras.backend.clear_session()
In [43]:
#Initializing the neural network
model_0 = Sequential()
model_0.add(Dense(7 ,activation="relu",input_dim=X_train.shape[1]))
model_0.add(Dense(1 ,activation="sigmoid"))
In [44]:
model_0.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 7)              │           287 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 1)              │             8 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 295 (1.15 KB)
 Trainable params: 295 (1.15 KB)
 Non-trainable params: 0 (0.00 B)
In [45]:
# defining the batch size and # epochs upfront as we'll be using the same values for all models
epochs = 50
batch_size = 32

optimizer = tf.keras.optimizers.SGD()
model_0.compile(loss='binary_crossentropy', optimizer=optimizer, metrics = ['recall'])
In [46]:
start = time.time()
history = model_0.fit(X_train, y_train, validation_data=(X_val,y_val) , batch_size=batch_size, epochs=epochs)
end=time.time()
Epoch 1/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - loss: 0.3437 - recall: 0.4404 - val_loss: 0.1273 - val_recall: 0.5144
Epoch 2/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.1124 - recall: 0.5566 - val_loss: 0.1125 - val_recall: 0.5612
Epoch 3/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.1012 - recall: 0.6290 - val_loss: 0.1034 - val_recall: 0.5971
Epoch 4/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0915 - recall: 0.6310 - val_loss: 0.0980 - val_recall: 0.6223
Epoch 5/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0889 - recall: 0.6468 - val_loss: 0.0942 - val_recall: 0.6547
Epoch 6/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0812 - recall: 0.6847 - val_loss: 0.0901 - val_recall: 0.6619
Epoch 7/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0864 - recall: 0.6579 - val_loss: 0.0882 - val_recall: 0.6655
Epoch 8/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - loss: 0.0809 - recall: 0.6764 - val_loss: 0.0853 - val_recall: 0.6799
Epoch 9/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0759 - recall: 0.7105 - val_loss: 0.0834 - val_recall: 0.6978
Epoch 10/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0761 - recall: 0.7368 - val_loss: 0.0802 - val_recall: 0.7122
Epoch 11/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0716 - recall: 0.7146 - val_loss: 0.0784 - val_recall: 0.7194
Epoch 12/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0672 - recall: 0.7672 - val_loss: 0.0754 - val_recall: 0.7662
Epoch 13/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0610 - recall: 0.7735 - val_loss: 0.0732 - val_recall: 0.7806
Epoch 14/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0699 - recall: 0.7594 - val_loss: 0.0719 - val_recall: 0.7842
Epoch 15/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0612 - recall: 0.7752 - val_loss: 0.0701 - val_recall: 0.7842
Epoch 16/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0642 - recall: 0.7599 - val_loss: 0.0693 - val_recall: 0.7986
Epoch 17/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - loss: 0.0623 - recall: 0.7596 - val_loss: 0.0687 - val_recall: 0.8058
Epoch 18/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0606 - recall: 0.7897 - val_loss: 0.0678 - val_recall: 0.8022
Epoch 19/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - loss: 0.0623 - recall: 0.7575 - val_loss: 0.0674 - val_recall: 0.8058
Epoch 20/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 2ms/step - loss: 0.0640 - recall: 0.7684 - val_loss: 0.0667 - val_recall: 0.8094
Epoch 21/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0515 - recall: 0.8072 - val_loss: 0.0667 - val_recall: 0.8094
Epoch 22/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0586 - recall: 0.7993 - val_loss: 0.0663 - val_recall: 0.8094
Epoch 23/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0627 - recall: 0.7824 - val_loss: 0.0657 - val_recall: 0.8094
Epoch 24/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0471 - recall: 0.8360 - val_loss: 0.0664 - val_recall: 0.8165
Epoch 25/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0568 - recall: 0.8282 - val_loss: 0.0657 - val_recall: 0.8129
Epoch 26/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0510 - recall: 0.8209 - val_loss: 0.0654 - val_recall: 0.8129
Epoch 27/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0566 - recall: 0.7898 - val_loss: 0.0654 - val_recall: 0.8094
Epoch 28/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0559 - recall: 0.7941 - val_loss: 0.0649 - val_recall: 0.8058
Epoch 29/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0540 - recall: 0.8113 - val_loss: 0.0646 - val_recall: 0.8129
Epoch 30/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0575 - recall: 0.7959 - val_loss: 0.0653 - val_recall: 0.8165
Epoch 31/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0544 - recall: 0.8150 - val_loss: 0.0648 - val_recall: 0.8129
Epoch 32/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0543 - recall: 0.8200 - val_loss: 0.0648 - val_recall: 0.8129
Epoch 33/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0551 - recall: 0.8018 - val_loss: 0.0646 - val_recall: 0.8094
Epoch 34/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0504 - recall: 0.8140 - val_loss: 0.0647 - val_recall: 0.8201
Epoch 35/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0565 - recall: 0.8173 - val_loss: 0.0647 - val_recall: 0.8094
Epoch 36/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0565 - recall: 0.7958 - val_loss: 0.0647 - val_recall: 0.8165
Epoch 37/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0539 - recall: 0.8036 - val_loss: 0.0649 - val_recall: 0.8165
Epoch 38/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0550 - recall: 0.8189 - val_loss: 0.0647 - val_recall: 0.8237
Epoch 39/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - loss: 0.0529 - recall: 0.8134 - val_loss: 0.0644 - val_recall: 0.8129
Epoch 40/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0512 - recall: 0.8338 - val_loss: 0.0647 - val_recall: 0.8201
Epoch 41/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0532 - recall: 0.8261 - val_loss: 0.0650 - val_recall: 0.8273
Epoch 42/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0523 - recall: 0.8060 - val_loss: 0.0647 - val_recall: 0.8165
Epoch 43/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0506 - recall: 0.8219 - val_loss: 0.0649 - val_recall: 0.8165
Epoch 44/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0543 - recall: 0.8104 - val_loss: 0.0647 - val_recall: 0.8129
Epoch 45/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0500 - recall: 0.8328 - val_loss: 0.0649 - val_recall: 0.8237
Epoch 46/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0516 - recall: 0.8086 - val_loss: 0.0645 - val_recall: 0.8129
Epoch 47/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0551 - recall: 0.8017 - val_loss: 0.0647 - val_recall: 0.8094
Epoch 48/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0462 - recall: 0.8208 - val_loss: 0.0641 - val_recall: 0.8165
Epoch 49/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 5ms/step - loss: 0.0550 - recall: 0.8048 - val_loss: 0.0645 - val_recall: 0.8201
Epoch 50/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0539 - recall: 0.7967 - val_loss: 0.0650 - val_recall: 0.8094
In [47]:
print("Time taken in seconds ",end-start)
Time taken in seconds  69.66447472572327
In [48]:
plot(history, 'loss')
No description has been provided for this image
In [49]:
plot(history,'recall')
No description has been provided for this image
In [50]:
model_0_train_perf = model_performance_classification(model_0, X_train, y_train)
model_0_train_perf
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step
Out[50]:
Accuracy Recall Precision F1 Score
0 0.9874 0.9034 0.9731 0.9351
In [51]:
model_0_val_perf = model_performance_classification(model_0,X_val,y_val)
model_0_val_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
Out[51]:
Accuracy Recall Precision F1 Score
0 0.9878 0.9038 0.9773 0.9371
In [52]:
y_train_pred_0 = model_0.predict(X_train)
y_val_pred_0 = model_0.predict(X_val)
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
In [53]:
print("Classification Report - Train data Model_0",end="\n\n")
cr_train_model_0 = classification_report(y_train,y_train_pred_0>0.5)
print(cr_train_model_0)
Classification Report - Train data Model_0

              precision    recall  f1-score   support

           0       0.99      1.00      0.99     14168
           1       0.96      0.81      0.88       832

    accuracy                           0.99     15000
   macro avg       0.97      0.90      0.94     15000
weighted avg       0.99      0.99      0.99     15000

In [54]:
print("Classification Report - Validation data Model_0",end="\n\n")
cr_val_model_0 = classification_report(y_val,y_val_pred_0>0.5)
print(cr_val_model_0)
Classification Report - Validation data Model_0

              precision    recall  f1-score   support

           0       0.99      1.00      0.99      4722
           1       0.97      0.81      0.88       278

    accuracy                           0.99      5000
   macro avg       0.98      0.90      0.94      5000
weighted avg       0.99      0.99      0.99      5000

Observations:¶
  1. Model Architecture
  • Model: Simple feedforward neural network (Sequential model)

  • Layers:

    • Hidden layer: 7 neurons, ReLU activation
    • Output layer: 1 neuron, Sigmoid activation
  • Optimizer: Stochastic Gradient Descent (SGD)

  • Loss function: Binary Crossentropy

  • Metric tracked: Recall

  • Training: 50 epochs, batch size = 32

  • Training time: ~69.66 seconds

Interpretation:

This is a lightweight binary classifier designed to detect turbine failures (Target = 1). The architecture is compact, making it efficient for large sensor datasets.

  1. Training and Validation Loss
  • Observation: Both training and validation loss curves decrease smoothly and stabilize around epoch 25–30.

  • No overfitting: The validation loss follows the training loss closely, with only a small gap.

  • Final loss values: Around 0.06 (train) and 0.07 (validation).

Conclusion:

The model is well-optimized — it generalizes effectively without signs of underfitting or overfitting.

  1. Model Recall Curve
  • Observation:

    • Recall starts around 0.45 and steadily improves to ~0.80–0.82 for both training and validation.
    • The lines nearly overlap — strong generalization.
  • Meaning:

The model detects about 80% of actual failures, which is good considering the dataset’s class imbalance (only ~5.5% failures).

Performance Metrics:

Train Data:

Metric Score
Accuracy 0.9874
Recall 0.9034
Precision 0.9731
F1-score 0.9351

Validation Data:

Metric Score
Accuracy 0.9878
Recall 0.9038
Precision 0.9773
F1-score 0.9371

Key Insights:

  • Almost identical train and validation metrics → no overfitting.

  • High accuracy and F1-score indicate robust performance.

  • Recall (~0.90) ensures most failures are detected.

  • Precision (~0.97) shows few false alarms.

Classification Report:

Class Precision Recall F1-score
0 (No Failure) 0.99 1.00 0.99
1 (Failure) 0.96–0.97 0.81 0.88
  • Interpretation:

  • The model is slightly conservative — it misses about 19% of real failures (FN).

  • However, it avoids unnecessary inspections (low FP rate).

  • Business Implication:

Since missing a failure (FN) leads to replacement cost, improving recall (even at the expense of some precision) would yield better cost savings overall.

  • Summary of Observations
Aspect Finding Remark
Model fit Excellent Balanced loss curves
Overfitting None Train ≈ Validation performance
Recall ~0.81–0.82 Could be optimized further
F1-score ~0.94 High overall model quality
Business alignment Good Needs minor recall optimization

Model Performance Improvement¶

Model 1¶

In [55]:
# clears the current Keras session, resetting all layers and models previously created, freeing up memory and resources.
tf.keras.backend.clear_session()
In [56]:
#Initializing the neural network
model_1 = Sequential()
model_1.add(Dense(128 ,activation="relu",input_dim=X_train.shape[1]))
model_1.add(Dense(7 ,activation="relu"))
model_1.add(Dense(1 ,activation="sigmoid"))
In [57]:
model_1.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 128)            │         5,248 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 7)              │           903 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 1)              │             8 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 6,159 (24.06 KB)
 Trainable params: 6,159 (24.06 KB)
 Non-trainable params: 0 (0.00 B)
In [58]:
# Setting training configuration
epochs = 50
batch_size = 64

optimizer = tf.keras.optimizers.SGD()
model_1.compile(loss='binary_crossentropy', optimizer=optimizer, metrics = ['recall'])
In [59]:
start = time.time()
history = model_1.fit(X_train, y_train, validation_data=(X_val,y_val) , batch_size=batch_size, epochs=epochs)
end=time.time()
Epoch 1/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 3s 8ms/step - loss: 0.2427 - recall: 0.2474 - val_loss: 0.1124 - val_recall: 0.5180
Epoch 2/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 2s 8ms/step - loss: 0.0996 - recall: 0.5248 - val_loss: 0.0945 - val_recall: 0.6511
Epoch 3/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 3s 9ms/step - loss: 0.0948 - recall: 0.6314 - val_loss: 0.0862 - val_recall: 0.6799
Epoch 4/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 2s 6ms/step - loss: 0.0736 - recall: 0.6957 - val_loss: 0.0805 - val_recall: 0.7374
Epoch 5/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - loss: 0.0712 - recall: 0.7633 - val_loss: 0.0760 - val_recall: 0.7518
Epoch 6/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - loss: 0.0667 - recall: 0.7511 - val_loss: 0.0727 - val_recall: 0.7842
Epoch 7/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0639 - recall: 0.7863 - val_loss: 0.0704 - val_recall: 0.7806
Epoch 8/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0635 - recall: 0.7907 - val_loss: 0.0682 - val_recall: 0.7842
Epoch 9/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0682 - recall: 0.7926 - val_loss: 0.0670 - val_recall: 0.7914
Epoch 10/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0568 - recall: 0.8021 - val_loss: 0.0662 - val_recall: 0.7950
Epoch 11/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0538 - recall: 0.8357 - val_loss: 0.0649 - val_recall: 0.8022
Epoch 12/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0573 - recall: 0.8112 - val_loss: 0.0643 - val_recall: 0.8058
Epoch 13/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0500 - recall: 0.8531 - val_loss: 0.0630 - val_recall: 0.8094
Epoch 14/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0528 - recall: 0.8359 - val_loss: 0.0624 - val_recall: 0.8165
Epoch 15/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0515 - recall: 0.8261 - val_loss: 0.0622 - val_recall: 0.8058
Epoch 16/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0500 - recall: 0.8525 - val_loss: 0.0613 - val_recall: 0.8309
Epoch 17/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0465 - recall: 0.8574 - val_loss: 0.0609 - val_recall: 0.8273
Epoch 18/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0526 - recall: 0.8452 - val_loss: 0.0603 - val_recall: 0.8237
Epoch 19/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0503 - recall: 0.8617 - val_loss: 0.0601 - val_recall: 0.8309
Epoch 20/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0462 - recall: 0.8583 - val_loss: 0.0605 - val_recall: 0.8201
Epoch 21/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0431 - recall: 0.8592 - val_loss: 0.0597 - val_recall: 0.8345
Epoch 22/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0470 - recall: 0.8612 - val_loss: 0.0591 - val_recall: 0.8345
Epoch 23/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0504 - recall: 0.8350 - val_loss: 0.0594 - val_recall: 0.8345
Epoch 24/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0470 - recall: 0.8639 - val_loss: 0.0591 - val_recall: 0.8345
Epoch 25/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0426 - recall: 0.8626 - val_loss: 0.0588 - val_recall: 0.8417
Epoch 26/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0421 - recall: 0.8751 - val_loss: 0.0587 - val_recall: 0.8345
Epoch 27/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0432 - recall: 0.8637 - val_loss: 0.0584 - val_recall: 0.8417
Epoch 28/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step - loss: 0.0419 - recall: 0.8617 - val_loss: 0.0582 - val_recall: 0.8453
Epoch 29/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0393 - recall: 0.8930 - val_loss: 0.0580 - val_recall: 0.8381
Epoch 30/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0448 - recall: 0.8600 - val_loss: 0.0585 - val_recall: 0.8525
Epoch 31/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0363 - recall: 0.8851 - val_loss: 0.0580 - val_recall: 0.8381
Epoch 32/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0426 - recall: 0.8602 - val_loss: 0.0580 - val_recall: 0.8453
Epoch 33/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0347 - recall: 0.9023 - val_loss: 0.0578 - val_recall: 0.8489
Epoch 34/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0352 - recall: 0.8885 - val_loss: 0.0578 - val_recall: 0.8525
Epoch 35/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0429 - recall: 0.8643 - val_loss: 0.0575 - val_recall: 0.8453
Epoch 36/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0403 - recall: 0.8762 - val_loss: 0.0572 - val_recall: 0.8453
Epoch 37/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0407 - recall: 0.8720 - val_loss: 0.0569 - val_recall: 0.8417
Epoch 38/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0442 - recall: 0.8700 - val_loss: 0.0570 - val_recall: 0.8453
Epoch 39/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - loss: 0.0414 - recall: 0.8747 - val_loss: 0.0568 - val_recall: 0.8453
Epoch 40/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 5ms/step - loss: 0.0378 - recall: 0.8911 - val_loss: 0.0569 - val_recall: 0.8453
Epoch 41/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0401 - recall: 0.8792 - val_loss: 0.0567 - val_recall: 0.8489
Epoch 42/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0444 - recall: 0.8830 - val_loss: 0.0571 - val_recall: 0.8489
Epoch 43/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0442 - recall: 0.8762 - val_loss: 0.0571 - val_recall: 0.8417
Epoch 44/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0340 - recall: 0.8856 - val_loss: 0.0564 - val_recall: 0.8417
Epoch 45/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0434 - recall: 0.8662 - val_loss: 0.0568 - val_recall: 0.8417
Epoch 46/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0407 - recall: 0.8773 - val_loss: 0.0569 - val_recall: 0.8525
Epoch 47/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0380 - recall: 0.8861 - val_loss: 0.0564 - val_recall: 0.8561
Epoch 48/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0414 - recall: 0.8785 - val_loss: 0.0566 - val_recall: 0.8489
Epoch 49/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0381 - recall: 0.8877 - val_loss: 0.0565 - val_recall: 0.8453
Epoch 50/50
235/235 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - loss: 0.0340 - recall: 0.9034 - val_loss: 0.0562 - val_recall: 0.8561
In [60]:
print("Time taken in seconds ",end-start)
Time taken in seconds  47.98444724082947
In [61]:
plot(history,'loss')
No description has been provided for this image
In [62]:
plot(history,'recall')
No description has been provided for this image
In [63]:
model_1_train_perf = model_performance_classification(model_1,X_train,y_train)
model_1_train_perf
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step
Out[63]:
Accuracy Recall Precision F1 Score
0 0.9931 0.9432 0.9907 0.9656
In [64]:
model_1_val_perf = model_performance_classification(model_1,X_val,y_val)
model_1_val_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
Out[64]:
Accuracy Recall Precision F1 Score
0 0.9906 0.9273 0.9815 0.9526
In [65]:
y_train_pred_1 = model_1.predict(X_train)
y_val_pred_1 = model_1.predict(X_val)
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
In [66]:
print("Classification Report - Train data Model_1", end="\n\n")
cr_train_model_1 = classification_report(y_train,y_train_pred_1 > 0.5)
print(cr_train_model_1)
Classification Report - Train data Model_1

              precision    recall  f1-score   support

           0       0.99      1.00      1.00     14168
           1       0.99      0.89      0.93       832

    accuracy                           0.99     15000
   macro avg       0.99      0.94      0.97     15000
weighted avg       0.99      0.99      0.99     15000

In [67]:
print("Classification Report - Validation data Model_1", end="\n\n")
cr_val_model_1 = classification_report(y_val,y_val_pred_1 > 0.5)
print(cr_val_model_1)
Classification Report - Validation data Model_1

              precision    recall  f1-score   support

           0       0.99      1.00      1.00      4722
           1       0.97      0.86      0.91       278

    accuracy                           0.99      5000
   macro avg       0.98      0.93      0.95      5000
weighted avg       0.99      0.99      0.99      5000

Observations:¶
  1. Model Architecture
  • Layers:

    • Dense (128 units, ReLU) — first hidden layer
    • Dense (7 units, ReLU) — second hidden layer
    • Dense (1 unit, Sigmoid) — output layer
  • Total Parameters: 6,159

  • Optimizer: Stochastic Gradient Descent (SGD)

  • Loss: Binary Crossentropy

  • Metric: Recall

  • Epochs: 50, Batch size: 64

  • Training time: ~48 seconds

  • Change from previous model: This model has deeper architecture and larger hidden dimension (128 neurons vs 7 previously), allowing it to capture more complex patterns from the sensor data.

  1. Training and Validation Loss
  • Observation:

    • Both curves decrease steadily and plateau after ~25 epochs.
    • Final training loss ≈ 0.04, validation loss ≈ 0.06.
    • Validation curve remains close to the training curve, showing no overfitting.
  • Conclusion: The model fits the data well and generalizes properly. The slightly lower loss compared to Model_0 confirms improved learning capacity.

  1. Model Recall
  • Training Recall: Increases from ~0.3 → 0.94

  • Validation Recall: Increases from ~0.5 → 0.86–0.88

  • The curves are stable and close, suggesting consistent recall across datasets.

  • Interpretation: The new network detects nearly 86–88% of all actual failures (vs 81–82% in the previous model). This means the model is catching more “failure” cases — a key improvement for predictive maintenance.

  • Performance Metrics

Metric Score
Accuracy 0.9931
Recall 0.9432
Precision 0.9907
F1-score 0.9656
  • Validation Data:
Metric Score
Accuracy 0.9906
Recall 0.9273
Precision 0.9815
F1-score 0.9526
  • Insights:

    • Accuracy improved slightly from 98.7% → 99.0%.
    • Recall jumped significantly (~0.90 → 0.93) — the most important metric for failure prediction.
    • Precision remains very high (~0.98), meaning false positives (unnecessary inspections) are minimal.
    • Train vs validation gap is tiny → excellent generalization.
  • Classification Report

Train Data

Class Precision Recall F1-score
0 (No Failure) 0.99 1.00 1.00
1 (Failure) 0.99 0.89 0.93

Validation Data

Class Precision Recall F1-score
0 (No Failure) 0.99 1.00 1.00
1 (Failure) 0.97 0.86 0.91
  • Interpretation:

    • The model correctly identifies almost all non-failure cases.
    • For failure cases (1), recall increased from 0.81 → 0.86, showing better fault detection.
    • F1-scores improved for both classes → model is more balanced.
  • Comparative Summary

Aspect Model_0 (Simple NN) Model_1 (Deeper NN) Observation
Layers 7 → 1 128 → 7 → 1 Added complexity
Training Time 69.6s 48s Slightly faster (larger batch size)
Accuracy 0.987 0.991 Improved
Recall 0.81–0.90 0.86–0.94 Strong improvement
Precision 0.97 0.98–0.99 Improved stability
Overfitting None None Stable performance
Business Value Good detection Better failure capture Fewer missed failures
  • Final Observations

    • The deeper network (Model_1) performs significantly better, especially on recall, meaning fewer costly undetected failures.
    • The validation performance closely follows training, confirming excellent generalization.
    • Training efficiency improved due to larger batch size (64) and well-scaled architecture.
    • For predictive maintenance, this model is more reliable and cost-effective.

Model 2¶

In [68]:
# clears the current Keras session, resetting all layers and models previously created, freeing up memory and resources.
tf.keras.backend.clear_session()
In [69]:
#Initializing the neural network
from tensorflow.keras.layers import Dropout
model_2 = Sequential()
model_2.add(Dense(16 ,activation="relu",input_dim=X_train.shape[1]))
model_2.add(Dropout(0.5))
model_2.add(Dense(128,activation = "relu"))
model_2.add(Dense(64,activation = "tanh"))
model_2.add(Dense(1,activation="sigmoid"))
In [70]:
model_2.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 16)             │           656 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 16)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 128)            │         2,176 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 64)             │         8,256 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 1)              │            65 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 11,153 (43.57 KB)
 Trainable params: 11,153 (43.57 KB)
 Non-trainable params: 0 (0.00 B)
In [71]:
# Setting training configuration
epochs = 50
batch_size = 32

optimizer = tf.keras.optimizers.SGD()
model_2.compile(loss='binary_crossentropy', optimizer='sgd', metrics = ['Recall'])
In [72]:
start = time.time()
history = model_2.fit(X_train, y_train, validation_data=(X_val,y_val) , batch_size=batch_size, epochs=epochs)
end=time.time()
Epoch 1/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 3.2082e-04 - loss: 0.2584 - val_Recall: 0.0000e+00 - val_loss: 0.1596
Epoch 2/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.0257 - loss: 0.1812 - val_Recall: 0.1727 - val_loss: 0.1353
Epoch 3/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.1632 - loss: 0.1687 - val_Recall: 0.3561 - val_loss: 0.1155
Epoch 4/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.2928 - loss: 0.1354 - val_Recall: 0.4604 - val_loss: 0.1029
Epoch 5/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.3660 - loss: 0.1342 - val_Recall: 0.5072 - val_loss: 0.0966
Epoch 6/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - Recall: 0.4254 - loss: 0.1180 - val_Recall: 0.5899 - val_loss: 0.0902
Epoch 7/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.4646 - loss: 0.1133 - val_Recall: 0.6223 - val_loss: 0.0866
Epoch 8/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.4403 - loss: 0.1104 - val_Recall: 0.6583 - val_loss: 0.0825
Epoch 9/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.4833 - loss: 0.1086 - val_Recall: 0.6691 - val_loss: 0.0805
Epoch 10/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.4992 - loss: 0.1093 - val_Recall: 0.6583 - val_loss: 0.0806
Epoch 11/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.4821 - loss: 0.1060 - val_Recall: 0.7014 - val_loss: 0.0769
Epoch 12/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.4950 - loss: 0.1051 - val_Recall: 0.7050 - val_loss: 0.0750
Epoch 13/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5098 - loss: 0.1088 - val_Recall: 0.7050 - val_loss: 0.0746
Epoch 14/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.5302 - loss: 0.0963 - val_Recall: 0.7158 - val_loss: 0.0737
Epoch 15/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - Recall: 0.5234 - loss: 0.0990 - val_Recall: 0.7302 - val_loss: 0.0719
Epoch 16/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5206 - loss: 0.0945 - val_Recall: 0.7122 - val_loss: 0.0720
Epoch 17/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5727 - loss: 0.0919 - val_Recall: 0.7266 - val_loss: 0.0718
Epoch 18/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5719 - loss: 0.0908 - val_Recall: 0.7194 - val_loss: 0.0715
Epoch 19/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5776 - loss: 0.0905 - val_Recall: 0.7482 - val_loss: 0.0709
Epoch 20/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5488 - loss: 0.1010 - val_Recall: 0.7374 - val_loss: 0.0702
Epoch 21/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5808 - loss: 0.0867 - val_Recall: 0.7626 - val_loss: 0.0693
Epoch 22/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 3ms/step - Recall: 0.5932 - loss: 0.0847 - val_Recall: 0.7626 - val_loss: 0.0688
Epoch 23/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.5821 - loss: 0.0898 - val_Recall: 0.7662 - val_loss: 0.0689
Epoch 24/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.5918 - loss: 0.0817 - val_Recall: 0.7446 - val_loss: 0.0685
Epoch 25/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5867 - loss: 0.0829 - val_Recall: 0.7662 - val_loss: 0.0678
Epoch 26/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5990 - loss: 0.0844 - val_Recall: 0.7626 - val_loss: 0.0671
Epoch 27/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5748 - loss: 0.0874 - val_Recall: 0.7806 - val_loss: 0.0676
Epoch 28/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5874 - loss: 0.0889 - val_Recall: 0.7662 - val_loss: 0.0669
Epoch 29/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5962 - loss: 0.0863 - val_Recall: 0.7554 - val_loss: 0.0670
Epoch 30/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5924 - loss: 0.0827 - val_Recall: 0.7806 - val_loss: 0.0662
Epoch 31/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5676 - loss: 0.0828 - val_Recall: 0.7842 - val_loss: 0.0656
Epoch 32/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.5906 - loss: 0.0842 - val_Recall: 0.7770 - val_loss: 0.0659
Epoch 33/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.5788 - loss: 0.0865 - val_Recall: 0.7626 - val_loss: 0.0663
Epoch 34/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5862 - loss: 0.0792 - val_Recall: 0.7734 - val_loss: 0.0653
Epoch 35/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5554 - loss: 0.0878 - val_Recall: 0.7662 - val_loss: 0.0658
Epoch 36/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.6388 - loss: 0.0845 - val_Recall: 0.7698 - val_loss: 0.0660
Epoch 37/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5914 - loss: 0.0791 - val_Recall: 0.7698 - val_loss: 0.0657
Epoch 38/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.6096 - loss: 0.0777 - val_Recall: 0.7698 - val_loss: 0.0658
Epoch 39/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.6274 - loss: 0.0776 - val_Recall: 0.7590 - val_loss: 0.0647
Epoch 40/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.6008 - loss: 0.0745 - val_Recall: 0.7662 - val_loss: 0.0650
Epoch 41/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.5825 - loss: 0.0813 - val_Recall: 0.7662 - val_loss: 0.0656
Epoch 42/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.5774 - loss: 0.0827 - val_Recall: 0.7734 - val_loss: 0.0655
Epoch 43/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.6380 - loss: 0.0774 - val_Recall: 0.7698 - val_loss: 0.0644
Epoch 44/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.6045 - loss: 0.0733 - val_Recall: 0.7770 - val_loss: 0.0649
Epoch 45/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5778 - loss: 0.0804 - val_Recall: 0.7770 - val_loss: 0.0647
Epoch 46/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5978 - loss: 0.0795 - val_Recall: 0.7878 - val_loss: 0.0650
Epoch 47/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 3ms/step - Recall: 0.5832 - loss: 0.0784 - val_Recall: 0.7662 - val_loss: 0.0648
Epoch 48/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.5862 - loss: 0.0848 - val_Recall: 0.7734 - val_loss: 0.0652
Epoch 49/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.6300 - loss: 0.0722 - val_Recall: 0.7626 - val_loss: 0.0657
Epoch 50/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.6016 - loss: 0.0741 - val_Recall: 0.7734 - val_loss: 0.0646
In [73]:
print("Time taken in seconds ",end-start)
Time taken in seconds  77.34527611732483
In [74]:
plot(history,'loss')
No description has been provided for this image
In [75]:
plot(history,'Recall')
No description has been provided for this image
In [76]:
model_2_train_perf = model_performance_classification(model_2,X_train,y_train)
model_2_train_perf
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
Out[76]:
Accuracy Recall Precision F1 Score
0 0.9849 0.8715 0.9815 0.9185
In [77]:
model_2_val_perf = model_performance_classification(model_2,X_val,y_val)
model_2_val_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
Out[77]:
Accuracy Recall Precision F1 Score
0 0.9856 0.8857 0.9733 0.9245
In [78]:
y_train_pred_2 = model_2.predict(X_train)
y_val_pred_2 = model_2.predict(X_val)
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
In [79]:
print("Classification Report - Train data Model_2", end="\n\n")
cr_train_model_2 = classification_report(y_train,y_train_pred_2 > 0.5)
print(cr_train_model_2)
Classification Report - Train data Model_2

              precision    recall  f1-score   support

           0       0.99      1.00      0.99     14168
           1       0.98      0.74      0.85       832

    accuracy                           0.98     15000
   macro avg       0.98      0.87      0.92     15000
weighted avg       0.98      0.98      0.98     15000

In [80]:
print("Classification Report - Validation data Model_2", end="\n\n")
cr_val_model_2 = classification_report(y_val , y_val_pred_2 > 0.5)
print(cr_val_model_2)
Classification Report - Validation data Model_2

              precision    recall  f1-score   support

           0       0.99      1.00      0.99      4722
           1       0.96      0.77      0.86       278

    accuracy                           0.99      5000
   macro avg       0.97      0.89      0.92      5000
weighted avg       0.99      0.99      0.98      5000

Observations:¶
  1. Model Architecture
  • Layers and Structure

    • Dense(16, ReLU) → initial layer
    • Dropout(0.5) → prevents overfitting
    • Dense(128, ReLU)
    • Dense(64, tanh)
    • Dense(1, Sigmoid) → output layer
    • Total Parameters: 11,153 (trainable)
    • Optimizer: SGD
    • Loss: Binary Crossentropy
    • Metric: Recall
    • Epochs: 50
    • Batch size: 32
    • Training time: ~77 seconds
  • Observation:

This is the most complex model so far — deeper, with more neurons and dropout to improve generalization. The inclusion of tanh activation adds nonlinearity diversity, potentially capturing different relationships in the turbine sensor data.

  1. Training and Validation Loss
  • Both loss curves decrease rapidly at first and plateau around epoch 25–30.

  • Final Loss:

    • Train ≈ 0.08
    • Validation ≈ 0.06
  • The validation loss is actually lower than training loss, indicating excellent generalization and that dropout is effectively preventing overfitting.

Conclusion:

The model is well-regularized. The slight gap between curves is healthy and expected with dropout layers.

  1. Model Recall Trend
  • Training Recall: Rises from 0.1 → stabilizes around 0.60–0.62

  • Validation Recall: Starts around 0.3 → improves to ~0.78–0.80

  • The validation recall consistently outperforms training recall.

  • Interpretation:

This pattern is typical when dropout is active during training but inactive during validation — it means the model performs slightly better during inference because dropout is not randomly disabling neurons.

However, the training recall being relatively low (~0.6) might suggest underfitting, possibly because 50% dropout is too high.

  • Performance Metrics

Train Data

Metric Score
Accuracy 0.9849
Recall 0.8715
Precision 0.9815
F1-score 0.9185

Validation Data

Metric Score
Accuracy 0.9856
Recall 0.8857
Precision 0.9733
F1-score 0.9245
  • Insights:

    • Accuracy slightly decreased (~0.99 → 0.985), but recall (~0.93 → 0.88) dipped compared to Model_1.
    • High precision (0.97+) remains consistent — meaning low false alarms.
    • F1-score is strong (~0.92), showing a good balance.
  • Interpretation:

The model is solid but slightly underfits the minority (failure) class due to aggressive dropout or too high regularization.

  • Classification Report

Train Data

Class Precision Recall F1-score
0 (No Failure) 0.99 1.00 0.99
1 (Failure) 0.98 0.74 0.85

Validation Data

Class Precision Recall F1-score
0 (No Failure) 0.99 1.00 0.99
1 (Failure) 0.96 0.77 0.86
  • Key Takeaways:

    • The model remains excellent at detecting non-failures (0).
    • Failure detection (1) recall decreased to 0.74–0.77 from 0.86–0.89 in Model_1.
    • This suggests dropout may be too aggressive (0.5) or the learning rate is too low for this deeper architecture.
  • Comparative Summary

Metric Model_0 Model_1 Model_2
Architecture Simple (7→1) 128→7→1 16→Dropout→128→64→1
Parameters 295 6,159 11,153
Training Time 69.6s 48s 77s
Accuracy (Val) 0.987 0.991 0.986
Recall (Val) 0.81 0.93 0.88
Precision (Val) 0.97 0.98 0.97
F1-score (Val) 0.88 0.95 0.92
Overfitting None None None (slightly underfit)
Generalization Good Excellent Good but conservative
  • Overall Observations

    • Model_2 introduces dropout and deeper layers, which improves stability and prevents overfitting but slightly sacrifices recall.
    • Model_1 remains the best trade-off:
      • High recall (0.93) → detects most failures
      • High precision (0.98) → few false positives
      • Balanced training/validation performance
    • Model_2 is safer (less overfit) but slightly under-sensitive to failures — not ideal for predictive maintenance where missing a failure is costlier.
  • Final Recommendations

Goal Recommendation
Best-performing model ✅ Model_1 (128 → 7 → 1)
If avoiding overfitting is priority Use Model_2 with Dropout=0.3 instead of 0.5

Model 3¶

In [81]:
# Calculate class weights for imbalanced dataset
cw = (y_train.shape[0]) / np.bincount(y_train.astype(int)) # Convert y_train to integers

# Create a dictionary mapping class indices to their respective class weights
cw_dict = {}
for i in range(cw.shape[0]):
    cw_dict[i] = cw[i]

cw_dict
Out[81]:
{0: np.float64(1.0587238848108413), 1: np.float64(18.028846153846153)}
In [82]:
# clears the current Keras session, resetting all layers and models previously created, freeing up memory and resources.
tf.keras.backend.clear_session()
In [83]:
model_3 = Sequential()
model_3.add(Dense(16 ,activation="relu",input_dim=X_train.shape[1]))
model_3.add(Dropout(0.5))
model_3.add(Dense(128 ,activation="relu"))
model_3.add(Dense(64 , activation = "tanh"))
model_3.add(Dense(1,activation="sigmoid"))
In [84]:
model_3.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 16)             │           656 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 16)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 128)            │         2,176 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 64)             │         8,256 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 1)              │            65 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 11,153 (43.57 KB)
 Trainable params: 11,153 (43.57 KB)
 Non-trainable params: 0 (0.00 B)
In [85]:
# Set training parameters
epochs = 50
batch_size = 32

optimizer = tf.keras.optimizers.SGD()
model_3.compile(loss='binary_crossentropy', optimizer="sgd", metrics = ['Recall'])
In [86]:
start = time.time()
history = model_3.fit(X_train, y_train, validation_data=(X_val,y_val) , batch_size=batch_size, epochs=epochs,class_weight=cw_dict)
end=time.time()
Epoch 1/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.7750 - loss: 1.0652 - val_Recall: 0.8777 - val_loss: 0.3157
Epoch 2/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8256 - loss: 0.6728 - val_Recall: 0.8777 - val_loss: 0.3066
Epoch 3/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8126 - loss: 0.6605 - val_Recall: 0.8741 - val_loss: 0.2608
Epoch 4/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8397 - loss: 0.5814 - val_Recall: 0.8813 - val_loss: 0.2573
Epoch 5/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.8319 - loss: 0.6040 - val_Recall: 0.8705 - val_loss: 0.2341
Epoch 6/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8391 - loss: 0.5825 - val_Recall: 0.8885 - val_loss: 0.2366
Epoch 7/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8072 - loss: 0.6055 - val_Recall: 0.8705 - val_loss: 0.1801
Epoch 8/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8215 - loss: 0.5811 - val_Recall: 0.8777 - val_loss: 0.1746
Epoch 9/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8398 - loss: 0.5684 - val_Recall: 0.8777 - val_loss: 0.1732
Epoch 10/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8333 - loss: 0.5679 - val_Recall: 0.8741 - val_loss: 0.1741
Epoch 11/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8297 - loss: 0.5199 - val_Recall: 0.8741 - val_loss: 0.1799
Epoch 12/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8540 - loss: 0.5300 - val_Recall: 0.8849 - val_loss: 0.1711
Epoch 13/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8383 - loss: 0.5681 - val_Recall: 0.8849 - val_loss: 0.1754
Epoch 14/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8438 - loss: 0.5169 - val_Recall: 0.8741 - val_loss: 0.1854
Epoch 15/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.8497 - loss: 0.5254 - val_Recall: 0.8741 - val_loss: 0.1753
Epoch 16/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - Recall: 0.8641 - loss: 0.4634 - val_Recall: 0.8777 - val_loss: 0.1553
Epoch 17/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8658 - loss: 0.4888 - val_Recall: 0.8993 - val_loss: 0.3066
Epoch 18/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8470 - loss: 0.5317 - val_Recall: 0.8921 - val_loss: 0.1890
Epoch 19/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8440 - loss: 0.5213 - val_Recall: 0.8669 - val_loss: 0.1490
Epoch 20/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8440 - loss: 0.5255 - val_Recall: 0.8849 - val_loss: 0.1928
Epoch 21/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8506 - loss: 0.4903 - val_Recall: 0.8777 - val_loss: 0.1655
Epoch 22/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8578 - loss: 0.5063 - val_Recall: 0.8849 - val_loss: 0.2571
Epoch 23/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8602 - loss: 0.5010 - val_Recall: 0.8813 - val_loss: 0.2446
Epoch 24/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.8589 - loss: 0.4591 - val_Recall: 0.8849 - val_loss: 0.2090
Epoch 25/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8615 - loss: 0.5053 - val_Recall: 0.8849 - val_loss: 0.1685
Epoch 26/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8540 - loss: 0.5017 - val_Recall: 0.8777 - val_loss: 0.1414
Epoch 27/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8535 - loss: 0.4807 - val_Recall: 0.8849 - val_loss: 0.1703
Epoch 28/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8641 - loss: 0.5025 - val_Recall: 0.8741 - val_loss: 0.1209
Epoch 29/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8463 - loss: 0.5142 - val_Recall: 0.8777 - val_loss: 0.1673
Epoch 30/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8592 - loss: 0.4733 - val_Recall: 0.8777 - val_loss: 0.1452
Epoch 31/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8556 - loss: 0.4779 - val_Recall: 0.8777 - val_loss: 0.1503
Epoch 32/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8537 - loss: 0.5168 - val_Recall: 0.8813 - val_loss: 0.1601
Epoch 33/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8728 - loss: 0.4638 - val_Recall: 0.8849 - val_loss: 0.1424
Epoch 34/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8206 - loss: 0.5561 - val_Recall: 0.8813 - val_loss: 0.1926
Epoch 35/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8683 - loss: 0.4765 - val_Recall: 0.8849 - val_loss: 0.1870
Epoch 36/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.8431 - loss: 0.4952 - val_Recall: 0.8777 - val_loss: 0.2281
Epoch 37/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8574 - loss: 0.5077 - val_Recall: 0.8777 - val_loss: 0.1259
Epoch 38/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8522 - loss: 0.4944 - val_Recall: 0.8849 - val_loss: 0.2105
Epoch 39/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 3ms/step - Recall: 0.8529 - loss: 0.5204 - val_Recall: 0.8813 - val_loss: 0.1649
Epoch 40/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8453 - loss: 0.5037 - val_Recall: 0.8813 - val_loss: 0.2207
Epoch 41/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8374 - loss: 0.5355 - val_Recall: 0.8813 - val_loss: 0.1753
Epoch 42/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.8607 - loss: 0.5025 - val_Recall: 0.8777 - val_loss: 0.1538
Epoch 43/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - Recall: 0.8477 - loss: 0.4973 - val_Recall: 0.8813 - val_loss: 0.1853
Epoch 44/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8722 - loss: 0.4854 - val_Recall: 0.8813 - val_loss: 0.1614
Epoch 45/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8627 - loss: 0.4681 - val_Recall: 0.8741 - val_loss: 0.1542
Epoch 46/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8591 - loss: 0.4948 - val_Recall: 0.8705 - val_loss: 0.1154
Epoch 47/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8149 - loss: 0.5637 - val_Recall: 0.8777 - val_loss: 0.1791
Epoch 48/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8592 - loss: 0.4865 - val_Recall: 0.8813 - val_loss: 0.1979
Epoch 49/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8458 - loss: 0.4863 - val_Recall: 0.8741 - val_loss: 0.1725
Epoch 50/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8394 - loss: 0.4926 - val_Recall: 0.8813 - val_loss: 0.1627
In [87]:
print("Time taken in seconds ",end-start)
Time taken in seconds  74.89451193809509
In [88]:
plot(history,'loss')
No description has been provided for this image
In [89]:
plot(history,'Recall')
No description has been provided for this image
In [90]:
model_3_train_perf = model_performance_classification(model_3,X_train,y_train)
model_3_train_perf
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step
Out[90]:
Accuracy Recall Precision F1 Score
0 0.9685 0.9403 0.8251 0.8724
In [91]:
model_3_val_perf = model_performance_classification(model_3,X_val,y_val)
model_3_val_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
Out[91]:
Accuracy Recall Precision F1 Score
0 0.9646 0.9254 0.8113 0.8578
In [92]:
y_train_pred_3 = model_3.predict(X_train)
y_val_pred_3 = model_3.predict(X_val)
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
In [93]:
print("Classification Report - Train data Model_3", end="\n\n")
cr_train_model_3 = classification_report(y_train,y_train_pred_3 > 0.5)
print(cr_train_model_3)
Classification Report - Train data Model_3

              precision    recall  f1-score   support

           0       0.99      0.97      0.98     14168
           1       0.66      0.91      0.76       832

    accuracy                           0.97     15000
   macro avg       0.83      0.94      0.87     15000
weighted avg       0.98      0.97      0.97     15000

In [94]:
print("Classification Report - Validation data Model_3", end="\n\n")
cr_val_model_3 = classification_report(y_val,y_val_pred_3 > 0.5)
print(cr_val_model_3)
Classification Report - Validation data Model_3

              precision    recall  f1-score   support

           0       0.99      0.97      0.98      4722
           1       0.63      0.88      0.73       278

    accuracy                           0.96      5000
   macro avg       0.81      0.93      0.86      5000
weighted avg       0.97      0.96      0.97      5000

Observations:¶
  1. Model 3: Architecture and Setup
  • Model Configuration

    • Layers:
      • Dense(16, ReLU)
      • Dropout(0.5)
      • Dense(128, ReLU)
      • Dense(64, tanh)
      • Dense(1, Sigmoid)
    • Total Parameters: 11,153
    • Optimizer: SGD
    • Loss Function: Binary Crossentropy
    • Metric: Recall
    • Epochs: 50
    • Batch Size: 32
    • Training Time: ~74.9 seconds
    • Additional Enhancement: Class weights applied to counter data imbalance (approx. {0: 1.05, 1: 18.02})
  • Purpose: To penalize misclassification of minority class (“failure”) more heavily, improving recall (detecting more true failures).

  1. Training and Validation Loss
  • Training loss: Starts around 0.9, stabilizes at ~0.5.

  • Validation loss: Starts around 0.3, stabilizes between 0.1–0.2.

  • Validation loss is consistently lower than training loss.

  • Interpretation:

This suggests the model generalizes well, and the regularization (dropout) effectively prevents overfitting.

The large initial loss gap is due to class weighting, which increases training difficulty.

  • Conclusion:

Stable convergence; the model is learning effectively despite the imbalance handling.

  1. Model Recall Behavior
  • Training Recall: Improves from ~0.78 → stabilizes near 0.85–0.86.

  • Validation Recall: Consistently higher (~0.88–0.89) throughout.

  • The validation recall curve is smoother and more stable.

  • Interpretation:

The model achieves strong recall (~0.89) for the validation set — better than Model_2 (~0.77).

This indicates that class weighting successfully improved sensitivity to failure cases.

  • Performance Metrics

Train Data:

Metric Score
Accuracy 0.9685
Recall 0.9403
Precision 0.8251
F1-score 0.8724

Validation Data:

Metric Score
Accuracy 0.9646
Recall 0.9254
Precision 0.8113
F1-score 0.8578
  • Key Takeaways:

  • Recall increased (important for catching failures).

  • Precision dropped slightly (~0.81) — expected trade-off due to class weights.

  • F1-score remains solid (~0.86–0.87).

  • Accuracy slightly reduced but acceptable for imbalanced datasets.

  • Interpretation:

The model correctly prioritizes failure detection (recall) over overall accuracy — which aligns perfectly with business goals (preventing costly generator replacements).

  • Classification Report

Train Data

Class Precision Recall F1-score
0 (No Failure) 0.99 0.97 0.98
1 (Failure) 0.66 0.91 0.76

Validation Data

Class Precision Recall F1-score
0 (No Failure) 0.99 0.97 0.98
1 (Failure) 0.63 0.88 0.73
  • Analysis:

    • Significant recall improvement for class 1 (failures):
      • Model_2: 0.77 → Model_3: 0.88 ✅
    • Precision dropped to 0.63–0.66 → indicates more false positives (extra inspections). But that’s a positive trade-off in predictive maintenance, since inspections cost less than replacements.
  • Comparative Summary

Metric Model_0 Model_1 Model_2 Model_3 (Class Weighted)
Architecture 7→1 128→7→1 16→Dropout→128→64→1 16→Dropout→128→64→1
Params 295 6,159 11,153 11,153
Time (s) 69.6 48 77.3 74.9
Accuracy (Val) 0.987 0.991 0.986 0.965
Recall (Val) 0.81 0.93 0.77 0.88–0.89
Precision (Val) 0.97 0.98 0.97 0.81
F1-score (Val) 0.88 0.95 0.92 0.86
Overfitting None None None None
Handling Imbalance ❌ ❌ ❌ ✅
Business Impact Good Excellent Conservative High sensitivity to failures
  • Observations Summary

    • Model_3 effectively addresses class imbalance using class_weight.
    • It detects ~88–89% of real failures, minimizing costly undetected breakdowns.
    • Trade-off: More false positives → slightly more inspections, but this is acceptable and cost-efficient in predictive maintenance contexts.
    • Loss and recall curves are stable, confirming solid generalization.
    • Precision drop is expected — could be mitigated via threshold tuning or additional post-processing (e.g., anomaly filtering).
  • Final Recommendation

Business Priority Recommended Model Reason
Cost-efficient operation ✅ Model_3 High recall ensures early detection and preventive maintenance.
Balanced performance (recall + precision) Model_1 Best overall metrics and generalization.

Model 4¶

In [95]:
# clears the current Keras session, resetting all layers and models previously created, freeing up memory and resources.
tf.keras.backend.clear_session()
In [96]:
#Initializing the neural network
model_4 = Sequential()
model_4.add(Dense(128 ,activation="relu",input_dim=X_train.shape[1]))
model_4.add(Dense(64 ,activation="tanh"))
model_4.add(Dense(32 ,activation="tanh"))
model_4.add(Dense(1 ,activation="sigmoid"))
In [97]:
model_4.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 128)            │         5,248 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 64)             │         8,256 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 32)             │         2,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 1)              │            33 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 15,617 (61.00 KB)
 Trainable params: 15,617 (61.00 KB)
 Non-trainable params: 0 (0.00 B)
In [98]:
# Set training parameters
epochs = 50
batch_size = 32

optimizer = tf.keras.optimizers.Adam()
model_4.compile(loss='binary_crossentropy', optimizer=optimizer, metrics = ['Recall'])
In [99]:
start = time.time()
history = model_4.fit(X_train, y_train, validation_data=(X_val,y_val) , batch_size=batch_size, epochs=epochs)
end=time.time()
Epoch 1/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 4s 4ms/step - Recall: 0.5219 - loss: 0.1082 - val_Recall: 0.8453 - val_loss: 0.0509
Epoch 2/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.8677 - loss: 0.0419 - val_Recall: 0.8453 - val_loss: 0.0535
Epoch 3/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8747 - loss: 0.0398 - val_Recall: 0.8453 - val_loss: 0.0468
Epoch 4/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8873 - loss: 0.0362 - val_Recall: 0.8489 - val_loss: 0.0485
Epoch 5/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8888 - loss: 0.0351 - val_Recall: 0.8777 - val_loss: 0.0470
Epoch 6/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9055 - loss: 0.0311 - val_Recall: 0.8597 - val_loss: 0.0478
Epoch 7/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9048 - loss: 0.0293 - val_Recall: 0.8741 - val_loss: 0.0446
Epoch 8/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8997 - loss: 0.0286 - val_Recall: 0.8885 - val_loss: 0.0463
Epoch 9/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9142 - loss: 0.0291 - val_Recall: 0.8885 - val_loss: 0.0461
Epoch 10/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.9018 - loss: 0.0305 - val_Recall: 0.8741 - val_loss: 0.0472
Epoch 11/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.9026 - loss: 0.0310 - val_Recall: 0.8561 - val_loss: 0.0466
Epoch 12/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.9150 - loss: 0.0258 - val_Recall: 0.8777 - val_loss: 0.0456
Epoch 13/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9191 - loss: 0.0259 - val_Recall: 0.8741 - val_loss: 0.0477
Epoch 14/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9186 - loss: 0.0238 - val_Recall: 0.8777 - val_loss: 0.0484
Epoch 15/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9074 - loss: 0.0264 - val_Recall: 0.8849 - val_loss: 0.0467
Epoch 16/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9271 - loss: 0.0211 - val_Recall: 0.8813 - val_loss: 0.0501
Epoch 17/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9152 - loss: 0.0252 - val_Recall: 0.8741 - val_loss: 0.0470
Epoch 18/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9158 - loss: 0.0252 - val_Recall: 0.8561 - val_loss: 0.0506
Epoch 19/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.9084 - loss: 0.0250 - val_Recall: 0.8705 - val_loss: 0.0502
Epoch 20/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.9136 - loss: 0.0254 - val_Recall: 0.8885 - val_loss: 0.0519
Epoch 21/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9263 - loss: 0.0208 - val_Recall: 0.8813 - val_loss: 0.0512
Epoch 22/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9192 - loss: 0.0235 - val_Recall: 0.8705 - val_loss: 0.0527
Epoch 23/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9101 - loss: 0.0232 - val_Recall: 0.8633 - val_loss: 0.0533
Epoch 24/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9128 - loss: 0.0212 - val_Recall: 0.8885 - val_loss: 0.0569
Epoch 25/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9396 - loss: 0.0189 - val_Recall: 0.8813 - val_loss: 0.0527
Epoch 26/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9222 - loss: 0.0187 - val_Recall: 0.8813 - val_loss: 0.0487
Epoch 27/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9357 - loss: 0.0164 - val_Recall: 0.8741 - val_loss: 0.0539
Epoch 28/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.9306 - loss: 0.0177 - val_Recall: 0.8741 - val_loss: 0.0523
Epoch 29/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.9142 - loss: 0.0191 - val_Recall: 0.8885 - val_loss: 0.0537
Epoch 30/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.9155 - loss: 0.0180 - val_Recall: 0.8669 - val_loss: 0.0597
Epoch 31/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9204 - loss: 0.0200 - val_Recall: 0.8849 - val_loss: 0.0548
Epoch 32/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9225 - loss: 0.0182 - val_Recall: 0.8777 - val_loss: 0.0566
Epoch 33/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9530 - loss: 0.0121 - val_Recall: 0.8633 - val_loss: 0.0599
Epoch 34/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9186 - loss: 0.0196 - val_Recall: 0.8813 - val_loss: 0.0586
Epoch 35/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9414 - loss: 0.0148 - val_Recall: 0.8741 - val_loss: 0.0569
Epoch 36/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9377 - loss: 0.0144 - val_Recall: 0.8705 - val_loss: 0.0567
Epoch 37/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.9379 - loss: 0.0145 - val_Recall: 0.8633 - val_loss: 0.0573
Epoch 38/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.9370 - loss: 0.0142 - val_Recall: 0.8597 - val_loss: 0.0614
Epoch 39/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9233 - loss: 0.0164 - val_Recall: 0.8777 - val_loss: 0.0570
Epoch 40/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9242 - loss: 0.0198 - val_Recall: 0.8813 - val_loss: 0.0569
Epoch 41/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9474 - loss: 0.0132 - val_Recall: 0.8777 - val_loss: 0.0563
Epoch 42/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9446 - loss: 0.0124 - val_Recall: 0.8741 - val_loss: 0.0588
Epoch 43/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9406 - loss: 0.0144 - val_Recall: 0.8777 - val_loss: 0.0633
Epoch 44/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9501 - loss: 0.0127 - val_Recall: 0.8633 - val_loss: 0.0593
Epoch 45/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.9414 - loss: 0.0104 - val_Recall: 0.8561 - val_loss: 0.0625
Epoch 46/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.9426 - loss: 0.0117 - val_Recall: 0.8777 - val_loss: 0.0633
Epoch 47/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.9471 - loss: 0.0126 - val_Recall: 0.8597 - val_loss: 0.0643
Epoch 48/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9353 - loss: 0.0139 - val_Recall: 0.8597 - val_loss: 0.0626
Epoch 49/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9501 - loss: 0.0115 - val_Recall: 0.8741 - val_loss: 0.0647
Epoch 50/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9421 - loss: 0.0144 - val_Recall: 0.8741 - val_loss: 0.0631
In [100]:
print("Time taken in seconds ",end-start)
Time taken in seconds  80.87897825241089
In [101]:
plot(history,'loss')
No description has been provided for this image
In [102]:
plot(history,'Recall')
No description has been provided for this image
In [103]:
model_4_train_perf = model_performance_classification(model_4,X_train,y_train)
model_4_train_perf
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
Out[103]:
Accuracy Recall Precision F1 Score
0 0.9968 0.9757 0.9936 0.9844
In [104]:
model_4_val_perf = model_performance_classification(model_4,X_val,y_val)
model_4_val_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
Out[104]:
Accuracy Recall Precision F1 Score
0 0.9904 0.9357 0.9709 0.9525
In [105]:
y_train_pred_4 = model_4.predict(X_train)
y_val_pred_4 = model_4.predict(X_val)
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 2ms/step
In [106]:
print("Classification Report - Train data Model_4", end="\n\n")
cr_train_model_4 = classification_report(y_train,y_train_pred_4 > 0.5)
print(cr_train_model_4)
Classification Report - Train data Model_4

              precision    recall  f1-score   support

           0       1.00      1.00      1.00     14168
           1       0.99      0.95      0.97       832

    accuracy                           1.00     15000
   macro avg       0.99      0.98      0.98     15000
weighted avg       1.00      1.00      1.00     15000

In [107]:
print("Classification Report - Validation data Model_4", end="\n\n")
cr_val_model_4 = classification_report(y_val,y_val_pred_4 > 0.5)
print(cr_val_model_4)
Classification Report - Validation data Model_4

              precision    recall  f1-score   support

           0       0.99      1.00      0.99      4722
           1       0.95      0.87      0.91       278

    accuracy                           0.99      5000
   macro avg       0.97      0.94      0.95      5000
weighted avg       0.99      0.99      0.99      5000

Observations:¶
  1. Model Architecture Overview
  • Structure

    • Dense(128, ReLU) — input layer
    • Dense(64, tanh) — first hidden layer
    • Dense(32, tanh) — second hidden layer
    • Dense(1, Sigmoid) — output layer
  • Total Parameters: 15,617

  • Optimizer: Adam (adaptive learning rate)

  • Loss Function: Binary Crossentropy

  • Metric Tracked: Recall

  • Epochs: 50

  • Batch Size: 32

  • Training Time: ~80.9 seconds

  • Purpose:

This model combines both ReLU and tanh activations to capture complex, nonlinear interactions between sensor readings and failure outcomes — while using Adam, which helps with faster and smoother optimization compared to SGD.

  1. Training vs Validation Loss
  • Training loss decreases sharply at first and stabilizes around 0.015–0.02.

  • Validation loss starts around 0.05, fluctuates slightly upward (~0.07) after ~epoch 30.

  • Interpretation:

    • Training loss is very low — the model fits the training data extremely well.
    • Validation loss increasing slightly → indicates mild overfitting, likely due to model depth and lack of dropout.
    • However, since the recall and precision remain strong on validation data, overfitting is minor and well-controlled.
  • Conclusion:

The model learns efficiently and generalizes well, though adding light dropout or early stopping could further stabilize it.

  1. Model Recall Curve
  • Training Recall: Increases from ~0.75 → stabilizes at 0.95–0.97.

  • Validation Recall: Starts at ~0.85 → oscillates between 0.85–0.90 across epochs.

  • Both curves show convergence and parallel behavior, which is a sign of good training consistency.

  • Interpretation:

This model achieves the highest recall so far, meaning it correctly detects almost all true failures, which directly translates to fewer missed breakdowns — a major cost-saving factor in predictive maintenance.

  • Performance Metrics

Train Data:

Metric Score
Accuracy 0.9968
Recall 0.9757
Precision 0.9936
F1-score 0.9844

Validation Data:

Metric Score
Accuracy 0.9904
Recall 0.9357
Precision 0.9709
F1-score 0.9525
  • Insights:

  • Accuracy improved slightly compared to Model_3 (from ~0.96 → 0.99).

  • Recall (0.93) and Precision (0.97) are both very high → balanced performance.

  • F1-score (0.95) shows near-perfect harmonic mean between recall and precision.

  • Interpretation:

Model_4 achieves the best trade-off between detecting true failures and minimizing false positives among all your models so far.

  • Classification Report

Train Data

Class Precision Recall F1-score
0 (No Failure) 1.00 1.00 1.00
1 (Failure) 0.99 0.95 0.97

Validation Data

Class Precision Recall F1-score
0 (No Failure) 0.99 1.00 0.99
1 (Failure) 0.95 0.87 0.91
  • Interpretation:

  • Near-perfect performance on the no-failure (0) class.

  • For the failure (1) class:

    • Slight recall drop to 0.87, but still strong given the class imbalance.
    • Precision remains high (0.95), meaning the model makes few false failure predictions.
  • Indicates minimal false negatives and very few false positives.

  • Comparative Summar

Metric Model_0 Model_1 Model_2 Model_3 Model_4 (Current)
Architecture 7→1 128→7→1 16→Dropout→128→64→1 16→Dropout→128→64→1 (Weighted) 128→64→32→1
Optimizer SGD SGD SGD SGD Adam
Params 295 6,159 11,153 11,153 15,617
Accuracy (Val) 0.987 0.991 0.986 0.965 0.990
Recall (Val) 0.81 0.93 0.77 0.88 0.94
Precision (Val) 0.97 0.98 0.97 0.81 0.97
F1-score (Val) 0.88 0.95 0.92 0.86 0.95
Overfitting None None None None Slight
Best Feature Simplicity Balance Deep generalization High recall (weighted) Best overall performance
  • Observations Summary

  • Model_4 outperforms all previous architectures in precision–recall balance.

  • Adam optimizer contributes to smoother convergence and higher stability.

  • Validation loss oscillation suggests slight overfitting — minor improvements possible with regularization or early stopping.

  • Recall (0.94) ensures very high failure detection → minimizes replacement cost.

  • Precision (0.97) means only a few unnecessary inspections.

  • Final Recommendation

Goal Recommended Model Reason
Best overall model ✅ Model_4 Excellent recall (0.94), precision (0.97), and generalization.
High recall, more preventive action Model_3 Slightly higher recall sensitivity (with class weights).
Balanced business trade-off Model_4 Best cost-effective solution (few misses + minimal false alarms).
  • In Business Terms

  • True Positives (TP): Prevents costly replacements via early repair.

  • False Negatives (FN): Reduced drastically (recall ↑).

  • False Positives (FP): Minimal — few unnecessary inspections.

→ Overall, Model_4 optimally balances maintenance cost and operational efficiency.

Model 5¶

In [108]:
# clears the current Keras session, resetting all layers and models previously created, freeing up memory and resources.
tf.keras.backend.clear_session()
In [109]:
#Initializing the neural network
from tensorflow.keras.layers import Dropout
model_5 = Sequential()
model_5.add(Dense(64 ,activation="relu",input_dim=X_train.shape[1]))
model_5.add(Dropout(0.3))
model_5.add(Dense(32 ,activation="relu"))
model_5.add(Dense(16 , activation = "tanh"))
model_5.add(Dense(1 ,activation="sigmoid"))
In [110]:
model_5.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 64)             │         2,624 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 64)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 32)             │         2,080 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 16)             │           528 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 1)              │            17 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 5,249 (20.50 KB)
 Trainable params: 5,249 (20.50 KB)
 Non-trainable params: 0 (0.00 B)
In [111]:
# Set training parameters
epochs = 50
batch_size = 32

optimizer = tf.keras.optimizers.Adam()
model_5.compile(loss='binary_crossentropy', optimizer=optimizer, metrics = ['Recall'])
In [112]:
start = time.time()
history = model_5.fit(X_train, y_train, validation_data=(X_val,y_val) , batch_size=batch_size, epochs=epochs)
end=time.time()
Epoch 1/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 3ms/step - Recall: 0.2898 - loss: 0.1580 - val_Recall: 0.7410 - val_loss: 0.0650
Epoch 2/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.7057 - loss: 0.0707 - val_Recall: 0.8273 - val_loss: 0.0543
Epoch 3/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8044 - loss: 0.0549 - val_Recall: 0.8381 - val_loss: 0.0501
Epoch 4/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8183 - loss: 0.0558 - val_Recall: 0.8453 - val_loss: 0.0482
Epoch 5/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 4ms/step - Recall: 0.8381 - loss: 0.0442 - val_Recall: 0.8525 - val_loss: 0.0475
Epoch 6/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8680 - loss: 0.0402 - val_Recall: 0.8633 - val_loss: 0.0463
Epoch 7/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8518 - loss: 0.0466 - val_Recall: 0.8417 - val_loss: 0.0460
Epoch 8/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8772 - loss: 0.0374 - val_Recall: 0.8489 - val_loss: 0.0465
Epoch 9/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8403 - loss: 0.0479 - val_Recall: 0.8597 - val_loss: 0.0457
Epoch 10/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8861 - loss: 0.0374 - val_Recall: 0.8633 - val_loss: 0.0449
Epoch 11/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8763 - loss: 0.0388 - val_Recall: 0.8669 - val_loss: 0.0449
Epoch 12/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8816 - loss: 0.0363 - val_Recall: 0.8705 - val_loss: 0.0435
Epoch 13/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.8785 - loss: 0.0396 - val_Recall: 0.8669 - val_loss: 0.0456
Epoch 14/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8825 - loss: 0.0410 - val_Recall: 0.8633 - val_loss: 0.0447
Epoch 15/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9028 - loss: 0.0333 - val_Recall: 0.8705 - val_loss: 0.0441
Epoch 16/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8921 - loss: 0.0355 - val_Recall: 0.8633 - val_loss: 0.0446
Epoch 17/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8838 - loss: 0.0374 - val_Recall: 0.8669 - val_loss: 0.0449
Epoch 18/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8936 - loss: 0.0355 - val_Recall: 0.8813 - val_loss: 0.0434
Epoch 19/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8945 - loss: 0.0374 - val_Recall: 0.8741 - val_loss: 0.0431
Epoch 20/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8873 - loss: 0.0350 - val_Recall: 0.8813 - val_loss: 0.0416
Epoch 21/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8738 - loss: 0.0376 - val_Recall: 0.8813 - val_loss: 0.0431
Epoch 22/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.8863 - loss: 0.0379 - val_Recall: 0.8741 - val_loss: 0.0443
Epoch 23/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.8960 - loss: 0.0346 - val_Recall: 0.8777 - val_loss: 0.0434
Epoch 24/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8891 - loss: 0.0369 - val_Recall: 0.8813 - val_loss: 0.0426
Epoch 25/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8907 - loss: 0.0375 - val_Recall: 0.8813 - val_loss: 0.0422
Epoch 26/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8867 - loss: 0.0357 - val_Recall: 0.8741 - val_loss: 0.0434
Epoch 27/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8791 - loss: 0.0383 - val_Recall: 0.8777 - val_loss: 0.0438
Epoch 28/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 3s 3ms/step - Recall: 0.8923 - loss: 0.0353 - val_Recall: 0.8777 - val_loss: 0.0440
Epoch 29/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8854 - loss: 0.0354 - val_Recall: 0.8813 - val_loss: 0.0437
Epoch 30/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.8953 - loss: 0.0335 - val_Recall: 0.8813 - val_loss: 0.0434
Epoch 31/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 3ms/step - Recall: 0.8970 - loss: 0.0321 - val_Recall: 0.8849 - val_loss: 0.0433
Epoch 32/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8879 - loss: 0.0356 - val_Recall: 0.8813 - val_loss: 0.0425
Epoch 33/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9124 - loss: 0.0290 - val_Recall: 0.8849 - val_loss: 0.0441
Epoch 34/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8969 - loss: 0.0361 - val_Recall: 0.8813 - val_loss: 0.0433
Epoch 35/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8942 - loss: 0.0331 - val_Recall: 0.8705 - val_loss: 0.0439
Epoch 36/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8946 - loss: 0.0338 - val_Recall: 0.8741 - val_loss: 0.0417
Epoch 37/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8935 - loss: 0.0345 - val_Recall: 0.8777 - val_loss: 0.0415
Epoch 38/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.8986 - loss: 0.0345 - val_Recall: 0.8849 - val_loss: 0.0416
Epoch 39/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8953 - loss: 0.0358 - val_Recall: 0.8777 - val_loss: 0.0414
Epoch 40/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8993 - loss: 0.0326 - val_Recall: 0.8885 - val_loss: 0.0410
Epoch 41/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9001 - loss: 0.0319 - val_Recall: 0.8885 - val_loss: 0.0424
Epoch 42/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8893 - loss: 0.0355 - val_Recall: 0.8849 - val_loss: 0.0420
Epoch 43/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9028 - loss: 0.0326 - val_Recall: 0.8849 - val_loss: 0.0432
Epoch 44/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9213 - loss: 0.0284 - val_Recall: 0.8849 - val_loss: 0.0436
Epoch 45/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8925 - loss: 0.0347 - val_Recall: 0.8849 - val_loss: 0.0422
Epoch 46/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.8933 - loss: 0.0350 - val_Recall: 0.8849 - val_loss: 0.0416
Epoch 47/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step - Recall: 0.9192 - loss: 0.0270 - val_Recall: 0.8849 - val_loss: 0.0413
Epoch 48/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 2s 4ms/step - Recall: 0.9150 - loss: 0.0316 - val_Recall: 0.8849 - val_loss: 0.0402
Epoch 49/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.9127 - loss: 0.0315 - val_Recall: 0.8813 - val_loss: 0.0404
Epoch 50/50
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - Recall: 0.8956 - loss: 0.0316 - val_Recall: 0.8813 - val_loss: 0.0406
In [113]:
print("Time taken in seconds ",end-start)
Time taken in seconds  83.61508584022522
In [114]:
plot(history,'loss')
No description has been provided for this image
In [115]:
plot(history,'Recall')
No description has been provided for this image
In [116]:
model_5_train_perf = model_performance_classification(model_5,X_train,y_train)
model_5_train_perf
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
Out[116]:
Accuracy Recall Precision F1 Score
0 0.9952 0.9579 0.9962 0.9762
In [117]:
model_5_val_perf = model_performance_classification(model_5,X_val,y_val)
model_5_val_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
Out[117]:
Accuracy Recall Precision F1 Score
0 0.9924 0.9401 0.9865 0.9620
In [118]:
y_train_pred_5 = model_5.predict(X_train)
y_val_pred_5 = model_5.predict(X_val)
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
In [119]:
print("Classification Report - Train data Model_2", end="\n\n")
cr_train_model_5 = classification_report(y_train,y_train_pred_5 > 0.5)
print(cr_train_model_5)
Classification Report - Train data Model_2

              precision    recall  f1-score   support

           0       1.00      1.00      1.00     14168
           1       1.00      0.92      0.95       832

    accuracy                           1.00     15000
   macro avg       1.00      0.96      0.98     15000
weighted avg       1.00      1.00      1.00     15000

In [120]:
print("Classification Report - Validation data Model_2", end="\n\n")
cr_val_model_5 = classification_report(y_val,y_val_pred_5 > 0.5)
print(cr_val_model_5)
Classification Report - Validation data Model_2

              precision    recall  f1-score   support

           0       0.99      1.00      1.00      4722
           1       0.98      0.88      0.93       278

    accuracy                           0.99      5000
   macro avg       0.99      0.94      0.96      5000
weighted avg       0.99      0.99      0.99      5000

Observations:¶
  1. Model Architecture Overview
  • Structure

    • Dense(64, ReLU) – input layer
    • Dropout(0.3) – regularization layer to prevent overfitting
    • Dense(32, ReLU) – second hidden layer
    • Dense(16, tanh) – third hidden layer
    • Dense(1, Sigmoid) – output layer for binary classification
  • Total Parameters: 5,249

  • Optimizer: Adam

  • Loss: Binary Crossentropy

  • Metric: Recall

  • Epochs: 50

  • Batch Size: 32

  • Training Time: ~83.6 seconds

  • Purpose:

This version aims to simplify the model (fewer parameters) while adding dropout and mixed activations for generalization and robustness — particularly to stabilize recall on unseen data.

  1. Training and Validation Loss
  • Training Loss: Steadily decreases from ~0.11 → ~0.03.

  • Validation Loss: Drops initially to ~0.04, stabilizes slightly above the training curve.

  • Interpretation:

    • The loss curves are smooth and parallel, indicating stable learning and minimal overfitting.

    • The small gap between train and validation losses suggests excellent generalization performance.

    • The dropout (0.3) effectively regularizes the model.

  • Conclusion:

This model achieves excellent convergence with consistent, balanced performance between training and validation sets.

  1. Model Recall Curve
  • Training Recall: Increases rapidly to ~0.96 and stabilizes.

  • Validation Recall: Follows closely, reaching ~0.90–0.92.

  • The two curves nearly overlap from epoch 10 onward, indicating very good model generalization.

  • Interpretation:

The model captures failure patterns well and maintains high recall across datasets. This means it correctly identifies over 90% of all actual generator failures — crucial for preventive maintenance.

  • Performance Metrics

Train Data

Metric Score
Accuracy 0.9952
Recall 0.9579
Precision 0.9962
F1-score 0.9762

Validation Data

Metric Score
Accuracy 0.9924
Recall 0.9401
Precision 0.9865
F1-score 0.9620
  • Key Takeaways:

  • High recall (0.94) → very few missed failures (false negatives).

  • High precision (0.99) → minimal false alarms (false positives).

  • F1-score (0.96) → nearly perfect harmonic balance between recall and precision.

  • Accuracy (0.99+) → strong consistency in overall classification.

  • Conclusion:

Model_5 achieves the best balance so far among all models — high precision, high recall, minimal overfitting, and efficient training.

  • Classification Report

Train Data

Class Precision Recall F1-score
0 (No Failure) 1.00 1.00 1.00
1 (Failure) 1.00 0.92 0.95

Validation Data

Class Precision Recall F1-score
0 (No Failure) 0.99 1.00 1.00
1 (Failure) 0.98 0.88 0.93
  • Analysis:

  • Train performance: Perfect classification for no-failure cases; strong recall for failures (0.92).

  • Validation performance: Slight drop in failure recall (0.88) but still robust.

  • Precision (0.98) on failures is excellent, showing the model rarely raises false alarms.

  • Comparative Summary (Model_0 → Model_5)

Metric Model_0 Model_1 Model_2 Model_3 Model_4 Model_5
Architecture 7→1 128→7→1 Dropout→128→64→1 Weighted 128→64→32→1 64→Dropout→32→16→1
Optimizer SGD SGD SGD SGD Adam Adam
Params 295 6,159 11,153 11,153 15,617 5,249
Accuracy (Val) 0.987 0.991 0.986 0.965 0.990 0.992
Recall (Val) 0.81 0.93 0.77 0.88 0.94 0.94
Precision (Val) 0.97 0.98 0.97 0.81 0.97 0.99
F1-score (Val) 0.88 0.95 0.92 0.86 0.95 0.96
Overfitting None None None None Slight None
Generalization Good Excellent Slightly underfit Weighted recall focus High Excellent
Strength Simplicity Balanced Deep learning Handles imbalance Strong performance Best overall
  • Observations Summary

  • Model_5 offers the most efficient and generalizable neural network so far:

    • It trains faster and uses fewer parameters than Model_4.
    • It achieves superior validation accuracy and F1-score.
    • The dropout layer (0.3) improves robustness without harming recall.
    • Adam optimizer ensures smoother convergence than SGD.
  • Overfitting is minimal — loss curves are parallel, and validation recall stays stable.

  • Business interpretation:

    • The model accurately identifies almost all failing turbines while keeping unnecessary inspections low.
    • This translates into optimal maintenance scheduling and reduced replacement costs.
  • Final Recommendation

Objective Best Model Rationale
Overall balance (accuracy, recall, precision) ✅ Model_5 Highest F1-score (0.96), minimal overfitting, and efficient architecture.
High recall (detect all failures) Model_4 Slightly higher recall but heavier model.
Operational efficiency Model_5 Lightweight yet accurate → ideal for deployment on production systems.
  • Model_5 is your best-performing model — it achieves production-level accuracy (99%), strong recall (94%), and excellent generalization at low computational cost.

Model 6¶

In [121]:
# clears the current Keras session, resetting all layers and models previously created, freeing up memory and resources.
tf.keras.backend.clear_session()
In [122]:
model_6 = Sequential()
model_6.add(Dense(32,activation="relu",input_dim=X_train.shape[1]))
model_6.add(Dropout(0.5))
model_6.add(Dense(16 ,activation="relu"))
model_6.add(Dense(8 , activation = "relu"))
model_6.add(Dense(1 ,activation="sigmoid"))
In [123]:
model_6.summary()
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 32)             │         1,312 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dropout (Dropout)               │ (None, 32)             │             0 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 16)             │           528 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 8)              │           136 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_3 (Dense)                 │ (None, 1)              │             9 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 1,985 (7.75 KB)
 Trainable params: 1,985 (7.75 KB)
 Non-trainable params: 0 (0.00 B)
In [124]:
# Set training parameters
epochs = 50
batch_size = 32

optimizer = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.9)
model_6.compile(loss='binary_crossentropy', optimizer=optimizer, metrics = ['Recall'])
In [125]:
start = time.time()
history = model_6.fit(X_train, y_train, validation_data=(X_val,y_val) , batch_size=batch_size, epochs=epochs,class_weight=cw_dict, verbose=0 )
end=time.time()
In [126]:
print("Time taken in seconds ",end-start)
Time taken in seconds  72.53119802474976
In [127]:
plot(history,'loss')
No description has been provided for this image
In [128]:
plot(history,'Recall')
No description has been provided for this image
In [129]:
model_6_train_perf = model_performance_classification(model_6,X_train,y_train)
model_6_train_perf
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 2ms/step
Out[129]:
Accuracy Recall Precision F1 Score
0 0.9605 0.7947 0.8158 0.8048
In [130]:
model_6_val_perf = model_performance_classification(model_6,X_val,y_val)
model_6_val_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
Out[130]:
Accuracy Recall Precision F1 Score
0 0.9616 0.8002 0.8222 0.8108
In [131]:
y_train_pred_6 = model_6.predict(X_train)
y_val_pred_6 = model_6.predict(X_val)
469/469 ━━━━━━━━━━━━━━━━━━━━ 1s 1ms/step
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
In [132]:
print("Classification Report - Train data Model_3", end="\n\n")
cr_train_model_6 = classification_report(y_train,y_train_pred_6 > 0.5)
print(cr_train_model_6)
Classification Report - Train data Model_3

              precision    recall  f1-score   support

           0       0.98      0.98      0.98     14168
           1       0.65      0.61      0.63       832

    accuracy                           0.96     15000
   macro avg       0.82      0.79      0.80     15000
weighted avg       0.96      0.96      0.96     15000

In [133]:
print("Classification Report - Validation data Model_3", end="\n\n")
cr_val_model_6 = classification_report(y_val,y_val_pred_6 > 0.5)
print(cr_val_model_6)
Classification Report - Validation data Model_3

              precision    recall  f1-score   support

           0       0.98      0.98      0.98      4722
           1       0.67      0.62      0.64       278

    accuracy                           0.96      5000
   macro avg       0.82      0.80      0.81      5000
weighted avg       0.96      0.96      0.96      5000

Observations:¶
  1. Model Architecture Overview
  • Architecture:
    • Dense(32, ReLU) → Dropout(0.5)
    • Dense(16, ReLU) → Dense(8, ReLU) → Dense(1, Sigmoid)
  • Total Parameters: 1,985
  • Optimizer: SGD with learning rate = 0.01 and momentum = 0.9
  • Loss Function: Binary Crossentropy
  • Metric: Recall
  • Epochs: 50
  • Batch Size: 32
  • Training Time: ~72.5 seconds

This is a relatively shallow and lightweight model compared to previous architectures. The dropout layer was added for regularization to combat overfitting.

  1. Model Loss Analysis
  • Observation:

    • The training loss decreases initially but becomes unstable after around epoch 15–20, showing sharp fluctuations and even spikes around epoch 25+.
    • The validation loss remains relatively low and more stable throughout training.
  • Interpretation:

    • This behavior indicates that the model is not converging properly — possibly due to the learning rate being too high or insufficient model complexity.
    • The divergence between training and validation loss suggests poor optimization or unstable gradient updates rather than overfitting.
  1. Model Recall Curve
  • Observation:

    • Both training and validation recall curves fluctuate heavily after 15 epochs.
    • There’s a sudden drop in recall (train) mid-training (near epoch 20) followed by irregular recovery.
    • Validation recall remains more stable but doesn’t improve much beyond 0.8.
  • Interpretation:

    • The unstable recall trend confirms optimization issues. The model might be oscillating due to a learning rate that’s too aggressive.
    • Recall plateauing around 0.8 suggests the model fails to capture enough of the “failure” (class 1) cases, which are rare in your dataset.
  • Classification Metrics

Training Performance

Metric Class 0 (No Failure) Class 1 (Failure) Overall
Precision 0.98 0.65 0.96
Recall 0.98 0.61 0.96
F1-Score 0.98 0.63 0.96
Accuracy — — 0.96

Validation Performance

Metric Class 0 (No Failure) Class 1 (Failure) Overall
Precision 0.98 0.67 0.96
Recall 0.98 0.62 0.96
F1-Score 0.98 0.64 0.96
  • Macro Avg F1-Score: ~0.81

  • Weighted Avg F1-Score: ~0.96

  • Interpretation

    • The model achieves very high accuracy (96%) primarily because of the dominant class (No Failure).

    • However, recall for the minority class (failures) is only ~0.61–0.62, meaning around 40% of actual failures are missed.

    • This is undesirable for predictive maintenance, since missed failures (false negatives) correspond to high replacement costs.

  • Potential Issues

    • Underfitting:

      • Low recall and flat learning curves indicate the model is too simple to capture underlying relationships.

      • The dropout rate (0.5) might be too high for such a small network — causing excessive regularization.

    • Optimizer Instability:

      • The SGD optimizer with momentum = 0.9 and lr = 0.01 might be overshooting minima, causing oscillations.

      • Switching to Adam optimizer could stabilize training and improve convergence.

    • Class Imbalance:

      • The minority “failure” class remains difficult to detect.

      • Re-weighting, oversampling (SMOTE), or custom loss functions (e.g., focal loss) should be used.

Model Performance Comparison and Final Model Selection¶

Now, in order to select the final model, we will compare the performances of all the models for the training and validation sets.

In [134]:
# training performance comparison

models_train_comp_df = pd.concat(
    [
        model_0_train_perf.T,
        model_1_train_perf.T,
        model_2_train_perf.T,
        model_3_train_perf.T,
        model_4_train_perf.T,
        model_5_train_perf.T,
        model_6_train_perf.T

    ],
    axis=1,
)
models_train_comp_df.columns = [
    "Model 0",
    "Model 1",
    "Model 2",
    "Model 3",
    "Model 4",
    "Model 5",
    "Model 6"
]
print("Training set performance comparison:")
models_train_comp_df
Training set performance comparison:
Out[134]:
Model 0 Model 1 Model 2 Model 3 Model 4 Model 5 Model 6
Accuracy 0.9874 0.9931 0.9849 0.9685 0.9968 0.9952 0.9605
Recall 0.9034 0.9432 0.8715 0.9403 0.9757 0.9579 0.7947
Precision 0.9731 0.9907 0.9815 0.8251 0.9936 0.9962 0.8158
F1 Score 0.9351 0.9656 0.9185 0.8724 0.9844 0.9762 0.8048
In [135]:
# Validation performance comparison

models_val_comp_df = pd.concat(
    [
        model_0_val_perf.T,
        model_1_val_perf.T,
        model_2_val_perf.T,
        model_3_val_perf.T,
        model_4_val_perf.T,
        model_5_val_perf.T,
        model_6_val_perf.T

    ],
    axis=1,
)
models_val_comp_df.columns = [
    "Model 0",
    "Model 1",
    "Model 2",
    "Model 3",
    "Model 4",
    "Model 5",
    "Model 6"
]
print("Validation set performance comparison:")
models_val_comp_df
Validation set performance comparison:
Out[135]:
Model 0 Model 1 Model 2 Model 3 Model 4 Model 5 Model 6
Accuracy 0.9878 0.9906 0.9856 0.9646 0.9904 0.9924 0.9616
Recall 0.9038 0.9273 0.8857 0.9254 0.9357 0.9401 0.8002
Precision 0.9773 0.9815 0.9733 0.8113 0.9709 0.9865 0.8222
F1 Score 0.9371 0.9526 0.9245 0.8578 0.9525 0.9620 0.8108

Now, let's check the performance of the final model on the test set.

Best Model:¶
  1. Validation Set Performance (Generalization Priority)
Metric Model 0 Model 1 Model 2 Model 3 Model 4 Model 5 Model 6
Accuracy 0.9878 0.9906 0.9856 0.9646 0.9904 0.9924 0.9616
Recall 0.9038 0.9273 0.8857 0.9254 0.9357 0.9401 0.8002
Precision 0.9773 0.9815 0.9733 0.8113 0.9709 0.9865 0.8222
F1 Score 0.9371 0.9526 0.9245 0.8578 0.9525 0.9620 0.8108

✅ Model 5 gives the best validation metrics overall:

  • Highest accuracy (0.9924), recall (0.9401), precision (0.9865), and F1 (0.9620).

  • This indicates excellent generalization without apparent overfitting.

  1. Training Set Performance (Check for Overfitting)
Metric Model 0 Model 1 Model 2 Model 3 Model 4 Model 5 Model 6
Accuracy 0.9874 0.9931 0.9849 0.9685 0.9968 0.9952 0.9605
Recall 0.9034 0.9432 0.8715 0.9403 0.9757 0.9579 0.7947
Precision 0.9731 0.9907 0.9815 0.8251 0.9936 0.9962 0.8158
F1 Score 0.9351 0.9656 0.9185 0.8724 0.9844 0.9762 0.8048

🔹 Model 4 performs marginally better on the training data, but Model 5’s training metrics remain very close and more balanced with its validation results.

  1. Generalization Check
  • Model 4 shows slightly higher training recall (0.9757) than validation recall (0.9357) → mild overfitting.

  • Model 5 shows training recall 0.9579 vs validation 0.9401 → excellent consistency → better generalization.

  • Model 6 and Model 3 underperform substantially on recall/F1, suggesting underfitting or unstable training.

🏆 ✅ Best Overall Model: Model 5

Why Model 5 wins:

  1. Highest Validation Metrics: Accuracy = 0.9924, F1 = 0.9620.

  2. Strong Recall (0.9401) → fewer false negatives.

  3. High Precision (0.9865) → minimal false positives.

  4. Balanced train–val gap, indicating stable learning and robust generalization.

  5. Reasonable training time (~83 s, from your earlier logs) with dropout and Adam optimizer, preventing overfitting.

  • Summary Recommendation
Model Key Traits Verdict
Model 3 High recall but low precision, unstable training ❌ Underfitting
Model 4 Very strong training but mild overfitting ⚠️ Slightly overfitted
Model 5 Best balance of recall, precision, F1, and stability 🏆 Final Choice
Model 6 Lowest recall, unstable loss ❌ Poor convergence

✅ Final Decision: → Select Model 5 as the final model for deployment or further fine-tuning (e.g., hyperparameter search or threshold optimization).

In [136]:
best_model = model_5
In [137]:
# Test set performance for the best model
best_model_test_perf = model_performance_classification(best_model,X_test,y_test)
best_model_test_perf
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step
Out[137]:
Accuracy Recall Precision F1 Score
0 0.9918 0.9356 0.9862 0.9594
In [138]:
y_test_pred_best = best_model.predict(X_test)

cr_test_best_model = classification_report(y_test, y_test_pred_best>0.5) # Check the classification report of best model on test data.
print(cr_test_best_model)
157/157 ━━━━━━━━━━━━━━━━━━━━ 0s 1ms/step
              precision    recall  f1-score   support

           0       0.99      1.00      1.00      4718
           1       0.98      0.87      0.92       282

    accuracy                           0.99      5000
   macro avg       0.99      0.94      0.96      5000
weighted avg       0.99      0.99      0.99      5000

Actionable Insights and Recommendations¶

Write down some insights and business recommendations based on your observations.

🔍 Insights from Model 5

  1. Model Performance Summary (Model 5)
Metric Value
Accuracy 0.9918
Recall 0.9356
Precision 0.9862
F1 Score 0.9594
  • Class-level (from classification report):

    • Class 0 (Negative Class) → Precision = 0.99, Recall = 1.00, F1 = 1.00
    • Class 1 (Positive Class) → Precision = 0.98, Recall = 0.87, F1 = 0.92
  1. Consistent Generalization
  • Training and validation curves show no major overfitting.
  • Validation recall is stable across epochs, indicating reliable performance on unseen data.
  1. Handles Class Imbalance Well
  • Compared to earlier models, Model 5 achieves higher recall for the minority class while still keeping precision high.
  • This means it identifies more true failures without dramatically increasing false alarms.
  1. Robust but Efficient
  • With ~5.2k parameters, Model 5 is less complex than Model 4 yet achieves better or equal results.
  • This makes it computationally efficient for production.

  • Business Implications
  1. Operational Risk Reduction
  • High recall ensures fewer missed failure cases, directly reducing risks (e.g., compliance penalties, service disruptions, or safety incidents).
  1. Cost Efficiency
  • Strong precision means false positives are minimized → fewer unnecessary interventions, saving time and operational costs.
  1. Scalability
  • Given its lower complexity, Model 5 can be deployed at scale without requiring heavy compute resources, lowering infrastructure expenses.
  1. Decision-Making Confidence
  • With F1 ~96%, stakeholders can trust predictions to guide inspections, audits, or approvals.
  • This helps align ML output with business workflows.

✅ Recommendations

  1. Adopt Model 5 as the Production Candidate
  • It offers the best balance of accuracy, recall, and precision while being lightweight.
  1. Threshold Tuning for Business Priorities
  • Run a probability threshold sweep to optimize for your business cost function.

  • Example: If missing a failure costs 10× more than a false alarm, slightly lower the threshold to favor recall.

  1. Cost-Aware Evaluation
  • Build a confusion-matrix cost model (assign $ cost to FN vs FP).

  • Validate that Model 5 minimizes total business cost, not just error rates.

  1. Monitoring & Retraining
  • Set up drift monitoring (data distribution & performance metrics).

  • Retrain quarterly or whenever recall drops below a set business threshold.

  1. Integration into Decision Workflow
  • Use Model 5’s predictions as a decision-support tool, not a black box.

  • Provide confidence scores so business teams can triage borderline cases.

👉 In summary: Model 5 is your best candidate for production—it maximizes recall (protecting the business from costly misses) while keeping precision high (avoiding wasteful actions). With proper threshold tuning and monitoring, it can deliver both financial savings and risk mitigation.