Cainvas
Model Files
predictive_maintenance.h5
keras
Model
deepSea Compiled Models
predictive_maintenance.exe
deepSea
Ubuntu

Recurrent Neural Networks for Predictive Maintenance

image

Photo by Al Boardman

In [1]:
import tensorflow.keras
import tensorflow as tf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os 
from tqdm.keras import TqdmCallback

# Setting seed for reproducibility
np.random.seed(1234)  
PYTHONHASHSEED = 0

from sklearn import preprocessing
from sklearn.metrics import confusion_matrix, recall_score, precision_score
from tensorflow.keras.models import Sequential,load_model
from tensorflow.keras.layers import Dense, Dropout, LSTM
In [2]:
# define path to save model
model_path = 'predictive_maintenance_model.h5'
In [3]:
##################################
# Data Ingestion
##################################

# read training data - It is the aircraft engine run-to-failure data.
train_df = pd.read_csv('https://cainvas-static.s3.amazonaws.com/media/user_data/dark/PM_train.txt', sep=" ", header=None)
train_df.drop(train_df.columns[[26, 27]], axis=1, inplace=True)
train_df.columns = ['id', 'cycle', 'setting1', 'setting2', 'setting3', 's1', 's2', 's3',
                     's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's12', 's13', 's14',
                     's15', 's16', 's17', 's18', 's19', 's20', 's21']

train_df = train_df.sort_values(['id','cycle'])
In [4]:
# read test data - It is the aircraft engine operating data without failure events recorded.
test_df = pd.read_csv('https://cainvas-static.s3.amazonaws.com/media/user_data/dark/PM_test.txt', sep=" ", header=None)
test_df.drop(test_df.columns[[26, 27]], axis=1, inplace=True)
test_df.columns = ['id', 'cycle', 'setting1', 'setting2', 'setting3', 's1', 's2', 's3',
                     's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's12', 's13', 's14',
                     's15', 's16', 's17', 's18', 's19', 's20', 's21']
In [5]:
# read ground truth data - It contains the information of true remaining cycles for each engine in the testing data.
truth_df = pd.read_csv('https://cainvas-static.s3.amazonaws.com/media/user_data/dark/PM_truth.txt', sep=" ", header=None)
truth_df.drop(truth_df.columns[[1]], axis=1, inplace=True)
In [6]:
#######
# TRAIN
#######
# Data Labeling - generate column RUL(Remaining Usefull Life or Time to Failure)
rul = pd.DataFrame(train_df.groupby('id')['cycle'].max()).reset_index()
rul.columns = ['id', 'max']
train_df = train_df.merge(rul, on=['id'], how='left')
train_df['RUL'] = train_df['max'] - train_df['cycle']
train_df.drop('max', axis=1, inplace=True)
# generate label columns for training data
# we will only make use of "label1" for binary classification, 
# while trying to answer the question: is a specific engine going to fail within w1 cycles?
w1 = 30
w0 = 15
train_df['label1'] = np.where(train_df['RUL'] <= w1, 1, 0 )
train_df['label2'] = train_df['label1']
train_df.loc[train_df['RUL'] <= w0, 'label2'] = 2
In [7]:
# MinMax normalization (from 0 to 1)
train_df['cycle_norm'] = train_df['cycle']
cols_normalize = train_df.columns.difference(['id','cycle','RUL','label1','label2'])
min_max_scaler = preprocessing.MinMaxScaler()
norm_train_df = pd.DataFrame(min_max_scaler.fit_transform(train_df[cols_normalize]), 
                             columns=cols_normalize, 
                             index=train_df.index)
join_df = train_df[train_df.columns.difference(cols_normalize)].join(norm_train_df)
train_df = join_df.reindex(columns = train_df.columns)
In [8]:
######
# TEST
######
# MinMax normalization (from 0 to 1)
test_df['cycle_norm'] = test_df['cycle']
norm_test_df = pd.DataFrame(min_max_scaler.transform(test_df[cols_normalize]), 
                            columns=cols_normalize, 
                            index=test_df.index)
test_join_df = test_df[test_df.columns.difference(cols_normalize)].join(norm_test_df)
test_df = test_join_df.reindex(columns = test_df.columns)
test_df = test_df.reset_index(drop=True)
In [9]:
# We use the ground truth dataset to generate labels for the test data.
# generate column max for test data
rul = pd.DataFrame(test_df.groupby('id')['cycle'].max()).reset_index()
rul.columns = ['id', 'max']
truth_df.columns = ['more']
truth_df['id'] = truth_df.index + 1
truth_df['max'] = rul['max'] + truth_df['more']
truth_df.drop('more', axis=1, inplace=True)
In [10]:
# generate RUL for test data
test_df = test_df.merge(truth_df, on=['id'], how='left')
test_df['RUL'] = test_df['max'] - test_df['cycle']
test_df.drop('max', axis=1, inplace=True)
In [11]:
# generate label columns w0 and w1 for test data
test_df['label1'] = np.where(test_df['RUL'] <= w1, 1, 0 )
test_df['label2'] = test_df['label1']
test_df.loc[test_df['RUL'] <= w0, 'label2'] = 2
In [12]:
##################################
# LSTM
##################################

# pick a large window size of 50 cycles
sequence_length = 50
In [13]:
# function to reshape features into (samples, time steps, features) 
def gen_sequence(id_df, seq_length, seq_cols):
    """ Only sequences that meet the window-length are considered, no padding is used. This means for testing
    we need to drop those which are below the window-length. An alternative would be to pad sequences so that
    we can use shorter ones """
    # for one id I put all the rows in a single matrix
    data_matrix = id_df[seq_cols].values
    num_elements = data_matrix.shape[0]
    # Iterate over two lists in parallel.
    # For example id1 have 192 rows and sequence_length is equal to 50
    # so zip iterate over two following list of numbers (0,112),(50,192)
    # 0 50 -> from row 0 to row 50
    # 1 51 -> from row 1 to row 51
    # 2 52 -> from row 2 to row 52
    # ...
    # 111 191 -> from row 111 to 191
    for start, stop in zip(range(0, num_elements-seq_length), range(seq_length, num_elements)):
        yield data_matrix[start:stop, :]
In [14]:
# pick the feature columns 
sensor_cols = ['s' + str(i) for i in range(1,22)]
sequence_cols = ['setting1', 'setting2', 'setting3', 'cycle_norm']
sequence_cols.extend(sensor_cols)
In [15]:
# generator for the sequences
seq_gen = (list(gen_sequence(train_df[train_df['id']==id], sequence_length, sequence_cols)) 
           for id in train_df['id'].unique())
In [16]:
# generate sequences and convert to numpy array
seq_array = np.concatenate(list(seq_gen)).astype(np.float32)
seq_array.shape
Out[16]:
(15631, 50, 25)
In [17]:
# function to generate labels
def gen_labels(id_df, seq_length, label):
    # For one id I put all the labels in a single matrix.
    # For example:
    # [[1]
    # [4]
    # [1]
    # [5]
    # [9]
    # ...
    # [200]] 
    data_matrix = id_df[label].values
    num_elements = data_matrix.shape[0]
    # I have to remove the first seq_length labels
    # because for one id the first sequence of seq_length size have as target
    # the last label (the previus ones are discarded).
    # All the next id's sequences will have associated step by step one label as target. 
    return data_matrix[seq_length:num_elements, :]
In [18]:
# generate labels
label_gen = [gen_labels(train_df[train_df['id']==id], sequence_length, ['label1']) 
             for id in train_df['id'].unique()]
label_array = np.concatenate(label_gen).astype(np.float32)
label_array.shape
Out[18]:
(15631, 1)
In [19]:
# Next, we build a deep network. 
# The first layer is an LSTM layer with 100 units followed by another LSTM layer with 50 units. 
# Dropout is also applied after each LSTM layer to control overfitting. 
# Final layer is a Dense output layer with single unit and sigmoid activation since this is a binary classification problem.
# build the network
nb_features = seq_array.shape[2]
nb_out = label_array.shape[1]

model = Sequential()

model.add(LSTM(
         input_shape=(sequence_length, nb_features),
         units=100,
         return_sequences=True))
model.add(Dropout(0.2))

model.add(LSTM(
          units=50,
          return_sequences=False))
model.add(Dropout(0.2))

model.add(Dense(units=nb_out, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
In [20]:
print(model.summary())
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
lstm (LSTM)                  (None, 50, 100)           50400     
_________________________________________________________________
dropout (Dropout)            (None, 50, 100)           0         
_________________________________________________________________
lstm_1 (LSTM)                (None, 50)                30200     
_________________________________________________________________
dropout_1 (Dropout)          (None, 50)                0         
_________________________________________________________________
dense (Dense)                (None, 1)                 51        
=================================================================
Total params: 80,651
Trainable params: 80,651
Non-trainable params: 0
_________________________________________________________________
None
In [21]:
# fit the network
history = model.fit(seq_array, label_array, epochs=100, batch_size=200, validation_split=0.05, verbose=2,
          callbacks = [tensorflow.keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=0, mode='min'),
                       tensorflow.keras.callbacks.ModelCheckpoint(model_path,monitor='val_loss', save_best_only=True, mode='min', verbose=0),
                       TqdmCallback(verbose=2)]
          )
Epoch 1/100
75/75 - 13s - loss: 0.2395 - accuracy: 0.8963 - val_loss: 0.1100 - val_accuracy: 0.9476

Epoch 2/100
75/75 - 13s - loss: 0.1161 - accuracy: 0.9518 - val_loss: 0.0946 - val_accuracy: 0.9655

Epoch 3/100
75/75 - 15s - loss: 0.0814 - accuracy: 0.9663 - val_loss: 0.0533 - val_accuracy: 0.9770

Epoch 4/100
75/75 - 14s - loss: 0.0738 - accuracy: 0.9689 - val_loss: 0.0919 - val_accuracy: 0.9578

Epoch 5/100
75/75 - 12s - loss: 0.0746 - accuracy: 0.9692 - val_loss: 0.0401 - val_accuracy: 0.9847

Epoch 6/100
75/75 - 13s - loss: 0.0673 - accuracy: 0.9716 - val_loss: 0.0564 - val_accuracy: 0.9731

Epoch 7/100
75/75 - 12s - loss: 0.0679 - accuracy: 0.9723 - val_loss: 0.0484 - val_accuracy: 0.9795

Epoch 8/100
75/75 - 12s - loss: 0.0591 - accuracy: 0.9749 - val_loss: 0.0724 - val_accuracy: 0.9680

Epoch 9/100
75/75 - 13s - loss: 0.0548 - accuracy: 0.9768 - val_loss: 0.0309 - val_accuracy: 0.9872

Epoch 10/100
75/75 - 15s - loss: 0.0524 - accuracy: 0.9771 - val_loss: 0.0371 - val_accuracy: 0.9834

Epoch 11/100
75/75 - 15s - loss: 0.0560 - accuracy: 0.9766 - val_loss: 0.0388 - val_accuracy: 0.9834

Epoch 12/100
75/75 - 13s - loss: 0.0536 - accuracy: 0.9784 - val_loss: 0.0397 - val_accuracy: 0.9834

Epoch 13/100
75/75 - 12s - loss: 0.0497 - accuracy: 0.9791 - val_loss: 0.0479 - val_accuracy: 0.9795

Epoch 14/100
75/75 - 12s - loss: 0.0468 - accuracy: 0.9814 - val_loss: 0.0415 - val_accuracy: 0.9808

Epoch 15/100
75/75 - 12s - loss: 0.0492 - accuracy: 0.9802 - val_loss: 0.0410 - val_accuracy: 0.9795

Epoch 16/100
75/75 - 12s - loss: 0.0459 - accuracy: 0.9810 - val_loss: 0.0668 - val_accuracy: 0.9719

Epoch 17/100
75/75 - 12s - loss: 0.0472 - accuracy: 0.9801 - val_loss: 0.0454 - val_accuracy: 0.9821

Epoch 18/100
75/75 - 12s - loss: 0.0512 - accuracy: 0.9778 - val_loss: 0.0695 - val_accuracy: 0.9680

Epoch 19/100
75/75 - 12s - loss: 0.0515 - accuracy: 0.9783 - val_loss: 0.0322 - val_accuracy: 0.9847


In [22]:
print(history.history.keys())
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
In [23]:
# summarize history for Accuracy
fig_acc = plt.figure(figsize=(10, 10))
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
fig_acc.savefig("model_accuracy.png")
In [24]:
# summarize history for Loss
fig_acc = plt.figure(figsize=(10, 10))
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
fig_acc.savefig("model_loss.png")
In [25]:
# training metrics
scores = model.evaluate(seq_array, label_array, verbose=1, batch_size=200)
print('Accurracy: {}'.format(scores[1]))
79/79 [==============================] - 5s 58ms/step - loss: 0.0398 - accuracy: 0.9832
Accurracy: 0.9831744432449341
In [26]:
# make predictions and compute confusion matrix
y_pred = model.predict_classes(seq_array,verbose=1, batch_size=200)
y_true = label_array
WARNING:tensorflow:From <ipython-input-26-6b8ec330f109>:2: Sequential.predict_classes (from tensorflow.python.keras.engine.sequential) is deprecated and will be removed after 2021-01-01.
Instructions for updating:
Please use instead:* `np.argmax(model.predict(x), axis=-1)`,   if your model does multi-class classification   (e.g. if it uses a `softmax` last-layer activation).* `(model.predict(x) > 0.5).astype("int32")`,   if your model does binary classification   (e.g. if it uses a `sigmoid` last-layer activation).
79/79 [==============================] - 5s 58ms/step
In [27]:
test_set = pd.DataFrame(y_pred)
test_set.to_csv('binary_submit_train.csv', index = None)
In [28]:
print('Confusion matrix\n- x-axis is true labels.\n- y-axis is predicted labels')
cm = confusion_matrix(y_true, y_pred)
print(cm)
Confusion matrix
- x-axis is true labels.
- y-axis is predicted labels
[[12487    44]
 [  219  2881]]
In [29]:
# compute precision and recall
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
print( 'precision = ', precision, '\n', 'recall = ', recall)
precision =  0.984957264957265 
 recall =  0.9293548387096774
In [30]:
##################################
# EVALUATE ON TEST DATA
##################################

# We pick the last sequence for each id in the test data

seq_array_test_last = [test_df[test_df['id']==id][sequence_cols].values[-sequence_length:] 
                       for id in test_df['id'].unique() if len(test_df[test_df['id']==id]) >= sequence_length]

seq_array_test_last = np.asarray(seq_array_test_last).astype(np.float32)
print("seq_array_test_last")
print(seq_array_test_last)
print(seq_array_test_last.shape)
seq_array_test_last
[[[0.3505747  0.8333333  0.         ... 0.         0.4728682  0.45457056]
  [0.39655173 0.5833333  0.         ... 0.         0.6511628  0.5608948 ]
  [0.5114943  0.25       0.         ... 0.         0.6356589  0.4975145 ]
  ...
  [0.43678162 0.75       0.         ... 0.         0.41860464 0.4710025 ]
  [0.5804598  0.6666667  0.         ... 0.         0.3255814  0.45954156]
  [0.40804598 0.8333333  0.         ... 0.         0.6124031  0.52444077]]

 [[0.55172414 0.25       0.         ... 0.         0.5503876  0.6974593 ]
  [0.39655173 0.5833333  0.         ... 0.         0.37209302 0.59748685]
  [0.5344828  0.16666667 0.         ... 0.         0.51937985 0.602596  ]
  ...
  [0.43103448 0.33333334 0.         ... 0.         0.56589144 0.49461475]
  [0.33908045 0.25       0.         ... 0.         0.4108527  0.5223695 ]
  [0.5689655  0.8333333  0.         ... 0.         0.34108528 0.5024855 ]]

 [[0.7356322  0.25       0.         ... 0.         0.53488374 0.6023198 ]
  [0.67241377 0.16666667 0.         ... 0.         0.58914727 0.68102735]
  [0.43678162 0.5        0.         ... 0.         0.58914727 0.546396  ]
  ...
  [0.5689655  0.33333334 0.         ... 0.         0.5503876  0.4584369 ]
  [0.29885057 0.5        0.         ... 0.         0.41860464 0.5625518 ]
  [0.42528737 0.16666667 0.         ... 0.         0.4728682  0.7145816 ]]

 ...

 [[0.49425286 0.75       0.         ... 0.         0.6666667  0.45042807]
  [0.28735632 0.33333334 0.         ... 0.         0.627907   0.6255178 ]
  [0.31034482 0.16666667 0.         ... 0.         0.44186047 0.4710025 ]
  ...
  [0.6436782  0.41666666 0.         ... 0.         0.4496124  0.51367027]
  [0.4827586  0.33333334 0.         ... 0.         0.4883721  0.5015189 ]
  [0.59770113 0.5833333  0.         ... 0.         0.48062015 0.6442972 ]]

 [[0.6896552  0.75       0.         ... 0.         0.76744187 0.51795083]
  [0.47126436 0.16666667 0.         ... 0.         0.6124031  0.77492404]
  [0.61494255 0.33333334 0.         ... 0.         0.68992245 0.7954985 ]
  ...
  [0.33908045 0.75       0.         ... 0.         0.7209302  0.54045844]
  [0.41954023 0.33333334 0.         ... 0.         0.64341086 0.7702292 ]
  [0.77011496 0.5        0.         ... 0.         0.627907   0.64250207]]

 [[0.47126436 0.5833333  0.         ... 0.         0.68217057 0.59279203]
  [0.6091954  0.5833333  0.         ... 0.         0.60465115 0.62510353]
  [0.55172414 0.6666667  0.         ... 0.         0.5968992  0.49047226]
  ...
  [0.46551725 0.25       0.         ... 0.         0.37209302 0.4293013 ]
  [0.2816092  0.5833333  0.         ... 0.         0.4031008  0.51877934]
  [0.57471263 0.75       0.         ... 0.         0.43410853 0.40223694]]]
(93, 50, 25)
In [31]:
# Similarly, we pick the labels

# serve per prendere solo le label delle sequenze che sono almeno lunghe 50
y_mask = [len(test_df[test_df['id']==id]) >= sequence_length for id in test_df['id'].unique()]
print("y_mask")
print(y_mask)
label_array_test_last = test_df.groupby('id')['label1'].nth(-1)[y_mask].values
label_array_test_last = label_array_test_last.reshape(label_array_test_last.shape[0],1).astype(np.float32)
print(label_array_test_last.shape)
print("label_array_test_last")
print(label_array_test_last)
y_mask
[False, False, True, True, True, True, True, True, True, True, True, True, True, False, True, True, True, True, True, True, True, False, True, True, False, True, True, True, True, True, True, True, True, True, True, True, True, True, False, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, False, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]
(93, 1)
label_array_test_last
[[0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]
 [0.]
 [1.]
 [0.]
 [0.]
 [1.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]
 [0.]
 [0.]
 [1.]
 [1.]
 [1.]
 [1.]
 [0.]
 [1.]
 [1.]
 [1.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]
 [0.]
 [0.]
 [1.]
 [1.]
 [0.]
 [0.]
 [1.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]
 [0.]
 [0.]
 [1.]
 [0.]
 [1.]
 [0.]
 [1.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]
 [1.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]
 [0.]
 [1.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [0.]
 [1.]]
In [32]:
# if best iteration's model was saved then load and use it
if os.path.isfile(model_path):
    estimator = load_model(model_path)
In [34]:
# test metrics
scores_test = estimator.evaluate(seq_array_test_last, label_array_test_last, verbose=2)
print('Accuracy: {}'.format(scores_test[1]))
3/3 - 0s - loss: 0.0454 - accuracy: 0.9677
Accuracy: 0.9677419066429138
In [35]:
# make predictions and compute confusion matrix
y_pred_test = estimator.predict_classes(seq_array_test_last)
y_true_test = label_array_test_last
In [36]:
test_set = pd.DataFrame(y_pred_test)
test_set.to_csv('binary_submit_test.csv', index = None)
In [37]:
print('Confusion matrix\n- x-axis is true labels.\n- y-axis is predicted labels')
cm = confusion_matrix(y_true_test, y_pred_test)
print(cm)
Confusion matrix
- x-axis is true labels.
- y-axis is predicted labels
[[66  2]
 [ 1 24]]
In [38]:
# compute precision and recall
precision_test = precision_score(y_true_test, y_pred_test)
recall_test = recall_score(y_true_test, y_pred_test)
f1_test = 2 * (precision_test * recall_test) / (precision_test + recall_test)
print( 'Precision: ', precision_test, '\n', 'Recall: ', recall_test,'\n', 'F1-score:', f1_test )
Precision:  0.9230769230769231 
 Recall:  0.96 
 F1-score: 0.9411764705882353
In [39]:
# Plot in blue color the predicted data and in green color the
# actual data to verify visually the accuracy of the model.
fig_verify = plt.figure(figsize=(100, 50))
plt.plot(y_pred_test, color="blue")
plt.plot(y_true_test, color="green")
plt.title('prediction')
plt.ylabel('value')
plt.xlabel('row')
plt.legend(['predicted', 'actual data'], loc='upper left')
plt.show()
fig_verify.savefig("model_verify.png")

Convert to tflite

In [39]:
# tflite_model_file = "predictive_maintenance_converted_model.tflite"
# model = load_model(model_path)
# converter = tf.lite.TFLiteConverter.from_keras_model(model)
# tflite_model = converter.convert()
# open(tflite_model_file, "wb").write(tflite_model)

Compile with deepCC

In [40]:
!deepCC predictive_maintenance_model.h5
reading [keras model] from 'predictive_maintenance_model.h5'
Saved 'predictive_maintenance_model.onnx'
reading onnx model from file  predictive_maintenance_model.onnx
Model info:
  ir_vesion :  3 
  doc       : 
WARN (ONNX): lstm (LSTM) has 4 inputs, that aren't connected.
WARN (ONNX): lstm_1 (LSTM) has 4 inputs, that aren't connected.
WARN (ONNX): terminal (input/output) lstm_input's shape is less than 1.
             changing it to 1.
WARN (ONNX): terminal (input/output) dense's shape is less than 1.
             changing it to 1.
WARN (GRAPH): found operator node with the same name (dense) as io node.
ERROR (GRAPH): some of graph sequential's node lstm's
               outputs are not connected to other nodes in the graph.
ERROR (GRAPH): some of graph sequential's node lstm_1's
               outputs are not connected to other nodes in the graph.
running DNNC graph sanity check ... FAILED. Please check your model.
graph sequential
operator Transpose1 {
	input lstm_input
	output lstm_X
}
operator lstm {
	input lstm_X
	input lstm_W
	input lstm_R
	input lstm_B
	output lstm_Y
	output lstm_Y_h
	output lstm_Y_c
}
operator Squeeze1 {
	input lstm_Y
	output lstm_y_squeezed
}
operator lstm_1 {
	input lstm_y_squeezed
	input lstm_1_W
	input lstm_1_R
	input lstm_1_B
	output lstm_1_Y
	output lstm_1_Y_h
	output lstm_1_Y_c
}
operator Squeeze {
	input lstm_1_Y_h
	output lstm_1_PartitionedCall_0
}
operator dense {
	input lstm_1_PartitionedCall_0
	input dense_kernel_0
	output dense0
}
operator Add {
	input dense0
	input dense_bias_0
	output biased_tensor_name
}
operator Sigmoid {
	input biased_tensor_name
	output dense
}
weight { float dense_kernel_0 [50,1] }
weight { float dense_bias_0 [1] }
weight { float lstm_1_W [1,200,100] }
weight { float lstm_1_R [1,200,50] }
weight { float lstm_1_B [1,400] }
weight { float lstm_W [1,400,25] }
weight { float lstm_R [1,400,100] }
weight { float lstm_B [1,800] }
input {float lstm_input[1,50,25]}
output {float dense[1,1]}


Writing C++ file  predictive_maintenance_model_deepC/predictive_maintenance_model.cpp
ERROR (TYPE INFER): cound not find all nodes for lstm,
ERROR (TYPE INFER): cound not find all nodes for lstm_1,
WARN (CODEGEN): cound not find all nodes for lstm,
                an instance of LSTM.
                Please check model's sanity and try again.
WARN (CODEGEN): cound not find all nodes for lstm_1,
                an instance of LSTM.
                Please check model's sanity and try again.
INFO (ONNX): model files are ready in dir predictive_maintenance_model_deepC
g++ -O3 -I. -I/opt/tljh/user/lib/python3.7/site-packages/deepC-0.13-py3.7-linux-x86_64.egg/deepC/include -isystem /opt/tljh/user/lib/python3.7/site-packages/deepC-0.13-py3.7-linux-x86_64.egg/deepC/packages/eigen-eigen-323c052e1731 predictive_maintenance_model_deepC/predictive_maintenance_model.cpp -o predictive_maintenance_model_deepC/predictive_maintenance_model.exe
Model executable  predictive_maintenance_model_deepC/predictive_maintenance_model.exe