From fc5b7a9cb3b4b5a5c29dec95dff7292beae60ea9 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Fri, 1 Nov 2019 18:20:38 -0400 Subject: [PATCH 01/30] Integrate Neuraxle In LSTM Human Activity Recognition Wip --- .gitignore | 5 + data_reading.py | 64 +++++ new.py | 81 ++++++ old.py | 277 +++++++++++++++++++++ savers/__init__.py | 0 savers/tensorflow1_step_saver.py | 79 ++++++ steps/__init__.py | 0 steps/lstm_rnn_tensorflow_model.py | 110 ++++++++ steps/lstm_rnn_tensorflow_model_wrapper.py | 116 +++++++++ steps/transform_expected_output_wrapper.py | 28 +++ 10 files changed, 760 insertions(+) create mode 100644 data_reading.py create mode 100644 new.py create mode 100644 old.py create mode 100644 savers/__init__.py create mode 100644 savers/tensorflow1_step_saver.py create mode 100644 steps/__init__.py create mode 100644 steps/lstm_rnn_tensorflow_model.py create mode 100644 steps/lstm_rnn_tensorflow_model_wrapper.py create mode 100644 steps/transform_expected_output_wrapper.py diff --git a/.gitignore b/.gitignore index 1b46987..d8c262c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ .ipynb_checkpoints ___* +neuraxle/** +.idea/** +steps/one_hot_encoder.py +steps/transform_expected_output_only_wrapper.py +venv/** diff --git a/data_reading.py b/data_reading.py new file mode 100644 index 0000000..1dac2ae --- /dev/null +++ b/data_reading.py @@ -0,0 +1,64 @@ +import numpy as np + +INPUT_SIGNAL_TYPES = [ + "body_acc_x_", + "body_acc_y_", + "body_acc_z_", + "body_gyro_x_", + "body_gyro_y_", + "body_gyro_z_", + "total_acc_x_", + "total_acc_y_", + "total_acc_z_" +] + +LABELS = [ + "WALKING", + "WALKING_UPSTAIRS", + "WALKING_DOWNSTAIRS", + "SITTING", + "STANDING", + "LAYING" +] + +DATA_PATH = "data/" +DATASET_PATH = DATA_PATH + "UCI HAR Dataset/" +TRAIN = "train/" +TEST = "test/" +X_train_signals_paths = [ + DATASET_PATH + TRAIN + "Inertial Signals/" + signal + "train.txt" for signal in INPUT_SIGNAL_TYPES +] +X_test_signals_paths = [ + DATASET_PATH + TEST + "Inertial Signals/" + signal + "test.txt" for signal in INPUT_SIGNAL_TYPES +] + + +def load_X(X_signals_paths): + X_signals = [] + + for signal_type_path in X_signals_paths: + file = open(signal_type_path, 'r') + # Read dataset from disk, dealing with text files' syntax + X_signals.append( + [np.array(serie, dtype=np.float32) for serie in [ + row.replace(' ', ' ').strip().split(' ') for row in file + ]] + ) + file.close() + + return np.transpose(np.array(X_signals), (1, 2, 0)) + + +def load_y(y_path): + file = open(y_path, 'r') + # Read dataset from disk, dealing with text file's syntax + y_ = np.array( + [elem for elem in [ + row.replace(' ', ' ').strip().split(' ') for row in file + ]], + dtype=np.int32 + ) + file.close() + + # Substract 1 to each output class for friendly 0-based indexing + return y_ - 1 \ No newline at end of file diff --git a/new.py b/new.py new file mode 100644 index 0000000..1ae428a --- /dev/null +++ b/new.py @@ -0,0 +1,81 @@ +# Those are separate normalised input features for the neural network +import math +import os + +import numpy as np + +from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y +from neuraxle.hyperparams.space import HyperparameterSamples +from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner +from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel +from steps.lstm_rnn_tensorflow_model_wrapper import LSTMRNNTensorflowModelTrainingWrapper +from steps.one_hot_encoder import OneHotEncoder +from steps.transform_expected_output_wrapper import TransformExpectedOutputWrapper + +TEST_FILE_NAME = "y_test.txt" +TRAIN_FILE_NAME = "y_train.txt" + +N_HIDDEN = 32 +LAMBDA_LOSS_AMOUNT = 0.0015 +LEARNING_RATE = 0.0025 +N_CLASSES = 6 +BATCH_SIZE = 1500 + + +def main(): + # Load "X" (the neural network's training and testing inputs) + + X_train = load_X(X_train_signals_paths) + X_test = load_X(X_test_signals_paths) + + # Load "y" (the neural network's training and testing outputs) + + y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME) + y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME) + + y_train = load_y(y_train_path) + y_test = load_y(y_test_path) + + print("Some useful info to get an insight on dataset's shape and normalisation:") + print("(X shape, y shape, every X's mean, every X's standard deviation)") + print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test)) + print("The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.") + + training_data_count = len(X_train) + training_iters = training_data_count * 300 + + lstm_rnn_model_hyperparams = { + 'n_steps': len(X_train[0]), # 128 timesteps per series + 'n_inputs': len(X_train[0][0]), # 9 input parameters per timestep + 'n_hidden': N_HIDDEN, # Hidden layer num of features + 'n_classes': N_CLASSES # Total classes (should go up, or should go down) + } + + lstm_rnn_wrapper_hyperparams = { + 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) + 'learning_rate': LEARNING_RATE, + 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT + } + + pipeline = MiniBatchSequentialPipeline([ + TransformExpectedOutputWrapper( + OneHotEncoder( + no_columns=lstm_rnn_wrapper_hyperparams['n_classes'], + name='one_hot_encoded_label' + ) + ), + LSTMRNNTensorflowModelTrainingWrapper( + tensorflow_model=LSTMRNNTensorflowModel( + hyperparams=HyperparameterSamples(lstm_rnn_model_hyperparams) + ), + hyperparams=HyperparameterSamples(lstm_rnn_wrapper_hyperparams), + X_test=X_test, + y_test=y_test + ), + Joiner(batch_size=BATCH_SIZE) + ]) + + no_iter = int(math.floor(training_iters / BATCH_SIZE)) + + for _ in range(no_iter): + pipeline = pipeline.fit(X_train, y_train) diff --git a/old.py b/old.py new file mode 100644 index 0000000..38c6b38 --- /dev/null +++ b/old.py @@ -0,0 +1,277 @@ +import numpy as np +import tensorflow as tf + +from steps.one_hot_encoder import OneHotEncoder + +# Those are separate normalised input features for the neural network +INPUT_SIGNAL_TYPES = [ + "body_acc_x_", + "body_acc_y_", + "body_acc_z_", + "body_gyro_x_", + "body_gyro_y_", + "body_gyro_z_", + "total_acc_x_", + "total_acc_y_", + "total_acc_z_" +] + +# Output classes to learn how to classify +LABELS = [ + "WALKING", + "WALKING_UPSTAIRS", + "WALKING_DOWNSTAIRS", + "SITTING", + "STANDING", + "LAYING" +] + +DATA_PATH = "data/" +DATASET_PATH = DATA_PATH + "UCI HAR Dataset/" + +TRAIN = "train/" +TEST = "test/" + +X_train_signals_paths = [ + DATASET_PATH + TRAIN + "Inertial Signals/" + signal + "train.txt" for signal in INPUT_SIGNAL_TYPES +] + +X_test_signals_paths = [ + DATASET_PATH + TEST + "Inertial Signals/" + signal + "test.txt" for signal in INPUT_SIGNAL_TYPES +] + + +def main(): + def load_X(X_signals_paths): + X_signals = [] + + for signal_type_path in X_signals_paths: + file = open(signal_type_path, 'r') + # Read dataset from disk, dealing with text files' syntax + X_signals.append( + [np.array(serie, dtype=np.float32) for serie in [ + row.replace(' ', ' ').strip().split(' ') for row in file + ]] + ) + file.close() + + return np.transpose(np.array(X_signals), (1, 2, 0)) + + + def load_y(y_path): + file = open(y_path, 'r') + # Read dataset from disk, dealing with text file's syntax + y_ = np.array( + [elem for elem in [ + row.replace(' ', ' ').strip().split(' ') for row in file + ]], + dtype=np.int32 + ) + file.close() + + # Substract 1 to each output class for friendly 0-based indexing + return y_ - 1 + + + def LSTM_RNN(_X, _weights, _biases): + # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. + # Moreover, two LSTM cells are stacked which adds deepness to the neural network. + # Note, some code of this notebook is inspired from an slightly different + # RNN architecture used on another dataset, some of the credits goes to + # "aymericdamien" under the MIT license. + + # (NOTE: This step could be greatly optimised by shaping the dataset once + # input shape: (batch_size, n_steps, n_input) + _X = tf.transpose(_X, [1, 0, 2]) # permute n_steps and batch_size + # Reshape to prepare input to hidden activation + _X = tf.reshape(_X, [-1, n_input]) + # new shape: (n_steps*batch_size, n_input) + + # ReLU activation, thanks to Yu Zhao for adding this improvement here: + _X = tf.nn.relu(tf.matmul(_X, _weights['hidden']) + _biases['hidden']) + # Split data because rnn cell needs a list of inputs for the RNN inner loop + _X = tf.split(_X, n_steps, 0) + # new shape: n_steps * (batch_size, n_hidden) + + # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow + lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True) + lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True) + lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) + # Get LSTM cell output + outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) + + # Get last time step's output feature for a "many-to-one" style classifier, + # as in the image describing RNNs at the top of this page + lstm_last_output = outputs[-1] + + # Linear activation + return tf.matmul(lstm_last_output, _weights['out']) + _biases['out'] + + + def extract_batch_size(_train, step, batch_size): + # Function to fetch a "batch_size" amount of data from "(X|y)_train" data. + + shape = list(_train.shape) + shape[0] = batch_size + batch_s = np.empty(shape) + + for i in range(batch_size): + # Loop index + index = ((step - 1) * batch_size + i) % len(_train) + batch_s[i] = _train[index] + + return batch_s + + # Load "X" (the neural network's training and testing inputs) + + X_train = load_X(X_train_signals_paths) + X_test = load_X(X_test_signals_paths) + + # Load "y" (the neural network's training and testing outputs) + + y_train_path = DATASET_PATH + TRAIN + "y_train.txt" + y_test_path = DATASET_PATH + TEST + "y_test.txt" + + y_train = load_y(y_train_path) + y_test = load_y(y_test_path) + + # Input Data + + training_data_count = len(X_train) # 7352 training series (with 50% overlap between each serie) + test_data_count = len(X_test) # 2947 testing series + n_steps = len(X_train[0]) # 128 timesteps per series + n_input = len(X_train[0][0]) # 9 input parameters per timestep + + # LSTM Neural Network's internal structure + + n_hidden = 32 # Hidden layer num of features + n_classes = 6 # Total classes (should go up, or should go down) + + # Training + + learning_rate = 0.0025 + lambda_loss_amount = 0.0015 + training_iters = training_data_count * 300 # Loop 300 times on the dataset + batch_size = 1500 + display_iter = 30000 # To show test set accuracy during training + + # Some debugging info + + print("Some useful info to get an insight on dataset's shape and normalisation:") + print("(X shape, y shape, every X's mean, every X's standard deviation)") + print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test)) + print("The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.") + + # Graph input/output + x = tf.placeholder(tf.float32, [None, n_steps, n_input]) + y = tf.placeholder(tf.float32, [None, n_classes]) + + # Graph weights + weights = { + 'hidden': tf.Variable(tf.random_normal([n_input, n_hidden])), # Hidden layer weights + 'out': tf.Variable(tf.random_normal([n_hidden, n_classes], mean=1.0)) + } + biases = { + 'hidden': tf.Variable(tf.random_normal([n_hidden])), + 'out': tf.Variable(tf.random_normal([n_classes])) + } + + pred = LSTM_RNN(x, weights, biases) + + # Loss, optimizer and evaluation + l2 = lambda_loss_amount * sum( + tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() + ) # L2 loss prevents this overkill neural network to overfit the data + cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=pred)) + l2 # Softmax loss + optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # Adam Optimizer + + correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) + accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) + + # To keep track of training's performance + test_losses = [] + test_accuracies = [] + train_losses = [] + train_accuracies = [] + + # Launch the graph + sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) + init = tf.global_variables_initializer() + sess.run(init) + + # Perform Training steps with "batch_size" amount of example data at each loop + step = 1 + while step * batch_size <= training_iters: + batch_xs = extract_batch_size(X_train, step, batch_size) + batch_size_train = extract_batch_size(y_train, step, batch_size) + + batch_ys = OneHotEncoder( + no_columns=n_classes, + name='batch_ys' + ).transform(batch_size_train) + + # Fit training using batch data + _, loss, acc = sess.run( + [optimizer, cost, accuracy], + feed_dict={ + x: batch_xs, + y: batch_ys + } + ) + train_losses.append(loss) + train_accuracies.append(acc) + + # Evaluate network only at some steps for faster training: + if (step * batch_size % display_iter == 0) or (step == 1) or (step * batch_size > training_iters): + # To not spam console, show training accuracy/loss in this "if" + print("Training iter #" + str(step * batch_size) + \ + ": Batch Loss = " + "{:.6f}".format(loss) + \ + ", Accuracy = {}".format(acc)) + + # Evaluation on the test set (no learning made here - just evaluation for diagnosis) + one_hot_encoded_y_test = OneHotEncoder( + no_columns=n_classes, + name='one_hot_encoded_y_test' + ).transform(y_test) + + loss, acc = sess.run( + [cost, accuracy], + feed_dict={ + x: X_test, + y: one_hot_encoded_y_test + } + ) + test_losses.append(loss) + test_accuracies.append(acc) + print("PERFORMANCE ON TEST SET: " + \ + "Batch Loss = {}".format(loss) + \ + ", Accuracy = {}".format(acc)) + + step += 1 + + print("Optimization Finished!") + + # Accuracy for test data + + one_host_encoded_y_test = OneHotEncoder( + no_columns=n_classes, + name='one_hot_predictions' + ).transform(y_test) + + one_hot_predictions, accuracy, final_loss = sess.run( + [pred, accuracy, cost], + feed_dict={ + x: X_test, + y: one_host_encoded_y_test + } + ) + + test_losses.append(final_loss) + test_accuracies.append(accuracy) + + print("FINAL RESULT: " + \ + "Batch Loss = {}".format(final_loss) + \ + ", Accuracy = {}".format(accuracy)) + + + diff --git a/savers/__init__.py b/savers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/savers/tensorflow1_step_saver.py b/savers/tensorflow1_step_saver.py new file mode 100644 index 0000000..bba15cc --- /dev/null +++ b/savers/tensorflow1_step_saver.py @@ -0,0 +1,79 @@ +import os +import tensorflow as tf + +from neuraxle.base import BaseSaver + + +class Tensorflow1StepSaver(BaseSaver): + """ + Step saver for a tensorflow Session using tf.train.Saver(). + It saves, or restores the tf.Session() checkpoint at the context path using the step name as file name. + + .. seealso:: + `Using the saved model format `_ + """ + + def save_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep': + """ + Save a step that is using tf.train.Saver(). + + :param step: step to save + :type step: BaseStep + :param context: execution context to save from + :type context: ExecutionContext + :return: saved step + """ + saver = tf.train.Saver() + with tf.Session() as sess: + saver.save( + sess, + self._get_saved_model_path(context, step) + ) + + return step + + def load_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep': + """ + Load a step that is using tensorflow using tf.train.Saver(). + + :param step: step to load + :type step: BaseStep + :param context: execution context to load from + :type context: ExecutionContext + :return: loaded step + """ + saver = tf.train.Saver() + with tf.Session() as sess: + saver.restore( + sess, + self._get_saved_model_path(context, step) + ) + + return step + + def can_load(self, step: 'BaseStep', context: 'ExecutionContext'): + """ + Returns whether or not we can load. + + :param step: step to load + :type step: BaseStep + :param context: execution context to load from + :type context: ExecutionContext + :return: loaded step + """ + return os.path.exists(self._get_saved_model_path(context, step)) + + def _get_saved_model_path(self, context, step): + """ + Returns the saved model path using the given execution context, and step name. + + :param step: step to load + :type step: BaseStep + :param context: execution context to load from + :type context: ExecutionContext + :return: loaded step + """ + return os.path.join( + context.get_path(), + "{0}.ckpt".format(step.get_name()) + ) \ No newline at end of file diff --git a/steps/__init__.py b/steps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/steps/lstm_rnn_tensorflow_model.py b/steps/lstm_rnn_tensorflow_model.py new file mode 100644 index 0000000..157bb7e --- /dev/null +++ b/steps/lstm_rnn_tensorflow_model.py @@ -0,0 +1,110 @@ +import tensorflow as tf + +from neuraxle.base import BaseStep +from neuraxle.hyperparams.space import HyperparameterSamples +from savers.tensorflow1_step_saver import Tensorflow1StepSaver + + +class LSTMRNNTensorflowModel(BaseStep): + HYPERPARAMS = HyperparameterSamples({ + 'n_steps': 128, + 'n_input': 9, + 'n_hidden': 32, + 'n_classes': 6 + }) + + def __init__(self, name=None, hyperparams=HYPERPARAMS): + BaseStep.__init__( + self, + name=name, + hyperparams=hyperparams, + savers=[Tensorflow1StepSaver()] + ) + + self.x = None + self.y = None + self.weights = None + self.biases = None + self.model = None + + def get_x_placeholder(self): + return self.x + + def get_y_placeholder(self): + return self.y + + def get_weights(self): + return self.weights + + def get_biases(self): + return self.biases + + def get_model(self): + return self.model + + def setup(self): + # Graph input/output + self.x = tf.placeholder(tf.float32, [None, self.hyperparams['n_steps'], self.hyperparams['n_input']]) + self.y = tf.placeholder(tf.float32, [None, self.hyperparams['n_classes']]) + + # Graph weights + self.weights = { + 'hidden': tf.Variable( + tf.random_normal([self.hyperparams['n_input'], self.hyperparams['n_hidden']]) + ), # Hidden layer weights + 'out': tf.Variable( + tf.random_normal([self.hyperparams['n_hidden'], self.hyperparams['n_classes']], mean=1.0) + ) + } + + self.biases = { + 'hidden': tf.Variable( + tf.random_normal([self.hyperparams['n_hidden']]) + ), + 'out': tf.Variable( + tf.random_normal([self.hyperparams['n_classes']]) + ) + } + + self.model = self._create_lstm_rnn_model(self.x) + + def _create_lstm_rnn_model(self, data_inputs): + # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. + # Moreover, two LSTM cells are stacked which adds deepness to the neural network. + # Note, some code of this notebook is inspired from an slightly different + # RNN architecture used on another dataset, some of the credits goes to + # "aymericdamien" under the MIT license. + # (NOTE: This step could be greatly optimised by shaping the dataset once + # input shape: (batch_size, n_steps, n_input) + + data_inputs = tf.transpose( + data_inputs, + [1, 0, 2]) # permute n_steps and batch_size + + # Reshape to prepare input to hidden activation + data_inputs = tf.reshape(data_inputs, [-1, self.hyperparams['n_input']]) + # new shape: (n_steps*batch_size, n_input) + + # ReLU activation, thanks to Yu Zhao for adding this improvement here: + _X = tf.nn.relu( + tf.matmul(data_inputs, self.weights['hidden']) + self.biases['hidden'] + ) + + # Split data because rnn cell needs a list of inputs for the RNN inner loop + _X = tf.split(_X, self.hyperparams['n_steps'], 0) + # new shape: n_steps * (batch_size, n_hidden) + + # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow + lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(self.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(self.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) + + # Get LSTM cell output + outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) + + # Get last time step's output feature for a "many-to-one" style classifier, + # as in the image describing RNNs at the top of this page + lstm_last_output = outputs[-1] + + # Linear activation + return tf.matmul(lstm_last_output, self.weights['out']) + self.biases['out'] diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py new file mode 100644 index 0000000..436b4f1 --- /dev/null +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -0,0 +1,116 @@ +import tensorflow as tf + +from neuraxle.base import MetaStepMixin, BaseStep +from neuraxle.hyperparams.space import HyperparameterSamples +from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel +from steps.one_hot_encoder import OneHotEncoder + + +class LSTMRNNTensorflowModelTrainingWrapper(MetaStepMixin, BaseStep): + HYPERPARAMS = HyperparameterSamples({ + 'n_classes': 6, + 'learning_rate': 0.0025, + 'lambda_loss_amount': 0.0015, + }) + + def __init__( + self, + tensorflow_model: LSTMRNNTensorflowModel, + hyperparams=HYPERPARAMS, + X_test=None, + y_test=None + ): + BaseStep.__init__(self, hyperparams=hyperparams) + MetaStepMixin.__init__(self, wrapped=tensorflow_model) + + self.y_test = y_test + self.X_test = X_test + self.l2 = None + self.cost = None + self.optimizer = None + self.correct_pred = None + self.accuracy = None + self.test_losses = None + self.test_accuracies = None + self.train_losses = None + self.train_accuracies = None + + def setup(self): + # Loss, optimizer and evaluation + # L2 loss prevents this overkill neural network to overfit the data + model: LSTMRNNTensorflowModel = self.wrapped + + self.l2 = self.hyperparams['lambda_loss_amount'] * sum( + tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() + ) + + # Softmax loss + self.cost = tf.reduce_mean( + tf.nn.softmax_cross_entropy_with_logits( + labels=model.get_y_placeholder(), + logits=model + ) + ) + self.l2 + + # Adam Optimizer + self.optimizer = tf.train.AdamOptimizer( + learning_rate=self.hyperparams['learning_rate'] + ).minimize(self.cost) + + self.correct_pred = tf.equal(tf.argmax(self.wrapped, 1), tf.argmax(model.get_y_placeholder(), 1)) + self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32)) + + # To keep track of training's performance + self.test_losses = [] + self.test_accuracies = [] + self.train_losses = [] + self.train_accuracies = [] + + # Launch the graph + self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) + init = tf.global_variables_initializer() + self.sess.run(init) + + def teardown(self): + self.sess.close() + + def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': + model: LSTMRNNTensorflowModel = self.wrapped + + _, loss, acc = self.sess.run( + [self.optimizer, self.cost, self.accuracy], + feed_dict={ + model.get_x_placeholder(): data_inputs, + model.get_y_placeholder(): expected_outputs + } + ) + self.train_losses.append(loss) + self.train_accuracies.append(acc) + + return self + + def transform(self, data_inputs): + pass + + def _evaluate_on_test_set(self): + model: LSTMRNNTensorflowModel = self.wrapped + + # Evaluation on the test set (no learning made here - just evaluation for diagnosis) + one_hot_encoded_y_test = OneHotEncoder( + no_columns=self.hyperparams['n_classes'], + name='one_hot_encoded_y_test' + ).transform(self.y_test) + + loss, acc = self.sess.run( + [self.cost, self.accuracy], + feed_dict={ + model.get_x_placeholder(): self.X_test, + model.get_y_placeholder(): one_hot_encoded_y_test + } + ) + + self.test_losses.append(loss) + self.test_accuracies.append(acc) + print("PERFORMANCE ON TEST SET: " + \ + "Batch Loss = {}".format(loss) + \ + ", Accuracy = {}".format(acc)) diff --git a/steps/transform_expected_output_wrapper.py b/steps/transform_expected_output_wrapper.py new file mode 100644 index 0000000..0e2df47 --- /dev/null +++ b/steps/transform_expected_output_wrapper.py @@ -0,0 +1,28 @@ +from neuraxle.base import NonFittableMixin, MetaStepMixin, BaseStep, DataContainer, ExecutionContext + + +class TransformExpectedOutputWrapper(NonFittableMixin, MetaStepMixin, BaseStep): + """ + Transform expected output wrapper step that can sends the expected_outputs to the wrapped step + so that it can transform the expected outputs. + """ + + def handle_transform(self, data_container: DataContainer, context: ExecutionContext) -> DataContainer: + new_expected_outputs_data_container = self.wrapped.handle_transform( + DataContainer( + current_ids=data_container.current_ids, + data_inputs=data_container.expected_outputs, + expected_outputs=None + ), + context.push(self.wrapped) + ) + + data_container.set_expected_outputs(new_expected_outputs_data_container.data_inputs) + + current_ids = self.hash(data_container.current_ids, self.hyperparams, data_container.data_inputs) + data_container.set_current_ids(current_ids) + + return data_container + + def transform(self, data_inputs): + raise NotImplementedError('must be used inside a pipeline') From c354c5ccf7fba0fd6c6dcb279ac0d0ef441fadff Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 2 Nov 2019 16:13:02 -0400 Subject: [PATCH 02/30] Add Fitted Pipeline Saving Call --- new.py | 9 +++++++++ steps/lstm_rnn_tensorflow_model.py | 22 +++++++++++++++------- steps/lstm_rnn_tensorflow_model_wrapper.py | 8 ++++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/new.py b/new.py index 1ae428a..14a883a 100644 --- a/new.py +++ b/new.py @@ -5,6 +5,7 @@ import numpy as np from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y +from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode from neuraxle.hyperparams.space import HyperparameterSamples from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel @@ -79,3 +80,11 @@ def main(): for _ in range(no_iter): pipeline = pipeline.fit(X_train, y_train) + + pipeline.save( + ExecutionContext.create_from_root( + pipeline, + ExecutionMode.FIT, + DEFAULT_CACHE_FOLDER + ) + ) diff --git a/steps/lstm_rnn_tensorflow_model.py b/steps/lstm_rnn_tensorflow_model.py index 157bb7e..5db78f5 100644 --- a/steps/lstm_rnn_tensorflow_model.py +++ b/steps/lstm_rnn_tensorflow_model.py @@ -13,13 +13,21 @@ class LSTMRNNTensorflowModel(BaseStep): 'n_classes': 6 }) - def __init__(self, name=None, hyperparams=HYPERPARAMS): - BaseStep.__init__( - self, - name=name, - hyperparams=hyperparams, - savers=[Tensorflow1StepSaver()] - ) + def __init__(self, name=None, hyperparams=None): + if hyperparams is None: + BaseStep.__init__( + self, + name=name, + hyperparams=hyperparams, + savers=[Tensorflow1StepSaver()] + ) + else: + BaseStep.__init__( + self, + name=name, + hyperparams=self.HYPERPARAMS, + savers=[Tensorflow1StepSaver()] + ) self.x = None self.y = None diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index 436b4f1..92e1ccd 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -16,11 +16,15 @@ class LSTMRNNTensorflowModelTrainingWrapper(MetaStepMixin, BaseStep): def __init__( self, tensorflow_model: LSTMRNNTensorflowModel, - hyperparams=HYPERPARAMS, + hyperparams=None, X_test=None, y_test=None ): - BaseStep.__init__(self, hyperparams=hyperparams) + if hyperparams is None: + BaseStep.__init__(self, hyperparams=self.HYPERPARAMS) + else: + BaseStep.__init__(self, hyperparams=hyperparams) + MetaStepMixin.__init__(self, wrapped=tensorflow_model) self.y_test = y_test From 4688416424322033aa28c329971d2025eac40271 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 2 Nov 2019 20:43:44 -0400 Subject: [PATCH 03/30] Add Api Serving Demonstration Code --- new.py | 90 ++++++++++++++++-------- steps/custom_json_decoder_for_2darray.py | 16 +++++ steps/custom_json_encoder_of_outputs.py | 16 +++++ 3 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 steps/custom_json_decoder_for_2darray.py create mode 100644 steps/custom_json_encoder_of_outputs.py diff --git a/new.py b/new.py index 14a883a..8cd0b1a 100644 --- a/new.py +++ b/new.py @@ -5,9 +5,12 @@ import numpy as np from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y +from neuraxle.api.flask import FlaskRestApiWrapper from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode from neuraxle.hyperparams.space import HyperparameterSamples from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner +from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray +from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel from steps.lstm_rnn_tensorflow_model_wrapper import LSTMRNNTensorflowModelTrainingWrapper from steps.one_hot_encoder import OneHotEncoder @@ -17,11 +20,45 @@ TRAIN_FILE_NAME = "y_train.txt" N_HIDDEN = 32 +N_STEPS = 128 +N_INPUTS = 9 LAMBDA_LOSS_AMOUNT = 0.0015 LEARNING_RATE = 0.0025 N_CLASSES = 6 BATCH_SIZE = 1500 +LSTM_RNN_MODEL_HYPERPARAMS = { + 'n_steps': N_STEPS, # 128 timesteps per series + 'n_inputs': N_INPUTS, # 9 input parameters per timestep + 'n_hidden': N_HIDDEN, # Hidden layer num of features + 'n_classes': N_CLASSES # Total classes (should go up, or should go down) +} + +LSTM_RNN_WRAPPER_HYPERPARAMS = { + 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) + 'learning_rate': LEARNING_RATE, + 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT +} + + +class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline): + def __init__(self): + MiniBatchSequentialPipeline.__init__(self, [ + TransformExpectedOutputWrapper( + OneHotEncoder( + no_columns=LSTM_RNN_WRAPPER_HYPERPARAMS['n_classes'], + name='one_hot_encoded_label' + ) + ), + LSTMRNNTensorflowModelTrainingWrapper( + tensorflow_model=LSTMRNNTensorflowModel( + hyperparams=HyperparameterSamples(LSTM_RNN_MODEL_HYPERPARAMS) + ), + hyperparams=HyperparameterSamples(LSTM_RNN_WRAPPER_HYPERPARAMS) + ), + Joiner(batch_size=BATCH_SIZE) + ]) + def main(): # Load "X" (the neural network's training and testing inputs) @@ -45,39 +82,9 @@ def main(): training_data_count = len(X_train) training_iters = training_data_count * 300 - lstm_rnn_model_hyperparams = { - 'n_steps': len(X_train[0]), # 128 timesteps per series - 'n_inputs': len(X_train[0][0]), # 9 input parameters per timestep - 'n_hidden': N_HIDDEN, # Hidden layer num of features - 'n_classes': N_CLASSES # Total classes (should go up, or should go down) - } - - lstm_rnn_wrapper_hyperparams = { - 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) - 'learning_rate': LEARNING_RATE, - 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT - } - - pipeline = MiniBatchSequentialPipeline([ - TransformExpectedOutputWrapper( - OneHotEncoder( - no_columns=lstm_rnn_wrapper_hyperparams['n_classes'], - name='one_hot_encoded_label' - ) - ), - LSTMRNNTensorflowModelTrainingWrapper( - tensorflow_model=LSTMRNNTensorflowModel( - hyperparams=HyperparameterSamples(lstm_rnn_model_hyperparams) - ), - hyperparams=HyperparameterSamples(lstm_rnn_wrapper_hyperparams), - X_test=X_test, - y_test=y_test - ), - Joiner(batch_size=BATCH_SIZE) - ]) + pipeline = HumanActivityRecognitionPipeline() no_iter = int(math.floor(training_iters / BATCH_SIZE)) - for _ in range(no_iter): pipeline = pipeline.fit(X_train, y_train) @@ -88,3 +95,24 @@ def main(): DEFAULT_CACHE_FOLDER ) ) + + +def serve_rest_api(): + pipeline = HumanActivityRecognitionPipeline() + + pipeline = pipeline.load( + ExecutionContext.create_from_root( + pipeline, + ExecutionMode.FIT, + DEFAULT_CACHE_FOLDER + ) + ) + + # Easy REST API deployment. + app = FlaskRestApiWrapper( + json_decoder=CustomJSONDecoderFor2DArray(), + wrapped=pipeline, + json_encoder=CustomJSONEncoderOfOutputs() + ).get_app() + + app.run(debug=False, port=5000) diff --git a/steps/custom_json_decoder_for_2darray.py b/steps/custom_json_decoder_for_2darray.py new file mode 100644 index 0000000..0d60898 --- /dev/null +++ b/steps/custom_json_decoder_for_2darray.py @@ -0,0 +1,16 @@ +import numpy as np + +from neuraxle.api.flask import JSONDataBodyDecoder + + +class CustomJSONDecoderFor2DArray(JSONDataBodyDecoder): + """This is a custom JSON decoder class that precedes the pipeline's transformation.""" + + def decode(self, data_inputs): + """ + Transform a JSON list object into an np.array object. + + :param data_inputs: json object + :return: np array for data inputs + """ + return np.array(data_inputs) diff --git a/steps/custom_json_encoder_of_outputs.py b/steps/custom_json_encoder_of_outputs.py new file mode 100644 index 0000000..b3120ce --- /dev/null +++ b/steps/custom_json_encoder_of_outputs.py @@ -0,0 +1,16 @@ +from neuraxle.api.flask import JSONDataResponseEncoder + + +class CustomJSONEncoderOfOutputs(JSONDataResponseEncoder): + """This is a custom JSON response encoder class for converting the pipeline's transformation outputs.""" + + def encode(self, data_inputs) -> dict: + """ + Convert predictions to a dict for creating a JSON Response object. + + :param data_inputs: + :return: + """ + return { + 'predictions': list(data_inputs) + } From db28a906137ed36198b7c1177a6bcda9a06d3080 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 2 Nov 2019 20:56:51 -0400 Subject: [PATCH 04/30] Extract Human Activity Recognition Pipeline Inside a Seprate File --- data_reading.py | 8 +++++++- new.py | 53 +++---------------------------------------------- pipeline.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 51 deletions(-) create mode 100644 pipeline.py diff --git a/data_reading.py b/data_reading.py index 1dac2ae..3b633c5 100644 --- a/data_reading.py +++ b/data_reading.py @@ -23,15 +23,21 @@ DATA_PATH = "data/" DATASET_PATH = DATA_PATH + "UCI HAR Dataset/" + TRAIN = "train/" TEST = "test/" + X_train_signals_paths = [ DATASET_PATH + TRAIN + "Inertial Signals/" + signal + "train.txt" for signal in INPUT_SIGNAL_TYPES ] + X_test_signals_paths = [ DATASET_PATH + TEST + "Inertial Signals/" + signal + "test.txt" for signal in INPUT_SIGNAL_TYPES ] +TEST_FILE_NAME = "y_test.txt" +TRAIN_FILE_NAME = "y_train.txt" + def load_X(X_signals_paths): X_signals = [] @@ -61,4 +67,4 @@ def load_y(y_path): file.close() # Substract 1 to each output class for friendly 0-based indexing - return y_ - 1 \ No newline at end of file + return y_ - 1 diff --git a/new.py b/new.py index 8cd0b1a..ff4aa78 100644 --- a/new.py +++ b/new.py @@ -4,60 +4,13 @@ import numpy as np -from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y +from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \ + TRAIN_FILE_NAME, TEST_FILE_NAME from neuraxle.api.flask import FlaskRestApiWrapper from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode -from neuraxle.hyperparams.space import HyperparameterSamples -from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner +from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs -from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel -from steps.lstm_rnn_tensorflow_model_wrapper import LSTMRNNTensorflowModelTrainingWrapper -from steps.one_hot_encoder import OneHotEncoder -from steps.transform_expected_output_wrapper import TransformExpectedOutputWrapper - -TEST_FILE_NAME = "y_test.txt" -TRAIN_FILE_NAME = "y_train.txt" - -N_HIDDEN = 32 -N_STEPS = 128 -N_INPUTS = 9 -LAMBDA_LOSS_AMOUNT = 0.0015 -LEARNING_RATE = 0.0025 -N_CLASSES = 6 -BATCH_SIZE = 1500 - -LSTM_RNN_MODEL_HYPERPARAMS = { - 'n_steps': N_STEPS, # 128 timesteps per series - 'n_inputs': N_INPUTS, # 9 input parameters per timestep - 'n_hidden': N_HIDDEN, # Hidden layer num of features - 'n_classes': N_CLASSES # Total classes (should go up, or should go down) -} - -LSTM_RNN_WRAPPER_HYPERPARAMS = { - 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) - 'learning_rate': LEARNING_RATE, - 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT -} - - -class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline): - def __init__(self): - MiniBatchSequentialPipeline.__init__(self, [ - TransformExpectedOutputWrapper( - OneHotEncoder( - no_columns=LSTM_RNN_WRAPPER_HYPERPARAMS['n_classes'], - name='one_hot_encoded_label' - ) - ), - LSTMRNNTensorflowModelTrainingWrapper( - tensorflow_model=LSTMRNNTensorflowModel( - hyperparams=HyperparameterSamples(LSTM_RNN_MODEL_HYPERPARAMS) - ), - hyperparams=HyperparameterSamples(LSTM_RNN_WRAPPER_HYPERPARAMS) - ), - Joiner(batch_size=BATCH_SIZE) - ]) def main(): diff --git a/pipeline.py b/pipeline.py new file mode 100644 index 0000000..52ef293 --- /dev/null +++ b/pipeline.py @@ -0,0 +1,46 @@ +from neuraxle.hyperparams.space import HyperparameterSamples +from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner +from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel +from steps.lstm_rnn_tensorflow_model_wrapper import LSTMRNNTensorflowModelTrainingWrapper +from steps.one_hot_encoder import OneHotEncoder +from steps.transform_expected_output_wrapper import TransformExpectedOutputWrapper + +N_HIDDEN = 32 +N_STEPS = 128 +N_INPUTS = 9 +LAMBDA_LOSS_AMOUNT = 0.0015 +LEARNING_RATE = 0.0025 +N_CLASSES = 6 +BATCH_SIZE = 1500 + +LSTM_RNN_MODEL_HYPERPARAMS = { + 'n_steps': N_STEPS, # 128 timesteps per series + 'n_inputs': N_INPUTS, # 9 input parameters per timestep + 'n_hidden': N_HIDDEN, # Hidden layer num of features + 'n_classes': N_CLASSES # Total classes (should go up, or should go down) +} + +LSTM_RNN_WRAPPER_HYPERPARAMS = { + 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) + 'learning_rate': LEARNING_RATE, + 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT +} + + +class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline): + def __init__(self): + MiniBatchSequentialPipeline.__init__(self, [ + TransformExpectedOutputWrapper( + OneHotEncoder( + no_columns=LSTM_RNN_WRAPPER_HYPERPARAMS['n_classes'], + name='one_hot_encoded_label' + ) + ), + LSTMRNNTensorflowModelTrainingWrapper( + tensorflow_model=LSTMRNNTensorflowModel( + hyperparams=HyperparameterSamples(LSTM_RNN_MODEL_HYPERPARAMS) + ), + hyperparams=HyperparameterSamples(LSTM_RNN_WRAPPER_HYPERPARAMS) + ), + Joiner(batch_size=BATCH_SIZE) + ]) From 5c80cb05431c09706f40d3c6a10a1e84d3c6a20d Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 2 Nov 2019 21:54:28 -0400 Subject: [PATCH 05/30] Add requirements.txt, and Fix TransformExpectedOutputWrapper, And LSTMRNNTensorflow fit, and transform methods --- new.py | 4 ++++ requirements.txt | 5 +++++ steps/lstm_rnn_tensorflow_model.py | 4 ++-- steps/transform_expected_output_wrapper.py | 6 +++++- 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 requirements.txt diff --git a/new.py b/new.py index ff4aa78..611251b 100644 --- a/new.py +++ b/new.py @@ -69,3 +69,7 @@ def serve_rest_api(): ).get_app() app.run(debug=False, port=5000) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c72b91c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +numpy +tensorflow==1.15 +conv +flask +matplotlib diff --git a/steps/lstm_rnn_tensorflow_model.py b/steps/lstm_rnn_tensorflow_model.py index 5db78f5..997f08f 100644 --- a/steps/lstm_rnn_tensorflow_model.py +++ b/steps/lstm_rnn_tensorflow_model.py @@ -1,11 +1,11 @@ import tensorflow as tf -from neuraxle.base import BaseStep +from neuraxle.base import BaseStep, NonTransformableMixin, NonFittableMixin from neuraxle.hyperparams.space import HyperparameterSamples from savers.tensorflow1_step_saver import Tensorflow1StepSaver -class LSTMRNNTensorflowModel(BaseStep): +class LSTMRNNTensorflowModel(NonFittableMixin, NonTransformableMixin, BaseStep): HYPERPARAMS = HyperparameterSamples({ 'n_steps': 128, 'n_input': 9, diff --git a/steps/transform_expected_output_wrapper.py b/steps/transform_expected_output_wrapper.py index 0e2df47..3d396e2 100644 --- a/steps/transform_expected_output_wrapper.py +++ b/steps/transform_expected_output_wrapper.py @@ -6,6 +6,10 @@ class TransformExpectedOutputWrapper(NonFittableMixin, MetaStepMixin, BaseStep): Transform expected output wrapper step that can sends the expected_outputs to the wrapped step so that it can transform the expected outputs. """ + def __init__(self, wrapped): + NonFittableMixin.__init__(self) + MetaStepMixin.__init__(self, wrapped) + BaseStep.__init__(self) def handle_transform(self, data_container: DataContainer, context: ExecutionContext) -> DataContainer: new_expected_outputs_data_container = self.wrapped.handle_transform( @@ -19,7 +23,7 @@ def handle_transform(self, data_container: DataContainer, context: ExecutionCont data_container.set_expected_outputs(new_expected_outputs_data_container.data_inputs) - current_ids = self.hash(data_container.current_ids, self.hyperparams, data_container.data_inputs) + current_ids = self.hash(data_container) data_container.set_current_ids(current_ids) return data_container From 654c433e292bf03405bc6213832b06f20ffd13fb Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 2 Nov 2019 23:32:33 -0400 Subject: [PATCH 06/30] Refactor Graph Forward Wip --- new.py | 2 +- old.py | 6 +- pipeline.py | 42 +---- savers/tensorflow1_step_saver.py | 2 +- steps/lstm_rnn_tensorflow_model.py | 176 ++++++++------------- steps/lstm_rnn_tensorflow_model_wrapper.py | 96 +++++++---- steps/transform_expected_output_wrapper.py | 2 +- 7 files changed, 136 insertions(+), 190 deletions(-) diff --git a/new.py b/new.py index 611251b..26f18f5 100644 --- a/new.py +++ b/new.py @@ -39,7 +39,7 @@ def main(): no_iter = int(math.floor(training_iters / BATCH_SIZE)) for _ in range(no_iter): - pipeline = pipeline.fit(X_train, y_train) + pipeline = pipeline.fit_transform(X_train, y_train) pipeline.save( ExecutionContext.create_from_root( diff --git a/old.py b/old.py index 38c6b38..9bb0ae3 100644 --- a/old.py +++ b/old.py @@ -206,7 +206,7 @@ def extract_batch_size(_train, step, batch_size): batch_size_train = extract_batch_size(y_train, step, batch_size) batch_ys = OneHotEncoder( - no_columns=n_classes, + nb_columns=n_classes, name='batch_ys' ).transform(batch_size_train) @@ -230,7 +230,7 @@ def extract_batch_size(_train, step, batch_size): # Evaluation on the test set (no learning made here - just evaluation for diagnosis) one_hot_encoded_y_test = OneHotEncoder( - no_columns=n_classes, + nb_columns=n_classes, name='one_hot_encoded_y_test' ).transform(y_test) @@ -254,7 +254,7 @@ def extract_batch_size(_train, step, batch_size): # Accuracy for test data one_host_encoded_y_test = OneHotEncoder( - no_columns=n_classes, + nb_columns=n_classes, name='one_hot_predictions' ).transform(y_test) diff --git a/pipeline.py b/pipeline.py index 52ef293..96ae597 100644 --- a/pipeline.py +++ b/pipeline.py @@ -1,46 +1,16 @@ -from neuraxle.hyperparams.space import HyperparameterSamples from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner -from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel -from steps.lstm_rnn_tensorflow_model_wrapper import LSTMRNNTensorflowModelTrainingWrapper +from steps.lstm_rnn_tensorflow_model_wrapper import ClassificationRNNTensorFlowModel, N_CLASSES, BATCH_SIZE from steps.one_hot_encoder import OneHotEncoder -from steps.transform_expected_output_wrapper import TransformExpectedOutputWrapper +from steps.transform_expected_output_wrapper import OutputTransformerWrapper -N_HIDDEN = 32 -N_STEPS = 128 -N_INPUTS = 9 -LAMBDA_LOSS_AMOUNT = 0.0015 -LEARNING_RATE = 0.0025 -N_CLASSES = 6 -BATCH_SIZE = 1500 - -LSTM_RNN_MODEL_HYPERPARAMS = { - 'n_steps': N_STEPS, # 128 timesteps per series - 'n_inputs': N_INPUTS, # 9 input parameters per timestep - 'n_hidden': N_HIDDEN, # Hidden layer num of features - 'n_classes': N_CLASSES # Total classes (should go up, or should go down) -} - -LSTM_RNN_WRAPPER_HYPERPARAMS = { - 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) - 'learning_rate': LEARNING_RATE, - 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT -} +# TODO: wrap by a validation split wrapper as issue #174 +# ValidationSplitWrapper(HumanActivityRecognitionPipeline) class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline): def __init__(self): MiniBatchSequentialPipeline.__init__(self, [ - TransformExpectedOutputWrapper( - OneHotEncoder( - no_columns=LSTM_RNN_WRAPPER_HYPERPARAMS['n_classes'], - name='one_hot_encoded_label' - ) - ), - LSTMRNNTensorflowModelTrainingWrapper( - tensorflow_model=LSTMRNNTensorflowModel( - hyperparams=HyperparameterSamples(LSTM_RNN_MODEL_HYPERPARAMS) - ), - hyperparams=HyperparameterSamples(LSTM_RNN_WRAPPER_HYPERPARAMS) - ), + OutputTransformerWrapper(OneHotEncoder(nb_columns=N_CLASSES, name='one_hot_encoded_label')), + ClassificationRNNTensorFlowModel(), Joiner(batch_size=BATCH_SIZE) ]) diff --git a/savers/tensorflow1_step_saver.py b/savers/tensorflow1_step_saver.py index bba15cc..687b086 100644 --- a/savers/tensorflow1_step_saver.py +++ b/savers/tensorflow1_step_saver.py @@ -4,7 +4,7 @@ from neuraxle.base import BaseSaver -class Tensorflow1StepSaver(BaseSaver): +class TensorflowV1StepSaver(BaseSaver): """ Step saver for a tensorflow Session using tf.train.Saver(). It saves, or restores the tf.Session() checkpoint at the context path using the step name as file name. diff --git a/steps/lstm_rnn_tensorflow_model.py b/steps/lstm_rnn_tensorflow_model.py index 997f08f..3562ad7 100644 --- a/steps/lstm_rnn_tensorflow_model.py +++ b/steps/lstm_rnn_tensorflow_model.py @@ -1,118 +1,66 @@ import tensorflow as tf -from neuraxle.base import BaseStep, NonTransformableMixin, NonFittableMixin -from neuraxle.hyperparams.space import HyperparameterSamples -from savers.tensorflow1_step_saver import Tensorflow1StepSaver - -class LSTMRNNTensorflowModel(NonFittableMixin, NonTransformableMixin, BaseStep): - HYPERPARAMS = HyperparameterSamples({ - 'n_steps': 128, - 'n_input': 9, - 'n_hidden': 32, - 'n_classes': 6 - }) - - def __init__(self, name=None, hyperparams=None): - if hyperparams is None: - BaseStep.__init__( - self, - name=name, - hyperparams=hyperparams, - savers=[Tensorflow1StepSaver()] - ) - else: - BaseStep.__init__( - self, - name=name, - hyperparams=self.HYPERPARAMS, - savers=[Tensorflow1StepSaver()] - ) - - self.x = None - self.y = None - self.weights = None - self.biases = None - self.model = None - - def get_x_placeholder(self): - return self.x - - def get_y_placeholder(self): - return self.y - - def get_weights(self): - return self.weights - - def get_biases(self): - return self.biases - - def get_model(self): - return self.model - - def setup(self): - # Graph input/output - self.x = tf.placeholder(tf.float32, [None, self.hyperparams['n_steps'], self.hyperparams['n_input']]) - self.y = tf.placeholder(tf.float32, [None, self.hyperparams['n_classes']]) - - # Graph weights - self.weights = { - 'hidden': tf.Variable( - tf.random_normal([self.hyperparams['n_input'], self.hyperparams['n_hidden']]) - ), # Hidden layer weights - 'out': tf.Variable( - tf.random_normal([self.hyperparams['n_hidden'], self.hyperparams['n_classes']], mean=1.0) - ) - } - - self.biases = { - 'hidden': tf.Variable( - tf.random_normal([self.hyperparams['n_hidden']]) - ), - 'out': tf.Variable( - tf.random_normal([self.hyperparams['n_classes']]) - ) - } - - self.model = self._create_lstm_rnn_model(self.x) - - def _create_lstm_rnn_model(self, data_inputs): - # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. - # Moreover, two LSTM cells are stacked which adds deepness to the neural network. - # Note, some code of this notebook is inspired from an slightly different - # RNN architecture used on another dataset, some of the credits goes to - # "aymericdamien" under the MIT license. - # (NOTE: This step could be greatly optimised by shaping the dataset once - # input shape: (batch_size, n_steps, n_input) - - data_inputs = tf.transpose( - data_inputs, - [1, 0, 2]) # permute n_steps and batch_size - - # Reshape to prepare input to hidden activation - data_inputs = tf.reshape(data_inputs, [-1, self.hyperparams['n_input']]) - # new shape: (n_steps*batch_size, n_input) - - # ReLU activation, thanks to Yu Zhao for adding this improvement here: - _X = tf.nn.relu( - tf.matmul(data_inputs, self.weights['hidden']) + self.biases['hidden'] +def tf_model_forward(graph, name_x, name_y, hyperparams): + # Graph input/output + x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_input']], name='x') + y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name='y') + + # Graph weights + weights = { + 'hidden': tf.Variable( + tf.random_normal([hyperparams['n_input'], hyperparams['n_hidden']]) + ), # Hidden layer weights + 'out': tf.Variable( + tf.random_normal([hyperparams['n_hidden'], hyperparams['n_classes']], mean=1.0) ) - - # Split data because rnn cell needs a list of inputs for the RNN inner loop - _X = tf.split(_X, self.hyperparams['n_steps'], 0) - # new shape: n_steps * (batch_size, n_hidden) - - # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow - lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(self.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) - lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(self.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) - lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) - - # Get LSTM cell output - outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) - - # Get last time step's output feature for a "many-to-one" style classifier, - # as in the image describing RNNs at the top of this page - lstm_last_output = outputs[-1] - - # Linear activation - return tf.matmul(lstm_last_output, self.weights['out']) + self.biases['out'] + } + + biases = { + 'hidden': tf.Variable( + tf.random_normal([hyperparams['n_hidden']]) + ), + 'out': tf.Variable( + tf.random_normal([hyperparams['n_classes']]) + ) + } + + # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. + # Moreover, two LSTM cells are stacked which adds deepness to the neural network. + # Note, some code of this notebook is inspired from an slightly different + # RNN architecture used on another dataset, some of the credits goes to + # "aymericdamien" under the MIT license. + # (NOTE: This step could be greatly optimised by shaping the dataset once + # input shape: (batch_size, n_steps, n_input) + + data_inputs = tf.transpose( + x, + [1, 0, 2]) # permute n_steps and batch_size + + # Reshape to prepare input to hidden activation + data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_input']]) + # new shape: (n_steps*batch_size, n_input) + + # ReLU activation, thanks to Yu Zhao for adding this improvement here: + _X = tf.nn.relu( + tf.matmul(data_inputs, weights['hidden']) + biases['hidden'] + ) + + # Split data because rnn cell needs a list of inputs for the RNN inner loop + _X = tf.split(_X, hyperparams['n_steps'], 0) + # new shape: n_steps * (batch_size, n_hidden) + + # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow + lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) + + # Get LSTM cell output + outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) + + # Get last time step's output feature for a "many-to-one" style classifier, + # as in the image describing RNNs at the top of this page + lstm_last_output = outputs[-1] + + # Linear activation + return tf.matmul(lstm_last_output, weights['out']) + biases['out'] diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index 92e1ccd..0bd8e78 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -1,34 +1,48 @@ import tensorflow as tf -from neuraxle.base import MetaStepMixin, BaseStep +from neuraxle.base import BaseStep from neuraxle.hyperparams.space import HyperparameterSamples -from steps.lstm_rnn_tensorflow_model import LSTMRNNTensorflowModel +from savers.tensorflow1_step_saver import TensorflowV1StepSaver +from steps.lstm_rnn_tensorflow_model import tf_model_forward from steps.one_hot_encoder import OneHotEncoder +N_HIDDEN = 32 +N_STEPS = 128 +N_INPUTS = 9 +LAMBDA_LOSS_AMOUNT = 0.0015 +LEARNING_RATE = 0.0025 +N_CLASSES = 6 +BATCH_SIZE = 1500 -class LSTMRNNTensorflowModelTrainingWrapper(MetaStepMixin, BaseStep): + +class ClassificationRNNTensorFlowModel(BaseStep): HYPERPARAMS = HyperparameterSamples({ - 'n_classes': 6, - 'learning_rate': 0.0025, - 'lambda_loss_amount': 0.0015, + 'n_steps': N_STEPS, # 128 timesteps per series + 'n_inputs': N_INPUTS, # 9 input parameters per timestep + 'n_hidden': N_HIDDEN, # Hidden layer num of features + 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) + 'learning_rate': LEARNING_RATE, + 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT }) def __init__( self, - tensorflow_model: LSTMRNNTensorflowModel, - hyperparams=None, + # TODO: replace with issue 174 + # X_test=None, y_test=None ): - if hyperparams is None: - BaseStep.__init__(self, hyperparams=self.HYPERPARAMS) - else: - BaseStep.__init__(self, hyperparams=hyperparams) - - MetaStepMixin.__init__(self, wrapped=tensorflow_model) + BaseStep.__init__( + self, + hyperparams=ClassificationRNNTensorFlowModel.HYPERPARAMS, + savers=[TensorflowV1StepSaver()] + ) + # TODO: replace with issue 174 + # self.y_test = y_test self.X_test = X_test + self.l2 = None self.cost = None self.optimizer = None @@ -39,20 +53,33 @@ def __init__( self.train_losses = None self.train_accuracies = None - def setup(self): + def setup(self) -> BaseStep: + # TODO: call with tf variable scope to put in a sub graph ???? + # with a tensorflow graph or variable scope + + # Launch the graph + self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) + init = tf.global_variables_initializer() + self.sess.run(init) + # Loss, optimizer and evaluation # L2 loss prevents this overkill neural network to overfit the data - model: LSTMRNNTensorflowModel = self.wrapped + self.x_name = '' + self.y_name = '' - self.l2 = self.hyperparams['lambda_loss_amount'] * sum( + # TODO: name pred (wrap identity ?) + self.pred_name = '' + pred = tf_model_forward(self.pred_name, self.x_name, self.y_name, self.hyperparams) + + l2 = self.hyperparams['lambda_loss_amount'] * sum( tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() ) # Softmax loss self.cost = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( - labels=model.get_y_placeholder(), - logits=model + labels=tf.get_variable(self.y_name), + logits=pred ) ) + self.l2 @@ -61,7 +88,7 @@ def setup(self): learning_rate=self.hyperparams['learning_rate'] ).minimize(self.cost) - self.correct_pred = tf.equal(tf.argmax(self.wrapped, 1), tf.argmax(model.get_y_placeholder(), 1)) + self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(tf.get_variable(self.y_name), 1)) self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32)) # To keep track of training's performance @@ -70,22 +97,19 @@ def setup(self): self.train_losses = [] self.train_accuracies = [] - # Launch the graph - self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) - init = tf.global_variables_initializer() - self.sess.run(init) + self.is_initialized = True + + return self def teardown(self): self.sess.close() def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': - model: LSTMRNNTensorflowModel = self.wrapped - _, loss, acc = self.sess.run( [self.optimizer, self.cost, self.accuracy], feed_dict={ - model.get_x_placeholder(): data_inputs, - model.get_y_placeholder(): expected_outputs + tf.get_variable(self.x_name): data_inputs, + tf.get_variable(self.y_name): expected_outputs } ) self.train_losses.append(loss) @@ -94,22 +118,26 @@ def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': return self def transform(self, data_inputs): - pass + # TODO: pass individual element instead of list to sess run ?? + return self.sess.run( + [tf.get_variable(self.pred_name)], + feed_dict={ + self.x_name: data_inputs + } + )[0] def _evaluate_on_test_set(self): - model: LSTMRNNTensorflowModel = self.wrapped - # Evaluation on the test set (no learning made here - just evaluation for diagnosis) one_hot_encoded_y_test = OneHotEncoder( - no_columns=self.hyperparams['n_classes'], + nb_columns=self.hyperparams['n_classes'], name='one_hot_encoded_y_test' ).transform(self.y_test) loss, acc = self.sess.run( [self.cost, self.accuracy], feed_dict={ - model.get_x_placeholder(): self.X_test, - model.get_y_placeholder(): one_hot_encoded_y_test + tf.get_variable(self.x_name): self.X_test, + tf.get_variable(self.y_name): one_hot_encoded_y_test } ) diff --git a/steps/transform_expected_output_wrapper.py b/steps/transform_expected_output_wrapper.py index 3d396e2..1cca900 100644 --- a/steps/transform_expected_output_wrapper.py +++ b/steps/transform_expected_output_wrapper.py @@ -1,7 +1,7 @@ from neuraxle.base import NonFittableMixin, MetaStepMixin, BaseStep, DataContainer, ExecutionContext -class TransformExpectedOutputWrapper(NonFittableMixin, MetaStepMixin, BaseStep): +class OutputTransformerWrapper(NonFittableMixin, MetaStepMixin, BaseStep): """ Transform expected output wrapper step that can sends the expected_outputs to the wrapped step so that it can transform the expected outputs. From 6c115ef9c5dc72bca3e6b0659aa2b49d5cc28031 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sun, 3 Nov 2019 14:20:15 -0500 Subject: [PATCH 07/30] Add Variable Scope, And Placeholder Names --- steps/lstm_rnn_tensorflow_model.py | 29 ++--- steps/lstm_rnn_tensorflow_model_wrapper.py | 123 +++++++++++---------- 2 files changed, 79 insertions(+), 73 deletions(-) diff --git a/steps/lstm_rnn_tensorflow_model.py b/steps/lstm_rnn_tensorflow_model.py index 3562ad7..1dbcbd8 100644 --- a/steps/lstm_rnn_tensorflow_model.py +++ b/steps/lstm_rnn_tensorflow_model.py @@ -1,15 +1,23 @@ import tensorflow as tf -def tf_model_forward(graph, name_x, name_y, hyperparams): +def tf_model_forward(pred_name, name_x, name_y, hyperparams): + # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. + # Moreover, two LSTM cells are stacked which adds deepness to the neural network. + # Note, some code of this notebook is inspired from an slightly different + # RNN architecture used on another dataset, some of the credits goes to + # "aymericdamien" under the MIT license. + # (NOTE: This step could be greatly optimised by shaping the dataset once + # input shape: (batch_size, n_steps, n_input) + # Graph input/output - x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_input']], name='x') - y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name='y') + x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_inputs']], name=name_x) + y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name=name_y) # Graph weights weights = { 'hidden': tf.Variable( - tf.random_normal([hyperparams['n_input'], hyperparams['n_hidden']]) + tf.random_normal([hyperparams['n_inputs'], hyperparams['n_hidden']]) ), # Hidden layer weights 'out': tf.Variable( tf.random_normal([hyperparams['n_hidden'], hyperparams['n_classes']], mean=1.0) @@ -25,20 +33,12 @@ def tf_model_forward(graph, name_x, name_y, hyperparams): ) } - # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. - # Moreover, two LSTM cells are stacked which adds deepness to the neural network. - # Note, some code of this notebook is inspired from an slightly different - # RNN architecture used on another dataset, some of the credits goes to - # "aymericdamien" under the MIT license. - # (NOTE: This step could be greatly optimised by shaping the dataset once - # input shape: (batch_size, n_steps, n_input) - data_inputs = tf.transpose( x, [1, 0, 2]) # permute n_steps and batch_size # Reshape to prepare input to hidden activation - data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_input']]) + data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_inputs']]) # new shape: (n_steps*batch_size, n_input) # ReLU activation, thanks to Yu Zhao for adding this improvement here: @@ -63,4 +63,5 @@ def tf_model_forward(graph, name_x, name_y, hyperparams): lstm_last_output = outputs[-1] # Linear activation - return tf.matmul(lstm_last_output, weights['out']) + biases['out'] + pred = tf.matmul(lstm_last_output, weights['out']) + biases['out'] + return tf.identity(pred, name=pred_name) diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index 0bd8e78..7f32194 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -6,6 +6,8 @@ from steps.lstm_rnn_tensorflow_model import tf_model_forward from steps.one_hot_encoder import OneHotEncoder +LSTM_RNN_VARIABLE_SCOPE = "lstm_rnn" + N_HIDDEN = 32 N_STEPS = 128 N_INPUTS = 9 @@ -54,92 +56,95 @@ def __init__( self.train_accuracies = None def setup(self) -> BaseStep: - # TODO: call with tf variable scope to put in a sub graph ???? - # with a tensorflow graph or variable scope - - # Launch the graph - self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) - init = tf.global_variables_initializer() - self.sess.run(init) + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): + self.pred_name = 'pred' + self.x_name = 'x' + self.y_name = 'y' - # Loss, optimizer and evaluation - # L2 loss prevents this overkill neural network to overfit the data - self.x_name = '' - self.y_name = '' + pred = tf_model_forward(self.pred_name, self.x_name, self.y_name, self.hyperparams) - # TODO: name pred (wrap identity ?) - self.pred_name = '' - pred = tf_model_forward(self.pred_name, self.x_name, self.y_name, self.hyperparams) + # Launch the graph + self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) + init = tf.global_variables_initializer() + self.sess.run(init) - l2 = self.hyperparams['lambda_loss_amount'] * sum( - tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() - ) + # Loss, optimizer and evaluation + # L2 loss prevents this overkill neural network to overfit the data - # Softmax loss - self.cost = tf.reduce_mean( - tf.nn.softmax_cross_entropy_with_logits( - labels=tf.get_variable(self.y_name), - logits=pred + l2 = self.hyperparams['lambda_loss_amount'] * sum( + tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() ) - ) + self.l2 - # Adam Optimizer - self.optimizer = tf.train.AdamOptimizer( - learning_rate=self.hyperparams['learning_rate'] - ).minimize(self.cost) + # Softmax loss + self.cost = tf.reduce_mean( + tf.nn.softmax_cross_entropy_with_logits( + labels=self.get_tensor_by_name(self.y_name), + logits=pred + ) + ) + l2 - self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(tf.get_variable(self.y_name), 1)) - self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32)) + # Adam Optimizer + self.optimizer = tf.train.AdamOptimizer( + learning_rate=self.hyperparams['learning_rate'] + ).minimize(self.cost) - # To keep track of training's performance - self.test_losses = [] - self.test_accuracies = [] - self.train_losses = [] - self.train_accuracies = [] + self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(self.y_name), 1)) + self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32)) - self.is_initialized = True + # To keep track of training's performance + self.test_losses = [] + self.test_accuracies = [] + self.train_losses = [] + self.train_accuracies = [] + + self.is_initialized = True return self + def get_tensor_by_name(self, name): + return tf.get_default_graph().get_tensor_by_name("{0}/{1}:0".format(LSTM_RNN_VARIABLE_SCOPE, name)) + def teardown(self): self.sess.close() def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': - _, loss, acc = self.sess.run( - [self.optimizer, self.cost, self.accuracy], - feed_dict={ - tf.get_variable(self.x_name): data_inputs, - tf.get_variable(self.y_name): expected_outputs - } - ) - self.train_losses.append(loss) - self.train_accuracies.append(acc) + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): + _, loss, acc = self.sess.run( + [self.optimizer, self.cost, self.accuracy], + feed_dict={ + self.get_tensor_by_name(self.x_name): data_inputs, + self.get_tensor_by_name(self.y_name): expected_outputs + } + ) + + self.train_losses.append(loss) + self.train_accuracies.append(acc) return self def transform(self, data_inputs): - # TODO: pass individual element instead of list to sess run ?? - return self.sess.run( - [tf.get_variable(self.pred_name)], - feed_dict={ - self.x_name: data_inputs - } - )[0] + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): + return self.sess.run( + self.get_tensor_by_name(self.pred_name), + feed_dict={ + self.get_tensor_by_name(self.x_name): data_inputs + } + ) def _evaluate_on_test_set(self): - # Evaluation on the test set (no learning made here - just evaluation for diagnosis) one_hot_encoded_y_test = OneHotEncoder( nb_columns=self.hyperparams['n_classes'], name='one_hot_encoded_y_test' ).transform(self.y_test) - loss, acc = self.sess.run( - [self.cost, self.accuracy], - feed_dict={ - tf.get_variable(self.x_name): self.X_test, - tf.get_variable(self.y_name): one_hot_encoded_y_test - } - ) + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): + loss, acc = self.sess.run( + [self.cost, self.accuracy], + feed_dict={ + self.get_tensor_by_name(self.x_name): self.X_test, + self.get_tensor_by_name(self.y_name): one_hot_encoded_y_test + } + ) self.test_losses.append(loss) self.test_accuracies.append(acc) From 44ccad8ad8126c6c4ce620a843f9ca14698c1589 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sun, 3 Nov 2019 15:32:50 -0500 Subject: [PATCH 08/30] Fix Tensorflow Graph Setup Initialization --- .gitignore | 1 + new.py | 2 +- old.py | 6 +- steps/lstm_rnn_tensorflow_model_wrapper.py | 66 +++++++++++++++------- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index d8c262c..d9322d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .ipynb_checkpoints ___* neuraxle/** +steps/__pycache__/** .idea/** steps/one_hot_encoder.py steps/transform_expected_output_only_wrapper.py diff --git a/new.py b/new.py index 26f18f5..6b9abc2 100644 --- a/new.py +++ b/new.py @@ -39,7 +39,7 @@ def main(): no_iter = int(math.floor(training_iters / BATCH_SIZE)) for _ in range(no_iter): - pipeline = pipeline.fit_transform(X_train, y_train) + pipeline, outputs = pipeline.fit_transform(X_train, y_train) pipeline.save( ExecutionContext.create_from_root( diff --git a/old.py b/old.py index 9bb0ae3..c99e002 100644 --- a/old.py +++ b/old.py @@ -57,7 +57,6 @@ def load_X(X_signals_paths): return np.transpose(np.array(X_signals), (1, 2, 0)) - def load_y(y_path): file = open(y_path, 'r') # Read dataset from disk, dealing with text file's syntax @@ -72,7 +71,6 @@ def load_y(y_path): # Substract 1 to each output class for friendly 0-based indexing return y_ - 1 - def LSTM_RNN(_X, _weights, _biases): # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. # Moreover, two LSTM cells are stacked which adds deepness to the neural network. @@ -107,7 +105,6 @@ def LSTM_RNN(_X, _weights, _biases): # Linear activation return tf.matmul(lstm_last_output, _weights['out']) + _biases['out'] - def extract_batch_size(_train, step, batch_size): # Function to fetch a "batch_size" amount of data from "(X|y)_train" data. @@ -274,4 +271,5 @@ def extract_batch_size(_train, step, batch_size): ", Accuracy = {}".format(accuracy)) - +if __name__ == '__main__': + main() diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index 7f32194..ee3d95a 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -1,3 +1,4 @@ +import numpy as np import tensorflow as tf from neuraxle.base import BaseStep @@ -24,7 +25,8 @@ class ClassificationRNNTensorFlowModel(BaseStep): 'n_hidden': N_HIDDEN, # Hidden layer num of features 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) 'learning_rate': LEARNING_RATE, - 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT + 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT, + 'batch_size': BATCH_SIZE }) def __init__( @@ -56,18 +58,14 @@ def __init__( self.train_accuracies = None def setup(self) -> BaseStep: - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): + # Launch the graph + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): self.pred_name = 'pred' self.x_name = 'x' self.y_name = 'y' pred = tf_model_forward(self.pred_name, self.x_name, self.y_name, self.hyperparams) - # Launch the graph - self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) - init = tf.global_variables_initializer() - self.sess.run(init) - # Loss, optimizer and evaluation # L2 loss prevents this overkill neural network to overfit the data @@ -78,7 +76,7 @@ def setup(self) -> BaseStep: # Softmax loss self.cost = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( - labels=self.get_tensor_by_name(self.y_name), + labels=self.get_y_placeholder(), logits=pred ) ) + l2 @@ -97,6 +95,10 @@ def setup(self) -> BaseStep: self.train_losses = [] self.train_accuracies = [] + self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) + init = tf.global_variables_initializer() + self.sess.run(init) + self.is_initialized = True return self @@ -104,32 +106,56 @@ def setup(self) -> BaseStep: def get_tensor_by_name(self, name): return tf.get_default_graph().get_tensor_by_name("{0}/{1}:0".format(LSTM_RNN_VARIABLE_SCOPE, name)) + def get_x_placeholder(self): + return self.get_tensor_by_name(self.x_name) + + def get_y_placeholder(self): + return self.get_tensor_by_name(self.y_name) + def teardown(self): - self.sess.close() + if self.sess is not None: + self.sess.close() def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): + if not isinstance(data_inputs, np.ndarray): + data_inputs = np.array(data_inputs) + + if not isinstance(expected_outputs, np.ndarray): + expected_outputs = np.array(expected_outputs) + + expected_outputs = np.reshape(expected_outputs, (self.hyperparams['batch_size'], self.hyperparams['n_classes'])) + + # shape x : (?, 128, 9) + # shape y : (?, 6) + + # shape data_inputs : (1500, 128, 9) + # shape expected_outputs : (1500, 6) + + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): _, loss, acc = self.sess.run( [self.optimizer, self.cost, self.accuracy], feed_dict={ - self.get_tensor_by_name(self.x_name): data_inputs, - self.get_tensor_by_name(self.y_name): expected_outputs + self.get_x_placeholder(): data_inputs, + self.get_y_placeholder(): expected_outputs } ) self.train_losses.append(loss) self.train_accuracies.append(acc) + print("Batch Loss = " + "{:.6f}".format(loss) + ", Accuracy = {}".format(acc)) + return self def transform(self, data_inputs): - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): - return self.sess.run( - self.get_tensor_by_name(self.pred_name), + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): + outputs = self.sess.run( + [self.get_tensor_by_name(self.pred_name)], feed_dict={ - self.get_tensor_by_name(self.x_name): data_inputs + self.get_x_placeholder(): data_inputs } - ) + )[0] + return outputs def _evaluate_on_test_set(self): one_hot_encoded_y_test = OneHotEncoder( @@ -137,12 +163,12 @@ def _evaluate_on_test_set(self): name='one_hot_encoded_y_test' ).transform(self.y_test) - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE): + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): loss, acc = self.sess.run( [self.cost, self.accuracy], feed_dict={ - self.get_tensor_by_name(self.x_name): self.X_test, - self.get_tensor_by_name(self.y_name): one_hot_encoded_y_test + self.get_x_placeholder(): self.X_test, + self.get_y_placeholder(): one_hot_encoded_y_test } ) From 4ba8e5a74a4aad34560587de865b35fcb6bf67d9 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sun, 3 Nov 2019 16:48:35 -0500 Subject: [PATCH 09/30] Setup tensorflow model wrapper once --- steps/lstm_rnn_tensorflow_model_wrapper.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index ee3d95a..f05358e 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -58,6 +58,9 @@ def __init__( self.train_accuracies = None def setup(self) -> BaseStep: + if self.is_initialized: + return self + # Launch the graph with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): self.pred_name = 'pred' From 53e54de6f12daa7545cd5c7ae026e5ff6214f564 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sun, 3 Nov 2019 16:56:32 -0500 Subject: [PATCH 10/30] Use step.sess in tensorflowv1stepsaver --- savers/tensorflow1_step_saver.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/savers/tensorflow1_step_saver.py b/savers/tensorflow1_step_saver.py index 687b086..0997b86 100644 --- a/savers/tensorflow1_step_saver.py +++ b/savers/tensorflow1_step_saver.py @@ -24,11 +24,10 @@ def save_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep' :return: saved step """ saver = tf.train.Saver() - with tf.Session() as sess: - saver.save( - sess, - self._get_saved_model_path(context, step) - ) + saver.save( + step.sess, + self._get_saved_model_path(context, step) + ) return step @@ -43,11 +42,10 @@ def load_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep' :return: loaded step """ saver = tf.train.Saver() - with tf.Session() as sess: - saver.restore( - sess, - self._get_saved_model_path(context, step) - ) + saver.restore( + step.sess, + self._get_saved_model_path(context, step) + ) return step @@ -76,4 +74,4 @@ def _get_saved_model_path(self, context, step): return os.path.join( context.get_path(), "{0}.ckpt".format(step.get_name()) - ) \ No newline at end of file + ) From 85d8ced2567a7d4c59d6045f9c758b5a3e5261ec Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sun, 3 Nov 2019 23:10:53 -0500 Subject: [PATCH 11/30] Use Default Graph In Tf Session, And Use Default Graph In Tensorflow v1 Saver --- .gitignore | 3 + new.py | 21 +++- requirements.txt | 2 +- savers/tensorflow1_step_saver.py | 30 ++++-- steps/lstm_rnn_tensorflow_model_wrapper.py | 113 +++++++++++++-------- 5 files changed, 112 insertions(+), 57 deletions(-) diff --git a/.gitignore b/.gitignore index d9322d3..9c8bea1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,10 @@ ___* neuraxle/** steps/__pycache__/** +savers/__pycache__/** +__pycache__/** .idea/** steps/one_hot_encoder.py steps/transform_expected_output_only_wrapper.py venv/** +cache/** diff --git a/new.py b/new.py index 6b9abc2..585f94c 100644 --- a/new.py +++ b/new.py @@ -33,7 +33,7 @@ def main(): print("The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.") training_data_count = len(X_train) - training_iters = training_data_count * 300 + training_iters = training_data_count * 3 pipeline = HumanActivityRecognitionPipeline() @@ -49,8 +49,23 @@ def main(): ) ) + pipeline.teardown() + def serve_rest_api(): + # Load "X" (the neural network's training and testing inputs) + + X_train = load_X(X_train_signals_paths) + X_test = load_X(X_test_signals_paths) + + # Load "y" (the neural network's training and testing outputs) + + y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME) + y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME) + + y_train = load_y(y_train_path) + y_test = load_y(y_test_path) + pipeline = HumanActivityRecognitionPipeline() pipeline = pipeline.load( @@ -61,6 +76,8 @@ def serve_rest_api(): ) ) + pipeline, outputs = pipeline.fit_transform(X_train, y_train) + # Easy REST API deployment. app = FlaskRestApiWrapper( json_decoder=CustomJSONDecoderFor2DArray(), @@ -72,4 +89,4 @@ def serve_rest_api(): if __name__ == '__main__': - main() + serve_rest_api() diff --git a/requirements.txt b/requirements.txt index c72b91c..d7dd4d7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ numpy tensorflow==1.15 -conv +conv==0.2 flask matplotlib diff --git a/savers/tensorflow1_step_saver.py b/savers/tensorflow1_step_saver.py index 0997b86..cae9f6a 100644 --- a/savers/tensorflow1_step_saver.py +++ b/savers/tensorflow1_step_saver.py @@ -23,11 +23,14 @@ def save_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep' :type context: ExecutionContext :return: saved step """ - saver = tf.train.Saver() - saver.save( - step.sess, - self._get_saved_model_path(context, step) - ) + with step.get_graph().as_default(): + saver = tf.train.Saver() + saver.save( + step.get_session(), + self._get_saved_model_path(context, step) + ) + + step.strip() return step @@ -41,11 +44,14 @@ def load_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep' :type context: ExecutionContext :return: loaded step """ - saver = tf.train.Saver() - saver.restore( - step.sess, - self._get_saved_model_path(context, step) - ) + step.is_initialized = False + step.setup() + with step.get_graph().as_default(): + saver = tf.train.Saver() + saver.restore( + step.get_session(), + self._get_saved_model_path(context, step) + ) return step @@ -59,7 +65,9 @@ def can_load(self, step: 'BaseStep', context: 'ExecutionContext'): :type context: ExecutionContext :return: loaded step """ - return os.path.exists(self._get_saved_model_path(context, step)) + meta_exists = os.path.exists(os.path.join(context.get_path(), "{0}.ckpt.meta".format(step.get_name()))) + index_exists = os.path.exists(os.path.join(context.get_path(), "{0}.ckpt.index".format(step.get_name()))) + return meta_exists and index_exists def _get_saved_model_path(self, context, step): """ diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index f05358e..0d4d53d 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -8,6 +8,9 @@ from steps.one_hot_encoder import OneHotEncoder LSTM_RNN_VARIABLE_SCOPE = "lstm_rnn" +X_NAME = 'x' +Y_NAME = 'y' +PRED_NAME = 'pred' N_HIDDEN = 32 N_STEPS = 128 @@ -47,6 +50,8 @@ def __init__( self.y_test = y_test self.X_test = X_test + self.graph = None + self.sess = None self.l2 = None self.cost = None self.optimizer = None @@ -57,67 +62,89 @@ def __init__( self.train_losses = None self.train_accuracies = None + def strip(self): + self.sess = None + self.graph = None + self.l2 = None + self.cost = None + self.optimizer = None + self.correct_pred = None + self.accuracy = None + def setup(self) -> BaseStep: if self.is_initialized: return self - # Launch the graph - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): - self.pred_name = 'pred' - self.x_name = 'x' - self.y_name = 'y' + self.create_graph() - pred = tf_model_forward(self.pred_name, self.x_name, self.y_name, self.hyperparams) + with self.graph.as_default(): + # Launch the graph + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): - # Loss, optimizer and evaluation - # L2 loss prevents this overkill neural network to overfit the data + pred = tf_model_forward(PRED_NAME, X_NAME, Y_NAME, self.hyperparams) - l2 = self.hyperparams['lambda_loss_amount'] * sum( - tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() - ) + # Loss, optimizer and evaluation + # L2 loss prevents this overkill neural network to overfit the data - # Softmax loss - self.cost = tf.reduce_mean( - tf.nn.softmax_cross_entropy_with_logits( - labels=self.get_y_placeholder(), - logits=pred + l2 = self.hyperparams['lambda_loss_amount'] * sum( + tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() ) - ) + l2 - # Adam Optimizer - self.optimizer = tf.train.AdamOptimizer( - learning_rate=self.hyperparams['learning_rate'] - ).minimize(self.cost) + # Softmax loss + self.cost = tf.reduce_mean( + tf.nn.softmax_cross_entropy_with_logits( + labels=self.get_y_placeholder(), + logits=pred + ) + ) + l2 - self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(self.y_name), 1)) - self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32)) + # Adam Optimizer + self.optimizer = tf.train.AdamOptimizer( + learning_rate=self.hyperparams['learning_rate'] + ).minimize(self.cost) - # To keep track of training's performance - self.test_losses = [] - self.test_accuracies = [] - self.train_losses = [] - self.train_accuracies = [] + self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(Y_NAME), 1)) + self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32)) - self.sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) - init = tf.global_variables_initializer() - self.sess.run(init) + # To keep track of training's performance + self.test_losses = [] + self.test_accuracies = [] + self.train_losses = [] + self.train_accuracies = [] - self.is_initialized = True + self.create_session() + + self.is_initialized = True return self + def create_graph(self): + self.graph = tf.Graph() + + def create_session(self): + self.sess = tf.Session(config=tf.ConfigProto(log_device_placement=True), graph=self.graph) + init = tf.global_variables_initializer() + self.sess.run(init) + def get_tensor_by_name(self, name): - return tf.get_default_graph().get_tensor_by_name("{0}/{1}:0".format(LSTM_RNN_VARIABLE_SCOPE, name)) + return self.graph.get_tensor_by_name("{0}/{1}:0".format(LSTM_RNN_VARIABLE_SCOPE, name)) + + def get_graph(self): + return self.graph + + def get_session(self): + return self.sess def get_x_placeholder(self): - return self.get_tensor_by_name(self.x_name) + return self.get_tensor_by_name(X_NAME) def get_y_placeholder(self): - return self.get_tensor_by_name(self.y_name) + return self.get_tensor_by_name(Y_NAME) def teardown(self): if self.sess is not None: self.sess.close() + self.is_initialized = False def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': if not isinstance(data_inputs, np.ndarray): @@ -126,13 +153,8 @@ def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': if not isinstance(expected_outputs, np.ndarray): expected_outputs = np.array(expected_outputs) - expected_outputs = np.reshape(expected_outputs, (self.hyperparams['batch_size'], self.hyperparams['n_classes'])) - - # shape x : (?, 128, 9) - # shape y : (?, 6) - - # shape data_inputs : (1500, 128, 9) - # shape expected_outputs : (1500, 6) + if expected_outputs.shape != (len(data_inputs), self.hyperparams['n_classes']): + expected_outputs = np.reshape(expected_outputs, (len(data_inputs), self.hyperparams['n_classes'])) with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): _, loss, acc = self.sess.run( @@ -148,12 +170,17 @@ def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': print("Batch Loss = " + "{:.6f}".format(loss) + ", Accuracy = {}".format(acc)) + self.is_invalidated = True + return self def transform(self, data_inputs): + if not isinstance(data_inputs, np.ndarray): + data_inputs = np.array(data_inputs) + with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): outputs = self.sess.run( - [self.get_tensor_by_name(self.pred_name)], + [self.get_tensor_by_name(PRED_NAME)], feed_dict={ self.get_x_placeholder(): data_inputs } From d5e402050565abc0c29494f89b814e5ed6c66bc8 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Mon, 4 Nov 2019 00:09:58 -0500 Subject: [PATCH 12/30] Add Demonstration Notebook For Pipeline Saving, And Loading For Api Serving --- LSTM_new.ipynb | 691 +++++++++++++++++++++++++++++++++++++++++++++++++ new.py | 9 +- 2 files changed, 692 insertions(+), 8 deletions(-) create mode 100644 LSTM_new.ipynb diff --git a/LSTM_new.ipynb b/LSTM_new.ipynb new file mode 100644 index 0000000..6cdfac7 --- /dev/null +++ b/LSTM_new.ipynb @@ -0,0 +1,691 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import os\n", + "import numpy as np\n", + "import tensorflow as tf\n", + "\n", + "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", + " TRAIN_FILE_NAME, TEST_FILE_NAME\n", + "from neuraxle.api.flask import FlaskRestApiWrapper\n", + "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", + "from neuraxle.hyperparams.space import HyperparameterSamples\n", + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", + "from steps.one_hot_encoder import OneHotEncoder\n", + "\n", + "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n", + "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", + "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", + "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", + "from steps.one_hot_encoder import OneHotEncoder\n", + "from steps.transform_expected_output_wrapper import OutputTransformerWrapper\n", + "\n", + "# TODO: move in a package neuraxle-tensorflow \n", + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Download Data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", + "cache\t\t LSTM_files\t new.py README.md\t\tvenv\n", + "data\t\t LSTM.ipynb\t old.py requirements.txt\n", + "data_reading.py LSTM_new.ipynb pipeline.py savers\n", + "LICENSE\t\t neuraxle\t __pycache__ steps\n", + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", + " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", + " __MACOSX\t 'UCI HAR Dataset'\n", + "\n", + "Downloading...\n", + "Dataset already downloaded. Did not download twice.\n", + "\n", + "Extracting...\n", + "Dataset already extracted. Did not extract twice.\n", + "\n", + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", + " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", + " __MACOSX\t 'UCI HAR Dataset'\n", + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", + "cache\t\t LSTM_files\t new.py README.md\t\tvenv\n", + "data\t\t LSTM.ipynb\t old.py requirements.txt\n", + "data_reading.py LSTM_new.ipynb pipeline.py savers\n", + "LICENSE\t\t neuraxle\t __pycache__ steps\n", + "\n", + "Dataset is now located at: data/UCI HAR Dataset/\n" + ] + } + ], + "source": [ + "# Note: Linux bash commands start with a \"!\" inside those \"ipython notebook\" cells\n", + "\n", + "DATA_PATH = \"data/\"\n", + "\n", + "!pwd && ls\n", + "os.chdir(DATA_PATH)\n", + "!pwd && ls\n", + "\n", + "!python download_dataset.py\n", + "\n", + "!pwd && ls\n", + "os.chdir(\"..\")\n", + "!pwd && ls\n", + "\n", + "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", + "print(\"\\n\" + \"Dataset is now located at: \" + DATASET_PATH)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load data" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some useful info to get an insight on dataset's shape and normalisation:\n", + "(X shape, y shape, every X's mean, every X's standard deviation)\n", + "(2947, 128, 9) (2947, 1) 0.09913992 0.39567086\n", + "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" + ] + } + ], + "source": [ + "# Load \"X\" (the neural network's training and testing inputs)\n", + "\n", + "X_train = load_X(X_train_signals_paths)\n", + "X_test = load_X(X_test_signals_paths)\n", + "\n", + "# Load \"y\" (the neural network's training and testing outputs)\n", + "\n", + "y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME)\n", + "y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME)\n", + "\n", + "y_train = load_y(y_train_path)\n", + "y_test = load_y(y_test_path)\n", + "\n", + "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", + "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", + "print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test))\n", + "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# LSTM RNN Model Forward" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def tf_model_forward(pred_name, name_x, name_y, hyperparams):\n", + " # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters.\n", + " # Moreover, two LSTM cells are stacked which adds deepness to the neural network.\n", + " # Note, some code of this notebook is inspired from an slightly different\n", + " # RNN architecture used on another dataset, some of the credits goes to\n", + " # \"aymericdamien\" under the MIT license.\n", + " # (NOTE: This step could be greatly optimised by shaping the dataset once\n", + " # input shape: (batch_size, n_steps, n_input)\n", + "\n", + " # Graph input/output\n", + " x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_inputs']], name=name_x)\n", + " y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name=name_y)\n", + "\n", + " # Graph weights\n", + " weights = {\n", + " 'hidden': tf.Variable(\n", + " tf.random_normal([hyperparams['n_inputs'], hyperparams['n_hidden']])\n", + " ), # Hidden layer weights\n", + " 'out': tf.Variable(\n", + " tf.random_normal([hyperparams['n_hidden'], hyperparams['n_classes']], mean=1.0)\n", + " )\n", + " }\n", + "\n", + " biases = {\n", + " 'hidden': tf.Variable(\n", + " tf.random_normal([hyperparams['n_hidden']])\n", + " ),\n", + " 'out': tf.Variable(\n", + " tf.random_normal([hyperparams['n_classes']])\n", + " )\n", + " }\n", + "\n", + " data_inputs = tf.transpose(\n", + " x,\n", + " [1, 0, 2]) # permute n_steps and batch_size\n", + "\n", + " # Reshape to prepare input to hidden activation\n", + " data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_inputs']])\n", + " # new shape: (n_steps*batch_size, n_input)\n", + "\n", + " # ReLU activation, thanks to Yu Zhao for adding this improvement here:\n", + " _X = tf.nn.relu(\n", + " tf.matmul(data_inputs, weights['hidden']) + biases['hidden']\n", + " )\n", + "\n", + " # Split data because rnn cell needs a list of inputs for the RNN inner loop\n", + " _X = tf.split(_X, hyperparams['n_steps'], 0)\n", + " # new shape: n_steps * (batch_size, n_hidden)\n", + "\n", + " # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow\n", + " lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", + " lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", + " lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True)\n", + "\n", + " # Get LSTM cell output\n", + " outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32)\n", + "\n", + " # Get last time step's output feature for a \"many-to-one\" style classifier,\n", + " # as in the image describing RNNs at the top of this page\n", + " lstm_last_output = outputs[-1]\n", + "\n", + " # Linear activation\n", + " pred = tf.matmul(lstm_last_output, weights['out']) + biases['out']\n", + " return tf.identity(pred, name=pred_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neuraxle RNN TensorFlow Model Step" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "LSTM_RNN_VARIABLE_SCOPE = \"lstm_rnn\"\n", + "X_NAME = 'x'\n", + "Y_NAME = 'y'\n", + "PRED_NAME = 'pred'\n", + "\n", + "N_HIDDEN = 32\n", + "N_STEPS = 128\n", + "N_INPUTS = 9\n", + "LAMBDA_LOSS_AMOUNT = 0.0015\n", + "LEARNING_RATE = 0.0025\n", + "N_CLASSES = 6\n", + "BATCH_SIZE = 1500\n", + "\n", + "class ClassificationRNNTensorFlowModel(BaseStep):\n", + " HYPERPARAMS = HyperparameterSamples({\n", + " 'n_steps': N_STEPS, # 128 timesteps per series\n", + " 'n_inputs': N_INPUTS, # 9 input parameters per timestep\n", + " 'n_hidden': N_HIDDEN, # Hidden layer num of features\n", + " 'n_classes': N_CLASSES, # Total classes (should go up, or should go down)\n", + " 'learning_rate': LEARNING_RATE,\n", + " 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT,\n", + " 'batch_size': BATCH_SIZE\n", + " })\n", + "\n", + " def __init__(\n", + " self\n", + " ):\n", + " BaseStep.__init__(\n", + " self,\n", + " hyperparams=ClassificationRNNTensorFlowModel.HYPERPARAMS,\n", + " savers=[TensorflowV1StepSaver()]\n", + " )\n", + "\n", + " self.graph = None\n", + " self.sess = None\n", + " self.l2 = None\n", + " self.cost = None\n", + " self.optimizer = None\n", + " self.correct_pred = None\n", + " self.accuracy = None\n", + " self.test_losses = None\n", + " self.test_accuracies = None\n", + " self.train_losses = None\n", + " self.train_accuracies = None\n", + "\n", + " def strip(self):\n", + " self.sess = None\n", + " self.graph = None\n", + " self.l2 = None\n", + " self.cost = None\n", + " self.optimizer = None\n", + " self.correct_pred = None\n", + " self.accuracy = None\n", + "\n", + " def setup(self) -> BaseStep:\n", + " if self.is_initialized:\n", + " return self\n", + "\n", + " self.create_graph()\n", + "\n", + " with self.graph.as_default():\n", + " # Launch the graph\n", + " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", + "\n", + " pred = tf_model_forward(PRED_NAME, X_NAME, Y_NAME, self.hyperparams)\n", + "\n", + " # Loss, optimizer and evaluation\n", + " # L2 loss prevents this overkill neural network to overfit the data\n", + "\n", + " l2 = self.hyperparams['lambda_loss_amount'] * sum(\n", + " tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables()\n", + " )\n", + "\n", + " # Softmax loss\n", + " self.cost = tf.reduce_mean(\n", + " tf.nn.softmax_cross_entropy_with_logits(\n", + " labels=self.get_y_placeholder(),\n", + " logits=pred\n", + " )\n", + " ) + l2\n", + "\n", + " # Adam Optimizer\n", + " self.optimizer = tf.train.AdamOptimizer(\n", + " learning_rate=self.hyperparams['learning_rate']\n", + " ).minimize(self.cost)\n", + "\n", + " self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(Y_NAME), 1))\n", + " self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32))\n", + "\n", + " # To keep track of training's performance\n", + " self.test_losses = []\n", + " self.test_accuracies = []\n", + " self.train_losses = []\n", + " self.train_accuracies = []\n", + "\n", + " self.create_session()\n", + "\n", + " self.is_initialized = True\n", + "\n", + " return self\n", + "\n", + " def create_graph(self):\n", + " self.graph = tf.Graph()\n", + "\n", + " def create_session(self):\n", + " self.sess = tf.Session(config=tf.ConfigProto(log_device_placement=True), graph=self.graph)\n", + " init = tf.global_variables_initializer()\n", + " self.sess.run(init)\n", + "\n", + " def get_tensor_by_name(self, name):\n", + " return self.graph.get_tensor_by_name(\"{0}/{1}:0\".format(LSTM_RNN_VARIABLE_SCOPE, name))\n", + "\n", + " def get_graph(self):\n", + " return self.graph\n", + "\n", + " def get_session(self):\n", + " return self.sess\n", + "\n", + " def get_x_placeholder(self):\n", + " return self.get_tensor_by_name(X_NAME)\n", + "\n", + " def get_y_placeholder(self):\n", + " return self.get_tensor_by_name(Y_NAME)\n", + "\n", + " def teardown(self):\n", + " if self.sess is not None:\n", + " self.sess.close()\n", + " self.is_initialized = False\n", + "\n", + " def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep':\n", + " if not isinstance(data_inputs, np.ndarray):\n", + " data_inputs = np.array(data_inputs)\n", + "\n", + " if not isinstance(expected_outputs, np.ndarray):\n", + " expected_outputs = np.array(expected_outputs)\n", + "\n", + " if expected_outputs.shape != (len(data_inputs), self.hyperparams['n_classes']):\n", + " expected_outputs = np.reshape(expected_outputs, (len(data_inputs), self.hyperparams['n_classes']))\n", + "\n", + " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", + " _, loss, acc = self.sess.run(\n", + " [self.optimizer, self.cost, self.accuracy],\n", + " feed_dict={\n", + " self.get_x_placeholder(): data_inputs,\n", + " self.get_y_placeholder(): expected_outputs\n", + " }\n", + " )\n", + "\n", + " self.train_losses.append(loss)\n", + " self.train_accuracies.append(acc)\n", + "\n", + " print(\"Batch Loss = \" + \"{:.6f}\".format(loss) + \", Accuracy = {}\".format(acc))\n", + "\n", + " self.is_invalidated = True\n", + "\n", + " return self\n", + "\n", + " def transform(self, data_inputs):\n", + " if not isinstance(data_inputs, np.ndarray):\n", + " data_inputs = np.array(data_inputs)\n", + "\n", + " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", + " outputs = self.sess.run(\n", + " [self.get_tensor_by_name(PRED_NAME)],\n", + " feed_dict={\n", + " self.get_x_placeholder(): data_inputs\n", + " }\n", + " )[0]\n", + " return outputs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neuraxle Pipeline " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline):\n", + " def __init__(self):\n", + " MiniBatchSequentialPipeline.__init__(self, [\n", + " OutputTransformerWrapper(OneHotEncoder(nb_columns=N_CLASSES, name='one_hot_encoded_label')),\n", + " ClassificationRNNTensorFlowModel(),\n", + " Joiner(batch_size=BATCH_SIZE)\n", + " ])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Train Pipeline " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:\n", + "The TensorFlow contrib module will not be included in TensorFlow 2.0.\n", + "For more information, please see:\n", + " * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n", + " * https://github.com/tensorflow/addons\n", + " * https://github.com/tensorflow/io (for I/O related ops)\n", + "If you depend on functionality not listed there, please file an issue.\n", + "\n", + "WARNING:tensorflow:From :51: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.\n", + "WARNING:tensorflow:From :53: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "This class is equivalent as tf.keras.layers.StackedRNNCells, and will be replaced by that in Tensorflow 2.0.\n", + "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Please use `layer.add_weight` method instead.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Call initializer instance with the dtype argument instead of passing it to the constructor\n", + "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "\n", + "Future major versions of TensorFlow will allow gradients to flow\n", + "into the labels input on backprop by default.\n", + "\n", + "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n", + "\n", + "Device mapping:\n", + "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", + "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", + "\n", + "Batch Loss = 2.826632, Accuracy = 0.14666666090488434\n", + "Batch Loss = 2.374201, Accuracy = 0.18933333456516266\n", + "Batch Loss = 2.165148, Accuracy = 0.4359999895095825\n", + "Batch Loss = 1.991644, Accuracy = 0.4806666672229767\n", + "Batch Loss = 1.997708, Accuracy = 0.4423076808452606\n", + "Batch Loss = 1.869701, Accuracy = 0.5046666860580444\n", + "Batch Loss = 1.734456, Accuracy = 0.6193333268165588\n", + "Batch Loss = 1.714716, Accuracy = 0.6439999938011169\n", + "Batch Loss = 1.639421, Accuracy = 0.625333309173584\n", + "Batch Loss = 1.722513, Accuracy = 0.6220414042472839\n", + "Batch Loss = 1.579464, Accuracy = 0.6613333225250244\n", + "Batch Loss = 1.476428, Accuracy = 0.6806666851043701\n", + "Batch Loss = 1.615788, Accuracy = 0.6240000128746033\n", + "Batch Loss = 1.343344, Accuracy = 0.7386666536331177\n", + "Batch Loss = 1.500012, Accuracy = 0.692307710647583\n", + "Batch Loss = 1.401856, Accuracy = 0.7026666402816772\n", + "Batch Loss = 1.282448, Accuracy = 0.7633333206176758\n", + "Batch Loss = 1.370500, Accuracy = 0.699999988079071\n", + "Batch Loss = 1.207804, Accuracy = 0.7706666588783264\n", + "Batch Loss = 1.350325, Accuracy = 0.7285503149032593\n", + "Batch Loss = 1.231125, Accuracy = 0.7706666588783264\n", + "Batch Loss = 1.201041, Accuracy = 0.7586666941642761\n", + "Batch Loss = 1.270912, Accuracy = 0.6613333225250244\n", + "Batch Loss = 1.133742, Accuracy = 0.7826666831970215\n", + "Batch Loss = 1.268553, Accuracy = 0.735946774482727\n", + "Batch Loss = 1.199111, Accuracy = 0.7879999876022339\n", + "Batch Loss = 1.159183, Accuracy = 0.7919999957084656\n", + "Batch Loss = 1.193569, Accuracy = 0.7286666631698608\n", + "Batch Loss = 1.117007, Accuracy = 0.7773333191871643\n", + "Batch Loss = 1.243366, Accuracy = 0.7477810382843018\n", + "Batch Loss = 1.116806, Accuracy = 0.8186666369438171\n", + "Batch Loss = 1.150177, Accuracy = 0.7680000066757202\n", + "Batch Loss = 1.173215, Accuracy = 0.7059999704360962\n", + "Batch Loss = 1.129342, Accuracy = 0.7846666574478149\n", + "Batch Loss = 1.159069, Accuracy = 0.7573964595794678\n", + "Batch Loss = 1.121240, Accuracy = 0.8006666898727417\n", + "Batch Loss = 1.138364, Accuracy = 0.7906666398048401\n", + "Batch Loss = 1.093842, Accuracy = 0.7919999957084656\n", + "Batch Loss = 1.105886, Accuracy = 0.8046666383743286\n", + "Batch Loss = 1.180819, Accuracy = 0.7625739574432373\n", + "Batch Loss = 1.024310, Accuracy = 0.8519999980926514\n", + "Batch Loss = 1.114419, Accuracy = 0.781333327293396\n", + "Batch Loss = 1.167690, Accuracy = 0.7113333344459534\n", + "Batch Loss = 0.947682, Accuracy = 0.8713333606719971\n", + "Batch Loss = 1.112047, Accuracy = 0.8062130212783813\n", + "Batch Loss = 1.010290, Accuracy = 0.8413333296775818\n", + "Batch Loss = 1.006666, Accuracy = 0.8386666774749756\n", + "Batch Loss = 1.061410, Accuracy = 0.7879999876022339\n", + "Batch Loss = 1.058079, Accuracy = 0.8353333473205566\n", + "Batch Loss = 1.062428, Accuracy = 0.8136094808578491\n", + "Batch Loss = 0.964536, Accuracy = 0.8786666393280029\n", + "Batch Loss = 1.000171, Accuracy = 0.8640000224113464\n", + "Batch Loss = 1.108438, Accuracy = 0.7773333191871643\n", + "Batch Loss = 1.010710, Accuracy = 0.8433333039283752\n", + "Batch Loss = 1.026997, Accuracy = 0.8321005702018738\n", + "Batch Loss = 0.948604, Accuracy = 0.887333333492279\n", + "Batch Loss = 1.076846, Accuracy = 0.8413333296775818\n", + "Batch Loss = 0.994279, Accuracy = 0.840666651725769\n", + "Batch Loss = 1.182574, Accuracy = 0.8053333163261414\n", + "Batch Loss = 1.063531, Accuracy = 0.8217455744743347\n", + "Batch Loss = 0.949576, Accuracy = 0.8766666650772095\n", + "Batch Loss = 1.011246, Accuracy = 0.8533333539962769\n", + "Batch Loss = 1.191805, Accuracy = 0.7706666588783264\n", + "Batch Loss = 0.935223, Accuracy = 0.9013333320617676\n", + "Batch Loss = 0.997599, Accuracy = 0.8454142212867737\n", + "Batch Loss = 0.979652, Accuracy = 0.8613333106040955\n", + "Batch Loss = 0.922143, Accuracy = 0.8799999952316284\n", + "Batch Loss = 1.012194, Accuracy = 0.7946666479110718\n", + "Batch Loss = 0.924530, Accuracy = 0.8820000290870667\n", + "Batch Loss = 0.947285, Accuracy = 0.875\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "HumanActivityRecognitionPipeline\n", + "(\n", + "\tHumanActivityRecognitionPipeline(\n", + "\tname=HumanActivityRecognitionPipeline,\n", + "\thyperparameters=HyperparameterSamples()\n", + ")(\n", + "\t\t[('OutputTransformerWrapper',\n", + " OutputTransformerWrapper(\n", + "\twrapped=OneHotEncoder(\n", + "\tname=one_hot_encoded_label,\n", + "\thyperparameters=HyperparameterSamples()\n", + "),\n", + "\thyperparameters=HyperparameterSamples()\n", + ")),\n", + " ('ClassificationRNNTensorFlowModel',\n", + " ClassificationRNNTensorFlowModel(\n", + "\tname=ClassificationRNNTensorFlowModel,\n", + "\thyperparameters=HyperparameterSamples([('n_steps', 128),\n", + " ('n_inputs', 9),\n", + " ('n_hidden', 32),\n", + " ('n_classes', 6),\n", + " ('learning_rate', 0.0025),\n", + " ('lambda_loss_amount', 0.0015),\n", + " ('batch_size', 1500)])\n", + ")),\n", + " ('Joiner', Joiner(\n", + "\tname=Joiner,\n", + "\thyperparameters=HyperparameterSamples()\n", + "))]\t\n", + ")\n", + ")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "training_data_count = len(X_train)\n", + "training_iters = training_data_count * 3\n", + "\n", + "pipeline = HumanActivityRecognitionPipeline()\n", + "\n", + "no_iter = int(math.floor(training_iters / BATCH_SIZE))\n", + "for _ in range(no_iter):\n", + " pipeline, outputs = pipeline.fit_transform(X_train, y_train)\n", + "\n", + "pipeline.save(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", + "\n", + "pipeline.teardown()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Serve Rest Api" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Device mapping:\n", + "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", + "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", + "\n", + "INFO:tensorflow:Restoring parameters from /home/alexandre/Documents/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n", + "Batch Loss = 0.896259, Accuracy = 0.8966666460037231\n", + "Batch Loss = 0.965855, Accuracy = 0.8573333621025085\n", + "Batch Loss = 1.054638, Accuracy = 0.8046666383743286\n", + "Batch Loss = 0.829448, Accuracy = 0.9120000004768372\n", + "Batch Loss = 0.944326, Accuracy = 0.8528106212615967\n", + " * Serving Flask app \"neuraxle.api.flask\" (lazy loading)\n", + " * Environment: production\n", + " WARNING: This is a development server. Do not use it in a production deployment.\n", + " Use a production WSGI server instead.\n", + " * Debug mode: off\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n" + ] + } + ], + "source": [ + "pipeline = HumanActivityRecognitionPipeline()\n", + "\n", + "pipeline = pipeline.load(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", + "\n", + "pipeline, outputs = pipeline.fit_transform(X_train, y_train)\n", + "\n", + "app = FlaskRestApiWrapper(\n", + " json_decoder=CustomJSONDecoderFor2DArray(),\n", + " wrapped=pipeline,\n", + " json_encoder=CustomJSONEncoderOfOutputs()\n", + ").get_app()\n", + "\n", + "app.run(debug=False, port=5000)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Human Activity Recognition", + "language": "python", + "name": "human-activity-recognition" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/new.py b/new.py index 585f94c..1b96659 100644 --- a/new.py +++ b/new.py @@ -1,7 +1,6 @@ # Those are separate normalised input features for the neural network import math import os - import numpy as np from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \ @@ -41,13 +40,7 @@ def main(): for _ in range(no_iter): pipeline, outputs = pipeline.fit_transform(X_train, y_train) - pipeline.save( - ExecutionContext.create_from_root( - pipeline, - ExecutionMode.FIT, - DEFAULT_CACHE_FOLDER - ) - ) + pipeline.save(ExecutionContext.create_from_root( pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER)) pipeline.teardown() From 84b360b940a18b2f32468d2c8833a30672da43f9 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Mon, 4 Nov 2019 18:45:15 -0500 Subject: [PATCH 13/30] Add Neuraxle Commit Version To Requirements.txt --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index d7dd4d7..3adc77a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,5 @@ tensorflow==1.15 conv==0.2 flask matplotlib +git+git://github.com/alexbrillant/Neuraxle@one-hot-encoder-step#egg=Neuraxle + From 5c6ec1d6eb043bc8be251442617e109228785730 Mon Sep 17 00:00:00 2001 From: guillaume-chevalier Date: Mon, 4 Nov 2019 19:26:34 -0500 Subject: [PATCH 14/30] Update requirements to avoid numpy version failure, and add example API call notebook. --- Call-API.ipynb | 169 +++++++++ LSTM.ipynb | 947 ----------------------------------------------- requirements.txt | 4 - 3 files changed, 169 insertions(+), 951 deletions(-) create mode 100644 Call-API.ipynb delete mode 100644 LSTM.ipynb diff --git a/Call-API.ipynb b/Call-API.ipynb new file mode 100644 index 0000000..d184ee9 --- /dev/null +++ b/Call-API.ipynb @@ -0,0 +1,169 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TODO: import *\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", + " TRAIN_FILE_NAME, TEST_FILE_NAME\n", + "\n", + "DATA_PATH = \"data/\"\n", + "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", + "\n", + "# Load \"X\" (the neural network's training and testing inputs)\n", + "\n", + "# X_train = load_X(X_train_signals_paths)\n", + "X_test = load_X(X_test_signals_paths)\n", + "\n", + "# Load \"y\" (the neural network's training and testing outputs)\n", + "\n", + "# y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME)\n", + "y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME)\n", + "\n", + "# y_train = load_y(y_train_path)\n", + "y_test = load_y(y_test_path)\n", + "\n", + "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", + "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", + "print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test))\n", + "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "class APICaller(NonFittableStep, BaseStep):\n", + " # TODO: use urllib code here.\n", + " pass\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p = Pipeline(\n", + " json_encoder=CustomJSONEncoderOfOutputs(),\n", + " wrapped=APICaller(url=\"http://localhost:5000/\"),\n", + " json_decoder=CustomJSONDecoderFor2DArray()\n", + ")\n", + "y_pred = p.predict(X_test)\n", + "\n", + "# TODO: \n", + "# y_test = y_test.argmax(1) ???? is this already made?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# (Inline plots: )\n", + "%matplotlib inline\n", + "\n", + "font = {\n", + " 'family' : 'Bitstream Vera Sans',\n", + " 'weight' : 'bold',\n", + " 'size' : 18\n", + "}\n", + "matplotlib.rc('font', **font)\n", + "\n", + "# Results\n", + "\n", + "predictions = y_pred.argmax(1)\n", + "\n", + "print(\"Testing Accuracy: {}%\".format(100*accuracy))\n", + "\n", + "print(\"\")\n", + "print(\"Precision: {}%\".format(100*metrics.precision_score(y_test, predictions, average=\"weighted\")))\n", + "print(\"Recall: {}%\".format(100*metrics.recall_score(y_test, predictions, average=\"weighted\")))\n", + "print(\"f1_score: {}%\".format(100*metrics.f1_score(y_test, predictions, average=\"weighted\")))\n", + "\n", + "print(\"\")\n", + "print(\"Confusion Matrix:\")\n", + "confusion_matrix = metrics.confusion_matrix(y_test, predictions)\n", + "print(confusion_matrix)\n", + "normalised_confusion_matrix = np.array(confusion_matrix, dtype=np.float32)/np.sum(confusion_matrix)*100\n", + "\n", + "print(\"\")\n", + "print(\"Confusion matrix (normalised to % of total test data):\")\n", + "print(normalised_confusion_matrix)\n", + "print(\"Note: training and testing data is not equally distributed amongst classes, \")\n", + "print(\"so it is normal that more than a 6th of the data is correctly classifier in the last category.\")\n", + "\n", + "# Plot Results:\n", + "width = 12\n", + "height = 12\n", + "plt.figure(figsize=(width, height))\n", + "plt.imshow(\n", + " normalised_confusion_matrix,\n", + " interpolation='nearest',\n", + " cmap=plt.cm.rainbow\n", + ")\n", + "plt.title(\"Confusion matrix \\n(normalised to % of total test data)\")\n", + "plt.colorbar()\n", + "tick_marks = np.arange(n_classes)\n", + "plt.xticks(tick_marks, LABELS, rotation=90)\n", + "plt.yticks(tick_marks, LABELS)\n", + "plt.tight_layout()\n", + "plt.ylabel('True label')\n", + "plt.xlabel('Predicted label')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p.teardown()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/LSTM.ipynb b/LSTM.ipynb deleted file mode 100644 index 29a57ad..0000000 --- a/LSTM.ipynb +++ /dev/null @@ -1,947 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# LSTMs for Human Activity Recognition\n", - "\n", - "Human Activity Recognition (HAR) using smartphones dataset and an LSTM RNN. Classifying the type of movement amongst six categories:\n", - "- WALKING,\n", - "- WALKING_UPSTAIRS,\n", - "- WALKING_DOWNSTAIRS,\n", - "- SITTING,\n", - "- STANDING,\n", - "- LAYING.\n", - "\n", - "Compared to a classical approach, using a Recurrent Neural Networks (RNN) with Long Short-Term Memory cells (LSTMs) require no or almost no feature engineering. Data can be fed directly into the neural network who acts like a black box, modeling the problem correctly. [Other research](https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.names) on the activity recognition dataset can use a big amount of feature engineering, which is rather a signal processing approach combined with classical data science techniques. The approach here is rather very simple in terms of how much was the data preprocessed. \n", - "\n", - "Let's use Google's neat Deep Learning library, TensorFlow, demonstrating the usage of an LSTM, a type of Artificial Neural Network that can process sequential data / time series. \n", - "\n", - "## Video dataset overview\n", - "\n", - "Follow this link to see a video of the 6 activities recorded in the experiment with one of the participants:\n", - "\n", - "

\n", - " \n", - "

[Watch video]
\n", - "

\n", - "\n", - "## Details about the input data\n", - "\n", - "I will be using an LSTM on the data to learn (as a cellphone attached on the waist) to recognise the type of activity that the user is doing. The dataset's description goes like this:\n", - "\n", - "> The sensor signals (accelerometer and gyroscope) were pre-processed by applying noise filters and then sampled in fixed-width sliding windows of 2.56 sec and 50% overlap (128 readings/window). The sensor acceleration signal, which has gravitational and body motion components, was separated using a Butterworth low-pass filter into body acceleration and gravity. The gravitational force is assumed to have only low frequency components, therefore a filter with 0.3 Hz cutoff frequency was used. \n", - "\n", - "That said, I will use the almost raw data: only the gravity effect has been filtered out of the accelerometer as a preprocessing step for another 3D feature as an input to help learning. If you'd ever want to extract the gravity by yourself, you could fork my code on using a [Butterworth Low-Pass Filter (LPF) in Python](https://github.com/guillaume-chevalier/filtering-stft-and-laplace-transform) and edit it to have the right cutoff frequency of 0.3 Hz which is a good frequency for activity recognition from body sensors.\n", - "\n", - "## What is an RNN?\n", - "\n", - "As explained in [this article](http://karpathy.github.io/2015/05/21/rnn-effectiveness/), an RNN takes many input vectors to process them and output other vectors. It can be roughly pictured like in the image below, imagining each rectangle has a vectorial depth and other special hidden quirks in the image below. **In our case, the \"many to one\" architecture is used**: we accept time series of [feature vectors](https://www.quora.com/What-do-samples-features-time-steps-mean-in-LSTM/answer/Guillaume-Chevalier-2) (one vector per [time step](https://www.quora.com/What-do-samples-features-time-steps-mean-in-LSTM/answer/Guillaume-Chevalier-2)) to convert them to a probability vector at the output for classification. Note that a \"one to one\" architecture would be a standard feedforward neural network. \n", - "\n", - "> \n", - "> http://karpathy.github.io/2015/05/21/rnn-effectiveness/\n", - "\n", - "## What is an LSTM?\n", - "\n", - "An LSTM is an improved RNN. It is more complex, but easier to train, avoiding what is called the vanishing gradient problem. I recommend [this article](http://colah.github.io/posts/2015-08-Understanding-LSTMs/) for you to learn more on LSTMs.\n", - "\n", - "\n", - "## Results \n", - "\n", - "Scroll on! Nice visuals awaits. " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# All Includes\n", - "\n", - "import numpy as np\n", - "import matplotlib\n", - "import matplotlib.pyplot as plt\n", - "import tensorflow as tf # Version 1.0.0 (some previous versions are used in past commits)\n", - "from sklearn import metrics\n", - "\n", - "import os" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "# Useful Constants\n", - "\n", - "# Those are separate normalised input features for the neural network\n", - "INPUT_SIGNAL_TYPES = [\n", - " \"body_acc_x_\",\n", - " \"body_acc_y_\",\n", - " \"body_acc_z_\",\n", - " \"body_gyro_x_\",\n", - " \"body_gyro_y_\",\n", - " \"body_gyro_z_\",\n", - " \"total_acc_x_\",\n", - " \"total_acc_y_\",\n", - " \"total_acc_z_\"\n", - "]\n", - "\n", - "# Output classes to learn how to classify\n", - "LABELS = [\n", - " \"WALKING\", \n", - " \"WALKING_UPSTAIRS\", \n", - " \"WALKING_DOWNSTAIRS\", \n", - " \"SITTING\", \n", - " \"STANDING\", \n", - " \"LAYING\"\n", - "] \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Let's start by downloading the data: " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/home/ubuntu/pynb/LSTM-Human-Activity-Recognition\n", - "data\t LSTM_files LSTM_OLD.ipynb README.md\n", - "LICENSE LSTM.ipynb lstm.py\t screenlog.0\n", - "/home/ubuntu/pynb/LSTM-Human-Activity-Recognition/data\n", - "download_dataset.py source.txt\n", - "\n", - "Downloading...\n", - "--2017-05-24 01:49:53-- https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.zip\n", - "Resolving archive.ics.uci.edu (archive.ics.uci.edu)... 128.195.10.249\n", - "Connecting to archive.ics.uci.edu (archive.ics.uci.edu)|128.195.10.249|:443... connected.\n", - "HTTP request sent, awaiting response... 200 OK\n", - "Length: 60999314 (58M) [application/zip]\n", - "Saving to: ‘UCI HAR Dataset.zip’\n", - "\n", - "100%[======================================>] 60,999,314 1.69MB/s in 38s \n", - "\n", - "2017-05-24 01:50:31 (1.55 MB/s) - ‘UCI HAR Dataset.zip’ saved [60999314/60999314]\n", - "\n", - "Downloading done.\n", - "\n", - "Extracting...\n", - "Extracting successfully done to /home/ubuntu/pynb/LSTM-Human-Activity-Recognition/data/UCI HAR Dataset.\n", - "/home/ubuntu/pynb/LSTM-Human-Activity-Recognition/data\n", - "download_dataset.py __MACOSX source.txt UCI HAR Dataset UCI HAR Dataset.zip\n", - "/home/ubuntu/pynb/LSTM-Human-Activity-Recognition\n", - "data\t LSTM_files LSTM_OLD.ipynb README.md\n", - "LICENSE LSTM.ipynb lstm.py\t screenlog.0\n", - "\n", - "Dataset is now located at: data/UCI HAR Dataset/\n" - ] - } - ], - "source": [ - "# Note: Linux bash commands start with a \"!\" inside those \"ipython notebook\" cells\n", - "\n", - "DATA_PATH = \"data/\"\n", - "\n", - "!pwd && ls\n", - "os.chdir(DATA_PATH)\n", - "!pwd && ls\n", - "\n", - "!python download_dataset.py\n", - "\n", - "!pwd && ls\n", - "os.chdir(\"..\")\n", - "!pwd && ls\n", - "\n", - "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", - "print(\"\\n\" + \"Dataset is now located at: \" + DATASET_PATH)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Preparing dataset:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "TRAIN = \"train/\"\n", - "TEST = \"test/\"\n", - "\n", - "\n", - "# Load \"X\" (the neural network's training and testing inputs)\n", - "\n", - "def load_X(X_signals_paths):\n", - " X_signals = []\n", - " \n", - " for signal_type_path in X_signals_paths:\n", - " file = open(signal_type_path, 'r')\n", - " # Read dataset from disk, dealing with text files' syntax\n", - " X_signals.append(\n", - " [np.array(serie, dtype=np.float32) for serie in [\n", - " row.replace(' ', ' ').strip().split(' ') for row in file\n", - " ]]\n", - " )\n", - " file.close()\n", - " \n", - " return np.transpose(np.array(X_signals), (1, 2, 0))\n", - "\n", - "X_train_signals_paths = [\n", - " DATASET_PATH + TRAIN + \"Inertial Signals/\" + signal + \"train.txt\" for signal in INPUT_SIGNAL_TYPES\n", - "]\n", - "X_test_signals_paths = [\n", - " DATASET_PATH + TEST + \"Inertial Signals/\" + signal + \"test.txt\" for signal in INPUT_SIGNAL_TYPES\n", - "]\n", - "\n", - "X_train = load_X(X_train_signals_paths)\n", - "X_test = load_X(X_test_signals_paths)\n", - "\n", - "\n", - "# Load \"y\" (the neural network's training and testing outputs)\n", - "\n", - "def load_y(y_path):\n", - " file = open(y_path, 'r')\n", - " # Read dataset from disk, dealing with text file's syntax\n", - " y_ = np.array(\n", - " [elem for elem in [\n", - " row.replace(' ', ' ').strip().split(' ') for row in file\n", - " ]], \n", - " dtype=np.int32\n", - " )\n", - " file.close()\n", - " \n", - " # Substract 1 to each output class for friendly 0-based indexing \n", - " return y_ - 1\n", - "\n", - "y_train_path = DATASET_PATH + TRAIN + \"y_train.txt\"\n", - "y_test_path = DATASET_PATH + TEST + \"y_test.txt\"\n", - "\n", - "y_train = load_y(y_train_path)\n", - "y_test = load_y(y_test_path)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Additionnal Parameters:\n", - "\n", - "Here are some core parameter definitions for the training. \n", - "\n", - "For example, the whole neural network's structure could be summarised by enumerating those parameters and the fact that two LSTM are used one on top of another (stacked) output-to-input as hidden layers through time steps. " - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Some useful info to get an insight on dataset's shape and normalisation:\n", - "(X shape, y shape, every X's mean, every X's standard deviation)\n", - "(2947, 128, 9) (2947, 1) 0.0991399 0.395671\n", - "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" - ] - } - ], - "source": [ - "# Input Data \n", - "\n", - "training_data_count = len(X_train) # 7352 training series (with 50% overlap between each serie)\n", - "test_data_count = len(X_test) # 2947 testing series\n", - "n_steps = len(X_train[0]) # 128 timesteps per series\n", - "n_input = len(X_train[0][0]) # 9 input parameters per timestep\n", - "\n", - "\n", - "# LSTM Neural Network's internal structure\n", - "\n", - "n_hidden = 32 # Hidden layer num of features\n", - "n_classes = 6 # Total classes (should go up, or should go down)\n", - "\n", - "\n", - "# Training \n", - "\n", - "learning_rate = 0.0025\n", - "lambda_loss_amount = 0.0015\n", - "training_iters = training_data_count * 300 # Loop 300 times on the dataset\n", - "batch_size = 1500\n", - "display_iter = 30000 # To show test set accuracy during training\n", - "\n", - "\n", - "# Some debugging info\n", - "\n", - "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", - "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", - "print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test))\n", - "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Utility functions for training:" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "def LSTM_RNN(_X, _weights, _biases):\n", - " # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. \n", - " # Moreover, two LSTM cells are stacked which adds deepness to the neural network. \n", - " # Note, some code of this notebook is inspired from an slightly different \n", - " # RNN architecture used on another dataset, some of the credits goes to \n", - " # \"aymericdamien\" under the MIT license.\n", - "\n", - " # (NOTE: This step could be greatly optimised by shaping the dataset once\n", - " # input shape: (batch_size, n_steps, n_input)\n", - " _X = tf.transpose(_X, [1, 0, 2]) # permute n_steps and batch_size\n", - " # Reshape to prepare input to hidden activation\n", - " _X = tf.reshape(_X, [-1, n_input]) \n", - " # new shape: (n_steps*batch_size, n_input)\n", - " \n", - " # ReLU activation, thanks to Yu Zhao for adding this improvement here:\n", - " _X = tf.nn.relu(tf.matmul(_X, _weights['hidden']) + _biases['hidden'])\n", - " # Split data because rnn cell needs a list of inputs for the RNN inner loop\n", - " _X = tf.split(_X, n_steps, 0) \n", - " # new shape: n_steps * (batch_size, n_hidden)\n", - "\n", - " # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow\n", - " lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True)\n", - " lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True)\n", - " lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True)\n", - " # Get LSTM cell output\n", - " outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32)\n", - "\n", - " # Get last time step's output feature for a \"many-to-one\" style classifier, \n", - " # as in the image describing RNNs at the top of this page\n", - " lstm_last_output = outputs[-1]\n", - " \n", - " # Linear activation\n", - " return tf.matmul(lstm_last_output, _weights['out']) + _biases['out']\n", - "\n", - "\n", - "def extract_batch_size(_train, step, batch_size):\n", - " # Function to fetch a \"batch_size\" amount of data from \"(X|y)_train\" data. \n", - " \n", - " shape = list(_train.shape)\n", - " shape[0] = batch_size\n", - " batch_s = np.empty(shape)\n", - "\n", - " for i in range(batch_size):\n", - " # Loop index\n", - " index = ((step-1)*batch_size + i) % len(_train)\n", - " batch_s[i] = _train[index] \n", - "\n", - " return batch_s\n", - "\n", - "\n", - "def one_hot(y_, n_classes=n_classes):\n", - " # Function to encode neural one-hot output labels from number indexes \n", - " # e.g.: \n", - " # one_hot(y_=[[5], [0], [3]], n_classes=6):\n", - " # return [[0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0]]\n", - " \n", - " y_ = y_.reshape(len(y_))\n", - " return np.eye(n_classes)[np.array(y_, dtype=np.int32)] # Returns FLOATS\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Let's get serious and build the neural network:" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# Graph input/output\n", - "x = tf.placeholder(tf.float32, [None, n_steps, n_input])\n", - "y = tf.placeholder(tf.float32, [None, n_classes])\n", - "\n", - "# Graph weights\n", - "weights = {\n", - " 'hidden': tf.Variable(tf.random_normal([n_input, n_hidden])), # Hidden layer weights\n", - " 'out': tf.Variable(tf.random_normal([n_hidden, n_classes], mean=1.0))\n", - "}\n", - "biases = {\n", - " 'hidden': tf.Variable(tf.random_normal([n_hidden])),\n", - " 'out': tf.Variable(tf.random_normal([n_classes]))\n", - "}\n", - "\n", - "pred = LSTM_RNN(x, weights, biases)\n", - "\n", - "# Loss, optimizer and evaluation\n", - "l2 = lambda_loss_amount * sum(\n", - " tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables()\n", - ") # L2 loss prevents this overkill neural network to overfit the data\n", - "cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=pred)) + l2 # Softmax loss\n", - "optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # Adam Optimizer\n", - "\n", - "correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))\n", - "accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Hooray, now train the neural network:" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING:tensorflow:From :9: initialize_all_variables (from tensorflow.python.ops.variables) is deprecated and will be removed after 2017-03-02.\n", - "Instructions for updating:\n", - "Use `tf.global_variables_initializer` instead.\n", - "Training iter #1500: Batch Loss = 5.416760, Accuracy = 0.15266665816307068\n", - "PERFORMANCE ON TEST SET: Batch Loss = 4.880829811096191, Accuracy = 0.05632847175002098\n", - "Training iter #30000: Batch Loss = 3.031930, Accuracy = 0.607333242893219\n", - "PERFORMANCE ON TEST SET: Batch Loss = 3.0515167713165283, Accuracy = 0.6067186594009399\n", - "Training iter #60000: Batch Loss = 2.672764, Accuracy = 0.7386666536331177\n", - "PERFORMANCE ON TEST SET: Batch Loss = 2.780435085296631, Accuracy = 0.7027485370635986\n", - "Training iter #90000: Batch Loss = 2.378301, Accuracy = 0.8366667032241821\n", - "PERFORMANCE ON TEST SET: Batch Loss = 2.6019773483276367, Accuracy = 0.7617915868759155\n", - "Training iter #120000: Batch Loss = 2.127290, Accuracy = 0.9066667556762695\n", - "PERFORMANCE ON TEST SET: Batch Loss = 2.3625404834747314, Accuracy = 0.8116728663444519\n", - "Training iter #150000: Batch Loss = 1.929805, Accuracy = 0.9380000233650208\n", - "PERFORMANCE ON TEST SET: Batch Loss = 2.306251049041748, Accuracy = 0.8276212215423584\n", - "Training iter #180000: Batch Loss = 1.971904, Accuracy = 0.9153333902359009\n", - "PERFORMANCE ON TEST SET: Batch Loss = 2.0835530757904053, Accuracy = 0.8771631121635437\n", - "Training iter #210000: Batch Loss = 1.860249, Accuracy = 0.8613333702087402\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.9994492530822754, Accuracy = 0.8788597583770752\n", - "Training iter #240000: Batch Loss = 1.626292, Accuracy = 0.9380000233650208\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.879166603088379, Accuracy = 0.8944689035415649\n", - "Training iter #270000: Batch Loss = 1.582758, Accuracy = 0.9386667013168335\n", - "PERFORMANCE ON TEST SET: Batch Loss = 2.0341007709503174, Accuracy = 0.8361043930053711\n", - "Training iter #300000: Batch Loss = 1.620352, Accuracy = 0.9306666851043701\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.8185184001922607, Accuracy = 0.8639293313026428\n", - "Training iter #330000: Batch Loss = 1.474394, Accuracy = 0.9693333506584167\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.7638503313064575, Accuracy = 0.8747878670692444\n", - "Training iter #360000: Batch Loss = 1.406998, Accuracy = 0.9420000314712524\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.5946787595748901, Accuracy = 0.902273416519165\n", - "Training iter #390000: Batch Loss = 1.362515, Accuracy = 0.940000057220459\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.5285792350769043, Accuracy = 0.9046487212181091\n", - "Training iter #420000: Batch Loss = 1.252860, Accuracy = 0.9566667079925537\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.4635565280914307, Accuracy = 0.9107565879821777\n", - "Training iter #450000: Batch Loss = 1.190078, Accuracy = 0.9553333520889282\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.442753553390503, Accuracy = 0.9093992710113525\n", - "Training iter #480000: Batch Loss = 1.159610, Accuracy = 0.9446667432785034\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.4130011796951294, Accuracy = 0.8971834778785706\n", - "Training iter #510000: Batch Loss = 1.100551, Accuracy = 0.9593333601951599\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.3075592517852783, Accuracy = 0.9117745757102966\n", - "Training iter #540000: Batch Loss = 1.123470, Accuracy = 0.9240000247955322\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.2605488300323486, Accuracy = 0.9165251851081848\n", - "Training iter #570000: Batch Loss = 1.103454, Accuracy = 0.909333348274231\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.2327136993408203, Accuracy = 0.9009160399436951\n", - "Training iter #600000: Batch Loss = 1.083368, Accuracy = 0.8966666460037231\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.2683708667755127, Accuracy = 0.8890395164489746\n", - "Training iter #630000: Batch Loss = 0.939185, Accuracy = 0.9700000882148743\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.2147629261016846, Accuracy = 0.8866642713546753\n", - "Training iter #660000: Batch Loss = 0.881242, Accuracy = 0.9806667566299438\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.1068334579467773, Accuracy = 0.9151678681373596\n", - "Training iter #690000: Batch Loss = 0.831674, Accuracy = 0.9853334426879883\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.0885852575302124, Accuracy = 0.9121139645576477\n", - "Training iter #720000: Batch Loss = 0.866615, Accuracy = 0.9573334455490112\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.0513516664505005, Accuracy = 0.9158465266227722\n", - "Training iter #750000: Batch Loss = 0.858979, Accuracy = 0.940000057220459\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.0598633289337158, Accuracy = 0.9063453674316406\n", - "Training iter #780000: Batch Loss = 0.750040, Accuracy = 0.9593334197998047\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.010966420173645, Accuracy = 0.9155071973800659\n", - "Training iter #810000: Batch Loss = 0.732136, Accuracy = 0.9620000123977661\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.9865696430206299, Accuracy = 0.9161858558654785\n", - "Training iter #840000: Batch Loss = 0.758945, Accuracy = 0.9406667351722717\n", - "PERFORMANCE ON TEST SET: Batch Loss = 1.0347753763198853, Accuracy = 0.8958262205123901\n", - "Training iter #870000: Batch Loss = 0.710809, Accuracy = 0.9660000205039978\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.9786491990089417, Accuracy = 0.893111526966095\n", - "Training iter #900000: Batch Loss = 0.705978, Accuracy = 0.9553333520889282\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.9204542636871338, Accuracy = 0.9002374410629272\n", - "Training iter #930000: Batch Loss = 0.759181, Accuracy = 0.9066667556762695\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.9086415767669678, Accuracy = 0.9036307334899902\n", - "Training iter #960000: Batch Loss = 0.705333, Accuracy = 0.9286667108535767\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.850454568862915, Accuracy = 0.9080419540405273\n", - "Training iter #990000: Batch Loss = 0.599754, Accuracy = 0.9693333506584167\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.8451057076454163, Accuracy = 0.9114353656768799\n", - "Training iter #1020000: Batch Loss = 0.585689, Accuracy = 0.9700000286102295\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.8170899152755737, Accuracy = 0.9110959768295288\n", - "Training iter #1050000: Batch Loss = 0.553970, Accuracy = 0.984000027179718\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.8555561304092407, Accuracy = 0.9114352464675903\n", - "Training iter #1080000: Batch Loss = 0.601349, Accuracy = 0.9693334102630615\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.8512595891952515, Accuracy = 0.8781810998916626\n", - "Training iter #1110000: Batch Loss = 0.601967, Accuracy = 0.937999963760376\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.7551606297492981, Accuracy = 0.9087206721305847\n", - "Training iter #1140000: Batch Loss = 0.597223, Accuracy = 0.9353333711624146\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.7431289553642273, Accuracy = 0.909060001373291\n", - "Training iter #1170000: Batch Loss = 0.523300, Accuracy = 0.9500000476837158\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.745741605758667, Accuracy = 0.9093992710113525\n", - "Training iter #1200000: Batch Loss = 0.500816, Accuracy = 0.9600000381469727\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6978224515914917, Accuracy = 0.9138105511665344\n", - "Training iter #1230000: Batch Loss = 0.495834, Accuracy = 0.9546667337417603\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6866210699081421, Accuracy = 0.9178825616836548\n", - "Training iter #1260000: Batch Loss = 0.480467, Accuracy = 0.9813334345817566\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6883729100227356, Accuracy = 0.9100779294967651\n", - "Training iter #1290000: Batch Loss = 0.516874, Accuracy = 0.9326666593551636\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6925369501113892, Accuracy = 0.9032914042472839\n", - "Training iter #1320000: Batch Loss = 0.570053, Accuracy = 0.9080000519752502\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.743996798992157, Accuracy = 0.8978621363639832\n", - "Training iter #1350000: Batch Loss = 0.491792, Accuracy = 0.9580000638961792\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6543726921081543, Accuracy = 0.8951475024223328\n", - "Training iter #1380000: Batch Loss = 0.423705, Accuracy = 0.9760000705718994\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6256207227706909, Accuracy = 0.91788250207901\n", - "Training iter #1410000: Batch Loss = 0.399226, Accuracy = 0.9840000867843628\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6232836246490479, Accuracy = 0.9205971360206604\n", - "Training iter #1440000: Batch Loss = 0.415493, Accuracy = 0.972000002861023\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.6083709001541138, Accuracy = 0.9104173183441162\n", - "Training iter #1470000: Batch Loss = 0.499316, Accuracy = 0.9306666851043701\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5882848501205444, Accuracy = 0.9117745757102966\n", - "Training iter #1500000: Batch Loss = 0.478666, Accuracy = 0.9346666932106018\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5803182125091553, Accuracy = 0.91652512550354\n", - "Training iter #1530000: Batch Loss = 0.366041, Accuracy = 0.968666672706604\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5783829689025879, Accuracy = 0.9114352464675903\n", - "Training iter #1560000: Batch Loss = 0.377644, Accuracy = 0.9506667256355286\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5899279117584229, Accuracy = 0.9070240259170532\n", - "Training iter #1590000: Batch Loss = 0.485060, Accuracy = 0.9133333563804626\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.7430599927902222, Accuracy = 0.8649473190307617\n", - "Training iter #1620000: Batch Loss = 0.386228, Accuracy = 0.9633333683013916\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5264637470245361, Accuracy = 0.9070240259170532\n", - "Training iter #1650000: Batch Loss = 0.416933, Accuracy = 0.9193333983421326\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5343363881111145, Accuracy = 0.914489209651947\n", - "Training iter #1680000: Batch Loss = 0.421477, Accuracy = 0.9300000667572021\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5374469757080078, Accuracy = 0.9243297576904297\n", - "Training iter #1710000: Batch Loss = 0.403527, Accuracy = 0.9300000071525574\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5439008474349976, Accuracy = 0.905666708946228\n", - "Training iter #1740000: Batch Loss = 0.331851, Accuracy = 0.9753334522247314\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5405154228210449, Accuracy = 0.9093992710113525\n", - "Training iter #1770000: Batch Loss = 0.337737, Accuracy = 0.9780000448226929\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5582258701324463, Accuracy = 0.9026126861572266\n", - "Training iter #1800000: Batch Loss = 0.332086, Accuracy = 0.9600000381469727\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5655900835990906, Accuracy = 0.8995587825775146\n", - "Training iter #1830000: Batch Loss = 0.400998, Accuracy = 0.9480000734329224\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.47865116596221924, Accuracy = 0.9144891500473022\n", - "Training iter #1860000: Batch Loss = 0.364531, Accuracy = 0.9493333697319031\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.49166250228881836, Accuracy = 0.9158465266227722\n", - "Training iter #1890000: Batch Loss = 0.316529, Accuracy = 0.9593334197998047\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5186017751693726, Accuracy = 0.9104173183441162\n", - "Training iter #1920000: Batch Loss = 0.309109, Accuracy = 0.9626667499542236\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5222393274307251, Accuracy = 0.9002374410629272\n", - "Training iter #1950000: Batch Loss = 0.427720, Accuracy = 0.9193333387374878\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5457150340080261, Accuracy = 0.9070240259170532\n", - "Training iter #1980000: Batch Loss = 0.330174, Accuracy = 0.9526667594909668\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.5917137861251831, Accuracy = 0.8812350034713745\n", - "Training iter #2010000: Batch Loss = 0.371541, Accuracy = 0.906000018119812\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.53951495885849, Accuracy = 0.8802171349525452\n", - "Training iter #2040000: Batch Loss = 0.382413, Accuracy = 0.9206666946411133\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.42567864060401917, Accuracy = 0.9324736595153809\n", - "Training iter #2070000: Batch Loss = 0.342763, Accuracy = 0.9326667189598083\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.4292983412742615, Accuracy = 0.9273836612701416\n", - "Training iter #2100000: Batch Loss = 0.259442, Accuracy = 0.9873334169387817\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.44131210446357727, Accuracy = 0.9273836612701416\n", - "Training iter #2130000: Batch Loss = 0.284630, Accuracy = 0.9593333601951599\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.46982717514038086, Accuracy = 0.9093992710113525\n", - "Training iter #2160000: Batch Loss = 0.299012, Accuracy = 0.9686667323112488\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.48389002680778503, Accuracy = 0.9138105511665344\n", - "Training iter #2190000: Batch Loss = 0.287106, Accuracy = 0.9700000286102295\n", - "PERFORMANCE ON TEST SET: Batch Loss = 0.4670214056968689, Accuracy = 0.9216151237487793\n", - "Optimization Finished!\n", - "FINAL RESULT: Batch Loss = 0.45611169934272766, Accuracy = 0.9165252447128296\n" - ] - } - ], - "source": [ - "# To keep track of training's performance\n", - "test_losses = []\n", - "test_accuracies = []\n", - "train_losses = []\n", - "train_accuracies = []\n", - "\n", - "# Launch the graph\n", - "sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True))\n", - "init = tf.global_variables_initializer()\n", - "sess.run(init)\n", - "\n", - "# Perform Training steps with \"batch_size\" amount of example data at each loop\n", - "step = 1\n", - "while step * batch_size <= training_iters:\n", - " batch_xs = extract_batch_size(X_train, step, batch_size)\n", - " batch_ys = one_hot(extract_batch_size(y_train, step, batch_size))\n", - "\n", - " # Fit training using batch data\n", - " _, loss, acc = sess.run(\n", - " [optimizer, cost, accuracy],\n", - " feed_dict={\n", - " x: batch_xs, \n", - " y: batch_ys\n", - " }\n", - " )\n", - " train_losses.append(loss)\n", - " train_accuracies.append(acc)\n", - " \n", - " # Evaluate network only at some steps for faster training: \n", - " if (step*batch_size % display_iter == 0) or (step == 1) or (step * batch_size > training_iters):\n", - " \n", - " # To not spam console, show training accuracy/loss in this \"if\"\n", - " print(\"Training iter #\" + str(step*batch_size) + \\\n", - " \": Batch Loss = \" + \"{:.6f}\".format(loss) + \\\n", - " \", Accuracy = {}\".format(acc))\n", - " \n", - " # Evaluation on the test set (no learning made here - just evaluation for diagnosis)\n", - " loss, acc = sess.run(\n", - " [cost, accuracy], \n", - " feed_dict={\n", - " x: X_test,\n", - " y: one_hot(y_test)\n", - " }\n", - " )\n", - " test_losses.append(loss)\n", - " test_accuracies.append(acc)\n", - " print(\"PERFORMANCE ON TEST SET: \" + \\\n", - " \"Batch Loss = {}\".format(loss) + \\\n", - " \", Accuracy = {}\".format(acc))\n", - "\n", - " step += 1\n", - "\n", - "print(\"Optimization Finished!\")\n", - "\n", - "# Accuracy for test data\n", - "\n", - "one_hot_predictions, accuracy, final_loss = sess.run(\n", - " [pred, accuracy, cost],\n", - " feed_dict={\n", - " x: X_test,\n", - " y: one_hot(y_test)\n", - " }\n", - ")\n", - "\n", - "test_losses.append(final_loss)\n", - "test_accuracies.append(accuracy)\n", - "\n", - "print(\"FINAL RESULT: \" + \\\n", - " \"Batch Loss = {}\".format(final_loss) + \\\n", - " \", Accuracy = {}\".format(accuracy))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Training is good, but having visual insight is even better:\n", - "\n", - "Okay, let's plot this simply in the notebook for now." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\nvkUxXS3dfoQQQghRDCZNmsSwYcOsvvhBQUE0b94cb29vVqxYwZkzZ8jOzmbw4MFUr17duko9ffp0\nEhISrK47lSpVomHDhqSlpbF//352795NVtaFsTzq1atH9+7dWbVqFUlJSYDR7adTp074+/sDWH8L\nQilFREQEkZGRhIaG4uPjQ3JyMhs2bCArKwutNV988QVdu3bl3nvvtfKNHDmSsWPHWvV29F2vU6cO\nGRkZrFmzJte6unXrxpIlS1BKobVGKUWdOnWIiooiJSWF9evXF7LVC2fatGl4e3tTt25dKlWqxNat\nW23LAwMDqV27NqGhoYSEhHDmzBk2bdrEkSNHANizZw+DBw/m+++/t/Js376drl27kpaWZrVFcHAw\nDRo0IDQ0lK1bt9q6jjlztIGz9PR02rdvz99//20tq127NjVq1CAxMZFt27YBsGnTJrp168bKlSut\nvKNHj7by+Pj4WL8kHT58mL179+a6p0VcouI++7iUCbkKJIQQJYZ85hdcQa/8Z2Vl6Ztuusm6wlq7\ndm195MgRa/nhw4d1hQoVrKuwDRo0sJYNHz7cdlU2MzPTVvaZM2f07NmzrSvUDj179rTVLTk5udDb\nd+7cOb1z5063yzZs2GC7etynTx9rWWpqqg4MDLRd2Xa94puenq6nTp1qvZ4zZ47tF4tSpUrp33//\n3ZYnJSVF//TTT9brorzyr5TSQUFBevHixbY058+f11prvWPHjlxtr7XWmZmZunHjxlZbBwYG6oyM\nDGv5/fffb6vD3XffrVNTU21lLF++XCckJFivHVf+Hfmcr/yPHTvWVt6nn35qK2vUqFG2vP/973+1\n1lrn5OTY8n300Ue5tmXbtm16woQJueYXBeTKvxBCCHF9UqOK7+ZSPUIX27rzsmrVKlJSUqyr/kop\n4uLi3KbVWrNlyxYOHDhAxYoVqVKlirXs2LFjDBkyhJYtW1KzZk2io6MJDg6ma9eul6XegYGBKKX4\n5z//ybJly0hMTLR+oQBsV+gTEi7cNrhgwQLS09OtbW3Xrh2DBg2yle3v788jjzxivf7xxx+t7VdK\n8dprr3HHHXfY8pQtW5bOnTtflm1VSvH0009z11132eZ7e3sDEBkZyeeff85PP/3E9u3bOX78uK1f\nvaMtMjIySExMpFatWmRlZTF//nxrmY+PD5MnTyY8PNy2jhYtWhS4no57LBzt9Ouvv7JkyRJr+YkT\nJ6z6AMybN4/77rsPpRQVK1bkwIEDKKX4+uuvCQgIoFatWkRHR1OpUiViY2NzjVYlLp4E/0IIIUqE\nqzUAL06JiReenam1JiEhwRYsu7Nnzx4qVqxIz549+fDDD9m5cydaa8aNG2fdKOvl5UXDhg3p1asX\nzz33HH5+fkVa7/nz59OtW7dcQa5rVxStNadOnbJeO7bXEaDefrvrI4Vyc24jgJYtW15K1QvFUc9W\nrVq5XX769On/Z+/Ow+ye7/6PP99JJplkkiARBCkRVELt+1JRoqiitlpba7U/FK1W676VqrutWkpL\nq6jirqXRIqiltST2JVFua2pJrCERpMnIns/vj+/smeV8Z86Z9fm4rnOd893fM3K5Xuczn4Xtt9++\npksNND+VavXv4sMPP6zX3WfkyJEMHz68TbVOmzat5n4pJW6//fZGz8sa0anXpejss8/mhBNOAODF\nF1/kxBNPrDk2dOhQdt99d77//e+z+eabt6lGZQz/kiT1UNVBrFpLU29GBJWVlUDWP/yZZ57hd7/7\nHRMmTOD555/ns88+q7nvs88+y7PPPssLL7zAn/70p6LWfeKJJ7Jo0aKaeocPH86mm27KgAEDWLZs\nGbfeemtNq3bdn7Hhz1uI1lzT0JIlS+pt5+3D3lQw//Wvf81LL71U83vo27cv2267LSuvvDIRwZNP\nPsm7775bc371z1KMn6mhuvcsZArX6n8rAMcddxyjR4/mqquuYtKkSfVWrP7444+56aabmDBhAs88\n8wyjR48ubuE9UGeY6lOSJHWAtddeu+ZzRHDyySezdOnSJl9Llixh9913r7mmoqKCH/zgBzz66KPM\nnTuXGTNmcP/997PddtvVnHP99dczd+7ces9pixkzZtRrjd9mm214++23ueuuuxg/fjwXXXRRk9eO\nHDmyXg2PPPJIi8+rvqZaIdc0/EvH7Nmzc9+jrl69Go9rjz32GFAbvKdMmcJDDz3ELbfcwvjx4xk1\nalSj16222mqUl5fXbE+bNq3elJ6tsfbaa9d82erVqxczZ85s9t9Sde3VdthhB6699lqmTZtGZWUl\nL730Er/+9a+B7L/X/Pnzufrqq9tUozKGf0mSeqhtt92WoUOHAlmAvOaaa+r10672wQcfcPnll3P6\n6afX7JsyZQrXXHNNvWC7yiqrsMsuu9QL/1C/i0f//v3rHXvvvfdy1dywFb1fv3414XjRokWcccYZ\nTV775S9/uWZGoZQS9913HxdffHG9VutFixZx44031mzvs88+QG3f+Z///Oc89NBD9e47e/Zs7rzz\nzprtVVddtaamlBKTJk3ijTfeAOC1116rmV2prS3wDX8XdX+3t956K4888kijX7bKysrYY489aroV\nLV26lG984xvMmjWr3nlPPfUUr7zySkG17L333kD2e1q2bBknnnhivdZ9gGXLljFp0iSOPfZYnn/+\n+Zr9l156KU888UTN76O8vJwNNtiAI444ot4XqaZmH1I+dvuRJKmbaqmVvaysjJ/97Gc1i1DNmzeP\nXXbZhY022oi1116bRYsW8cYbb/Dmm2+SUqo3Zebrr7/OcccdxwknnMD666/PWmutRb9+/XjnnXd4\n9tlna8Jt37596/2FYf31169X21577cU222xDWVkZO++8MyeddFKzNY8YMYLhw4czY8YMACZNmsTo\n0aMZNWoUzz33HB988EGTP/ewYcP4wQ9+wHnnnQdkwfz000/n0ksvZcMNN2Tx4sVMmTKF8vJyDjvs\nMCAL/zvttFNNa/1//vMfdt1115rf0ezZs5kyZQr7779/zaDfAQMGsNVWW/HUU08REXzyySeMHj2a\n1VdfvWaa00L++7Rk66235qGHHqq5z+abb84OO+zAzJkzmTx5cpN/MYBses377ruP+fPnA9kiW6NG\njWKTTTZhpZVWYurUqbz22mvce++9y3W1aazub3/721x22WU1f5UZP3489913H5ttthmDBw9m1qxZ\nvPDCC8ybN4+IqOnjD3DFFVdw2mmnMWTIEMaMGcPQoUNZsGABTz/9dM24joio+bejNuro6Yba8sJp\n3ySpx/D/+YXLu8jXueeem/r06dPoAlF1F1zaZ599aq65+eabGz2/4TUXXHBBvWdNmzYtDRgwoNHr\njjzyyIJ+vhtuuKHJWn/5y1/W2zd69Ojlrj/llFOaXBCrV69eyy3yNXv27LTLLrs0e03dRb5SSumf\n//xnKisra/Tck046qd6+pqb6rD6/4XSp1WbOnJnWXHPNRp+x3Xbbpf3226/Z+/zjH/9Iw4YNa/a/\nY2OLfDU21WdKtYt8tfTvonfv3mny5Mk1122wwQYtXjNq1Kg0c+bMZv5VtA49cKpPu/1IktQNNTfr\nS0NnnXUWzz33HCeeeCIbbbQRgwcPpk+fPqy44opsvPHGHH300dx8882MHz++5povfelLXH755Rx6\n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\nbwFNGxMAAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# (Inline plots: )\n", - "%matplotlib inline\n", - "\n", - "font = {\n", - " 'family' : 'Bitstream Vera Sans',\n", - " 'weight' : 'bold',\n", - " 'size' : 18\n", - "}\n", - "matplotlib.rc('font', **font)\n", - "\n", - "width = 12\n", - "height = 12\n", - "plt.figure(figsize=(width, height))\n", - "\n", - "indep_train_axis = np.array(range(batch_size, (len(train_losses)+1)*batch_size, batch_size))\n", - "plt.plot(indep_train_axis, np.array(train_losses), \"b--\", label=\"Train losses\")\n", - "plt.plot(indep_train_axis, np.array(train_accuracies), \"g--\", label=\"Train accuracies\")\n", - "\n", - "indep_test_axis = np.append(\n", - " np.array(range(batch_size, len(test_losses)*display_iter, display_iter)[:-1]),\n", - " [training_iters]\n", - ")\n", - "plt.plot(indep_test_axis, np.array(test_losses), \"b-\", label=\"Test losses\")\n", - "plt.plot(indep_test_axis, np.array(test_accuracies), \"g-\", label=\"Test accuracies\")\n", - "\n", - "plt.title(\"Training session's progress over iterations\")\n", - "plt.legend(loc='upper right', shadow=True)\n", - "plt.ylabel('Training Progress (Loss or Accuracy values)')\n", - "plt.xlabel('Training iteration')\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## And finally, the multi-class confusion matrix and metrics!" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Testing Accuracy: 91.65252447128296%\n", - "\n", - "Precision: 91.76286479743305%\n", - "Recall: 91.65252799457076%\n", - "f1_score: 91.6437546304815%\n", - "\n", - "Confusion Matrix:\n", - "[[466 2 26 0 2 0]\n", - " [ 5 441 25 0 0 0]\n", - " [ 1 0 419 0 0 0]\n", - " [ 1 1 0 396 87 6]\n", - " [ 2 1 0 87 442 0]\n", - " [ 0 0 0 0 0 537]]\n", - "\n", - "Confusion matrix (normalised to % of total test data):\n", - "[[ 15.81269073 0.06786563 0.88225317 0. 0.06786563 0. ]\n", - " [ 0.16966406 14.96437073 0.84832031 0. 0. 0. ]\n", - " [ 0.03393281 0. 14.21784878 0. 0. 0. ]\n", - " [ 0.03393281 0.03393281 0. 13.43739319 2.95215464\n", - " 0.20359688]\n", - " [ 0.06786563 0.03393281 0. 2.95215464 14.99830341 0. ]\n", - " [ 0. 0. 0. 0. 0. 18.22192001]]\n", - "Note: training and testing data is not equally distributed amongst classes, \n", - "so it is normal that more than a 6th of the data is correctly classifier in the last category.\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0UAAANGCAYAAAAyEyUbAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XecXFX9//HXOyH0gIL0liAgTUHpYEhAoyKiVAENJPK1\n0UXFH4pAQOwNCwgiVZRiAQsoqJAC0i2gtAhBOlJDb9nP749zJnv3ZmZ2ZnezO5v7fj4e85iZe869\n99wzd2fvZ065igjMzMzMzMyqasRQF8DMzMzMzGwoOSgyMzMzM7NKc1BkZmZmZmaV5qDIzMzMzMwq\nzUGRmZmZmZlVmoMiMzMzMzOrNAdFZmZmZmZWaQ6KzMzMzMwqZIwUGvrHvfXKJulwSRdJukdSV+Gx\nf4P8+0v6k6RHJb0i6QVJd0r6kaSxrdaJfPNWMzMzM7PqkDTkEYCAiNB8y6WngGXqrPKRiDi3lPfb\nwBH5bfmQBDwNbBERd/dWHrcUmZmZmZlZp7gFOAM4CHiMFNzMR9ISwCGkYCiAXwHvAj4KvJCXLZvf\n92qR/pbazMzMzMyGmZFD3DYyt6vu4ogYX3st6agmW1gSGFVbDZgaEbfl9XYHds7LF22lOG4pMjMz\nMzOzYSUingD+Tne3uRMkvVPS/wET8vJXgQta2Z5biszMzMzMqmZk3V5pg2fugGxlN+AsYAdg9/yA\nFBDdBBwRETe2siG3FJmZmZmZ2XD0HPAf4GW6xxbVWo42AT4sqaXuc24pMjMzMzOzBWpaVzCta+Dm\nvJM0EpgBbEgKhI4BvgcsD/wM2A74JClg+nSv2/OU3GZmZmZm1SEpYolRvWdckGV48dW6U3L3yCPN\nBtYiBT09puSWtCPw55z2dEQsX0h7P3BJTnskIlbrrTzuPmdmZmZmZsPNCvlZwGKSilHe6wppy7ay\nMXefMzMzMzOrmkWGeKKFBiRNJE23TeEZ4G2S5uTXM4F/FtKWAC6UdBopWPpSIe2Glvbr7nNmZmZm\nZtUhKWJ0S/MPLLgyPPtK3e5zku4F1uxl9QkRMUPSj4CP11Yt5QnSTVx3iIibeiuPu8+ZmZmZmVmn\n6KLnTHLlx7y7vkbEgcAk4ArgUdJ9iV4CZgGnA29tJSACtxSZmZmZmVWKpIjXLT60ZXj6pV4nWhhM\nbikyMzMzM7NK80QLZmZmZmZVM7JjGmk6gluKzMzMzMys0hwUmZmZmZlZpbn7nJmZmZlZ1Yx020iR\na8PMzMzMzCrNLUVmZmZmZlXjlqIeXBtmZmZmZlZpDorMzMzMzKzS3H3OzMzMzKxqfJ+iHtxSZGZm\nZmZmleaWIjMzMzOzqvFECz24NszMzMzMrNIcFJmZmZmZWaW5+5yZmZmZWdV4ooUe3FJkZmZmZmaV\n5pYiMzMzM7OqWcRtI0WuDTMzMzMzqzQHRWZmZmZmVmnuPmdmZmZmVjWeaKEHtxSZmZmZmVmluaXI\nzMzMzKxqRrptpMi1YWZmZmZmleagyMzMzMzMKs3d58zMzMzMqsbd53pwbZiZmZmZWaW5pcjMzMzM\nrGo8JXcPbikyMzMzM7NKc1BkZmZmZmaV5u5zZmZmZmZV44kWenBtmJmZmZlZpbmlyMzMzMysajzR\nQg9uKTIzMzMzs0pzUGRmZkNG0p6SLpQ0W9Lz+XG3pAsk7S5pSP9PSdpR0nRJcyR1SZorac1B2vf4\nvM8rB2N/VSdpaq7vY4e6LGY2+Nx9zszMBp2k1YFfA5sDXcAtwI359drAnsAHgZuALYewjJcASwLT\ngPtz+Z4bxGJEflgTkrqAiIiR/diM69qqxRMt9OCgyMzMBpWk5YFrgNWBPwMHRsTdpTwrA18A9h38\nEs4zEVgaOCciPjIE+78e2AB4YQj2XUU/AM4HHh/qgpjZ4HNQZGZmg+1UYA1S68tOETG3nCEiHgEO\nk3ThIJetaI38PHsodh4RLwF3DcW+qygingSeHOpymA0aT7TQg9vNzMxs0EhaB9id1E3p4HoBUVFE\nXFNnGytI+rakOyW9KOmpPO5nvwb7PDuPFdlf0psk/UrSY5JekHSzpA+W8k/O3bGm5kW1sSZdks4s\n5qm9r7PP4+qNT5E0IpfjakkPSXpJ0sOSrpN0oqRFC3mbjimSNE7SJZIelfSypAck/VTSRg3yd0ma\nm1/vJ+nGPIbrCUm/kLR2vfUaKdaBpOUknSLp/lyv/5S0TyHv2yVdLulJSc9KulTSm+psc5Fctgvy\n5/tsfvxD0jGSlqxXBtL5pMLnNO9Yc75544UkrS3pvFz/r0k6rJynsN76kp7Ln9Nb65T3vXmdhyWt\n2E79mVlncUuRmZkNpvcBAv4REbe3u7Kk9YCrgFVIY3wuAZYBdgDGSXpXRJSDo9pYkc2Ak4H/An8C\nxpLGK50vaUREXJDz/wc4G9g0P/6RHwBXt1HceuNTzgYmAc/nbT0BrAisB3we+D7wv942LOlQ4KT8\n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\nt0sWkt4maU5+fTXwE+CqOps4Dnh9fn0+cAPwYG/7dVBkZmZmZmad4nRgzdIyAYflB8CEiLi03sqS\njqA7KLoiIs5tZacOiszMzMzMqmaQJ1poQxfNu7210iWu5W5zNQ6KzMzMzMysI0TE2v1cf2xf1nNQ\nZGZmZmZWNSMWiokWBkzHtpuZmZmZmZkNBgdFZmZmZmZWae4+Z2ZmZmZWNQvHfYoGjFuKzMzMzMys\n0txSZGZmZmZWNZ07JfeQcG2YmZmZmVmlOSgyMzMzM7NKc/c5MzMzM7OK6fJ9inpwS5GZmZmZmVWa\nW4rMzMzMzCqmyxMt9OCgyMwG1BfPvXGoizCsnPiVdw11EYalg35y+VAXwczMFiIOEc3MzMzMrNLc\nUmRmZmZmVjGeaKEntxSZmZmZmVmluaXIzMzMzKxi5o5020iRa8PMzMzMzCrNQZGZmZmZmVWau8+Z\nmZmZmVWMJ1royS1FZmZmZmZWaW4pMjMzMzOrmBjhtpEi14aZmZmZmVWagyIzMzMzM6s0d58zMzMz\nM6sYT7TQk1uKzMzMzMys0txSZGZmZmZWMW4p6sktRWZmZmZmVmkOiszMzMzMrNLcfc7MzMzMrGK6\nfJ+iHlwbZmZmZmZWaW4pMjMzMzOrGE+00JNbiszMzMzMrNIcFJmZmZmZWaW5+5yZmZmZWcXMldtG\nilwbZmZmZmZWaW4pMjMzMzOrGE+00JNbiszMzMzMrNIcFJmZmZmZWaW5+5yZmZmZWcW4+1xPbiky\nMzMzM7NKc1BkZmZmZmaV5u5zZmZmZmYVEyPcNlLk2jAzMzMzs0pzUFQiaV9JXflxf530Wwvpvyil\njZb0WiF9o1L6pEJal6QXJb2uQTnOKuS7spcyjy9td81S+gmFtLmSDs7LJxeXl9aZVtrmPk32OVfS\nonXKNVrSYZL+KOkhSS9JelrSXZIul3SEpLHNjq3B8d5b2PexddKL5d6/sHxyKa32eFnSA5J+KWl8\nne0tJukoSTdJeibnfzSfCxdIOrRJvfX22L/O/srb+FmDehhZyvehQtr/NdjfC5JmSTpD0voNtruB\npLMlzc6f2XOS7pN0taSTJW3V+6dkZmZmnaxrhIb00WncfW5+M/JzAKtKGhsRswFyALNhTgMYV1p3\nW1KgGcBTEfHvUvrkwroAiwL7Aj9qUp5oktZrXklfB47MaQF8IiLOaGEfUVp+vKSLIqKrlfJJmgic\n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\npfFGTTkoMjMzMzOzBWqLbcayxTZj570/9XvTBmzbkr4DfIo069zTwK7t3KMIHBSZmZmZmVVOl4b/\n1AKSFgXOA/YkBUT3ATu1Oo6oyEGRmZmZmZl1BEkTgSXz2yULSW+TNCe/ngm8AFwOjKO7hehIYHlJ\n2xXWuysiHuttvw6KzMzMzMwqpoMnWjgdWLO0TMBh+QEwAfgvKSCqpb8euKjO9qYA5/a2UwdFZmZm\nZmbWKbpILT+NRIPXveVtykGRmZmZmZl1hIhYu43sIwdqvw6KzMzMzMwqpksd231uSAz/aSfMzMzM\nzMz6wS1FZmZmZmYVM3eE20aKXBtmZmZmZlZpDorMzMzMzKzS3H3OzMzMzKxiPNFCT24pMjMzMzOz\nSnNLkZmZmZlZxbilqCe3FJmZmZmZWaU5KDIzMzMzs0pz9zkzMzMzs4oJ36eoB9eGmZmZmZlVmluK\nzMzMzMwqxhMt9OSWIjMzMzMzqzQHRWZmZmZmVmnuPmdmZmZmVjHuPteTW4rMzMzMzKzS3FJkZgNq\nkVf8y1M7pn7hiqEuwrB0yrl7D3URhp2p+1841EUwsw7ilqKe3FJkZmZmZmaV5qDIzMzMzMwqzd3n\nzMzMzMwqpktuGylybZiZmZmZWaW5pcjMzMzMrGI80UJPbikyMzMzM7NKc1BkZmZmZmaV5u5zZmZm\nZmYVM3eEu88VuaXIzMzMzMwqzS1FZmZmZmYV4ym5e3JtmJmZmZlZpTkoMjMzMzOzSnP3OTMzMzOz\nignfp6gHtxSZmZmZmVmlOSgyMzMzM7NKc/c5MzMzM7OK6cLd54rcUmRmZmZmZpXmliIzMzMzs4rp\n8kQLPbilyMzMzMzMKs1BkZmZmZmZVZq7z5mZmZmZVUyX3DZS5NowMzMzM7NKc0uRmZmZmVnFeKKF\nntxSZGZmZmZmleagyMzMzMzMKs3d58zMzMzMKmauu8/14JYiMzMzMzOrNLcUmZmZmZlVjCda6Mkt\nRWZmZmZmVmkOiszMzMzMrNLcfc7MzMzMrGK63DbSw5DWhqR9JXXlx/110m8tpP+ilDZa0muF9I1K\n6ZMKaV2SXpT0ugblOKuQ78peyjy+tN01S+knFNLmSjo4L59cXF5aZ1ppm/s02edcSYvWKddoSYdJ\n+qOkhyS9JOlpSXdJulzSEZLGNju2Bsd7b6lsr0iaI+k/ki6TdKikZXrZxlsknSbpdknP5M/iv5J+\n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\ntf8HGEwmAny82PIAAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Results\n", - "\n", - "predictions = one_hot_predictions.argmax(1)\n", - "\n", - "print(\"Testing Accuracy: {}%\".format(100*accuracy))\n", - "\n", - "print(\"\")\n", - "print(\"Precision: {}%\".format(100*metrics.precision_score(y_test, predictions, average=\"weighted\")))\n", - "print(\"Recall: {}%\".format(100*metrics.recall_score(y_test, predictions, average=\"weighted\")))\n", - "print(\"f1_score: {}%\".format(100*metrics.f1_score(y_test, predictions, average=\"weighted\")))\n", - "\n", - "print(\"\")\n", - "print(\"Confusion Matrix:\")\n", - "confusion_matrix = metrics.confusion_matrix(y_test, predictions)\n", - "print(confusion_matrix)\n", - "normalised_confusion_matrix = np.array(confusion_matrix, dtype=np.float32)/np.sum(confusion_matrix)*100\n", - "\n", - "print(\"\")\n", - "print(\"Confusion matrix (normalised to % of total test data):\")\n", - "print(normalised_confusion_matrix)\n", - "print(\"Note: training and testing data is not equally distributed amongst classes, \")\n", - "print(\"so it is normal that more than a 6th of the data is correctly classifier in the last category.\")\n", - "\n", - "# Plot Results: \n", - "width = 12\n", - "height = 12\n", - "plt.figure(figsize=(width, height))\n", - "plt.imshow(\n", - " normalised_confusion_matrix, \n", - " interpolation='nearest', \n", - " cmap=plt.cm.rainbow\n", - ")\n", - "plt.title(\"Confusion matrix \\n(normalised to % of total test data)\")\n", - "plt.colorbar()\n", - "tick_marks = np.arange(n_classes)\n", - "plt.xticks(tick_marks, LABELS, rotation=90)\n", - "plt.yticks(tick_marks, LABELS)\n", - "plt.tight_layout()\n", - "plt.ylabel('True label')\n", - "plt.xlabel('Predicted label')\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "sess.close()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Conclusion\n", - "\n", - "Outstandingly, **the final accuracy is of 91%**! And it can peak to values such as 93.25%, at some moments of luck during the training, depending on how the neural network's weights got initialized at the start of the training, randomly. \n", - "\n", - "This means that the neural networks is almost always able to correctly identify the movement type! Remember, the phone is attached on the waist and each series to classify has just a 128 sample window of two internal sensors (a.k.a. 2.56 seconds at 50 FPS), so it amazes me how those predictions are extremely accurate given this small window of context and raw data. I've validated and re-validated that there is no important bug, and the community used and tried this code a lot. (Note: be sure to report something in the issue tab if you find bugs, otherwise [Quora](https://www.quora.com/), [StackOverflow](https://stackoverflow.com/questions/tagged/tensorflow?sort=votes&pageSize=50), and other [StackExchange](https://stackexchange.com/sites#science) sites are the places for asking questions.)\n", - "\n", - "I specially did not expect such good results for guessing between the labels \"SITTING\" and \"STANDING\". Those are seemingly almost the same thing from the point of view of a device placed at waist level according to how the dataset was originally gathered. Thought, it is still possible to see a little cluster on the matrix between those classes, which drifts away just a bit from the identity. This is great.\n", - "\n", - "It is also possible to see that there was a slight difficulty in doing the difference between \"WALKING\", \"WALKING_UPSTAIRS\" and \"WALKING_DOWNSTAIRS\". Obviously, those activities are quite similar in terms of movements. \n", - "\n", - "I also tried my code without the gyroscope, using only the 3D accelerometer's 6 features (and not changing the training hyperparameters), and got an accuracy of 87%. In general, gyroscopes consumes more power than accelerometers, so it is preferable to turn them off. \n", - "\n", - "\n", - "## Improvements\n", - "\n", - "In [another open-source repository of mine](https://github.com/guillaume-chevalier/HAR-stacked-residual-bidir-LSTMs), the accuracy is pushed up to nearly 94% using a special deep LSTM architecture which combines the concepts of bidirectional RNNs, residual connections, and stacked cells. This architecture is also tested on another similar activity dataset. It resembles the nice architecture used in \"[Google’s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation](https://arxiv.org/pdf/1609.08144.pdf)\", without an attention mechanism, and with just the encoder part - as a \"many to one\" architecture instead of a \"many to many\" to be adapted to the Human Activity Recognition (HAR) problem. I also worked more on the problem and came up with the [LARNN](https://github.com/guillaume-chevalier/Linear-Attention-Recurrent-Neural-Network), however it's complicated for just a little gain. Thus the current, original activity recognition project is simply better to use for its outstanding simplicity. \n", - "\n", - "If you want to learn more about deep learning, I have also built a list of the learning ressources for deep learning which have revealed to be the most useful to me [here](https://github.com/guillaume-chevalier/Awesome-Deep-Learning-Resources). \n", - "\n", - "\n", - "## References\n", - "\n", - "The [dataset](https://archive.ics.uci.edu/ml/datasets/Human+Activity+Recognition+Using+Smartphones) can be found on the UCI Machine Learning Repository: \n", - "\n", - "> Davide Anguita, Alessandro Ghio, Luca Oneto, Xavier Parra and Jorge L. Reyes-Ortiz. A Public Domain Dataset for Human Activity Recognition Using Smartphones. 21th European Symposium on Artificial Neural Networks, Computational Intelligence and Machine Learning, ESANN 2013. Bruges, Belgium 24-26 April 2013.\n", - "\n", - "The RNN image for \"many-to-one\" is taken from Karpathy's post: \n", - "\n", - "> Andrej Karpathy, The Unreasonable Effectiveness of Recurrent Neural Networks, 2015, \n", - "> http://karpathy.github.io/2015/05/21/rnn-effectiveness/\n", - "\n", - "## Citation\n", - "\n", - "Copyright (c) 2016 Guillaume Chevalier. To cite my code, you can point to the URL of the GitHub repository, for example: \n", - "\n", - "> Guillaume Chevalier, LSTMs for Human Activity Recognition, 2016, \n", - "> https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition\n", - "\n", - "My code is available for free and even for private usage for anyone under the [MIT License](https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition/blob/master/LICENSE), however I ask to cite for using the code. \n", - "\n", - "## Extra links\n", - "\n", - "### Connect with me\n", - "\n", - "- [LinkedIn](https://ca.linkedin.com/in/chevalierg)\n", - "- [Twitter](https://twitter.com/guillaume_che)\n", - "- [GitHub](https://github.com/guillaume-chevalier/)\n", - "- [Quora](https://www.quora.com/profile/Guillaume-Chevalier-2)\n", - "- [YouTube](https://www.youtube.com/c/GuillaumeChevalier)\n", - "- [Dev/Consulting](http://www.neuraxio.com/en/)\n", - "\n", - "### Liked this project? Did it help you? Leave a [star](https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition/stargazers), [fork](https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition/network/members) and share the love!\n", - "\n", - "This activity recognition project has been seen in:\n", - "\n", - "- [Hacker News 1st page](https://news.ycombinator.com/item?id=13049143)\n", - "- [Awesome TensorFlow](https://github.com/jtoy/awesome-tensorflow#tutorials)\n", - "- [TensorFlow World](https://github.com/astorfi/TensorFlow-World#some-useful-tutorials)\n", - "- And more.\n", - "\n", - "---\n" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": { - "scrolled": true - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[NbConvertApp] Converting notebook LSTM.ipynb to markdown\n", - "[NbConvertApp] Support files will be in LSTM_files/\n", - "[NbConvertApp] Making directory LSTM_files\n", - "[NbConvertApp] Making directory LSTM_files\n", - "[NbConvertApp] Writing 38654 bytes to LSTM.md\n" - ] - } - ], - "source": [ - "# Let's convert this notebook to a README automatically for the GitHub project's title page:\n", - "!jupyter nbconvert --to markdown LSTM.ipynb\n", - "!mv LSTM.md README.md" - ] - } - ], - "metadata": { - "hide_input": false, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/requirements.txt b/requirements.txt index 3adc77a..e74a09a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,3 @@ -numpy tensorflow==1.15 conv==0.2 -flask -matplotlib git+git://github.com/alexbrillant/Neuraxle@one-hot-encoder-step#egg=Neuraxle - From 348f28a18f9f644d15cf55503006635d93084b7c Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Mon, 4 Nov 2019 19:44:34 -0500 Subject: [PATCH 15/30] Edit Call Api notebook for gucci --- Call-API.ipynb | 72 +++++++++++++++++++++++++++++++++++++------------- call_api.py | 0 2 files changed, 54 insertions(+), 18 deletions(-) create mode 100644 call_api.py diff --git a/Call-API.ipynb b/Call-API.ipynb index d184ee9..3ab1bd6 100644 --- a/Call-API.ipynb +++ b/Call-API.ipynb @@ -2,18 +2,54 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "# TODO: import *\n" + "import math\n", + "import os\n", + "import numpy as np\n", + "import tensorflow as tf\n", + "\n", + "import json\n", + "import urllib\n", + "\n", + "from neuraxle.base import NonFittableMixin\n", + "\n", + "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", + " TRAIN_FILE_NAME, TEST_FILE_NAME\n", + "from neuraxle.api.flask import FlaskRestApiWrapper\n", + "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", + "from neuraxle.hyperparams.space import HyperparameterSamples\n", + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", + "from neuraxle.steps.encoding import OneHotEncoder\n", + "\n", + "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n", + "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", + "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", + "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", + "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", + "\n", + "# TODO: move in a package neuraxle-tensorflow \n", + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some useful info to get an insight on dataset's shape and normalisation:\n", + "(X shape, y shape, every X's mean, every X's standard deviation)\n", + "(2947, 128, 9) (2947, 1) 0.09913992 0.39567086\n", + "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" + ] + } + ], "source": [ "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", " TRAIN_FILE_NAME, TEST_FILE_NAME\n", @@ -42,14 +78,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ - "\n", - "class APICaller(NonFittableStep, BaseStep):\n", - " # TODO: use urllib code here.\n", - " pass\n" + "class APICaller(NonFittableMixin, BaseStep):\n", + " def transform(self, data_inputs):\n", + " req = urllib.request.Request(\n", + " 'http://127.0.0.1:5000/',\n", + " method=\"GET\",\n", + " headers={'content-type': 'application/json'},\n", + " data=json.dumps(data_inputs.tolist()).encode('utf8')\n", + " )\n", + " response = urllib.request.urlopen(req)\n", + " test_predictions = json.loads(response.read())\n", + " return test_predictions" ] }, { @@ -136,20 +179,13 @@ "source": [ "p.teardown()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Human Activity Recognition", "language": "python", - "name": "python3" + "name": "human-activity-recognition" }, "language_info": { "codemirror_mode": { diff --git a/call_api.py b/call_api.py new file mode 100644 index 0000000..e69de29 From e288133d353a60643a276d02d0c137216215590f Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Mon, 4 Nov 2019 20:11:34 -0500 Subject: [PATCH 16/30] Update Demonstration Notebooks --- Call-API.ipynb | 184 ++++++++++++++++++++++++++++++++++++++++++------- LSTM_new.ipynb | 175 +++++++++++++++++++++++----------------------- 2 files changed, 249 insertions(+), 110 deletions(-) diff --git a/Call-API.ipynb b/Call-API.ipynb index 3ab1bd6..4baa068 100644 --- a/Call-API.ipynb +++ b/Call-API.ipynb @@ -1,8 +1,15 @@ { "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Install " + ] + }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -10,6 +17,10 @@ "import os\n", "import numpy as np\n", "import tensorflow as tf\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "\n", + "from sklearn import metrics\n", "\n", "import json\n", "import urllib\n", @@ -17,9 +28,11 @@ "from neuraxle.base import NonFittableMixin\n", "\n", "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", - " TRAIN_FILE_NAME, TEST_FILE_NAME\n", + " TRAIN_FILE_NAME, TEST_FILE_NAME, LABELS\n", + "\n", "from neuraxle.api.flask import FlaskRestApiWrapper\n", "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", + "\n", "from neuraxle.hyperparams.space import HyperparameterSamples\n", "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", "from neuraxle.steps.encoding import OneHotEncoder\n", @@ -31,12 +44,20 @@ "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", "\n", "# TODO: move in a package neuraxle-tensorflow \n", - "from savers.tensorflow1_step_saver import TensorflowV1StepSaver" + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", + "from neuraxle.pipeline import Pipeline\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Read Data" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -51,9 +72,6 @@ } ], "source": [ - "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", - " TRAIN_FILE_NAME, TEST_FILE_NAME\n", - "\n", "DATA_PATH = \"data/\"\n", "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", "\n", @@ -76,47 +94,141 @@ "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# API Caller " + ] + }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "class APICaller(NonFittableMixin, BaseStep):\n", + " def __init__(self, url):\n", + " BaseStep.__init__(self)\n", + " self.url = url\n", + " \n", " def transform(self, data_inputs):\n", + " data = json.dumps(data_inputs.tolist()).encode('utf8')\n", " req = urllib.request.Request(\n", " 'http://127.0.0.1:5000/',\n", " method=\"GET\",\n", " headers={'content-type': 'application/json'},\n", - " data=json.dumps(data_inputs.tolist()).encode('utf8')\n", + " data=data\n", " )\n", " response = urllib.request.urlopen(req)\n", " test_predictions = json.loads(response.read())\n", - " return test_predictions" + " return np.array(test_predictions['predictions'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Call Rest Api " ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 1.18599892e+00 5.41315079e-01 5.01255989e-02 5.32620525e+00\n", + " 7.96708488e+00 1.05193663e+00]\n", + " [ 1.32715702e+00 5.79396248e-01 1.43231392e-01 5.13996363e+00\n", + " 8.01558208e+00 9.53553915e-01]\n", + " [ 1.38326144e+00 6.24054670e-01 1.96704865e-01 5.10453653e+00\n", + " 8.03478718e+00 9.46316242e-01]\n", + " ...\n", + " [-2.59126878e+00 1.63446629e+00 -1.15046430e+00 -4.65883112e+00\n", + " -6.26892710e+00 -4.49010229e+00]\n", + " [-4.39656258e+00 -5.00252008e-01 -4.03625870e+00 -6.34975433e+00\n", + " -6.50696850e+00 -4.80641174e+00]\n", + " [ 5.38370669e-01 2.58066088e-01 -1.84605551e+00 -2.31253886e+00\n", + " -5.79619408e-03 -4.39832497e+00]]\n" + ] + } + ], "source": [ - "p = Pipeline(\n", - " json_encoder=CustomJSONEncoderOfOutputs(),\n", - " wrapped=APICaller(url=\"http://localhost:5000/\"),\n", - " json_decoder=CustomJSONDecoderFor2DArray()\n", - ")\n", - "y_pred = p.predict(X_test)\n", - "\n", + "p = Pipeline([\n", + " APICaller(url=\"http://localhost:5000/\")\n", + "])\n", + "y_pred = p.transform(X_test)\n", + "print(y_pred)\n", "# TODO: \n", "# y_test = y_test.argmax(1) ???? is this already made?" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot " + ] + }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Precision: 83.53261074023052%\n", + "Recall: 82.62639972853749%\n", + "f1_score: 82.6189023152726%\n", + "\n", + "Confusion Matrix:\n", + "[[439 10 45 0 2 0]\n", + " [103 320 45 1 1 1]\n", + " [ 47 23 347 1 2 0]\n", + " [ 8 6 0 361 116 0]\n", + " [ 6 2 0 66 458 0]\n", + " [ 0 2 25 0 0 510]]\n", + "\n", + "Confusion matrix (normalised to % of total test data):\n", + "[[14.896504 0.3393281 1.5269766 0. 0.06786563 0. ]\n", + " [ 3.4950798 10.8585 1.5269766 0.03393281 0.03393281 0.03393281]\n", + " [ 1.5948422 0.7804547 11.774686 0.03393281 0.06786563 0. ]\n", + " [ 0.2714625 0.20359688 0. 12.249745 3.936206 0. ]\n", + " [ 0.20359688 0.06786563 0. 2.2395658 15.541228 0. ]\n", + " [ 0. 0.06786563 0.8483203 0. 0. 17.305735 ]]\n", + "Note: training and testing data is not equally distributed amongst classes, \n", + "so it is normal that more than a 6th of the data is correctly classifier in the last category.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ "# (Inline plots: )\n", "%matplotlib inline\n", @@ -131,8 +243,7 @@ "# Results\n", "\n", "predictions = y_pred.argmax(1)\n", - "\n", - "print(\"Testing Accuracy: {}%\".format(100*accuracy))\n", + "n_classes = 6\n", "\n", "print(\"\")\n", "print(\"Precision: {}%\".format(100*metrics.precision_score(y_test, predictions, average=\"weighted\")))\n", @@ -173,9 +284,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "Pipeline\n", + "(\n", + "\tPipeline(\n", + "\tname=Pipeline,\n", + "\thyperparameters=HyperparameterSamples()\n", + ")(\n", + "\t\t[('APICaller',\n", + " APICaller(\n", + "\tname=APICaller,\n", + "\thyperparameters=HyperparameterSamples()\n", + "))]\t\n", + ")\n", + ")" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "p.teardown()" ] diff --git a/LSTM_new.ipynb b/LSTM_new.ipynb index 6cdfac7..a69de9d 100644 --- a/LSTM_new.ipynb +++ b/LSTM_new.ipynb @@ -54,10 +54,10 @@ "output_type": "stream", "text": [ "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "cache\t\t LSTM_files\t new.py README.md\t\tvenv\n", - "data\t\t LSTM.ipynb\t old.py requirements.txt\n", - "data_reading.py LSTM_new.ipynb pipeline.py savers\n", - "LICENSE\t\t neuraxle\t __pycache__ steps\n", + "cache\t\tdata_reading.py new.py README.md\t\tvenv\n", + "Call-API.ipynb\tLICENSE\t\t old.py requirements.txt\n", + "call_api.py\tLSTM_files\t pipeline.py savers\n", + "data\t\tLSTM_new.ipynb\t __pycache__ steps\n", "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", @@ -72,10 +72,10 @@ " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "cache\t\t LSTM_files\t new.py README.md\t\tvenv\n", - "data\t\t LSTM.ipynb\t old.py requirements.txt\n", - "data_reading.py LSTM_new.ipynb pipeline.py savers\n", - "LICENSE\t\t neuraxle\t __pycache__ steps\n", + "cache\t\tdata_reading.py new.py README.md\t\tvenv\n", + "Call-API.ipynb\tLICENSE\t\t old.py requirements.txt\n", + "call_api.py\tLSTM_files\t pipeline.py savers\n", + "data\t\tLSTM_new.ipynb\t __pycache__ steps\n", "\n", "Dataset is now located at: data/UCI HAR Dataset/\n" ] @@ -479,76 +479,76 @@ "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", - "Batch Loss = 2.826632, Accuracy = 0.14666666090488434\n", - "Batch Loss = 2.374201, Accuracy = 0.18933333456516266\n", - "Batch Loss = 2.165148, Accuracy = 0.4359999895095825\n", - "Batch Loss = 1.991644, Accuracy = 0.4806666672229767\n", - "Batch Loss = 1.997708, Accuracy = 0.4423076808452606\n", - "Batch Loss = 1.869701, Accuracy = 0.5046666860580444\n", - "Batch Loss = 1.734456, Accuracy = 0.6193333268165588\n", - "Batch Loss = 1.714716, Accuracy = 0.6439999938011169\n", - "Batch Loss = 1.639421, Accuracy = 0.625333309173584\n", - "Batch Loss = 1.722513, Accuracy = 0.6220414042472839\n", - "Batch Loss = 1.579464, Accuracy = 0.6613333225250244\n", - "Batch Loss = 1.476428, Accuracy = 0.6806666851043701\n", - "Batch Loss = 1.615788, Accuracy = 0.6240000128746033\n", - "Batch Loss = 1.343344, Accuracy = 0.7386666536331177\n", - "Batch Loss = 1.500012, Accuracy = 0.692307710647583\n", - "Batch Loss = 1.401856, Accuracy = 0.7026666402816772\n", - "Batch Loss = 1.282448, Accuracy = 0.7633333206176758\n", - "Batch Loss = 1.370500, Accuracy = 0.699999988079071\n", - "Batch Loss = 1.207804, Accuracy = 0.7706666588783264\n", - "Batch Loss = 1.350325, Accuracy = 0.7285503149032593\n", - "Batch Loss = 1.231125, Accuracy = 0.7706666588783264\n", - "Batch Loss = 1.201041, Accuracy = 0.7586666941642761\n", - "Batch Loss = 1.270912, Accuracy = 0.6613333225250244\n", - "Batch Loss = 1.133742, Accuracy = 0.7826666831970215\n", - "Batch Loss = 1.268553, Accuracy = 0.735946774482727\n", - "Batch Loss = 1.199111, Accuracy = 0.7879999876022339\n", - "Batch Loss = 1.159183, Accuracy = 0.7919999957084656\n", - "Batch Loss = 1.193569, Accuracy = 0.7286666631698608\n", - "Batch Loss = 1.117007, Accuracy = 0.7773333191871643\n", - "Batch Loss = 1.243366, Accuracy = 0.7477810382843018\n", - "Batch Loss = 1.116806, Accuracy = 0.8186666369438171\n", - "Batch Loss = 1.150177, Accuracy = 0.7680000066757202\n", - "Batch Loss = 1.173215, Accuracy = 0.7059999704360962\n", - "Batch Loss = 1.129342, Accuracy = 0.7846666574478149\n", - "Batch Loss = 1.159069, Accuracy = 0.7573964595794678\n", - "Batch Loss = 1.121240, Accuracy = 0.8006666898727417\n", - "Batch Loss = 1.138364, Accuracy = 0.7906666398048401\n", - "Batch Loss = 1.093842, Accuracy = 0.7919999957084656\n", - "Batch Loss = 1.105886, Accuracy = 0.8046666383743286\n", - "Batch Loss = 1.180819, Accuracy = 0.7625739574432373\n", - "Batch Loss = 1.024310, Accuracy = 0.8519999980926514\n", - "Batch Loss = 1.114419, Accuracy = 0.781333327293396\n", - "Batch Loss = 1.167690, Accuracy = 0.7113333344459534\n", - "Batch Loss = 0.947682, Accuracy = 0.8713333606719971\n", - "Batch Loss = 1.112047, Accuracy = 0.8062130212783813\n", - "Batch Loss = 1.010290, Accuracy = 0.8413333296775818\n", - "Batch Loss = 1.006666, Accuracy = 0.8386666774749756\n", - "Batch Loss = 1.061410, Accuracy = 0.7879999876022339\n", - "Batch Loss = 1.058079, Accuracy = 0.8353333473205566\n", - "Batch Loss = 1.062428, Accuracy = 0.8136094808578491\n", - "Batch Loss = 0.964536, Accuracy = 0.8786666393280029\n", - "Batch Loss = 1.000171, Accuracy = 0.8640000224113464\n", - "Batch Loss = 1.108438, Accuracy = 0.7773333191871643\n", - "Batch Loss = 1.010710, Accuracy = 0.8433333039283752\n", - "Batch Loss = 1.026997, Accuracy = 0.8321005702018738\n", - "Batch Loss = 0.948604, Accuracy = 0.887333333492279\n", - "Batch Loss = 1.076846, Accuracy = 0.8413333296775818\n", - "Batch Loss = 0.994279, Accuracy = 0.840666651725769\n", - "Batch Loss = 1.182574, Accuracy = 0.8053333163261414\n", - "Batch Loss = 1.063531, Accuracy = 0.8217455744743347\n", - "Batch Loss = 0.949576, Accuracy = 0.8766666650772095\n", - "Batch Loss = 1.011246, Accuracy = 0.8533333539962769\n", - "Batch Loss = 1.191805, Accuracy = 0.7706666588783264\n", - "Batch Loss = 0.935223, Accuracy = 0.9013333320617676\n", - "Batch Loss = 0.997599, Accuracy = 0.8454142212867737\n", - "Batch Loss = 0.979652, Accuracy = 0.8613333106040955\n", - "Batch Loss = 0.922143, Accuracy = 0.8799999952316284\n", - "Batch Loss = 1.012194, Accuracy = 0.7946666479110718\n", - "Batch Loss = 0.924530, Accuracy = 0.8820000290870667\n", - "Batch Loss = 0.947285, Accuracy = 0.875\n", + "Batch Loss = 3.993926, Accuracy = 0.14533333480358124\n", + "Batch Loss = 3.166194, Accuracy = 0.12666666507720947\n", + "Batch Loss = 2.666343, Accuracy = 0.11533333361148834\n", + "Batch Loss = 2.333051, Accuracy = 0.2593333423137665\n", + "Batch Loss = 2.365340, Accuracy = 0.3949704170227051\n", + "Batch Loss = 2.335133, Accuracy = 0.3606666624546051\n", + "Batch Loss = 2.192836, Accuracy = 0.41999998688697815\n", + "Batch Loss = 2.004998, Accuracy = 0.5460000038146973\n", + "Batch Loss = 1.979304, Accuracy = 0.5799999833106995\n", + "Batch Loss = 2.063153, Accuracy = 0.40606507658958435\n", + "Batch Loss = 1.972361, Accuracy = 0.4779999852180481\n", + "Batch Loss = 1.922048, Accuracy = 0.5360000133514404\n", + "Batch Loss = 1.827589, Accuracy = 0.6633333563804626\n", + "Batch Loss = 1.794267, Accuracy = 0.6666666865348816\n", + "Batch Loss = 1.888613, Accuracy = 0.5872781276702881\n", + "Batch Loss = 1.797241, Accuracy = 0.5446666479110718\n", + "Batch Loss = 1.742341, Accuracy = 0.5633333325386047\n", + "Batch Loss = 1.670088, Accuracy = 0.5920000076293945\n", + "Batch Loss = 1.623381, Accuracy = 0.6146666407585144\n", + "Batch Loss = 1.714245, Accuracy = 0.5828402638435364\n", + "Batch Loss = 1.597082, Accuracy = 0.6466666460037231\n", + "Batch Loss = 1.518446, Accuracy = 0.6706666946411133\n", + "Batch Loss = 1.474818, Accuracy = 0.6446666717529297\n", + "Batch Loss = 1.429932, Accuracy = 0.6933333277702332\n", + "Batch Loss = 1.522832, Accuracy = 0.6309171319007874\n", + "Batch Loss = 1.424594, Accuracy = 0.6859999895095825\n", + "Batch Loss = 1.370649, Accuracy = 0.7046666741371155\n", + "Batch Loss = 1.383567, Accuracy = 0.6600000262260437\n", + "Batch Loss = 1.283080, Accuracy = 0.734000027179718\n", + "Batch Loss = 1.396952, Accuracy = 0.6730769276618958\n", + "Batch Loss = 1.306781, Accuracy = 0.7179999947547913\n", + "Batch Loss = 1.265723, Accuracy = 0.7446666955947876\n", + "Batch Loss = 1.266257, Accuracy = 0.6946666836738586\n", + "Batch Loss = 1.234961, Accuracy = 0.7613333463668823\n", + "Batch Loss = 1.299717, Accuracy = 0.7107987999916077\n", + "Batch Loss = 1.269762, Accuracy = 0.7526666522026062\n", + "Batch Loss = 1.228487, Accuracy = 0.75\n", + "Batch Loss = 1.227822, Accuracy = 0.7206666469573975\n", + "Batch Loss = 1.164757, Accuracy = 0.7986666560173035\n", + "Batch Loss = 1.247713, Accuracy = 0.7278106212615967\n", + "Batch Loss = 1.241613, Accuracy = 0.7599999904632568\n", + "Batch Loss = 1.189182, Accuracy = 0.7986666560173035\n", + "Batch Loss = 1.183320, Accuracy = 0.7559999823570251\n", + "Batch Loss = 1.131927, Accuracy = 0.8306666612625122\n", + "Batch Loss = 1.204420, Accuracy = 0.7788461446762085\n", + "Batch Loss = 1.195225, Accuracy = 0.8113333582878113\n", + "Batch Loss = 1.146203, Accuracy = 0.8100000023841858\n", + "Batch Loss = 1.152838, Accuracy = 0.7586666941642761\n", + "Batch Loss = 1.081216, Accuracy = 0.8426666855812073\n", + "Batch Loss = 1.148535, Accuracy = 0.8106508851051331\n", + "Batch Loss = 1.112431, Accuracy = 0.8293333053588867\n", + "Batch Loss = 1.068065, Accuracy = 0.8266666531562805\n", + "Batch Loss = 1.096894, Accuracy = 0.7906666398048401\n", + "Batch Loss = 1.032260, Accuracy = 0.8533333539962769\n", + "Batch Loss = 1.066560, Accuracy = 0.8284023404121399\n", + "Batch Loss = 1.165151, Accuracy = 0.8146666884422302\n", + "Batch Loss = 1.084572, Accuracy = 0.79666668176651\n", + "Batch Loss = 1.230495, Accuracy = 0.7486666440963745\n", + "Batch Loss = 0.995977, Accuracy = 0.8600000143051147\n", + "Batch Loss = 1.026274, Accuracy = 0.8269230723381042\n", + "Batch Loss = 1.085740, Accuracy = 0.7960000038146973\n", + "Batch Loss = 1.036215, Accuracy = 0.8373333215713501\n", + "Batch Loss = 1.102915, Accuracy = 0.7806666493415833\n", + "Batch Loss = 0.927328, Accuracy = 0.9053333401679993\n", + "Batch Loss = 0.965782, Accuracy = 0.8853550553321838\n", + "Batch Loss = 0.951709, Accuracy = 0.8946666717529297\n", + "Batch Loss = 0.965536, Accuracy = 0.874666690826416\n", + "Batch Loss = 0.977196, Accuracy = 0.8659999966621399\n", + "Batch Loss = 0.941533, Accuracy = 0.8826666474342346\n", + "Batch Loss = 0.957944, Accuracy = 0.8735207319259644\n", "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", "\n" ] @@ -618,7 +618,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -630,11 +630,11 @@ "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", "INFO:tensorflow:Restoring parameters from /home/alexandre/Documents/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n", - "Batch Loss = 0.896259, Accuracy = 0.8966666460037231\n", - "Batch Loss = 0.965855, Accuracy = 0.8573333621025085\n", - "Batch Loss = 1.054638, Accuracy = 0.8046666383743286\n", - "Batch Loss = 0.829448, Accuracy = 0.9120000004768372\n", - "Batch Loss = 0.944326, Accuracy = 0.8528106212615967\n", + "Batch Loss = 0.866899, Accuracy = 0.9213333129882812\n", + "Batch Loss = 0.937756, Accuracy = 0.8640000224113464\n", + "Batch Loss = 1.042669, Accuracy = 0.7886666655540466\n", + "Batch Loss = 0.863248, Accuracy = 0.9206666946411133\n", + "Batch Loss = 0.916062, Accuracy = 0.8831360936164856\n", " * Serving Flask app \"neuraxle.api.flask\" (lazy loading)\n", " * Environment: production\n", " WARNING: This is a development server. Do not use it in a production deployment.\n", @@ -646,7 +646,12 @@ "name": "stderr", "output_type": "stream", "text": [ - " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n" + " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", + "127.0.0.1 - - [04/Nov/2019 20:01:56] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", + "127.0.0.1 - - [04/Nov/2019 20:02:33] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", + "127.0.0.1 - - [04/Nov/2019 20:03:06] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", + "127.0.0.1 - - [04/Nov/2019 20:03:55] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", + "127.0.0.1 - - [04/Nov/2019 20:09:05] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" ] } ], From 7b342309c4dc62cd8b46f718293f429dfc014bd4 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Mon, 4 Nov 2019 21:00:17 -0500 Subject: [PATCH 17/30] Fix Notebook Demonstration --- Call-API.ipynb | 52 +++++++-------- LSTM_new.ipynb | 167 ++++++++++++++++++++++++------------------------- 2 files changed, 106 insertions(+), 113 deletions(-) diff --git a/Call-API.ipynb b/Call-API.ipynb index 4baa068..f65e593 100644 --- a/Call-API.ipynb +++ b/Call-API.ipynb @@ -113,7 +113,7 @@ " self.url = url\n", " \n", " def transform(self, data_inputs):\n", - " data = json.dumps(data_inputs.tolist()).encode('utf8')\n", + " data = json.dumps(np.array(data_inputs).tolist()).encode('utf8')\n", " req = urllib.request.Request(\n", " 'http://127.0.0.1:5000/',\n", " method=\"GET\",\n", @@ -141,19 +141,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[ 1.18599892e+00 5.41315079e-01 5.01255989e-02 5.32620525e+00\n", - " 7.96708488e+00 1.05193663e+00]\n", - " [ 1.32715702e+00 5.79396248e-01 1.43231392e-01 5.13996363e+00\n", - " 8.01558208e+00 9.53553915e-01]\n", - " [ 1.38326144e+00 6.24054670e-01 1.96704865e-01 5.10453653e+00\n", - " 8.03478718e+00 9.46316242e-01]\n", + "[[-0.03531179 0.18158735 -3.0826726 3.00903678 5.91984081 -2.97888589]\n", + " [-0.02582957 0.31272477 -3.01284409 2.68027282 6.0538106 -3.13478899]\n", + " [ 0.12298633 0.45438862 -2.9635911 2.63191962 6.16904402 -3.11883259]\n", " ...\n", - " [-2.59126878e+00 1.63446629e+00 -1.15046430e+00 -4.65883112e+00\n", - " -6.26892710e+00 -4.49010229e+00]\n", - " [-4.39656258e+00 -5.00252008e-01 -4.03625870e+00 -6.34975433e+00\n", - " -6.50696850e+00 -4.80641174e+00]\n", - " [ 5.38370669e-01 2.58066088e-01 -1.84605551e+00 -2.31253886e+00\n", - " -5.79619408e-03 -4.39832497e+00]]\n" + " [ 5.13892221 9.45673847 4.57333708 -0.56252116 4.21247244 1.09177899]\n", + " [ 4.97646046 8.32038403 4.04000902 0.38786072 3.45109177 0.72300702]\n", + " [ 4.45698881 7.74218273 4.54759359 -0.06746368 2.63403916 0.53912646]]\n" ] } ], @@ -193,32 +187,32 @@ "output_type": "stream", "text": [ "\n", - "Precision: 83.53261074023052%\n", - "Recall: 82.62639972853749%\n", - "f1_score: 82.6189023152726%\n", + "Precision: 79.75777133050042%\n", + "Recall: 79.33491686460808%\n", + "f1_score: 79.35276493576643%\n", "\n", "Confusion Matrix:\n", - "[[439 10 45 0 2 0]\n", - " [103 320 45 1 1 1]\n", - " [ 47 23 347 1 2 0]\n", - " [ 8 6 0 361 116 0]\n", - " [ 6 2 0 66 458 0]\n", - " [ 0 2 25 0 0 510]]\n", + "[[358 50 40 14 34 0]\n", + " [ 62 361 48 0 0 0]\n", + " [117 31 271 0 1 0]\n", + " [ 1 4 1 363 122 0]\n", + " [ 3 3 0 61 465 0]\n", + " [ 0 0 17 0 0 520]]\n", "\n", "Confusion matrix (normalised to % of total test data):\n", - "[[14.896504 0.3393281 1.5269766 0. 0.06786563 0. ]\n", - " [ 3.4950798 10.8585 1.5269766 0.03393281 0.03393281 0.03393281]\n", - " [ 1.5948422 0.7804547 11.774686 0.03393281 0.06786563 0. ]\n", - " [ 0.2714625 0.20359688 0. 12.249745 3.936206 0. ]\n", - " [ 0.20359688 0.06786563 0. 2.2395658 15.541228 0. ]\n", - " [ 0. 0.06786563 0.8483203 0. 0. 17.305735 ]]\n", + "[[12.147947 1.6966406 1.3573124 0.4750594 1.1537156 0. ]\n", + " [ 2.1038344 12.249745 1.628775 0. 0. 0. ]\n", + " [ 3.970139 1.0519172 9.195793 0. 0.03393281 0. ]\n", + " [ 0.03393281 0.13573125 0.03393281 12.317612 4.1398034 0. ]\n", + " [ 0.10179844 0.10179844 0. 2.0699017 15.778758 0. ]\n", + " [ 0. 0. 0.5768578 0. 0. 17.645063 ]]\n", "Note: training and testing data is not equally distributed amongst classes, \n", "so it is normal that more than a 6th of the data is correctly classifier in the last category.\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0kAAAMxCAYAAAA+CPjsAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOzdd7icVbn+8ftOpIoU6b1KFxECKBCKglJEQQFBKfF4LCAgiooICggq4k8PiKBylKKCoCCg4gEshCQgYBClSJFepIXeIcnz+2Otl732y7Rdktk78/1c11wzb1+zZrLzPrPWepYjQgAAAACAZEy3CwAAAAAAIwlBEgAAAAAUCJIAAAAAoECQBAAAAAAFgiQAAAAAKBAkAQAAAEDhDd0uAAAAAIDZZzs7pnW7EJKuky6NiO26XY5GCJIAAACAHjJN0tRuF0KSpcW6XYZm6G4HAAAAAAVakgAAAIBeM3YEtJXMmNntEjQ1AmoHAAAAAEYOgiQAAAAAKNDdDgAAAOglljTW3S6FNKPbBWiOliQAAAAAKBAkAQAAAECB7nYAAABAT/HIyG43gvvbjYTaAQAAAIARg5YkAAAAoJdY0htGQOKGEYyWJAAAAAAoECQBAAAAQIHudgAAAEAvsUZI4oaRi9oBAAAAgAJBEgAAAAAU6G4HAAAA9JqxZLdrhZYkAAAAACjQkgQAAAD0EpvEDW1QOwAAAABQIEgCAAAAgALd7QAAAIBewjxJbVE7AAAAAFAgSAIAAACAAt3tAAAAgF7DPEkt0ZIEAAAAAAVakgAAAIBewjxJbVE7AAAAAFAgSAIAAACAAt3tAAAAgF5D4oaWaEkCAAAAgAItSQAAAEAvsaQ30FbSCrUDAAAAAAWCJAAAAAAo0N0OAAAA6CU2iRvaoCUJAAAAAAoESQAAAABQoLsdAAAA0GvG0lbSCrUDAAAAAAVakgAAAIBeYtGS1Aa1AwAAAAAFgiQAAAAAKNDdDgAAAOglzJPUFi1JAAAAAFAgSAIAAACAAt3tAAAAgF5DdruWqB0AAAAAKNCSBAAAAPQSi8QNbdCSBAAAAAAFgiQAAAAAKNDdDgAAAOgpJnFDG9QOAAAAABQIkgAAAACgQHc7AAAAoJeQ3a4tWpIAAAAAoEBLEgAAANBLLBI3tEHtAAAAABiRbB9s+9e277YdxWNCi2PWt/0z2/faftn2E7ZvsP1j24t1cl1akgAAAACMVEdJWqjTnW0fIOlE9W8MmlvSIpLeKukkSdPanYcgCQAAAOg1oydxw42Sbpc0VSlgWqLZjra3lfR9pQ6Fr0g6VdLlkp6VtJykzSQ938lFCZIAAAAAjEgRMb56bfvQNrt/UylAkqRPRcQZte2nd3pdxiQBAAAAGNVsLydpXF58SdIStm+0/aLth2yfnvfpCC1JAAAAQC+x58Tsdm8rXs8r6dvF8lKSJkja3vY7IuKedieb42oHAAAAwKiwmO2pxeOTQzjXwrXl/0jaMz/+k9ctKem4Tk5GSxIAAADQa0ZGS9K0iBjXfreOvFRbPiYizpEk2wtL+mFev0MnJxsRtQMAAAAAQ3BvbfnuJq8XsN02BiJIAgAAADDa3SDpqWJ5pSavH4iIme1ORnc7AAAAoJdYo2aeJNvvkTR/Xpy/2LSB7SoomhIR02z/WFKVJvyIYvsRxXFnd3TdiBhsmQEAAACMMuMWnjembrFSt4sh/+6269qNSbJ9j6QV25xq64iYaHs+SX+StGmT/a6R9O6IaDuhLN3tAAAAAIx6EfGipHcrtSb9U9KLSgkd/inpMElbdhIgSXS3AwAAAHrLKJonKSJWGuD+L0k6Pj8GbXTUDgAAAADMJrQkAQAAAL1mlCRu6BZakgAAAACgQJAEAAAAAAW62wEAAAC9xBo1iRu6hdoBAAAAgAJBEgAAAAAU6G4HAAAA9BKb7HZt0JIEAAAAAAVakgAAAIBeQ+KGlqgdAAAAACgQJAEAAABAge52AAAAQC+xSNzQBi1JAAAAAFAgSAIAAACAAt3tAAAAgJ5istu1Qe0AAAAAQIGWJAAAAKCXkLihLVqSAAAAAKBAkAQAAAAABbrbAQAAAL3EInFDG9QOAAAAABRoSQIAAAB6CinA26F2AAAAAKBAkAQAAAAABbrbAQAAAL3EksYwT1IrtCQBAAAAQIEgCQAAAAAKdLcDAAAAeg3Z7VqidgAAAACgQEsSAAAA0EssaSyJG1qhJQkA0DVOdrV9ru17bL+QH3fldR+yPbbLZXyX7StsP2M78mOl2XTtrfL1Js6O6/U620fl+j6q22UB0F20JAEAusL2cpJ+I2kjSSHpBklTJc2UtIqk3STtntdt1KUyLi/pIklvlDRR0v25rM91ozxoznZIUkTw8ziAISNIAgDMdrYXk3SlpBUk/UXSfhFxe22fZSR9RdKes7+Er9lW0gKSfh4R+3Th+tdKWkvSC124di/6gaRzJE3rdkGAWcskbmiDIAkA0A0/VAqQJknaLiJere8QEf+RdIDtc2d34QrL5ee7unHxiHhB0q3duHYviohpIkACIMYkAQBmM9tvkfShvPiZRgFSKSImNzjHEra/a/t22y/Zfsr2JNv72H5ddyvbZ+SxJhNsr2n7fNvT8rF/t/3h2v4Tcveto/OqI4vxSGeU+1TLDa7ZcHyL7bG5nFNsP2T7ZdsP277G9jdsz1vs23JMku3xti+0/ajtV2w/aPsXttdtsn9U3dJs7217ah4D9oTt82yv2ui4Zso6sL2o7VNsP2D7Rds32N6z2Hdz25faftL2c7b/YHvNBuecK5ft3Pz5Ppcf/7T9NdtvbFSG+nss32te/9rnYXvVXE8P2Z5h++D6PsVxa9p+Pn9OGzQo7w62Z9p+xPZSA6k/ACMXLUkAgNntfUq5lf4ZETcN9GDbq0u6XNIykh5QGjO0oKStJY2X9F7be0VENDh8A6UuVfdJ+pOklSVtLOkc22Mj4uy83x2SzpS0vqS3SfqnpH/kbVMGWuaa0yXtrdSFboqkxyUtIWl1pe6FJ0l6uN1JbB8o6USluvyrpHskrS3po5J2tb17RPy2ybHflPQFpZa8P0h6h1Lguqntt0bE4wN8T4tIulrSfErdKJdS+izOtj1G0otK3diuk3SZpHGStpe0oe11cgtOZUlJP5P0hKRbJP1d0puVPqejJb3f9viIeDHvX31W++blM9uUdXWlcW7P5vf/RrXozhgRt+a6/qnS92SDiHhOeq1LaHW9fSKi7ecGjAhkt2uLIAkAMLtVv8ZPHeTxZykFSGdK+mREvCJJttdQGt/0EaXg44cNjj1Q0qERcXy1wvYXJH1H0rGSzpakiJgiaUpuUXibpAsj4qhBlvc1tldUCpDukzQuIh6rbd9U0jMdnGd9Sf8jabqkD0bE74ttBygFWj+3vXpEPNLgFP8taYMqSLW9gFLQuImkz0j6+gDf2vuVgqB9i8/jE5JOlfRtSfNL2j0iLszb5pF0iaSt8vWOLs71tKSdJF0SEdOL97WQ0uezg6TPSjpO6vdZ7ZuXJ7Qp656SfiJp/3atmJWIOM32NvnYUyTtk4O/syQtJuk7EXFpJ+cCMDrQ3Q4AMLstlp8fa7lXA7a3UGqFeELSgdUNuSRFxG2SDs+LhzQ5xdVlgJSdKOlJSSvnIGZWWiI/X18PkCQpIq7K45DaOUjSWElnlgFSPscPJF2h1Lr2iSbHf61sxcstI/8vL27dwfXrnpF0QPl5SDpNaXzPspL+UAVI+XovSzohL25VK/+zEfH7MkDK65+WdHBe/JAG73FJn+s0QCp8StKdkva2vbekI5TKfq36vnfA6DFmTPcfIxgtSQCA0WSL/HxBRDzbYPsvlFovVrW9bEQ8WNt+Sf2AiHjV9t1KXcaWkXTvcBa45lal9OE72j5U0tkRcf8gzlPVQ7OuZadJ2jI/jm2w/f8arLstPy8ziPJcV++iFxEzbN+rFBRf1uCYO1tdz/ZGSgHbikotUc4PKXWZG6w/Vd3lBiIinrW9h1J3wh9KmlcpONxzEAEXgBGOIAkAMLtV408WH8Sxy+bnuxttjIjptu+TtGretx4kNQtIqoBrnkGUqWP5RnuCUnev4yQdZ/t+pe6BF0k6v96C0kTLelBfNr5lm2xvVA9DqYMHmqx/rsX2alu/6+Wuf+dI2rHF9RYcUOn6G3QQHBFTbX9L0pF51WcioiuZDwHMWiO7nQsAMCf6e34e14Vrz5yN12r4f2xEnK+UMGIvpZagV5XGupwj6e957M0sFRHDXQ/tzjeQ6x2nFCDdrJTkYylJc+dJYocjiH2x/S6N5cyDuxSrNh56cYAusFPihm4/RjCCJADA7HaxpJD0NtvrDPDYqmVolUYbbb9Baf6lct9ZpRp/s0CT7cs3OzAinoqIsyJiQkSsKmkdpUQWb5X05Q6u3bIeivWzug5mhV3z8x4RcXFEPFJ0Z1utW4XKvidpPaXugw9JOtD2+7tbJACzAkESAGC2iojbJV2QF0+2PVer/W1vXixOys87235Tg90/KmkuSXc2GI803P6Tn9eob7A9t2oJCVqJiH8pZauT0k14O1U97NNk+8fy8xWdlmEEeXN+btQlcM8G6yqvSq8FysPO9i6S9lMKPD+ilKVwpqTTbDfr1ghglCJIAgB0w35K41S2lPR/ThPM9mN7SdsnKI3VkSRFxCSluXbeLOn7ZYCVz/GNvPjdWVj2yt8kPS9pXduvZVvLAdIJklaqH2D77bZ3dzFhbF5vpdTWUkoP3s73Jc2QtK/tHcoNtvdTCtCeURr7NNrcmp/3L1fmFNzNshZKfa1maw13gWyvoDRP0kxJe0XE4xHxZ6X05otKOiunBAdGB0saO6b7jxGMxA0AgNkuIh61vZmk30h6t6TbbP9TaWLQmUpjdjZU+jHvmtrhH1GaTHaCpHfbvkppIP+7lMas/FLSj2bDe3g+D+I/VtKvbE9WSiU+Tqk163T1tehUVpR0rqTnbV+ndGM/bz5meUmPSKqnKG907X/Y/pxS+vKLcx3cozSZ7PqSXtbondz0WKU6+qbt3ZSCppUkvVNpvFKz7ogXSPqcpD/b/otyYoiI+O+hFMb2WKX5kBaRdGxETCw2f00pA9+WSinBBzq/FIARamSHcACAOVZE3Kc08P3Dks5X+kX+fUoTky4m6ddKg+Q3rR13u6S3K3VPeznvs7lSy84ESR+NiJhN7+Ebkg5QSp/9TkmbSZqoFPQ0ahG6WtJXlLLZrahU9q2U5n06RtJ6EdEsY1392iflY3+rlBJ7N0lLKk24ulFEXNT86JErIn4laRtJk5WC5Z2U7lf2iYjDWhx6uNKYoeckfVDSx/NjqI5U+n5dJemoWlmnK3UBfFrS12yPH4brAbNHt5M2jPDEDZ5N/48AAAAAGAHGLbtgTN1/k24XQz7iT9dFRDcynbZFSxIAAAAAFBiTBAAAAPQSWxpDW0kr1A4AAAAAFAiSAAAAAKBAdzsAAACg14zw7HLdRksSAAAAABRoSQIAAAB6iSWNpa2kFWoHQM+zvY3tsP3FbpdlNLC9Uq6vezpZPxLYPiOXbUK3y9KK7Y1tT7b9ou1HbZ9i+41N9l3I9kO2L5nNZRxj+2u2b7f9Sq7XibOzDLOa7a3mxPc1VLlORuwEm7YXtf2M7Qu7XRaMfgRJAHqa7bGS/kfSQ5J+0OXiYASxPTHfFG41m663rKS/SNpA0mWSHpW0n6RfNznkW5IWlvSZ2VG+wkGSjpa0mKSLJJ0pqW2gNqvqc6TcuNuekMtyRhfLMGqCu1nxfYiIxyWdKOkDtrcervOiN9HdDkCv20fSupIOiYgXu12YUe5BSWtJerXbBRmlviTpjZK2joiJtt8g6Y+Stre9UUT8rdrR9iaSPiXpyIi4czaX84P5edeI+MtsvjbQznclfV7SdySN63JZRrYxJG5ohZYkAL3uQKWb+p93uyCjXUS8GhG3duGmfU6xgaR/R8RESYqI6ZJ+kre9s9opB08/lnS7pONncxklabn8fFcXrg20FBFPSbpQ0oa239luf6AZgiQAPcv2xpLeLunSiHiswfajcneQo2wvY/t02w/bfsn2v2wf0OLcb7J9pO0bbb9g+1nbf7N9kO252lxrVdu/yONNZtg+uME+K+Z9HrH9vO2rbb+nON/7bU/J/fOftH2O7WWalPNTtn9r+848FuYZ29fa/my+Ie+0PpuOSbK9vu2zbd+Rr/FkHtNyhu0NGuw/t+0DbF9l+6lc57fYPsb2m5pcf27bX8nnfcn2g7Z/bHvxTt9D+T4kbZlXXV516WrUPcj2eNsXOo0heiVf9xe21x3IdSUtKumJ2rrH8/O8xbrPSnqbpP0i4pUBXqOfPL5ogtM4qKqeb7P9HduL1fadmOtl5bzq7mZ1UjtultRnLncUy+U5y/VL2D7Y9mW278nv8Unbk2zvM5h6a/AeJ0o6PS/uWyvLGbV9B/Tdtj3W9j753/NDtl92+jt0je1v2J4373eGpMvzYVvWyjBxAO9lVdu/tD3N6W/XP23v1+aYbZ3Gz91g+4n8nu6y/SPbK9b27ej7YHsu23vbPjf/m34uP/7pNCau4Vi97Gf5uWW5gVbobgegl30gP7frMrSCpOskvSRpoqSlJI2XdJLtBSPim+XOtpdQullZW9I0SX+QNJekdyn1l9/F9vYR8VKDa60uaaqkZyVNUup+9UJtn5XyPk/l66wiaRNJF9veRukG+nuSJiuNbdlU0oclrWf77RHxcnGut0n6kaSHJd0m6VpJS+RjTpC0je33R8Sgx3w4BW8XK/2fc11+zKtUr3tLulXS34v9F1aqs3cqBQ3X5jrYSNIRSvW3RUQ8URwzVml8zHaSnlfqpvaypA9J2lbSjQMo8nNK42y2k7SkpEuV6qfy2mvbByp9ppb0V0n3KH3uH5W0q+3dI+K3HV73Hkkb2Z4rIqoui2vl57vz9VZQGg/0s6rFabBsW9IvJe2uVFeXS3pG0maSviDpw7bfFRF35EMuyWXcVel7eb5SXUn966duVtXnHfm8++blM5tc/z1K4w7vk/TvfN5llb7j421vEhFDHdd1idL3ezNJd0qaUmx77fVgvttKwdfeeb8pSoHzEkp/K74i6SSlOpyi9LfpvZIeUf9xYrd28iZsv1XSFZIWUWop/GM+5w9sr9Hi0B8q1enNSt+juZT+tnxK0m62N42I2/K+nX4fllQKdp6QdIvS34g3S9pY6d/A+22Pb9JNerKk6ZLeZ3tMRMzs5P33FJvsdm0QJAHoZVvl56vb7PcxpaQOB0fEDEmyvavSgPrDbJ8YEc8X+5+idGN3mdK4jWfzMUsr3XRspfSf/KENrrWnUher/Ysb5bp9lfrdf6n6z9/2N5RumE5VuoEaHxF/zdsWVroxXEvSHup/M3mPUvA2sQyEbC+pdDP3PqUA65yWNdTaYUr/3+wREeeWG5xatxau7X+q0k3k2UqtJc/kfedVCuj2VQrgylaAA5Vuuu6StFVE3J+PWUgpQHt/p4WNiGmSJuRf35eUdFyjgMT2+ko339MlfTAifl9sO0Dp5vXntlePiEc6uPTvJG0v6Tjbx0h6i1Kw8oxScK58zpfz+qH6jFKAdL+k14Ih2/NIOk3SRySdpRSAKyKOy9u3UgqSvhAR97S7yKyqz4iYImmK7X3zdSY0KcJ1kjYux3Tlc66q9APJ/rZ/HhHt/g60eo/H2X5YKUia0qIsA/pu51aYvZUCvHH1Fm/bmyp9PxQRP7F9h1KQdGuLMjSUg+afKQVIP5J0QPH3brxaJ+c4ROlvyNPF+cZK+lp+nKj077Pj74OkpyXtJOmS3PW0Ou9CSvW3g1Kr6nH1AyPiBds3KvUUWE/SP9rXANAfISSAXrZ+fr6lzX73Kt0QzqhWRMR5Sr+aLqBicHC+qfmg0jinT1UBUj7mIUlVF739q24yNY9L+lyLAElKrQpfqf06+p38vLqkk6sAKV/3KaWbHqkvMKy2PRARl9dbivJN/Zfz4odalKUTS+TnS+sbIuI/EfGvatn2OpJ2U/rF/7+qm8i870uS9lf6lXxP228uTnVQfj6sCpDyMU/nY2ZF9rODJI2VdGZ5Q5+v+wOlX+QXlPSJDs/3v0oB++clPanUyrCMpEMj4jHbOysFe18ub5Ztz5dvcAfq8/n5sKK1SLml8TNKN6kb2958EOcejOGuz+rYW+oBUl5/p6Rj8+JQv+NtDfK7Xf3bub5Rl+CIuCoi6i3NgzVe6W/iNKVENuXfu8nq+xvyOhFxURkg5XUzIuJIpYQu2zbqSthKRDwbEb8vA6S8/mlJB+fFVp9b9Xd9/Rb79LYx7v5jBCNIAtCTcn/2+SXNULoZbOXyWhe1StV9pBzrM16pq9CkRr+y519M71YKrjZscM4/RcRzDdaXJtbHouRAqBq/clmDY6pkCo3GJdn2lraPyOMKTs/jGz6dd1m9TXnamZqff2H7nfkX5ma2y8+/bVTn+YZwqlLL1Lhc/uWVxsm8LOm8BsfcIOmGwRe/qS3yc7NuXqfl5y2bbO8n3wxuqRQE/FiptXDTiPiR7QUkfV/SVcrJHGzv6zT+6wVJz9k+Le/Xlu3llOrsFTVoJczfp98MpPzDYFjrs5THt2xv++g8Tqb6ju+adxnqd7wTA/5uK3WTe07SjrYPzd/1WaWq1wubBF4tk9s4jZPc3/YJtn/qNN7wDKWud2MkrTaYQtneyPaXbJ9cfG5H5M2tPrfq7+ESLfbBKOA0pvDXtstxkG3nvXMaz3flQI4p0d0OQK+qung918F4m/ubrK9aieYp1i2bn+9ucb67lG5Ql22w7d42ZZGkB5qsf05p8H+j7VXgVZZVtpdSygS1SYvrLdhBmVr5sqQ1Je2YH8/ZvlbSn5RaDf5T7LtKfj7E9iFtzlslZKjq8f4WYw/uURojMZzafdZ31fZrKwe/P1FfVrvK1yUtLWmHiIg8zusMpTr8rFK3oiOUPt+PDqDs95UtBkMt/xANe31Kku01lcartbqhHup3vBMD/m5HxLP5pu4nSt3KjrN9v9L4o4sknV9vaRmCql7vabK92XrZPlbp33mrH0AGVMc54D9H6W/GYM5ZtdTVu/Ni9DlK0kKDOO7LSmMPB4UgCUCveio/L2DbbQKl2Tnot5O5mtqVZyDl/YlSgDRZ0pFKLS5PR8R026srtZYNqU9ERDzklIp3c6UxN1sotbi9S9JXbe8WERfn3aubrGvVWTfIOZ7ttyt1RfteRNyUVx+hFPh+KHfbusj2KpL2tv3ViCA9d5/zlAKkCyV9W+k7/UxEzMjB5qUa4ne8Q4P6bkfE+bb/rBQsbKv0b2fP/LgxJy9o1xo+y+TxmYcrBSUHKyVueKhqLbN9ldI4rIHW8XFK7/lmpfGbUyU9ERGv2p5bqeW4leqm+qmWe/UqazQlbrhRacqDqUoBU9vWQaesqUcqdbV+RbUfCDtBkASgJ0XE87afVxqAvpCG7z/SB/PzKi32qbY92GKfWS53OdxeqcvhTg1utAbVPaaR3MIzKT9ke0GlhA5fVhqLU3UDrFrtLouIr3Z4+qoel2+RyWqlwZS7g+uuqvR5Nvosh/w52x6j1PXuQaVkH5V1JN1SjmuRdI3SIP911H4Oo6pMK9ge26Q1aXZ/T4e9PnMr0jpKY312bfA+h+073oHBfLclvdb98az8kO21lboljlP6N3TYMJSvqtcVm2xfqcn6qsvi4RFxeoPtg63j6rx7FD8ODOSc1biuRwd5fYwQETG+em27UcKjfvJ4358rdfX8H6Vxws2+102NmhASAGaBKuPR2sN4zslKv1xtYXul+kbbWyp1tXtOKetWNy2k9P/As01+id5zVl0439x/RekXvqXdN5dRlUFrlxwgdHKu+5W6As2j9J9hP07z66w3iGJW476a/aA4KT83m2vnY/n5ikFcu7KfUnroA6J/BkUpBfil+fNz25bEiHhAqVvb3EoZD/vJGcR2yYtDKX9pVtXnq9Jrk+zWVTfKDzUJBF/33oeg3fsb8He7mZzs5H/yYvndbleGVqr639n2fA22N+vGWdXx67ol2363+rrF1rUra9PzqrO/TdXf9es72BdzluOUPv+bNYQfEAiSAPSyifn5HcN1woi4V9IFSv/xVwPuJb2WVvukvHhKNJ4naXZ6RKkFbWHb/W46bO+lzsa2tGX7kJwooG5bpZv0Z3I5FBHXSfqt0q//Z+U6q59vSdv1DGdVvX6rvFZusTpFg+tOVf2yvlaT7d9XaoXb1/YOtTLup5RJ8Bm9fnxRR5xSxn9DaSD972qbb5C0lu0qeUUV7ISk+q/uzVQ32d9ySoddXXdupZT3C0u6NqfaHg6zqj5bnfffSkHjuk5prKvz2fZXlLquDZeW728w323bb7e9u2uZMG1bKQW2lNKD18uwWpOgsZVJSt+rxSV9p0ywYnszNZ+YtZqD6RMuJsrOPxL9sMX12n0fqvPuX650mguu5Zgu2/NLeqvSHEuzImnLHGAEZLabBdntcmB+kFIQvleTpEsdIUgC0MuqSSnfNczn3U9pzMF7Jd2Vs/JcqHTD9lal4OzIYb7mgOVf1quJcM+2PcX22bb/odRV4dvDdKmvSrrP9k22z8vX+Kv6flk/rJbyfF+lgel7KNXflfmY39i+SdJDko6pXeP7SnNQrSbpVtsX2f61Urez5dX3WQ/EBfn5O7Z/a/sn+bGGJEXEPyR9TikgvjiX8yzb1ysFZi9L2iciWk202soJSuNYDmqw7RilwO8vti9QuhHcQNIZOVDvxMlKc30tL+km23+wfY5SJsS9lBKADEugnM2q+qzO+2fb51Tnzed8TCl19RskXW77T7Z/qTQu6euS/t8wvr+rlSZC3cD2VNtn5rJ8rNhnoN/tFSWdK2ma7SuqfZXGLH1U6YeO46ud82d/vdL8QzfY/nkuwxfbFT6Py9xb6QeLz0i6zfYv83ioKyT9tMmh31cKXneU9G/bv7J9idLfwIeUMjI20vL7oL707N+0/ff83q9S+nd+Ypu3s6XSv52LO0jMg+5aLP97qR6fHOyJnOYEPEPpb+PX8t+UQSNIAtCzIuJapRuK99oetjSxEfGoUjKEo5X6w79P0jZKA08PlvTeEdCKJEmKiO8o3bD9Tanbzg5KcxLdzxYAACAASURBVPTsqDQWZjgcoBR0WdK7Je2s9Gv1ryRtFhGn1Mr0lKStlbpX/VXSGkrzy2wq6SX19TEvj5muNPHkV5VuzLZTmtjzIqWWwicHWuiI+K3Sr9i3Kn1+H8+PpYt9TlJq4fitUnKA3ZRuUM+WtFFEXDTQ60qS7e2UJno9qpz3qbjuX/L2e5Q+q4WVbhw/0+k18titPST9l1LXz82Vuti9pJR+fINy/qShmoX1ebik7yl1Yf1gcd7KgUr1crNSAoFtlf4tjleaaHi43t/LSt+7i5W61O6Vy7Flsc9Av9tXK3VLnaIUMO2iVD9PKAVT60VEPRvgB5X+bb1ZqVvax9U6Q1z5Hm6QtHE+fhGlf6tLKP3dOrjJMXcoTWdwntIYkJ2Uxi99W9J7lLtDNjiu5fchIn6V109Wqs+dlO5b94mIdl2o9s7PrVqyeluVuKHbD2laRIwrHqcO4V0dK2k5pe/Md9rs276KCLAxmuUuQmfnxYciYpna9huUfrmXpAsi4oPFtgWV/qOpuhSsFxE3Ftv3Uv95IV6RtHREPNGgHGco/UIoSVdExFYtyryVUvafysrlfDq2j1HfHBAh6bMRcZJTGtjXBsVGhItjJqr/3CEfjYizi+31a85Xv0nP9TFB6T/T9ZT+g31J6Sb/bqVf/S+c07Jm5V95T5P0xYgYzl+VAQCzWW5NeFApscm4dvv3qnErLRJTjxjuThQD50/85rqBfE5Oc8NVSRg+FhFnFNsulPSBDk/19nYtTbQkYbSbXLxeutavfhFJ6xbbN6sdu6n6AqQn9fp+/BNqy3NrFg5klyTb31b/AOnT+ZfVgTp6IP3RbVe/rJ6o9MvfUkrvd0Gl7kvbKv2y/M1m5xjFfqb02R/ixoOVAQCjxyFKSUzadjEEWiEFOEa1iHjA9t1KTfFSmn/lzvx6M/UfrL2E7TUi4rZi38qVZb9lp1nNt25wyQlK/fiHne0TlCaFlNJA44+Xv5AM0GpK3Tn+t4Prbi7pd+qbQ+AJpT78f5X0vFJXl03Ul451jpLnSvm8pMuUuoUNuYkeADD72V5U6f/RiyLi8nb797xZkDhhVnCaz6zK3jl/sWkD29X0HVOUxs1NbHCKryl1H5WkXyrNVdZ2KgGCJMwJqv7KUupjfnrxWkqD5ZeXNG9ed1tte3WO0j7qa2m9QGlg/0KSxtleO6dfHS62fYr6MgfNUOpzfXaLYzrxVds/a5XZxX1zsFQB0oOSNo2I+2q7npMH/q4zxDKNSBHxR82eySQBALNIRDyu1AMCc5ZT1XieowPzQ5K2bpAFVJJk+2D1BUmXdfoDNN3tMCeYVLwe3+D1n5UmWXxtne15lOYeqdSDpH2L1z9VGpBamTDYgjZxsvoCpFeVJs4bSoB0dX5eXrXUqQ1spP5zBB3RIECSlAbGR8Q/h1AuAACAUYGWJMwJygBnNae5RZ5SyrZTbX9MKbFBFThtor7WkxdVTOqZ54N4S16cJulSSS+oL1vSXrYPazIx4WBsn59fkbT7YLNhFa6Q9KzSOKLDbLfqcrdhbblKySyn+X3Wb3DMdRHx4hDLCAAAusWussuNeBGxUjeOJ0jCqBcRt9t+RGnsjJQCoUeVEg9IKUh6NL9e2fay6t/idE1EvFIsl61I50bEdNtXKM0ZspxSetL3SPq/4X0nekjS1GE61+FKQdLiSmlbm00GuUhteVrxek29voVNShP/3Vpfmec2+KQkzT/fXBuuuvKiAyxyb7vvgWW7XYRRZexw/UTRQ+YaEUnnR5fpc7ffB/3NzU9oA/aQrpsWEYt3uxzojyAJc4rJ6kssUAVJknRPRNxv+wlJ05W+8+PVZDxSzm62e7HtLCnNJ5InIKyy5UzQ8AVJoTQeZkVJE22/q9G8KAM6YcTfcirMnSV9QWl+kEaeqi0vqjQ54WCueapSv2Gtt87S8btz9m1zBEqf/fI3ul2EUWX+pxlCNlDL3EKdDdS0FZkmZaBWun50tE6MJEfLnU4APbxGSeKGbuGbjDlFOS5pC/UFQZMlKSKel/T3vG5rpfTfKvfJdlFK0FC5ynbYDvVPJ/qBPBfDcPi0Upc/KWWlu8J2owGKA/VVpSx5C6l5KtTrasvbVi8iYmqei2llAQAA9BCCJMwpykBnXfXNiTS5wT4fkfSm/HqGUqrrSqfNH/MozVQ/HC5Tmkn8hby8slKgNKTgJCJuUkp1KaVZ5hu5VtItxfKxtpcaynUBAABGO7rbYU5xg6SnlVpNxqgvj34ZJE1SmmRugWLd9RHxnCTlsUrbFNsO0+u7o20l6cP59QSl+YTqVrF9XIP1N0fEzxsVPiL+bHtHSb+X9EalrndX5K53dzQ6pkNH5vI2/LeeuxHurxSozZWve73tkyX9TSnb3kaNjgUAAKOUNWoSN3QLQRLmCPlm/0pJOxSrH4uIMsHAFPWN/6mUQVQ5N9JtEfG6QMf2ZPUFSZvYXrN2DSml3j60QTEvktQwSMrvYaLt7ST9Qamla3n1BUq3NTuulYi40/ZpygkVWlx3F0lnKo1JWkrSMc12VxrbBQAAMMcihMScZFJtuV9Gt4h4Qq9PYFDuU3a1O7/RBSLiZkm3F6smDKyIrUXEFEnvlfRMXrWMUjKHtYZw2q9LapnXKiIuVkp7fqhSCvFpSsHQi5LulnSxpC9JWmWILVsAAKDrnBI3dPsxgtGShDlGRHxb0rfb7PPWFtvW7PA6azRYN0EdBkwRMVH9W7Pq2/+q/skjKrdIOqPJMVu1ON+DkubroFxPSjo+PwAAAHoWLUkAAAAAUKAlCQAAAOglljSGtpJWqB0AAAAAKBAkAQAAAECB7nYAAABArxk7srPLdRstSQAAAABQoCUJAAAA6CU2iRvaoHYAAAAAoECQBAAAAAAFutsBAAAAvWYMiRtaoSUJAAAAAAoESQAAAABQoLsdAAAA0Ess5klqg5YkAAAAACjQkgQAAAD0GuZJaonaAQAAAIACQRIAAAAAFOhuBwAAAPQSWzOZJ6klWpIAAAAAoECQBAAAAAAFutsBAAAAPSQkzSS7XUvUDgAAAAAUaEkCAAAAegyJG1qjJQkAAAAACgRJAAAAAFCgux0AAADQQ8LWjLG0lbRC7QAAAABAgSAJAAAAAAp0twMAAAB6DNntWqMlCQAAAAAKtCQBGHb3PLyMPn78Md0uxqhy+rcO63YRRpXPfulb3S7CqLPAE/wuOlALPMEv7ZhDWYox/E1ohdoBAAAAgAJBEgAAAAAU6G4HAAAA9JAQiRvaoSUJAAAAAAoESQAAAABQoLsdAAAA0Etsutu1QUsSAAAAABRoSQIAAAB6SErcQFtJK9QOAAAAABQIkgAAAACgQHc7AAAAoMeQuKE1WpIAAAAAoECQBAAAAAAFutsBAAAAPSRszTBtJa1QOwAAAABQoCUJAAAA6DEkbmiNliQAAAAAKBAkAQAAAECB7nYAAABAj6G7XWu0JAEAAABAgSAJAAAAAAp0twMAAAB6SFiKMbSVtELtAAAAAECBliQAAACgp5jEDW3QkgQAAABgRLJ9sO1f277bdhSPCbX95rb9Sdtn2/6X7cdtv2L7IdsX2N5yINelJQkAAADASHWUpIU62O/Nkn7cYP1SknaWtLPtT0bE/3ZyUYIkAAAAoJdYmjl6EjfcKOl2SVOVAqYl2uw/SdI5kv4tafV8zOJ52/dsnxURL7S7KEESAAAAgBEpIsZXr20f2mLX5yVtGRGTinV/sv2wpPPz8gKS1pV0bbvrEiQBAAAAPSQkzfSclbghIp5VakWqu622/Fwn5xs17WwAAAAAMEAfLl7/W9ItnRxESxIAAACAbljM9tRi+dSIOHW4Tm57D0lfyYuvSvpEREQnx9KSVLC9Z5FW8D8Ntt9QbP9NbduCtqcX299a275XLW3hy7bf3KQcZxT7TWxT5q1q512ptv2YYttM2wfm9RPK42rHTKyd8yNtrjlvg3ItaPsg25fm1Isv237a9r9tX2b787ZXafXemrzfe4rrHtVge8PUkPX3Wzxesf2g7d/Y3qrB+ea1/WXb19l+Nu//qO2bbJ9r+7Mt6q3dY0KD69XP8csm9fCG2n57Fdv+u8n1XrR9h+3TbK/V5Lxr5+/fPfkze972/bavtH2K7Xe2/oQAAMBoMHOMu/6QNC0ixhWP4QyQPifpbEljJb0safeIuKLT42lJ6m9y8Xpp26tGxJ2SZHsRpYFelc1qx26q9CFI0pOSbqptn1BbnlvSnpJOHkqBW7H9bUlfyosh6dOD/PIdbftXETG9w+tuK+nnkpasbZpb0oKSVpO0raSNJe0xiPIMp7kkLSNpF0m72D4oIk6SJNtzSbpc0jtqxyyeH+sofQ9OHI6C2F5Z0ha11TvbXiginh6GS8wradX82M32OyLi5uL6m0r6k6T5imPmljS/pOWUvuOPSvrrMJQFAABg2Nm2pO9JOjivelrSLhFx+UDOQ5BUiIgHbN8taeW8agtJd+bXm0kqR7gtYXuNiLit2LdyZdmUZ3t5SVs3uOQEzaIgyfYJkqpWjpmSPh4RZwzydKtJ+piktnnlbW8u6XeS5smrnpD0I6Ub6+eVAqdNJO06yLIMlypTyvJKqSFXz8vfsX1uRDwq6aPqC5CelHSkpH8p/btZU9IOxXGSdKD65/HfXn1NvOU1K7fXlvdV/++YlAKbPdQ4738nZkjaSikY3FjSN5VakBeQ9BlJ+xf7Hq++AGmipFMkTVOad2AjSR8cZBkAAABmOdvzKP1Qv1tedZ+kHcofhTtFkPR6k9UXJI2XdHrxWkoDvpZXunkdr76MGeUNcNkiJUn7qK9r4wWS3qV0Mz3O9toR8a9hK30KoE+RtF9eniFpn4g4e4jn/artn0XEyy0uPEbpZr4KkB6UtGlE3Ffb9RzbX1RqiemKiJhSvXZKDfmXvDiPUovJhUpBReWMqoUpu1TSibbfVJzzxvIatldrds26/KvHPuX11Nf6uK8GHySV173c9taS3puXV6jtulHx+jO17+X5kr5cvl8AADA6hT1q5kmy/R6lXi0qniVpA9tP5ddTlH6Mv0R9DRdPSfqipEXyj/iV2/OP4S2NjtqZvcrUgeMbvP6zpGvKdTlqLW8w60HSvsXrn0o6r1ieMNiCNnGy+gKkVyXtMcQA6er8vLz6tzo0spGktYvlIxoESJKkiJgeEf8cQrmG01O15bnzc9nFbQ/bH7PdL7DI6SaHwxbqC87/o9QK+Hxefqft1RseNTQP1JafKV4fb/vdthcodxjG9wsAANCJU5UaGS5Q36SwUurBU61fV6m3Utmza2FJ5yrdl5ePHTq5KEHS65UBzmq2l7Y9n6QNi+1VIFUFTpuor/XkRUnXVSewvZmkt+TFaUotEGcV19jL9lgNn+3z8yuSdouI81rt3IErJP0xvz6sftNcs2Ft+ZLqhe0FbG/e4DGfusj2cpK+Xlv9j/z8h2Ld0pJOk3RvTtxwgVMyjuFqjZ1QvP5lRDwj6aIm2wck1/PWufVum7z6ZaVukKWLi9c7Ko1Petr2zbZPsr3+YMsAAABGlhl21x8jGUFSTUTcLumRYtV4pSCoal0og6SVbS+r/i1O10TEK8Vy2Yp0bk5+cIX6fsVfWtJ7hqn4pYckTW27V2cOz8+Lq28QXCOL1JanFa/X1Osj+cmSVhymMg5IlfFN0v2S3ldsOjN/BxQRkyUdptQiV1pc0s5KfV6vdIPsfgMsyxvVf4zWL2rPkrR37s44UGOV6vkvSmOOxirNMr11RPyjtu8hkq6srRuj1Dp4gKS/l9n86mx/0vZU21NffXlas90AAAA6FhErRYTbPCZGxD0d7OdOx+gTJDVWtiaNV18QdE9E3K+UhGB6g+39js2tJLsX286SpIiYKalM7TxhWEqdVAkjVpQ0MSeNGNoJI/6mNEZHkr6g1wdDlXq3tUWHeu0GZhav+/0Ekcf1NNu3nceUEjh8olwZEccpZYP7olJCisdqx22s1oFjJz6klEhBkv5VBC9/VMomJ6Xscu8e4nUq60hatr4yIh6PiM2Vxsx9T6lbaRnwW6kb3jKNThoRp1YpPOeaZ7FhKioAAMDsR5DUWDkuaQv1BUGTJSkinpf097xua6WB/ir3yXZR/2xnVxUtGF8s1n/A9sLDUXBJn1bq8ielrHRX2B6O1pqvKgUdC6l/2UvX1Za3rV5ExNSIsPrG3QxWOSamfie+eG25VdrsKrh9h1IQtGREHB0R9VYjRcT9EfH/IuL9Sv1dN5V0V7HLJp0WvokJxeu1i+/Iq5KWaLJfp2bkel9Sfd083yjpF7bXaHRARFweEYdExDuUAt3PFZvnlvT2QZQDAACMECFp5pgxXX+MZCO7dN1TBjrrqm9OpMkN9vmIpCrj1wz1n0Om7GrXyjwavvmCLpO0k6QX8vLKSoHSkIKTiLhJfa1fzSYUvVbSLcXysbaXGsp1GyhTOG5bGxP0vtq+TbMGRsSU/LgmIu5qNPuy7XfYXrp2XETEX5XquTLof0c5gN2qw913sb3gYK6Ts7j8t6R786p5JH2rVpad6mOsIuI5Sd9XGsNU4e8GAACYo5ECvLEblFohFlK6IazSDZZB0iSlMRxlIoPr802l8lilbYpth+n13dG2kvTh/HqCXj+QXpJWsX1cg/U3R8TPGxU+Iv5se0dJv1dqNVhRKVB6V0Tc0eiYDh2Zy9vwexMRM23vrxRAzJWve73tkyX9TallZKNGxw7AL5Qm4ZVSQoyrbV+i1OJSptC+NiL+PcRrvU/Sl2xfppTE4Fal97C+pL2L/YYyueo+6us2eLOkHzTY53Cl7nZV982fDOZCEfGS7W+p73u2s+31IuKGvPy/ksL2BZKuUsqy90ZJe6kvMcl0pc8SAABgjkWQ1EC+2b9S/VMEPhYRtxbLU5RaK8txMGUQVc6NdFse29KP7cnqC5I2sb1m7RpSSr19aINiXqSUOKDZe5hoezulDG1vyuepAqXbmh3XSkTcafs0SZ9sc91dJJ2p1FVrKUnHNNtdfWO7Oi3DH2z/WNKn8qoN9fqsetMk/ddAztvCXEqZ3nZssv1WpUlXB6tsbTwjIl4XKOducdW4pwkaZJCUna7UdXJZpe/uV9U34ZqUPq/91JdGvu6bEfHwEK4PAAC6zooRnl2u2+g209yk2nK/iUAj4gn17/pV36e8+T2/0QXy7L+3F6smDKyIreVJRN+rvvlvllFK5rDWEE77dUkvtbnuxUqtPIcqZfKbphQMvSjpbqVU01+StMpgWrYi4tNK2eD+TymxwXSl7oU3SfqupLcNZmblBn6oFIydk8/9WL7Ws0pj0o6WtElO1z1geWKzVYtVDb8nkn5TvN6sPkntQOTMi8cXqz5ku5rb6kNK7+kvku5Uep/TlbI9/kHSLhFx5GCvDQAAMFq4wVAMABiSBRfdMDbe4er2O+I1p3/x8PY74TWf/dK32u+Efta7dDin5AMwXI6Wr4uIcbPzmuust2z88nefnp2XbOhtK31ttr/3TtHdDl1newVJK7TZ7b6IuG92lAcAAAC9jSAJI8F/KSWFaOVopXmMAAAAgFmKIAkAAADoISFppklN0ApBErouIo4SrUQAAAAYIQghAQAAAKBASxIAAADQY2aOYZ6kVmhJAgAAAIACLUkAAABAL7E107QktUJLEgAAAAAUCJIAAAAAoEB3OwAAAKCHhKQZY2graYXaAQAAAIACQRIAAAAAFOhuBwAAAPQYstu1RksSAAAAABRoSQIAAAB6SIiWpHZoSQIAAACAAkESAAAAABTobgcAAAD0ElvBPEktUTsAAAAAUCBIAgAAAIAC3e0AAACAHkN2u9ZoSQIAAACAAi1JAAAAQA9hnqT2aEkCAAAAgAJBEgAAAAAU6G4HAAAA9Bi627VGSxIAAAAAFAiSAAAAAKBAdzsAAACgh4StmaatpBVqBwAAAAAKtCQBGHbPLzRT1273fLeLMaocufdx3S7CqPLOf1zU7SKMOs+P2aXbRQAwgpC4oTVakgAAAACgQJAEAAAAAAW62wEAAAA9JCTNGEN3u1ZoSQIAAACAAkESAAAAABTobgcAAAD0EuZJaovaAQAAAIACLUkAAABAjwnmSWqJliQAAAAAKBAkAQAAAECB7nYAAABADwlJM0V3u1ZoSQIAAACAAi1JAAAAQI+ZSeKGlmhJAgAAAIACQRIAAAAAFOhuBwAAAPQUa6ZpK2mF2gEAAACAAkESAAAAABTobgcAAAD0kBDZ7dqhJQkAAAAACrQkAQAAAL3E0gxaklqiJQkAAAAACgRJAAAAAFCgux0AAADQQ0jc0B4tSQAAAABGJNsH2/617bttR/GY0GT/xWx/1/a/bb9k+wnbf7T9voFcl5YkAAAAACPVUZIW6mRH2ytKmiRphWL1PJK2kbSN7a9FxDGdnIuWJAAAAKCnWDM1puuPDt0o6TRJ+0t6tM2+P1VfgHSNpF0kfUXSzLzuaNubdnJRWpIAAAAAjEgRMb56bfvQZvvZfqukd1eHSdo1Ih6QdKHtVST9tyRL+pykq9pdl5YkAAAAoMeE3fXHMHtX8freHCBVrixeb93JyQiSAAAAAIx2qxSvH65tK5cXtb1wu5MRJAEAAADohsVsTy0enxzCud5YvH6ltq2+vEC7k3UtSLK9Z5HC7z8Ntt9QbP9NbduCtqcX299a275XLUXgy7bf3KQcZxT7TWxT5q1q512ptv2YYttM2wfm9RPK42rHTKyd8yNtrjlvg3ItaPsg25fafii/36dz6sPLbH8+98UcENv31K79qu1nbN9p+//yNVtmG7H9Ntun2r7V9rM5FeN9ts+zvZPdv63V9o+L651d27Zortdq+0G17TsW26ZV5x5qHdvexPavbD9g+5X8Pu7N5z3R9rpNztPucU+D+mp0jjWa1O2xxT531LY90OA8020/bnuS7f1tj21wzjfkbVfZfip/5tPy5/cb20c2KgsAABg9qnmSuv2QNC0ixhWPU4fwtp4vXs9T21Zffq7dyZombrC9xAAK9ZqIaJd1ojK5eL207VUj4s587UUkrVts36x27KaSqhu8JyXdVNs+obY8t6Q9JZ3cYdkGzPa3JX0pL4akTw/ygz7a9q8iYnqH191W0s8lLVnbNLekBSWtJmlbSRtL2mMQ5Sm9QdKb8mMVSdtJOsL2RyPijw3Kdoykw5UGyZWWz48PSbrY9p4R8WzeNllS9SvC+Npxm9fOtbmk7xfL5f5TIqJfQFrouI5t7yzpPPV93yRpLqVfIFaQtKVS1pX6d3CwJjRZd9gwnHuspDcr1dN4SRtJ+lhtn/MkfaC2btH8WEMpS8zRw1AWAACA4XRX8Xqp2rali9ePR8RT7U7WqiXpYUkPDeLRkTyY6u5i1RbF683U/2Z4idqv6eW+V5Y3w7aXV+MBWRM6LdtA2T5BfQHSTEn/NYRIeDW9/sa12XU3l/Q79QVIT0j6pqSdlAav7SnpBEkPNDzBwJyudGO9k6RvSJqW1y8u6fe2+wU0tg+RdIT6PseLlG6w3yvpfyTNyOt3lHRWceik4vVytlculutBU325/F5MVnMd17FSWasA6UKl97C1UsB5ovrX7fXqC0CqR9kH9vTatl3LC9leoL4u29v2UFp9/zdfbxdJVxfr97X9WnBtexv1BUgvKQVm78mP/ZU+w5eGUA4AAIBZ5S/F6xVsl3MllfeIl3dyslYpwI9XahGZlSZLqm6CxyvdRFavJenfSi0O8+Z1t9W2V+co7aO+4O8CpWBhIUnjbK8dEf8attJLtn2KpP3y8gxJ+0TE2S2O6cRXbf8sIl5uceExkn6svubDByVtGhH31XY9x/YXJa0zxDLdFxFT8uvf2/6hUvrEFZRarX5oe72ImGl7UaWJvyoXRMQHi+XLbD8g6bt5eSfb74mIyyLiPtv3qS/H/Xj1BdPV536DpPUkLWV7tYi4w/Z8kjYsrtEqSJI6q+MlJK1UrNo3Ip4pls+1/TnlPrAR8bSkKcV22S7PX9ZhIx9SX3/aKUq/gqwmaVmlSdAua/mOmnvturYflvTXqnhK/74eycsbF8f8LiKOK5b/qPQZv2mQZQAAACPIzNd19BmZbL9H0vx5cf5i0wa2qxahKRFxo+3LlX7MtqRf2/6WpLWV4gMpxTYndnLdpkFSRHx5AOUfrEnqK3QZ+FSv/yxpLaUuTeMl/cT2PErdhCr1m+F9i9c/VWpd+XhenqC+Fp/hcLKk7fPrVyV9JCLOG8L5rpb0DqUb1/2VWjGa2UjpQ68c0SBAkiTlbmX/HEK5Gp3zQduHK3X1k1IQNk7StUqtQ+WAuEYzG5+i1FKxWF7eXX1BwCRJe+XX4yX9zPYbJW2Q131PfQHieEl3SNpEKViTUp/Uvzcp+kDq+FmllsEq6D7Z9o8kXRcRL0lSbsVs26+1QxOK179Qaho+stg22CCplQeL108Xr99r+wBJl0i6s2qtLbpFAgAAzA6nSlqxwfoD80NKgdFEpXv+SZKWU/rx94LaMV9v84P1a7qd3a4McFazvXStRWCy+rpfVYHTJuprPXlR0nXVCWxvJukteXGapEvVvyvXXo0Gqw9BFSC9Imm3IQZIknSF0i/2knRY7n7VzIa15UuqF7YXsL15g8d8QyxfXX0c0rj8vF6x7lU1CNBykFGO43lb8br8XlTNo+9QX1D/R6VgTOr7XpTNqFe3GG/UcR1HxIvq3yS7l1ILz7O2r7d9vO23ND56YJySgGyZF1+R9Gv1/+7u7DZJMlpYIX/+H1Bf650k/Soiyi6yf1Rf9pcFJZ2k1Jr7hFOijk+5QeIQAAAwuoSsmR7T9cewv6+Iu5XukU+QdKfSfc1TSl3xPhARR3V6rgGVzsnutn9i+3e218vrF87r64OkWoqI29XX1UdKN7xli0AZJK1se1n1b3G6JiLKlH5lK9K5+Ub5CvWNG1laaXzFcHtI0tRhOtfh+XlxSQe32G+R2vK04vWaSnVXu/VJkwAAIABJREFUfzSKwofi8dpylXO+vJl/PCJmNjm+/OzLY8pxSavnbm/V535XRPxHrw+eW3XBrOu0jvX/2bvvMNmqKmHj77pXsiIoIBKVIBJUkglBAUXEgCJBHQkXRzGnGTEOA5IUZ/jMiTGggIBiVgQVlCgqiMogBkBhQBEQASVze31/7FPU6brV1VV9uyt0vb/nqaeqztlVte651d1nnb332pTVmVuLMjwI2BI4GLg8Il4yzXt0Y3+a87e+l5m3ZOYfaCaDK1B622biVZRj8g1K0ZO7KfPK9qs3qn4eXwPc2fL6VShFOj4F/LoaTrmEiDgoqhKeefvN7ZpIkiT1JDMflZkxze3HtfY3ZuZbM3OjzFwuM1fNzGdm5rd6+dyuk6TqCvJZwCmUK+rPpTlU6p+Uq86vbf/qjuontI0J7QB/ysz/o8yfuL/N/kmvrXpJ6ieRJwFUJ+gn17YvmkGMU2nM2Vof+HFVNGLp3jDz55STWYC3sWQy1NBalaPtiescW73leSOm+rCth3coOlCvyPfAazLzt8BNtX31//dGctT4v9+oOu5PrbXvmCT1cIzJzD9REqIXUIZXXkqz6ASUSnefWpoelogImsNOYXIPUv3xopl+RovlKT1zy7buyMzPU+ZhvZFS6e76liYbM0V1u8w8rlHCM1ZerV0TSZKkkdBLT9KhlKpzL6MkBQ/M9qp6bL5Gudrcq3qvwdNpngyfV713fX7JTpQr4dTbVPZgcm/EhY31YShX/BteGF2sstul11CG/EGZYH9ORMxGb80hlLkwD2Vy7HWXtDzfpfEgMy/OzKBZFGOutPbKNXrTfl3btgyTh9IBUM0tq5d5/3VLk/r/7TMpJ/X17RfSTFbeRLPgwX1MruA2lW6OMQCZuTgzv5OZb8jMrSnJYb2owSqU8tgztQOwYe35l2vf3frkwu1mOLzvEEpCtCvNZPSZlAsbS8jMmzLzY5m5d2auQ0kS63O8njyDGCRJ0hCZIAZ+G2a9JEn7AJ/JzFNp9uzU/Z6ZnZTXT4a3oLkm0nlt2vwLZY0eKCfIP6m1qQ+162Q5ln69oIbvU3oYGsOTHk1JlJYqOcnM/6XZ+/XUKZr9DLii9vzIXoc7Lo2IWAc4srbpNzSTpO8yeUGv97Ck19LsiQQ4tWV//f9/P5rVTBrJ8z+AX1bbXlNr+4vMbB0utoRujnFELIiI3auenvpr/04Z61q3NANru/3u9tr2AZl5X2Z+n8nrLR0QEVs2nkTE4yNiwzav/RXlIkjDoOcySpIkzalOJcBbrUMZajSVOyiTvXv1a8rV7YdSTr4mnQxXzgX+nckV0y7NzH8CVHOVnlXb9y6WHI62I9CYO7KIMr+i1QYR8f422y/PzBPabCczz4qI5wHfofRmrE9JlHbOzCvbvaZLh1bxtv0/qkptv46SqC1Tfe6lEfFx4OeUHpUntnvtDK1Xrcv0UEpS8RqaQ/zupSyeO1HF9reIeC+ljDzAnhHxNeB4Ss/brsCba+/93eoEvq7ew9j4f/9rNU+n3mYbJn8vppuPVNfxGFO+j98Erqni/ylwI2V43qtr7W6nJIk9i4gVgb1rm44B/tTS7Ak0E8H9IuI/O8zzms5ngHdSSqwHpZdpz2rfdpQKfudQCoFcTvn/2oTJ/1/1ixOSJEnzTi9J0t9ZcvXauk3pYTHZhupk/wLKHKeGm6p5KQ3nU+b/1K/o10+G62sj/a5lfRcAIuI8mknSkyPisS2fAaUs9DvahPlNmqWu2/0bfhwRzwFOp/R0rUszUfrdVK/rJDOviojPAQdN87l7AF+gJCxr0r7cNpTjN1XFt24cSPsFWG8CXp6Zk5KTzPyviHgY5XgGZTjkHm1efwalh7DVryjJRz3xbk2AzgPe2mZbV7o5xpX123xO3ds6rbc0jT1p9o7eChySmffVG1SFEl5J+Xldj7L21w9n8mGZeV9EfAD4WLVpj4jYoupZg/JztBPtF2QG+DOThxpKkqQRkwETMdzD3Qatl2EzZwOL2k1Qr4ZevYKZr+NybsvzSfXLM/MWylXtqdrUhyB9td0HZObllCGBDYt6C7Gzqub6rpQTe4C1KMUcNl2Ktz2cUoms0+d+lzKZ/h2USn43U5KhuyiLsH6XsjbUBkvZswVlDs8/gaspyc2bgI0zs7UUeCO2d1HKgn+GUkr6Tkqv0/WUuvV7AM9tWaC18drFlHlHde2SpPqCxwlc0Ns/aepjXM2125WSFJxP6eG5k9JLdz1lCNrOmfk/PX5mXf27+63WBKmK429M/hlZtBSfB2X9sMYFjUZvEpRCDYsoSfevgBso36U7KBX+/hvYKjOvQ5IkaR6Lao3I6RtGPJYyD+ZqSsWtY4CjKSeMb6Bc5d6qqgYmaYwt3GCrXOmIH03fUA/Y6wMzGa08vjb95TcHHcLIuWNBu858SYP2XuKSzNx2+pazZ/1tN8h3/vTofn5kW6970Mv6/m/vVtfD7TLztxHxbODzlAQJ4N3V/e+BfU2Qhl9ErEcZstXJtZl5bT/ikSRJkoZNL3OSyMyLImIzymT5TSlDdf5AWdR1phPJ1V+voBQs6OS9wGFzH4okSZI0fHpKkgCyjM+7mGa5Z0mSJEkjIgkLN0yj5yQpIlYDngdsUG26Gjg9M2+azcA0NzLzMOwlkiRJkqbUU5IUEQdTqoEty+Ry3PdExGGZeUz7V0qSJEnSaOg6SYqIV1MKNvwK+DDNxTM3pyw0eXRE3JqZn571KCVJkiTNmsThdp300pP0FuAS4GmZeW9t+88i4kuUNW3eCpgkSZIkSRpZvSRJjwbe2ZIgAZCZ90TEiZR1kyRJkiQNsYlYMOgQhlovR+f/gJU67F8RuG7pwpEkSZKkweolSfok8KqIWL11R0Q8AjgI+MRsBSZJkiRJgzDlcLuI2Kdl0/XAzcDvIuLzwG+r7ZsCB1BKgf95LoKUJEmSNDsSmLBwQ0ed5iSdQjmGjSNYf/zWNu23Ab4EnDpr0UmSJElSn3VKknbrWxSSJEmSNCSmTJIy88x+BiJJkiSpH8LhdtOw9p8kSZIk1fSyThIAEbEF8GRgVZZMsjIz/2s2ApMkSZI0N+xJ6qzrJCkilqMUc9idUsChXVGHBEySJEmSJI2sXobb/QfwQuBY4DmUpOhVwIuBnwE/B7ac7QAlSZIkqZ96GW63D/DVzHx7RDy82vbHzDw7Ik4HLq7aXDbbQUqSJEmaHQksDofbddJLT9L6wI+qxxPV/bIAmXkvZY2kl89eaJIkSZLUf730JP2TZlL1D0qitGZt/y3AI2cpLkmSJElzxMINnfXSk3Q1sDFAZt4PXEGZj9TwQuD62QtNkiRJkvqvlyTph8CeEdF4zWeA50fEbyLiN5RiDl+Y7QAlSZIkqZ96GW53DHAqsBCYyMwPR8RKwL7AYuBw4KjZD1GSJEnSbEmCiZ76SsZP10lSZt4G/Kpl29HA0bMdlCRJkiQNiimkJEmSJNVM2ZMUEU+ayRtm5s9mHo4kSZKkuZZWt+uo03C7iyhrTXUrqvYLlyoiSZIkSRqgTknSa/sWhSRJkqS+cZ2kzqZMkjLz0/0MRJIkSZKGgYUbJEmSJKmml3WSJKkrj/jjQg7ad5VBhzFSJhb2MgVUdyzYY9AhjJxXXXrwoEMYORte+a5BhzBy3rn3wwYdgrqQONxuOvYkSZIkSVKNSZIkSZIk1TjcTpIkSRozDrfrzJ4kSZIkSaqZUU9SRCwAVgVuy8z7ZzckSZIkSXMlCRbbk9RRTz1JEfG4iDgduAP4K/D0avsaEfHdiNhx9kOUJEmSpP7pOkmKiC2AC4EtgdOgmX5m5o3AasCiWY5PkiRJkvqql+F2RwA3AVtXr3t5y/4fAHvNUlySJEmS5kg63K6jXobbPR04LjNvpaxB1epaYK1ZiUqSJEmSBqSXJGlF4JYO+x8MpqSSJEmSRlsvw+2uBrbqsH9H4LdLFY0kSZKkOec6SZ310pN0KnBARDy9ti0BIuL1wPOAk2YxNkmSJEnqu156kj4A7AqcBVxGSZCOiYjVgPWBc4CPznqEkiRJkmZNAovTnqROuu5Jysy7gZ2A/wSWBSYole7uq7Y9JzMXz0WQkiRJktQvvfQkkZn3Au+rbkREZGa7SneSJEmSNJJ6SpJamSBJkiRJo8fCDZ11nSRFxD7dtMvML888HEmSJEkarF56kk6hzPNqTTtbe5NMkiRJkiSNrF6SpN2meP2GwGuAW4HDZyMoSZIkSXMjCdLhdh11nSRl5plT7YuI/wEuBh4DnDELcUmSJEnSQCxV4YaGzLwrIr4IvBH4yGy8pyRJkqS5MdH9SkBjaTaPzp3AurP4fpIkSZLUd7OSJEXEasBBwDWz8X6SJEmSNCi9lAA/fYpdDwMeB6wAvHI2gpIkSZI0dybSwg2d9DInaWuWLPedwC3AmcDHMvPs2QpMkiRJ0niLiPWBg4FnUab2LAf8Hfhf4ATg85nZmqMstV6q26052x8uSZIkSe1UCdIvKCPX6lYDdqxu2wKvm+3P7mpOUkSsGBFvj4hnznYAkiRJkvongcXEwG9deBXNBOl24BXArsC3a20OiogHz+oBosskKTPvBI4ANpjtACRJkiSpjVVqj3+QmZ/PzO8Dh9e2L6xus6qXOUlXA2vMdgCSJEmS+inI0Sjc8H3g9dXjXSLiQOA6ytqsDd/OzNtm+4N7KQH+KeAVEfHQ2Q5CkiRJkuoy81vAWymF4lYGPkdJnF4A3AscDbxkLj67l56kGyhjAX8XEZ8F/kBZQHaSzPzyLMUmSZIkaf5aLSIurj0/LjOPa2lzHXA9SxZvWBbYBzgDOG+2A+slSTq59vhdU7RJwCRJIyUiVgHeTrkqsQHl5+LvlAsDlwHfz8wTqraLgM83XpuZERE7Aj/q4SMbiy6v38NrdsrMH0dEvcTlgZl5fLu4KON2n11/g4j4U+0zX5uZn2r9kIh4NrAv8BTgkcAywE2UY3E+8J3MPKuHuCVJ0pBJYKK7wglz7ebM3HaqnRHxMuBL1dM/AHsBV1b3xwMbAd+LiE0y8/rZDKyXJGm32fxgaRhExKrAzyg/ZHWPqG5PAB5NqcM/SnaJiGdk5jndNI6I1YETgWe32b1OddsWeEtErJCZd89eqJIkSW3VS3t/IjN/XT3+YkS8FdgSWAl4PvDp2fzgjklSRKwH3JSZd2XmmbP5wdKQeDPNBOlaShXHq4EVgM2B3YGJad7jUmCHlm1fARpri32eMoa2oZFgLF/b9grgwOrxDcDeLe932TQxtHM08LTpGkXE8sD3gG1qm08Dvg78hfLLZ3PKL6DtZxCHJEnSTKxee7xy40FERP05MOs1E6brSfojsB/Nbi5pvnlS7fGxmfmZ2vPvAh+IiId0eoOqosr59W0RcU/t6bWZeT4dRMSzak/vma59l7aLiOdl5nenafdmJidIr8zMz7a0+Q5wTERsCdw3C7FJkqRBSVg8GtXtfgVsUj1+a0TcSLmYvSeTlyb6+Wx/8HRJ0kgcPWkp1EtGvi4i/gr8ODP/2tiYmf/of1hL5W+UOVUbAUdGxOmZmR3aL6o9PqdNgvSAzPzl7IQoSZI0rcOAXYBVKWsmfbJNm69mZi9zw7vSSwlwaT6q97JsApwC3BAR10XEyRGxe9WlO0ruBw6tHm/JkkP3HhARKwKPrW06o2X/VhGxfcutl4ITkiRpyDQKNwz6Nm2cmVdQzmU+CvyGUll7MaUk+DnAq5mjEuAmSRprmXki8HHK74u6tYGXAt8Evj6CidIpNOcxHR4RU61EvWrL85tbnp9AKatZv7223RtFxEERcXFEXHwnN80sakmSpJrMvDYz35SZm2fmSpn5oMx8eGbumJnHZebiufjcbqrb7RARXVfBy8wvLkU8Ut9l5hsi4qOUHpcdgCczeQLgCylXKU4ZQHgzkpkTEXEI8A1KD9n+UzS9teX5w5fiM48DjgNYK7btNLxPkiRpqHWT/BxU3aYTlKvxJkkaOZn5O+BIgKrX5VmUtcEaPS1PZoSSJIDM/GZE/IxSnOJQyqJrrW3uiIjf0ZwUuQtwTG3/FgAR8WPgGXMdsyRJ6o8cjcINA9NNknQccNFcByINQkTsBFyamQ/0qFTdtmdGxE+B51SbR3Vo6nuAH9B54drjgfdVj58ZES/NzJFKCCVJkmZTN0nSeZlpCXDNV/8KvDgivgP8CLiK0iO6PaVXpeEnA4htqWXmDyPiR8BOHZp9mDL/6gnV85MiYjfg25Q5SqsB681poJIkSUOk67lG0jy2AmU+0lRV4M6lLA47qt4DXDjVzsy8KyJ2BU6lDKlbQJnDNNU8pntnPUJJktRH3VWXG2cmSRp3hwE/o/S0bAI8glK04R/AFcBpwMfnqnJKP2TmT6qesud3aPPXaujhC4CXU+YxrQkspBR3uBL4KWVR2bPnPGhJkqQBMknSWMvMK4GPVLdu2h9PmcMzXbtH9RjHYZSEbbp2bS/7TBdXZr6gi/dO4FvVTZIkzVMJTFi4oaOOSVJmjupkdUmSJEmaEZMgSZIkSapxuJ0kSZI0ZhY73K4je5IkSZIkqcaeJEmSJGnMpCXAO7InSZIkSZJqTJIkSZIkqcbhdpIkSdIYcZ2k6dmTJEmSJEk1JkmSJEmSVONwO0mSJGmcZLhO0jTsSZIkSZKkGnuSJEmSpDFSCjcMOorhZk+SJEmSJNWYJEmSJElSjcPtJEmSpDGTFm7oyJ4kSZIkSaoxSZIkSZKkGofbSZIkSWOkVLdzuF0n9iRJkiRJUo09SZIkSdKYmcCepE7sSZIkSZKkGpMkSZIkSapxuJ0kSZI0RhJYbOGGjuxJkiRJkqQakyRJkiRJqnG4nSRJkjROMkiH23VkT5IkSZIk1diTJEmSJI2ZiQl7kjqxJ0mSJEmSakySJEmSJKnG4XaS5sTEwhx0CJJqDjj26EGHMHLuvmvLQYcwcg7j8kGHoC64TtL07EmSJEmSpBqTJEmSJEmqcbidJEmSNE4SJhxu15E9SZIkSZJUY0+SJEmSNGbSnqSO7EmSJEmSpBqTJEmSJEmqcbidJEmSNEaSsHDDNOxJkiRJkqQakyRJkiRJqnG4nSRJkjRmJnLQEQw3e5IkSZIkqcaeJEmSJGmMZMLiCQs3dGJPkiRJkiTVmCRJkiRJUo3D7SRJkqQxk66T1JE9SZIkSZJUY5IkSZIkSTUOt5MkSZLGzITD7TqyJ0mSJEmSauxJkiRJksZI4jpJ07EnSZIkSZJqTJIkSZIkqcbhdpIkSdI4ybBwwzTsSZIkSZI0tCJi+Yh4S0RcGBF/j4i7I+LaiDgjIl42F59pT5IkSZKkoRQRjwS+BzyhZde61e2fwMmz/bkmSZIkSdIYSSAnBh3F9CIigFNpJkiXAR8HrgIeAmwG3D8Xn22SJEmSJGkYPRfYoXp8BfCUzLyztv/rc/XBzkmSJEmSxsxEVbxhkLcuvLj2+BfACRHxl4i4MyIujoj95+bo2JMkSZIkaTg9vvb45S37tgG+EBGbZeY7Z/uD7UmSJEmSNAirVT1CjdtBLftXaXl+HLBbdd/w9ojYbLYDM0nSUIuIVSLi6Ii4LCLuiIh7IuKGiPhlRJwQEftV7f4UEdnDbceWz4mIuLqlzfumiGmjlnY3RsSDW9qcWNt/YofXZkTcGxF/i4jLI+LUiHhxRCzs4nO3r+07smXfJ1pe+6CW/c9q8/4Pioi9I+K06njeUXVn/6kquXlkRDypy/86SZI0rBImJmLgN+DmzNy2djuuJdK7a4//DLw2M88AXgv8pdoewHNm+xCZJGloRcSqwM+BdwFbACsCywKPoFQ52Rd49Sx93DOAR7ds2y8iuvkZWR14y1J89jLAwygVWvYBvgpcFBGPWor3fGVEtP57phQRGwE/Bb4M7AmsTzneK1SPnwq8B/jaUsQkSZLUi2tqj6/NLDX5qvv6vofO9gc7J0nD7M3ARtXja4EjgKspJ+6bA7sDjQKWewHL1177CuDA6vENwN4t731Zy/NFbT5/bWAX4MwuYn1bRHw8M//eRdu61wGXA2sCuwL7U34utwXOjognZebNPb4nlMTrMOCA6RpGxCOAsylrDQDcB3wROAO4mdLVvSXwImC1GcQiSZKGSAKLuyucMGjnAM+rHq8XEQsyc6K6iL1erd01S7506ZgkaZjVh3Ydm5mfqT3/LvCBiHgIQGZeXH9hy3CyezLz/Kk+JCJWovSeNBxPM2k6gO6SpIcCb6f0evXislpsX46IkykLpj2I0rN1KPDGHt+zYd+IeH9mXjFNuyOZnCA9NzN/2NLmG8BhEbHNDGORJEnq1ReA/wBWBtYCPh4R36BcuF2ravNP4Nuz/cEOt9Mwu632+HUR8ZKq1+MBmfmPWficvYDGnKKLgMNr+14UEdN14V5U3b+pNb5eVcnJSbVN+7abnzSNK4DbKT/fR3ZqGBHLAS+rbfpimwSpHt8lPcYiSZI0I5l5I2V0UGPB2NdQRrq8pnp+P/CqzLxptj/bJEnD7Lu1x5sApwA3RMR1EXFyROxercS8tBbVHp+UmX8ELqyerwC8ZJrXH0b5IV2RMm9naf2g9ngVYMMeX38LcGz1+MXT9P5sAqxUe35GfWdEbBcR27fclioRlCRJg5cTMfBbV3FmfhV4CnAacCPlnOvG6vlTM/OUuTg+JkkaWpl5IvBxytDZurWBlwLfBL6+NIlSRKxPKdoA5Yfu1OrxibVm083ruQr4XPX41RGxXqfGXfhby/PW8pfd+CBlPhHAUR3ardryvHX+09nAeS23F7R7o4g4qFHC805m/YKOJEkaU5l5SWbunZmPyMxlqvu9W6dbzCaTJA21zHwDsClwCPB9Jg/BA3gh0/f0dHIApXQkwPdr3bVfpszPAdguIjae5n0Op5SpXJYyj2hprN7y/NZe36Aahvj+6umuwA5TNG1974f3+lm1zzyuUcJzxSX+CZIkSaPDJElDLzN/l5lHZuaulJP45wD1KnJPnsn7Vj1Q+9c2PbexjhClR2WZ2r5F08R4PfDJ6ukBwGNmElPl2bXHt1J6qmbi45Q1BWDq3qTfAnfUnu9S35mZy2dmANfPMAZJkjRkEpjIwd+GmUmShlZE7BQRk4aaZebizDyTsqZPw0y/x9vT/XyfbtZMeh+lwspC4IkzCSgidmVyIYUTM3PxTN4rM++mlE2Hss5Ruzb3UOZ6NRwYEU+byedJkiTNF5YA1zD7V0rhge8AP6L0qCQluan3ePxkhu+/qPb4XODkNm3+H6V4w7rAzkCnym83RcQHKUMDu/W4Kvl6BKWHbH9KkgXwR+C9PbxXO58FDgY26NDmkOqz16YMF/xhRPwP5d96e7X9IUsZhyRJGhYZLO6ycMK4MknSsFuBshBs62KwDecCX+n1TSNixZb3/Ghmntam3a6UWvxQkqopk6TKscDrgYd1Gconpth+MbD3DBeSfUBm3hcRh1EWh52qzV+qdaVOoyzSuzxlbaap1me6d2likiRJGnYOt9MwOwx4M2Uh0ysopa0XU+bp/AT4d+DZMxyO9mKavSN3URZwbedrtcd7RMTKnd40M28DPtBjLPdT5lj9hlIwYk/gKZn5px7fZyonAZd3apCZvwW2ovRkfZsyB+keSkL0F8qK10cDT87MKRMuSZKk+SAyh3zWlKSRs1Zsm69c+PNBh6F5bMFih4n06vx975u+kSb54V1bDjqEkXPYVztek1Mb7yUuycxt+/mZy23x+FznK9/u50e2dfVmj+r7v71b9iRJkiRJUo1JkiRJkiTVWLhBkiRJGjNlGURNxZ4kSZIkSaqxJ0mSJEkaJwkTE4MOYrjZkyRJkiRJNSZJkiRJklTjcDtJkiRpjCQwMWHhhk7sSZIkSZKkGpMkSZIkSapxuJ0kSZI0ThIWO9yuI3uSJEmSJKnGniRJkiRpjCRh4YZp2JMkSZIkSTUmSZIkSZJU43A7SZIkaczkxKAjGG72JEmSJElSjUmSJEmSJNU43E6SJEkaJwmL0+p2ndiTJEmSJEk19iRJkiRJYyTBdZKmYU+SJEmSJNWYJEmSJElSjcPtJEmSpDEz4TpJHdmTJEmSJEk1JkmSJEmSVONwO0mSJGmcJKTV7TqyJ0mSJEmSauxJkiRJksaI6yRNz54kSZIkSaqxJ0nSnFiw2CtUmju3PjIHHcLI2f7EZQYdwsg5jMsHHcLIOQx/9/fqvYMOQG2ZJEmSJEnjJGGx6yR15HA7SZIkSaoxSZIkSZKkGofbSZIkSWMkCavbTcOeJEmSJEmqsSdJkiRJGicJaRXajuxJkiRJkqQakyRJkiRJqnG4nSRJkjRGEtdJmo49SZIkSZJUY5IkSZIkSTUOt5MkSZLGjOskdWZPkiRJkiTV2JMkSZIkjZOECQs3dGRPkiRJkiTVmCRJkiRJUo3D7SRJkqQxE0NQuCEHHUAH9iRJkiRJUo09SZIkSdI4SVi4ePA9SfcPOoAO7EmSJEmSpBqTJEmSJEmqcbidJEmSNEYCWOA6SR3ZkyRJkiRJNSZJkiRJklTjcDtJkiRpnGSwYAjWSRpm9iRJkiRJGgkRsVtEZO32p7n4HHuSJEmSpDETiwcdQe8i4uHA5/rxWfYkSZIkSRoFnwbWBO6e6w8ySZIkSZI01CJif2BP4DbgfXP9eQ63kyRJksZIJCwcocINEbEe8NHq6RvoQw5jT5IkSZKkoRQRC4AvACsDX87ME/vxufYkSZIkSRqE1SLi4trz4zLzuJY2/wbsCPwZeG2/ArMnSWojIhbVy0v28Lo/tJSl/K82bT5Y2393RGzaps1/1drcExGPi4gHtbz3vrX2r2zZd3qb97yutv+VbfZHROwaESdU/45/VPH9X0T8PCKOjYiduz0WkiRpeC2YGPwNuDkzt63dJiVIEbE2cCSQwIGZeUvfjk+/Pkia7yJie2Cjls37RsTClm3vBq6sHi8HfKbqSm68zzbAW2vtj8jMy3oMZ7cqnq5aFVp7AAAgAElEQVRExBrA94EzgH0p/44HV/GtA2xLuZJzVkTYAy1Jkvphdcq5SABn1i5ef77WZv1q+zdm84M92ZFmz6I229YEngN8t7EhM++KiFcA51B+6LcDXgd8rEpAPgs0EqtfAO+fYTxHAc+YrlFErAicCWzZCBH4CvBN4C/ASsAWwPOBp80wFkmSNCQiYcHi0SncMAgmSdIsiIgVgL1rm46nmTQdQC1JAsjM8yLiI8Cbq03vi4hvUXpxnlBtuxdYlJn3zzCsp0fErpl55jTt3kozQYLSnf2FljbfAd4fEVsBI7j8nCRJGkHXM3l0TcOTgJdVj/8OHA5cNZsfbJIkzY4XU6quAFwMHEpJjgLYPSJWzcy/t7zm3cDzaA5tOxXYqrb/yBkMswO4EbgDeDRlHO90SdKi2uOz2iRID8jMS2cQjyRJUs8y8ybgQ63bI2IRzSTp9sxcos3Sck6SNDsW1R6flJnXAudVz5ej+YP8gMy8E/hXyvA2gKdUbQEuZeYLpd0HHFY93jYiXjxVw4hYmcnzqM5o2b91RGzfcltvhnFJkqQhERMx8NswsydJWkoRsQ7QqPq2GDi5enwi8PTq8QHAJ1pfm5nnRsRHgTfVNt/H0g2za3z2O4FNgSM6TGZcteX5zS3PvwRs0rLtKOA/Wt8oIg4CDqqe/vO9xO96irh/VmPJf6c6G75j9pdBB9DR8B2v4ecx691QHrP3DjqAzobymAHrDzqAUZOZx1OmNswZkyRp6e1Ps1f2rMz8a/X4NMrq0MsBT4qITTPzijavfxcliXpo9fxTmfnrpQkoMyci4pAqhs2Al0/R9NaW5w9fis88Dmhd22DoRMTFmbntoOMYJR6z3ni8eucx653HrHceM/XC4XbS0jug9vjZtfKUt9AcPgftq981ht39s7bpxlmK62uU6nhQht8t0+azb2PyRMddWvY/NjMDuGCWYpIkSQMWCQsXD/42zEySpKUQEU8FHtNl83ZrJs2ZzEzgPdXTDYA1pmh6fO3xrhGx9xTtJEmSxoLD7aQuRES7tYruAtaqPb+AMheo1X9T1hpai9JTc0abNnMiM8+IiPOAHTo0+yDwEspaSAAnR8RzKWW//0ZZyG2dOQ20v4Z+SOAQ8pj1xuPVO49Z7zxmvfOYPSBYMOSFEwbNJEnqzjvabLsHuLv2/GOZeUpro4h4JrBX9XQRfUySKu8Bzp1qZ2beERHPppQg34GykO0iphgeSFm/aWRVc6fUA49ZbzxevfOY9c5j1juPmXrhcDtp6TSKLdxDy4KxNV+rPX5hRKwytyFNlpnnMU1ilpl/AXYE9gC+AlxDSQDvA24CLqT0OO0MHDGH4UqSJA1clGkLkiRJksbBMutvnau9+5xBh8ENr1n5kmGtOGhPkiRJkiTVOCdJ0rwWEcvRfqG++zPz6n7HM0oiYk1gK8oFtZ9n5myVp5ckaaiZJEmaNyLi0cBR1dP3ZeZlwLa0L1wxEREbZua1fQtwCEXEk4Fdq6fHZuYd1fY3Ah8Alq323R8RR2Xm4QMIcyRExKrAe4EnUxLLC4FjMvPPAw1sSERE47t0X2ZmRKwHvKZN03sz87D+RTY6ImIN4Pltdt2bme2qq46diFgAZVH16vm6wKvaNL03M4/sZ2zDJICwul1HJkmS5pMXAS8F/g+4vLa93V+CBcDewLF9iGuY7U85Uf1DIwGKiCcAH6J53JKyGPGhEXFpZn57IJEOiYj4T+BQyjIAj6gqRC4HXARsVGu6NbBHRGydmTcPINShERE7Az+gfJe2Bn4NrAu8s9rW2v6czPxRX4McMhHxROCrlOPzosy8FNgY+Aztj9lvM/Pi/kY5XCJiJ+CHQEbENpn5K2A94D9of8zOy8zBT8zRUHJOkqT55DmUP4SnNa4i1rSrUvOMuQ9p6D2huq9XYfxXSoLUOGaNsu8BHNSnuIbZEyjH4vuNnjfgAMoJLNW+xm1t4G19j3D4PJ9yPC7IzF+32V8/Zo324253yhp1f6sSpFatx+yF/QpsiD2PcjwurBKkVq3H7AX9CmzoJCxcPPjbMDNJkjSfPLq6/2mH/Y8G3kX5I7lZP4IacutV9z+vbdu59vgtwIqU0vAA2/QjqCG3GSWBPLO27UW1x7+gXLm+ifI9261/oQ2t7SnH7DtT7D+nuv2Rcsye3Ke4htnOlGP2jSn2X1/dbq+eP60fQQ25p9P5e3ZBdbsGv2eahkmSpPlkjer+r+12ZuY1mXkN8LNq05p9iWq4Pay6vwUgIlYCHlttuxf4TJa1Ik6otj28v+ENpdWq+ytr255Se3xgZh4NHFY9fzRq/Kxd3m5nZu6UmTsBb682bdSu3ZhZq7r/ZbudmbluZq4LvJ5ywv+YfgU2xBrfs/9ttzMzd8jMHYCDq00bt2sngXOSJM0vy1f3K9W2/RJ4Yku7ZVrux1nj70DjxL9RdCCBSzPzzmp7Y1jZXX2MbVg9tP4kIjYEVqEcs+urgiEAv63ul+tjbMNq9er+jtq2O4DfMHkobGPuVl8X3R5SjYs+t9a2TVAuXtSHE19X3XsBo/337E7g90w+ZjdV92P7PQtgQeugdE1iT5Kk+eSW6v6Bq/qZeUdmXpKZl9TaNZKm+snHuPpLdf/aiHgs8NbavgtqjxtXtS0D3jwB27q6f05t309qj1eo7se6aEPlvur+kY0NmfnLzNwiMx9Xa9dI1od8tkJfNJLH1R7YkPmTzFw+M1estVu5pf04a/c9uzQzH5uZ9eHVjYTS75mmZJIkaT75NeUC2WsjYu12DSLiEZThKckUQ3/GzDmUY7YT5Xg8t7avPq5/u+r+mj7FNcx+Qzlmh0XEKUC9jHC9Utbm1X3b4Z9jppGMv6hjq+ZE+hvmMJZR0ejt2LVjK3hWdW8y3vze7D5Nu0ZhEH82NSWTJEnzyenV/cOAn0TEfhGxRkQsiIjVI+JfKFf612xpP86OoTmErl4q/YJGadyIWAjsQUksz+tveEPppOp+eUoZ+caV/DuBU2vtGtUW284pGTMXUb5fe0fEonYNImIfYF/KMftZuzZj5mLKMTugKm29hIh4KmUNoKQUDBl3je/ZPhGxb7sGEbEnsB/j/j1LWLA4Bn4bZs5JkjSffJ5SuW4NSunc49u0afxW/hvwP/0Ja3hl5hURsQtl4dhtKZWyvgv8e63ZbpSLajdS1roZd58Gns3kq9X3Am/IzL8BRMQGNEvMj/V6P5UTKSemAJ+NiP2BMyi9Hw8HdgGeSbP0/Ent3mTMfBnYkzJ38nsR8QVKRcX6MTuQMuctaVagHGcnUhLtAL4wxfdsV5rfsy8NKE6NgChFiyRpfqhO+L8FLNvYVNud1fP7KIszfq/P4WkeiYhnAk8CbgN+kJl/qO17HM0hiqc1kqdxFhFnUk5SpzrxaJy4npOZO0/RZmxUPbg/A7Zk8rplk5rRHDq8VWaO/RybiPgBJeGe7nt2Xmbu2K+4hs1y626da79l8AMD/vi2B1+SmdsOOo527EmSNK9k5g+qROnTwKYtuwO4Anidq6zPTEQ8NjN/O33L+S8zzwLOmmLfZcBl7faNsZdTrupvzZInsI2LGb8GXtLPoIZVZi6OiJcAZ1N6xmHJiz4Afwb2MkF6wL8A36cs+jzV9+xy/J5pGs5JkjTvZOb5wBaUKnevpyzs+QbgqcAWJki9i4gtIuLLTLH+iNqLiM0i4uRBxzEMMvNmyoKn7wJ+Rzlhbdx+V21/ambeNOWbjJnMvJIyDPazlDlvdXdW25+Ymb/vd2zDqvr+bAccAlzF5O/ZVdX2p2SmRRvUkcPtJGnMRcSmlGRyPcqaK5/KzF9X+zYG3kepSrYAyMxcOKhYh0lErA6sS1kb6a8t+x5PORnbg/K31mPWIiJWAFYFbq2tx6UpRMSywCZUxwz4bWbeO9iohl9EPITm9+z2QcczLJZfZ+tc903nDzoMrnzHSg63k6S5Vp1E9GTcTzKqtZEuAh5c27yoqqa1NmUi9HJMHuYz1iJiAaXoxwFUxyUiTgX2p8yF+2ht31RzScZeZt6FixN3rfpd5RDOHmXmP4B/DDoOjR6TJEnzSa8nXIm/Bw8GHkKzqAWUpOhDlHV+lqd5kn89pQreuHs1papY3UuAKylDPOtV2qC5RtDYioiDen1NZh43F7GMiojouXhFZp49F7GMioh4Ra+vyczPzUUsGn3jfnIgaX5pnJja69G9p1f3SZnsDGVxyifRPI7XUobcfT4z70MvbbMtKGXTl69t+z9KUvnZfgQ15D5F7z1qY50kAT+kt2PmRR/4DL1/z8Y2SVpgqY+OLNwgab6ZLkFKHP5UtzbleLwrM3fLzN2Ad9NMOE8EHpuZx5kgPWBzyrH5CaUYyHbAhcAKlON2O/A6YKPM/ERm3jOoQIdQdHlTZx63zvyeaamN+xUHSfNL21XpK6tShpY9lWaSdPecRzT8GsPp6ivP/7z2+N88yV/CQ6v7YzLzpwAR8QHgG5RjuU9muujuZNfS+eLEssAjsSe4VTcXfbppNy7+TOfv2TKUxcbH/nsWCQsmxvoQTMskSdK80a60d0SsCLwFeBvNk9vFlCEWh/cvuqFXL2DxQI9RVbZZky2knGTVj019sdixnhfSTmY+qt32qgjG/sChTD5x/U5/Ihtqy3TYtwNwFGUOXMPlcxvO8MvMddptj4igrNN1GLA6ze/ZGf2JTKPIJEnSvBQRy1CGPL2L5h/FCeBLwKGZefUAwxtG55TziMkiorX6X2bmcv0JaejtHxE7Vo/Xq21/R+uxzMyj+xXUqIiIvSgXKjZpbAJ+BLwnMy8aWGBDot3isBGxFSU52rWxCbiacvJ/Ut+CGyER8SLgCGCzxibgPMr3bPA1sDW0TJIkzSvVlekDKWvUrEvziuE3gEMyc+yvtk6h9e9BTrNd8KqW541jc0SbtiZJlYjYDTgS2JLmz+fPKCetZw0ssCEWEZtQjtmLG5soQ8uOAD6bmfcPKrZhFRG7UI7ZtjS/Z7+gfM/OHFhgQyQs3NCRSZKkeSMiXgq8F9iI5h/F71P+KF4ysMCGX7uB6Q5Wn17rMWqdH9IYPmZiCUTEDpRkcTuax+gyysWLbw0ssCEWEetTeon2pRTbCsowz/cDH3e+4JIiYjtKb9vTaX7PrqB8z742sMA0ckySJM0nX2LyielFlCuHe0bEnu1ekJnv7l94Q6m1N0TTm25yuFpExBnALo2nwB8ow15PGVxUwy0iPga8kjI3KYDbgGOBD2bmHYOMbVhFxHeA3RpPqYYiZuaJg4tqSGWwcLHXwjqJTH/PS5ofImKCHk9eM3PhHIUjqVL72WxcwPgb0GmIWGbm2v2IbVi1OWZXALd0eElm5jP6EduwanPMbqRWiKaNzMz1+xHbsFlhrW1yo1ddMOgw+N/DV7gkM7cddBzt2JMkaT7q9vKYV4mk/mr8zD28um83ZNEhipM1jsWmHdp4zCZrHIs1qnu/Z+qZSZKk+eRc/KPXk4j4l15fk5lfmotYNO91c/HC8T+TeTx65/esC5GwwMINHZkkSZo3MnPHQccwgk6k98RyrJOkiPh9jy/JzNxk+mbz2oGDDmAEHTXoAEaQcyw1a0ySJEndcnhKsRGTFz6dikN6Kpn5hUHHMGoy85BBxzBqMvOzg45B84dJkiRp7IeezIBDeiSNtAUTg45guJkkSZo3InpeGi8zc9x/Dy4z6ABGkMOgehQR+/f6msz84lzEMiqq9X56kpkXzkUso8I5lppN435yIGl+aQxt8gp+lzKzp8QyIjagrD0ythwGNSPH0/uww7FOkoDz6e2YJZ7XOceyWwnhOkkdLRh0AJI0y/ytP8si4sER8YqIOBfotWjBWIuInSNi3E/2e+XP8GQxzY2Wx+qOx0sdjfsVB0nzixW0ZlFEPBNYBOwBrIBFCLoSERsCBwD7AetVm3sebjYPeVLaO+e+9c7joVlhkiRp3ui1glZEjHtZ5iVExEaUxGg/YJ3G5oEFNCIi4iHAPpRjV59LYmIJZKYjV3q38aADGEHOsexSAAtdJ6kjkyRJ80ZEvD4zP95l28cAZwNrz21Uw686wX8ppffjqfVd1X0C1wOnAV/rb3TDLSKeRUmMXkTpbYPJSeX9wDl9DmvoRMTTq4eXZuY/BhrMiMjMqwYdwwh6bGZePuggND+YJEmaTz4SEXdPt1ZGNRzqbGDN/oQ1vCLiJMoJ/vKNTbXdNwJrVI+PzMzj+hnbsIqIjWn2tjWS7NbetgQ+BRySmbf0L7qh9WPKMdkBGOsKbDMVEQFsCKwC3ApclZlj30vZ4uKIOBw4JjMtcK2lYve3pPkkgE9HxH5TNoh4FHAWsFafYhp2L6M53yiAOyjVnp5Lc7idJvsd8E7K8alPmP8Z8OZau1+ZIGlpRcTDIuLDwM2U795Pq/ubI+JDEfGwgQY4XJYDjgQuiojNBh3MUEtYsHjwt2FmkiRpvgngcxHxkiV2RKxH6UFqTKa/vZ+BDbGsbp8HHpmZ+2bmGZl5/4DjGnYJ/AF4L/CYzHxKZn50wDFpHomI9SnJ9xuAVZlc1W5V4I3AT6vfbWraFrgkIt4dEZ7rakb84kiaT46imo8KnBARezR2RMQ6lATpUdWm24Hd+h3gkFsEXBERx0TE4wYdzIi4Afgz5Sq/pufwsC5VJ/dfATaYpumGwJer4Xjj7lhggvI9Ww44gpJEbj7QqIZQAAsWx8Bvw8w5SZLmjcw8JCKWBQ6m/H47OSL2Ai6hJEiNk41/As/LzIsGE+lQOZcyT6Tx12pt4G3A2yLiNwOLavg1qtZtX90+EhGnM64LU3bvtIi4p4t2mZkbznk0w+2FlB6RBBYDJwE/AP4GrA48mzJcdiHwxKr9NwYS6ZDIzIMj4hTgs8DjKT+n21B6lT5FmWfZ+pqj+xulRoVJkqR5JTPfERHLAW8ClqVcif0LzR6kO4AXZOYFg4lwuGTmjtU8rUWUQgSPppkwbUbzyv8bImIl4GuZeU2fwxw2O1HW5NoTWKnathylAMaLau0eFRELM3PIR9731SOn2Z9YNr2hMWT4fuC5mXlWy/4Tq8Ir36YkSi9hzJMkgMy8JCK2Ad4BHEL5O7AsZWhiOyZJasvhdpLmncx8C6WyGJST10dVj+8CXpiZY1+SuS4z/5SZh1VX7ncEvkDpbasXJdgc+G9g7MsSZ+Y5mbmIUh3xQJqV2xrHq3GC/w7gxoj43ADCHFXDPf6mv7agfJe+2CZBAiAzzwROoBy3LfoY21CrLkycAFzK5J/J+u+08f6uJcTE4G/DzJ4kSfNGy+TlDwDr05x3NEGpPHZlvV1mXtu/CIdfZp4LnBsRrwf2oqydtCPjfkLRRmbeQUkov1BNsD+A0hvXGCaWlMn1BwCvGEiQw+fzgD9z3WksUXDmNO2+R0nWp+ulGxsR8TrgfcCDaV7AuI1y8UfqikmSpPnkTyw5TKd+BfHTbfb5e7CNzLwT+CLwxSqpPKC6PXqggQ2pagji4cDhEbEDZfjiXsBDBhnXEPpsZrpOUnca350bpmn315b2YysiNgE+A2zH5MWwPwK8KzPvGlRsGj2eHEiaj+p/HNttVyUi9q8enp6ZbSu0Vb1tRwBHRMTT+hbciMrM84DzIuINlERp/2leIrWzDOV32OYRHQcmNdYD8pwOfkmZf9RwJfCv1c+kagJY6GzJjvyBkjTfxBSP1d7xlBOxHeiijLUFLyAi/kgZvrl3Zv5iqnbVVesTqps0U58YdAAjZDmaF8c+DLzb3iPNlEmSpPlkp0EHoLGwPuVEbPlBBzJCrqEcs7sHHciI6nTBx0qAk10FvCIzzx90IEMtGfp1igbNJEnSvGHVOknzTDdnsZ7pNn2I0nvUVTIeEStn5u1zHJNGlEmSpLETEfsCL6YsWLnnoOMZEl6N1lyy9613Gw86gFGTmf82XZuICGAXSnGV3SkV8KQlmCRJGkebUxb9NDFoOr+cO0wrM9O/HcUrIuJZ3TTMzMPnOhjNL5k59muSzaaq8t0iYF9gLcZ90eKEBRZu6Mg/dJIkcMjOTBzYQ1uTJKnPIuKhwMsoyxc8qbF5cBFplJgkSZI0M92ebI3v1eol2fvWpYj4Yo8vycw8YE6CGSHVcLrnUBKj3SkV72Dyz+sdlEV4v9bf6DRKTJIkSQBHA38YdBAj5gbgnkEHMWLsfevevnSfYDeGjo11khQRx1CO25qNTbXd99AsEf7vmXlcn8MbKoHD7aZjkiRJgrKY7IWDDmLE7OUx65m9b71xaFhvDqZ8d+oLip8HnAh8FfjbgOLSDEXEVsDelLX81gdWp6xTdyWlJ/DYzPznXHy2SZKkeSMizu6y6QZzGoikqdj71r2Tptn/GOCJTE4KVCRloexDM/O6xsYui9OMh9FZJ+nV1a3V46vbPhGxXWbeNtsfbJIkaT7ZEa9AS8PM3rcuZeZ+7bZHxLrAocBWNBOkW4AP9C+6kbAI2DwiTgJOzsybBxyPZu4W4ATgR8D9lGGle1f7NgPezBwMzzVJkjTfjMSlsSFyLuVEa9avwkmaPRGxOvAeylX1ZSm/6/4JfJAy5MhFUeEqYMPqcVJ62p4IHBsRZw0sKi2NLwEHZ+Y/Ghsi4nvAJpSeJICnzMUHmyRJmk++MOgARk1m7jjoGEbQTtX9/w40Co2Fqoz124E3AStSkqO7gU8C77OHpCkzN46Ip1EKhOwFrFztehDw7FrTfSPi75S5mHf0OcyhMCqFGzLz3DbbJiLi9zSTJOckSVInmdlL5Sx1EBHLAm8BngwsAC4EPjlXE2RHSWaeM9W+6rhtQjlmv8nM+/oWmOaViFiR8jP4NuChlPPa+4HPAYdn5p8HGN7QyswLgAsi4g3AnpShWTtTfiYbw7GfVt3uBlYaRJyauYh4OPDM2qZvzcXnmCRJGksR8RhgUWa+e9CxDFJEvIlyEnYv8ITMvCMiFgLn0Fx8Ecp6IwdExFPGPVGKiM1oDu84JTPvrLbvDXwKWKXad3tE/Htmfm4AYQ4be99690dgNZpDiH8CHEYZUrZ8RCxRgCYzr+5bdEMuM++mFL84KSLWpiRL+1MKXjQsP4jYNMlqEXFx7flxncqzVz2r3wRWrTadQRmSN+si0znOksZDRKwMvJQyoffJAJm5cJAxDVpEnALsA5yZmbtV215OmSTbWjUrgcMy84i+BzpEIuJDwBuB6zJz/WrbRsDlwDJVs8axS2CndkNGpE4iYoLeCtFkZnrxexoR8RTK34CXACuP69+Ahz5sm9xul58OOgzO+PIyl2Tmtt20jYh1KIsAb1FtOht4QeNC1WxbMBdvKknDIopdI+JkSvnhT1ISJAs8FJtTTsROr23bs/b4akrvSGPc/u59imuYPaG6/2pt26soCVLjpDZq92/sU1xDKyIW93i7f9AxD6mY5qZpZOZFwJ8pvUiWox8REfE4Sm9qI0H6MvDcuUqQwOF2kuapiHgs5WrhvsAjG5tbmv2+nzENqTWq+9/Wtj2t9vjlmfnTiLgK+C9g475FNrweXd3/pLZtl9rj9wEfpvTG7ULVaznmGr1qnsj3xuM1N1YAlmOcl4zI0SjcABAROwFfp8zNAziWUvFuTv//TJIkzRsRsQrwMkpyVO++bx0ydipwZGb+pn/RDa3G/Jn74YE1WFanHKebMrMxHuPS6n6F/oY3lB5W3f8VICKWo3l1czHwgcy8LSI+RUmS1ljyLcaSJ/y98YKExl5E7AGcQil7D3Ay8A3gabXFge/OzIvbvHypmCRJmk9uoAx5aj0Zu47yi/Xg6vmPTZAecA/lb8GmlIX66hWDLqo9bsy1+Xuf4hpmy1X3jQRzG8oxTODXtZXfG8fKCnelJLN6kJlX9dI+Ih48V7FIA/RCmgkSlAuhL2tpcw3wqNn+YJMkSfPJsjSHT9wGnEapbnROZmZEHDzlK8fX74GtgcOralkvre2rFxt4bHX/134FNsRuBNYC9o+Is4HX1PZdWHu8Rq39WMtM1zCbIxGxM6X3/EU01wWSOgqCBYvt3O3EJEnSfJTAWcDXgHPnetzyiDuNkiStCry1tv0+yrDEhmdRjqslnEsitDewR3WrO6P2uDEX6bp+BDWqImIfJq/HdZo/s51FxIaUxGg/YF2ac76keSUzF1G+631nkiRpvmqcwN5clbmek3UU5oEPUoYztBYX+M/MvB4gItakuVr9j/oY27D6f8CLKSf19ZPT/6WUp214cbXv/L5GN4QiYl/gIMqcredn5h3V9q8zuWLim4BzImJXF+KdLCIeQilbfQCwXWPz4CIaPhExIqUINApMkiTNJ/tTTiB2ornEwerAG6pbwyoIgMy8JyJ2AF5OWTz2NuD0zKyf2K8PfKB6/N0+hzh0qmp/e1OSpUdREqGzgVc2ekAi4gWU4wYmllCS7O2B82sJ0gsoCXqrZwCvo1QIHHsRsQvl99qLaBZOqSdHdwFnUnrOx123VRTtdRuh6naD4mKykuadasG5xurq9QpR9V94v6MM6/nPfsam+SUiVgf+kZl3t2wPqkQ9M8f+VCQiLgUeD7wjM/+72vYlyhy4BG4FfkqpBrgA+Elmbj+gcIdCRBxFGU63dmNTbfd9NNflem1mHtfn8IZStQBvL3JcF5NdZZVt8xlPH/xist/69oO6Xky23+xJkjTvZOZ1wFHAURGxHWU8894011hISiGC9wAmSUBErEYZwrMs8MvMvHLAIY2EzLxpiu1JGVqmYs3q/vLath1qj1+amT+IiPcCh1CqLY67d7Fkr8iFwInAV4C2370xZxXFLoU9SdMySZI0r2XmhcCFEfEmyhyR/SlFCBzLX4mI11MWil2utu144CB7QZYUEctO32qyzLx3LmIZIatW93cBRMQaNHtIbgN+WD0+r7p/SP9CG3oJHA8cnpnXNDbW1ohRxSqKmk0Lpm8iSaMhIjafal9m3p2ZX8rM5wDrUXqRft+34IZURDwD+Ciw/P9v797DJKvrO4+/v4MMOAEq5hcAACAASURBVIiA60BGARluXsbLJkBAySAYIBphBRQXUbm4uSCoIVkNKwYEDLK4MVGfVaMGkAAqyhoWlWwQEWa4r4M6yl0RBggQFBjCwIAz880fv1PW6aa6u2q6u67v1/PUc7rO+VXXt8qRqk//zvn+aAbHoMy+fbhHZfW7pzq8PdmbMvvKmmq7sNruXW0TuKHWza7xb7Cx1pSKo4ALIuK9EfGCqQZLmj5nkiQNk59ExCPANZS/SC8FlmXmmvqgzPxX4IzqNuo+UG0bp/XUg9L7gL/uRVF9rt2Lw9V0F7AIODkiNgGOqx1bWvt5h2rrelxwN80FMhN4bXX7VERc1qOaNEQ83W5yziRJGjZbAAcAZ1LO338sIr4XEadExO9HxLzeltd3dqd8AbsDeANlzaRGB7v5EbFdb8rqe+10z7IzUtO3Ke/ZtpSudTtX+5NyfU3DPtW+27taXR/KzO0p78c/Aqto/hFjQ+APa0PfEREHR8Rzn/1bJK0vZ5IkDZN1PPuPP/Mop/bsXd1fW3XaWkppR3xx16rrT/Or7SmZeSVARLwXWFE7fnf3y+pr+0xybAvgQ5S/+DdC0uqJh4+MMynNU3YYt/9zmXknQERsRvkDB9g2HYDMvIqybtSxlPfvSEqL9Pr6XHtVtyfxWi5pxhiSJA2TLSgd2vaidM7ajdKMoP5X/+cAu1a34/G/g402wvc2dmTmfbWLwjfsRVH9rPriOkY1Q3k88EGaXRTXAmcDp3Wvuv6UmSsjYjfKe7Qb5Zqj72TmBbVhv01zrZ9Lu1xiX8vMJ4FzgXMjYlvKNUrvZmzodJZcbSvd7TxjeDKj/uVA0hDJzH+nLKr4L/CbLmS7UwLTYkqAavyl1U+HsSY6NcxTxiYRERtSFj79MGXWLSgzml8BPpqZd/WwvL6SmY8Bp0xy/Ergyi6VM7AycwUleJ8WEb9Hc4mD5/WyLmnYGJIkDa2q7fLSiPgZ8HPgHuBwYJOeFtafrm7RUjha7M/MHPnPjoiYQ1mT5SRgG5qh+2LgpMy8eaLHSu2IiL2qH39Y/QHoWTLzasr/R99PWeJAapuNGyY38h90koZPROxEc/ZoMc22w9D8MpvAT7tcWj8bn5DGt2RWJSIOA04FdqT5/lwGfCQzl/WsMA2bKymzkntRmtBMKDOfAi6YbIykzhiSJA2NiLgI2BPYsrGrdngNcBOlYcMSStOGR7tbYd9qFYQMRxP7Cs0W4AlcT/m39daIeGurB2Tmid0rT0PE/x9KPWJIkjRMDqH55XU1cAMlEC0BrqsuftZYR/e6gAHWmG3bo7pNxpAkqX+kp9tNxZAkaRglpYX1LcCtwG0GpNYy89xe1zDA2v0rv80vNB3++5F6wJAkaZj8FFhE+fK6M7ATcAxARNxNOdVuKbA0M+/oUY0afEvwi6u656KIeLqNcZmZ49ehkloKZ5KmZEiSNDQy89URsTnluqRG04ZdgLmU5g0LKWuLEBEPU8LSoT0qty9ExBGTHF5HWc/mlsz8eZdK6nuZuXeva9BIWTDF8fr1cZJmiCFJ0lCp1mL5TnUjIjZm7FpJe1IWXdwSW+YCfJk2vlxFxA3AezLztlmvSFInbO4gzQJDkqRhtxllkc/5wFbAxjT/8qr2BKUxwZUR8Z8z88FeF9RLEfFvwNU0T9+8KTPX9bYqDbFzKNdYSjPK0+0mZ0iSNFQiYiFlXZHGzNGOva1oILQbGOcDfw6cMIu1DIIXAm+pbgCrIuJ6mqHp+sxc3aviNHTOysxJ10mSNPMMSZKGRkTcx7PP328VAFYC11AuwB9pmTlnsuMRMR84EPgs5dquN2JIgrH/rp4H/H51A/h1RDTW5FoKXOOaXJI0WAxJkobJi2h9Kt1DNBeRXQosz0wvcm5DZj4MnB0RuwN/DGzf45L6wQGUWcq9aDYGqf+bm0u5Dm534IOUBhgbdrlGSZpQ6W7nWeeTMSRJGjYB/IJaKMrMO3tb0lD4t2o7t6dV9IHMvBS4FMY0Bmmc4vlaYBPGhqZJZ+ukCayg/NHHUzelHjAkSRomh1NC0f29LmSYRMQcyml2AI/0spZ+U117dFV1IyK2Bj5AWZ9rfFiS2paZ2010LCLeTgnnc4DrgG84O65O2bhhcoYkSUMjM7/W6xoGTUScPNlhSpOC/SiL8yawrBt1DYqI2J5mk5DxjUIaAckvr+pYRLwT+FNgLXBAZq6q9n+TZtMQqEJ5RPxBZv66+5VKw8mQJEmj7RQ6+xJ/1izVMTAi4n3A71FC0W/VD1XbtcCPaF4Dt7SrBWpY/AHl39nVtYB0IHBQi7GvB44FPt298qThZkiSJEF7p4V9ITP/adYr6X+fYWyDkKeBG2mGomsz84ke1abh8SrKv7NLavveUW0TeAy4gTLTOwc4FEOS2pWebjcVQ5IkjbbGxeGtrAP+HbgZOD8z/7lrVQ2GBJYDfwt8d9QX2dWMa8xS3lzbt7j282GZ+d2IOBU4CXh51yqTRoAhSZJG2GQXh2tSjVmkVwNfBoiIu6idYpeZP+9NaRoSW1TbpwAiYkvgxdW+lcDl1c+N0zk37V5p0vAzJEmSOhYR2wBHA2TmaT0up9teSPOapMXA71A+T3egrCN1FEBE/GZ9rsz8bE8q1SBbQ1lfayFwJbB3tT+BG2rd7BqBfWU3i9NgC0+3m5IhSZK0Pral2fRhpEJSZj5CuU7kEoCImAfsQTM07QHMo5wudSjwNsCQpE7dBSwCTo6ITYDjasfqzUB2qLYPdaswaRQYkiRJmobMfBK4IiKWAz8BbgfejeskaXq+DbyS8geJekOGBL5Ru79Pte/27pWmYeBM0uQMSZIkrYeIeAnN2aO9KGtJSTPlTMpM5A7j9n8uM+8EiIjNgAOq/d/vYm3S0DMkSZLUgYg4nxKMth5/qMXwx4FrZ70oDZ3MXBkRuwHHA7tRrjn6TmZeUBv228A3q58v7XKJ0lAzJEmS1JnDGbtOUt3DNBeQXQL8ODPXdbE2DZHMfIxy7d9Ex6+kNHWQOmLjhqkZkiRJ6lwjIK2g2fZ7SWZ6XYgkDQFDkiRJnfkizVB0b6+LkSTNPEOSJEkdyMxjel2DJE1Lwpw1vS6ivxmSJEnrYwVwaq+L6LWI2Bd4O/AaYDPKxfXLgQsz8/Je1iZJWn+GJEnSGBGxEfCSFofWZOZdANVpZiMbkiLiecD5wIH13ZSGDrsC74mIbwPvzMwnelCiJE1qzlqXcZuMIUmSRlhELAROr+6ekZk/oXzJX9Ji+LqI2CEzV3StwP71VeDN1c/1TndRu38A8DWa69hIkgbEnF4XIEnqqYOAw4A9gZtr+6PFbQ5lccuRFhEHUAJSVrt+RWnDfGG1/VVjKPCmiDgQSdJAcSZJkkbbGylf9i9qsZ5Pq7WAXg98shuF9bEjq+1a4ATgc5n5dONgRMwFjgU+AWxQjf9Wt4uUpIm4TtLUnEmSpNG2sNreMMnxhcCHKYHpFd0oqs/9LiVAfiYz/64ekAAy85nM/BTwGcp79rs9qFGSNA2GJEkabVtW24daHczMezLzHuDGatdvdaWq/tZ4z/7fFOMax+fPYi2SpFng6XaSNNo2rrab1Pb9CNht3LgNx21H2a+BucC8KcY9t9q6GomkvuPpdpNzJkmSRtsj1XaPxo7MXJWZyzJzWW1cIzQ91rXK+tf91fbISUfBUePGS5IGhCFJkkbbcsp1M++NiBe3GhARWwHHUa7DubnVmBGzhPKeHRQR34iI19QPRsRrIuLrwMGU9+yqHtQoSRNqNG7o9a2fGZIkabRdWm1fAFwXEe+OiC0jYk5EzI+Iw4HraF6LdGnL3zJaPkuz/fchwE0RsToi7o+I1cBNwFur4wl8vgc1SpKmwZAkSaPtHJpNG7YGvgw8QLnu5kHgPGC76vivgC91t7z+k5nLgY8zdgHZucCCahu1Y2dm5o+6XqQkaVps3CBJIywz/z0ijgAuoXzBh7FrIzVmTH4NHJmZK7tZX7/KzJMi4jHgo8DzWgxZBZyamX/T3cokqQ2ukzQlQ5IkjbjM/G5E7Ad8AXj5uMMB3Aocm5leW1OTmZ+MiHOANwOvBjYDVlKu8/pOZj4y2eMlSf3LkCRJIjOvjohXUrrY7QJsQelktwy4MTNzssePqioIndfrOiRJM8uQJEkCoApCN9JcOFZtiIhdgV2BzSnB8geZ+YPeViVJk/N0u8kZkiRphEXE3KlHjZWZz8xGLYMmIvakdK5b1OLYzcAxmXlt1wuTJE2bIUmSRttTHY5P/OwgIvYFvkWzm139dMQAXgl8LyLenJlX9KBESZpQ2LhhSrYAl6TRVm9j3e5tpEXE84ELgI3quxn7/mR1/IKI2LS7FUqSpsuQJEmaKvgkY2dKRt2RwHzKe3Iv8G7KYrsbAi8Cjgbur8ZuWR2XJA2QkT9lQpJG3D6THNsC+BDwWpohafWsV9T/3lRtfwm8NjMfqB17EDg3Ir4H/BB4AaVF+Oe6W6IkTSJhzppeF9HfDEmSNMJarX0UEfOA44EPUtb+AVgLnA2c1r3q+tbOlND4hXEB6Tcy876I+HvgI9V4SdIAMSRJkgCIiA2BY4EPU04nC2Ad8BXgo5l5Vw/L6yfzq+31U4y7rtpuOYu1SJJmgSFJkkZcRMyhXEdzErANzWuULgZOysybe1Vbn3putX1sinErq+28WaxFkjoW2N1uKoYkSRphEXEYcCqwI81wdBnwkcxc1rPC+ttzKKfbvadqBT6RbautTZIkaZoi4r8A7wd2ofzx6V7gEuDjmfmrGX++ssC6JGkURcQ6yhf+xlo/1wPPuk6pLjNP7EJpfav2nrU1HMjM3GAWS5Kkjrwods0/3uAHvS6D09bGsszcdapxEXEqcPIEh+8G9srMe2eyNmeSJEnQ/NK/R3WbzEiHpJp2WqdLkqYhIhbTDEjrgL8CbgVOoHxebQf8A/AHM/m8hiRJErS/SKxf/GEFvg+S1C3H134+OzPPAIiIZcA9lM+v/SNi0UxeQ2tIkqTRtgS/8HckM7frdQ2SNF0D1Lihvp7f1Y0fMvPeiFgBvKTa9QbAkCRJmr7M3LvXNYyKiNiG0kWQzHS9KUmaQkRsQVnYvOHBcUMepBmSdpjJ5zYkSZLUHdsCp1Bm7gxJknrmAZb9yynEC3tdB7BxRNQ7SHwxM79Yu7/JuPHPTHL/eTNZmCFJkiRJGiGZ+cZe19CmVePubzTJ/Sdm8okNSZI0wiKi07PSMzP97JAkzbrMfDQiHqV5yt1vjRuyoPbzz2fyuV3gTpJGW9S27d4kSeqW79d+Xtz4ISIWAtvUjl0xk0/qXwMlSQYfSVK/+gxwSPXzURHxc+AWxq7Zd/lMtv8GQ5Ikjbqje12AJEkTycyrIuJ04COUs+BOHzdkBfBHM/28kenyGJKk9kTESzPz9l7XMYgiYk9gKeW6rg16XY8kDZKIOAh4P/A7wDzgXuAS4IzMfHjGn8+QJEmjKyKOy8zPtjl2Z+D7mfniWS5rKBmSJGlweLqdJI22z0TE6sw8a7JBEbED5aLY8Z2F1L4VwKm9LkKSNDVnkiRphEXEOmAdcHRmnjfBmO2AKymLoToL0kJEbERz1fe6NZl5V7frkSRNjzNJkqQAzo6IZzLzwjEHIralzCBtW+16vNvF9Zuq7WzjwuEzMvMnwK7AkhbD10XEDpm5omsFSpKmzXWSJGm0nU4JSRsA50XEwY0DEbE1JSBtV+16HHhTtwvsQwcBhwF7AvWWs63WlJoDHNrtAiVJ02NIkqQRlpknAf+ruvsc4KsRcUBELKAEpO2rY08Ab87M63tQZr95I5DARZm5btyxVuewv372S5IkzSRDkiSNuMw8gbJYH8Bc4BvANcCO1b5VwIGZeU0PyutHC6vtDZMcXwh8mDKb9IpuFCVJmjlekyRJIjOPj4i5wDHARjRPsXsKeEtmXtWr2vrQltX2oVYHM/MegIi4sdplR0BJGjCGJEkaYVVjhoZPUDq0Na47Wgf8GfCz+jibELBxtd2ktu9HwG7jxm04bitJGhCGJEkabXfz7OtoGvcD+EKLY6P+2fEIsBWwB3ApQGauApaNG9cITY91rzRJ0kzwmiRJEjS7sU20PyYZM2qWU96H90bEi1sNiIitgOMoofLmVmMkSf3LkCRJinE/G4gmd2m1fQFwXUS8OyK2jIg5ETE/Ig4HrqN5LdKlLX+LJKlvRWarbqWSpFEQER23px71Jg4RsSlwB6WBQ9C67XcjYP4S2CkzV3apPEnSDDAkSZLUoYjYD7iE0jIdxs66ZXX/18BBmfnPXS5PkjRNnm4nSZpSRLwrIr4ZEf+n17X0g8z8LrAfcBvPPi0xgFuB/Q1IkjSYRr1DkSSpPYuAg2h9atlIysyrI+KVlC52uwBbUDrZLQNuTE/VkKSBZUiSJGk9VUHoxuomSRoShiRJkjoQEXOnHjVWZj4zG7VIkmaHIUmSpM481eF4F+CVpAHjf7QlSepMo+2360hJ0pAyJEnSCIuIK9ocuv2sFjJ4pgpIjaYNBilJGkCukyRJIywi1tF+x7qg9CrYYBZL6ntTLMC7BfAh4LU0Z5ueysxNulGbJGlmGJIkaYRVIakTIx+SWomIecDxwAeBzSjhaA1wNnBaZv5rD8uTJHXI0+0kabSd2+sCBllEbAgcC3wYmE8JR+uArwAfzcy7elieJGk9OZMkSVKHImIOcDRwErANzWuPLgZOysybe1WbJGn65vS6AEnSYIiInSPi472uo9ci4jDgVuCLwLaUgHQZsFtmHmJAkqTB50ySJGlCEfF84DDgKGB3gFG/JqnW7KLRCvx64KrJHpOZJ3ahNEnSDDEkSZLGiIgA9qcEo7cAGzUOYeOGTjsCAgZLSRo0Nm6QJAEQES+jBKN3AQsau8cNu6ObNfW5dtdA8q+RkjRgDEmSNMIiYnPgHZRwtGv9UO3nBC4E/jozb+ledX1rCQYfSRpqhiRJGm0PAhvy7FmR+4CvUhZGBbjSgFRk5t69rkGSNLvsbidJo21u7eeVwFnAG4CXZOYJvSlJkqTeMiRJkqCcPvY94JvAkrSrjyRphHm6nSSp4eDq9suI+BrwlR7X05ciYm2HD8nM9PNWkgaIM0mSNNqOAK6gue5PAPOB9wHX1sZt3v3S+lbUtu3eJEkDxHWSJElExNbAkZTQtFPtUP1D4nbgosw8uZu19ZtqnaROjPzaUpI0aAxJkqQxIuJ1lJbghwKbVbsbM00j/4U/Io7s9DGZee5s1CJJmh2GJElSSxGxMXAIZXZpX8op2iMfkjoVES/NzNt7XYckqX1ekyRJIywiFk10LDNXZ+ZXMvONwLbAR4A7ulZcn4qI4zoYuzPlmi9J0gBxJkmSRlh1fc0jwDXA0uq2LDPX9LSwPlZ1t/uTzDxrinE7AFcBC5x9k6TBYkiSpBFWhaTxHwRPATfQDE3XZeaT3a6tX1Xv2Trg6Mw8b4Ix2wFXUmbgPEVRkgaMIUmSRlhErKH1qdf1D4e1wA8pgenqzLy4G7X1q1qwXAe8KzMvHHd8W0pA2q7atTIzt+hmjZKk6TEkSdIIi4hNgdcBewGLgd2AjVoMbXxYjPzCqBHxMcr1WQBrgP+amf9UHduaEpC2r44/DrwxM6/vdp2SpPVnSJIk/UZEzAV2pwSmxZQAtSm2AB8jIs4EPlTdfQZ4G7CMcg3SjtX+J4A3ZeY13a9QkjQdhiRJ0rNExALK7NI+wOHAJhiSxoiITwEfqO4+DTxA8xS7VcABmXlVD0qTJE2TIUmSRETsRHP2aDGwsH642ibw08x8TZfL61sR8TngmHG7nwIOzExbf0vSgDIkSdIIi4iLgD2BLRu7aofXADdRGjYsoTRteLS7FfafqjFDwxzgs8CbqvvrKKHpsvpjMnNFd6qTJM0EQ5IkjbBap7YAVlNafy+pbrb+bmGCtumTGflmF5I0aPyPtiQJypf+FcAtwK3AbQakKdVPQ2y1X5I0oJxJkqQRFhHLgUW0/sJ/N80FZZdm5h3dra4/VTNJnbDZhSQNGEOSJI24iNiccl1So2nDLsDc2pDGB8XDlLB0aHcr7C8R8fpOH2OXO0kaLIYkSdIYEbExY9dK2hOYVx12VkSSNPS8JkmSNN5mwPzqthWwMc3mDmpTRLwLOIQSLN/a63okSe0zJEnSiIuIhZSFYxszRzv2tqKhsQg4iM464UmS+oAhSZJGWETcBywYv7vF0JXANZTW4JIkDTVDkiSNthfR+lS6h2guIrsUWJ5exCpJGhGGJElSAL+gFooy887eliRJUu8YkiRptB1OCUX397oQSZL6hS3AJUnqQERc0ebQ7YFtsW26JA0cQ5IkSR2IiHW037EuMCRJ0sDxdDtJkjrnmlGSNMQMSZIkdebcXhcgSZpdnm4nSZIkSTVzel2AJEnDKiJ2joiP97oOSVJnnEmSJGkGRcTzgcOAo4DdAWzcIEmDxWuSJEmapogIYH9KMHoLsFHjEO13wpMk9QlDkiRJ6ykiXkYJRu8CFjR2jxt2RzdrkiRNnyFJkqQORMTmwDso4WjX+qHazwlcCPx1Zt7SveokSTPBkCRJUmceBDbk2TNG9wFfBT5U3b/SgCRJg8nudpIkdWZu7eeVwFnAG4CXZOYJvSlJkjSTDEmSJK2fBL4HfBNYkraLlaShYUiSJGn9HQx8G3ggIj4dEbv3uiBJ0vQZkiRJ6swRwBWUmaSobvOB9wHX1sZt3v3SJEkzwcVkJUlaDxGxNXAkJTTtVDtU/2C9HbgoM0/uZm2SpOkxJEmSNE0R8TpKS/BDgc2q3Y2ZpszMDXpUmiRpPRiSJEmaIRGxMXAIZXZpX8pp7YYkSRowhiRJkjoQEYsy8+Y2xr2I6nS8zHz57FcmSZophiRJkjoQEeuAR4BrgKXVbVlmrulpYZKkGWNIkiSpA1VIGv/h+RRwA83QdF1mPtnt2iRJM8OQJElSByJiDa2X0Kh/oK4FfkgJTFdn5sXdqE2SNDMMSZIkdSAiNgVeB+wFLAZ2AzZqMbTxAZuZ+ZwulSdJmgGGJEmSpiEi5gK7UwLTYkqA2hRbgEvSwDIkSZI0AyJiAWV2aR/gcGATDEmSNJCc/pckaT1ExE40Z48WAwvrh6ttAj/tcmmSpGkyJEmS1IGIuAjYE9iysat2eA1wE6VhwxJK04ZHu1uhJGm6PN1OkqQO1FqAB7Ca0vp7SXWz9bckDYFWLUwlSdLUElgB3ALcCtxmQJKk4eBMkiRJHYiI5cAixl531HA3zQVll2bmHd2tTpI0EwxJkiR1KCI2p1yX1GjasAswtzak8eH6MCUsHdrdCiVJ02FIkiRpmiJiY8aulbQnMK86bAtwSRowXpMkSdL0bQbMr25bARsz9jQ8SdIAsQW4JEkdioiFlIVjGzNHO/a2IknSTDIkSZLUgYi4D1gwfneLoSuBayitwSVJA8RrkiRJ6sC4dZLqHqK5iOxSYHn6IStJA8mZJEmSOhfAL6iFosy8s7clSZJmiiFJkqTOHE4JRff3uhBJ0uzwdDtJkiRJqrEFuCRJkiTVGJIkSZIkqcaQJElSF0XEURGREbH3ZPv6SUTcHRFXtjFuu+p1nDKN58qI+PL6Pn6S37t39buPmunfLWn4GJIkSUOt9uW4fnsiIpZFxJ9FxAa9rnE6qtd3SkRs3utaJGlYGJIkSaPiq8C7gSOAjwHzgE8Bn+9lUZXzgOeyfgvP7g18FDAkSdIMsQW4JGlU3JSZ5zfuRMTngVuBP4qIkzLzoVYPiogNgQ0yc/VsFZaZa4G1s/X7JUmdcSZJkjSSMvNx4DrKwrDbA1SnrWVELIqIv42I+4DVwB6Nx0XEvhFxWUQ8FhGrI2J5RBzT6jki4o8j4raIeDoifhYRx1fPN35cy2uSImJuRPxlRPwoIp6MiJUR8YOIeF91/MuUWSSAX9ROJzyl9js2i4gzq+d/OiIejoivRsT2LerYJiK+Xj3P4xHxrYjYoYO3taWIOLZ6z+6PiGci4oGIOD8itpvkMftGxPXV634wIj4dEc9rMa7t1ydJ7XImSZI0kiIigB2ru78cd/gC4Cngk0ACD1SP+RPg74HrgdOBVcB+wOcjYofM/FDt9x8P/B3wY+BEyul9HwT+rc365gL/Qjmd7jLgfEpgexVwCPC/gS8AzwcOBv689jqWV79jM+BaYFvgbOBmYAFwLHBDROyamfdUYzennO63TfUabwFeD3yfcirgdHyQ8p59BngEeCXwR8AbIuJVmfmrceN/B3gb8CXgH4F9gA8Ar4yI/TJzXaevT5I6YUiSJI2KeRHxQspMzgLg/cBrgOsz885xYx8D9s3MNY0dEbGA8iX/a5l5eG3s5yLi08BfRMTnM/OuKnCcTjmd73WZ+WT1O84Bbmuz3uMpAemMzDyxfiAi5gBk5nURsZwSki7OzLvH/Y7TKLNke2Tmj2uP/zLwE+BU4Khq918C2wHvycxzaq/tU8CftVnzRF6VmavGvYZLgMuB/wZ8Yvx44ODMvLhWx6cpQentwNfW4/VJUts83U6SNCpOBR6mzOT8GHgPcAlwUIuxn6oHpMrbgI2AsyLihfUb8C3KZ+q+1dj9KTNHn20EJIDMvI8yS9WOdwKPUoLAGI2ZlMlUM2XvpMwO3T+u3lWUmZ39aw85CHiIMnNTd2ab9U6oEZAiYk51etwLKf8brAR2b/GQ22sBqeF/VtuDq9/V6euTpLY5kyRJGhVfBL5BOX1uFXBHZj4ywdg7Wux7ebW9fJLn2KraNq6HaTVrdMsUdTbsBPxoGg0j5gP/iRIUHp5gTD1sbQ/8/6qJxG9k5gMR8dh61gBARLwBOJkSiDYed3iLFg+5dfyOWh2N97bT1ydJbTMkSZJGxZ2ZOVnAqXuyxb5Gw4UjqK5RauGujquaPY16L2cGZoPWu4iI3SjXVP0M+B/ALyjXeyXltLn1PaulL16fpOFkSJIkqT2N65Z+2UbYaoSllwHfG3fsFW0+3x3AF50WCAAAAlVJREFUyyJio8x8epJxOcH+hynXVj2/zXB4F7BTRGxQn02qrsWazhpMhwMbAG/KzF/Ufu8mtJ5Fguas3W/U6mi8t52+Pklqm9ckSZLUnq8DTwOnRsSzur1V19psVN39LmW25LiImFcbszUlNLTjAkqI+KsWz1VvI/5EtX1BfUx13dIFwO9GxNtaPUFEbFm7+38ppwseMW7YCW3WO5FG4Brf+vxEJv4e8tKIGH+tWKOOi2G9Xp8ktc2ZJEmS2pCZ90XEe4F/AG6NiPOAeyjXxryK0vjgFcDdmfloRJwE/A1wbUT8I6WRwzGUGanfbuMpPw0cCPxV7ZS11cAi4KU0m0RcX23PjIgLqjE/zcyfAh8B9gS+HhFfr8Y+A7wE+ENgGc3ub5+gBLgvRcQulHbaewOv5dkt0jvxT5T25JdGxBer598PePUkv/cnwPkR8SXK+7UPpXHGVcCFtXGdvD5JapshSZKkNmXmORFxB2Xdnz+lnP71S+B24CTgwdrYT0bEE8BfAGcA91JC00rKmj5TPdczEbE/8N8p4eXjlAB0J3BObdw1EXECJYB9ifLZfiolKK2MiD2r3/F24C3AGuA+4GpK4Gv8nkcjYjHwtzRnk66iBJTxpwy2rarvrZT352OUGbbLKWswLZngYTdR3rfTq9f1OGVdqBPrnf06eX2S1InInOhUZkmSJEkaPV6TJEmSJEk1hiRJkiRJqjEkSZIkSVKNIUmSJEmSagxJkiRJklRjSJIkSZKkGkOSJEmSJNUYkiRJkiSpxpAkSZIkSTWGJEmSJEmq+Q9iV+zTN4gjigAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] diff --git a/LSTM_new.ipynb b/LSTM_new.ipynb index a69de9d..9c380bf 100644 --- a/LSTM_new.ipynb +++ b/LSTM_new.ipynb @@ -24,14 +24,16 @@ "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", "from neuraxle.hyperparams.space import HyperparameterSamples\n", "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", - "from steps.one_hot_encoder import OneHotEncoder\n", + "from neuraxle.steps.encoding import OneHotEncoder\n", "\n", "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n", + "from neuraxle.api.flask import JSONDataBodyDecoder\n", + "from neuraxle.api.flask import JSONDataResponseEncoder\n", "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", + "\n", "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", - "from steps.one_hot_encoder import OneHotEncoder\n", - "from steps.transform_expected_output_wrapper import OutputTransformerWrapper\n", + "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", "\n", "# TODO: move in a package neuraxle-tensorflow \n", "from savers.tensorflow1_step_saver import TensorflowV1StepSaver" @@ -479,76 +481,76 @@ "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", - "Batch Loss = 3.993926, Accuracy = 0.14533333480358124\n", - "Batch Loss = 3.166194, Accuracy = 0.12666666507720947\n", - "Batch Loss = 2.666343, Accuracy = 0.11533333361148834\n", - "Batch Loss = 2.333051, Accuracy = 0.2593333423137665\n", - "Batch Loss = 2.365340, Accuracy = 0.3949704170227051\n", - "Batch Loss = 2.335133, Accuracy = 0.3606666624546051\n", - "Batch Loss = 2.192836, Accuracy = 0.41999998688697815\n", - "Batch Loss = 2.004998, Accuracy = 0.5460000038146973\n", - "Batch Loss = 1.979304, Accuracy = 0.5799999833106995\n", - "Batch Loss = 2.063153, Accuracy = 0.40606507658958435\n", - "Batch Loss = 1.972361, Accuracy = 0.4779999852180481\n", - "Batch Loss = 1.922048, Accuracy = 0.5360000133514404\n", - "Batch Loss = 1.827589, Accuracy = 0.6633333563804626\n", - "Batch Loss = 1.794267, Accuracy = 0.6666666865348816\n", - "Batch Loss = 1.888613, Accuracy = 0.5872781276702881\n", - "Batch Loss = 1.797241, Accuracy = 0.5446666479110718\n", - "Batch Loss = 1.742341, Accuracy = 0.5633333325386047\n", - "Batch Loss = 1.670088, Accuracy = 0.5920000076293945\n", - "Batch Loss = 1.623381, Accuracy = 0.6146666407585144\n", - "Batch Loss = 1.714245, Accuracy = 0.5828402638435364\n", - "Batch Loss = 1.597082, Accuracy = 0.6466666460037231\n", - "Batch Loss = 1.518446, Accuracy = 0.6706666946411133\n", - "Batch Loss = 1.474818, Accuracy = 0.6446666717529297\n", - "Batch Loss = 1.429932, Accuracy = 0.6933333277702332\n", - "Batch Loss = 1.522832, Accuracy = 0.6309171319007874\n", - "Batch Loss = 1.424594, Accuracy = 0.6859999895095825\n", - "Batch Loss = 1.370649, Accuracy = 0.7046666741371155\n", - "Batch Loss = 1.383567, Accuracy = 0.6600000262260437\n", - "Batch Loss = 1.283080, Accuracy = 0.734000027179718\n", - "Batch Loss = 1.396952, Accuracy = 0.6730769276618958\n", - "Batch Loss = 1.306781, Accuracy = 0.7179999947547913\n", - "Batch Loss = 1.265723, Accuracy = 0.7446666955947876\n", - "Batch Loss = 1.266257, Accuracy = 0.6946666836738586\n", - "Batch Loss = 1.234961, Accuracy = 0.7613333463668823\n", - "Batch Loss = 1.299717, Accuracy = 0.7107987999916077\n", - "Batch Loss = 1.269762, Accuracy = 0.7526666522026062\n", - "Batch Loss = 1.228487, Accuracy = 0.75\n", - "Batch Loss = 1.227822, Accuracy = 0.7206666469573975\n", - "Batch Loss = 1.164757, Accuracy = 0.7986666560173035\n", - "Batch Loss = 1.247713, Accuracy = 0.7278106212615967\n", - "Batch Loss = 1.241613, Accuracy = 0.7599999904632568\n", - "Batch Loss = 1.189182, Accuracy = 0.7986666560173035\n", - "Batch Loss = 1.183320, Accuracy = 0.7559999823570251\n", - "Batch Loss = 1.131927, Accuracy = 0.8306666612625122\n", - "Batch Loss = 1.204420, Accuracy = 0.7788461446762085\n", - "Batch Loss = 1.195225, Accuracy = 0.8113333582878113\n", - "Batch Loss = 1.146203, Accuracy = 0.8100000023841858\n", - "Batch Loss = 1.152838, Accuracy = 0.7586666941642761\n", - "Batch Loss = 1.081216, Accuracy = 0.8426666855812073\n", - "Batch Loss = 1.148535, Accuracy = 0.8106508851051331\n", - "Batch Loss = 1.112431, Accuracy = 0.8293333053588867\n", - "Batch Loss = 1.068065, Accuracy = 0.8266666531562805\n", - "Batch Loss = 1.096894, Accuracy = 0.7906666398048401\n", - "Batch Loss = 1.032260, Accuracy = 0.8533333539962769\n", - "Batch Loss = 1.066560, Accuracy = 0.8284023404121399\n", - "Batch Loss = 1.165151, Accuracy = 0.8146666884422302\n", - "Batch Loss = 1.084572, Accuracy = 0.79666668176651\n", - "Batch Loss = 1.230495, Accuracy = 0.7486666440963745\n", - "Batch Loss = 0.995977, Accuracy = 0.8600000143051147\n", - "Batch Loss = 1.026274, Accuracy = 0.8269230723381042\n", - "Batch Loss = 1.085740, Accuracy = 0.7960000038146973\n", - "Batch Loss = 1.036215, Accuracy = 0.8373333215713501\n", - "Batch Loss = 1.102915, Accuracy = 0.7806666493415833\n", - "Batch Loss = 0.927328, Accuracy = 0.9053333401679993\n", - "Batch Loss = 0.965782, Accuracy = 0.8853550553321838\n", - "Batch Loss = 0.951709, Accuracy = 0.8946666717529297\n", - "Batch Loss = 0.965536, Accuracy = 0.874666690826416\n", - "Batch Loss = 0.977196, Accuracy = 0.8659999966621399\n", - "Batch Loss = 0.941533, Accuracy = 0.8826666474342346\n", - "Batch Loss = 0.957944, Accuracy = 0.8735207319259644\n", + "Batch Loss = 2.689994, Accuracy = 0.1599999964237213\n", + "Batch Loss = 2.481512, Accuracy = 0.1106666699051857\n", + "Batch Loss = 2.218577, Accuracy = 0.3606666624546051\n", + "Batch Loss = 2.114502, Accuracy = 0.4593333303928375\n", + "Batch Loss = 2.015675, Accuracy = 0.4504437744617462\n", + "Batch Loss = 1.959644, Accuracy = 0.5580000281333923\n", + "Batch Loss = 1.832221, Accuracy = 0.5666666626930237\n", + "Batch Loss = 1.679870, Accuracy = 0.5979999899864197\n", + "Batch Loss = 1.707634, Accuracy = 0.6466666460037231\n", + "Batch Loss = 1.635818, Accuracy = 0.6205621361732483\n", + "Batch Loss = 1.556575, Accuracy = 0.640666663646698\n", + "Batch Loss = 1.418897, Accuracy = 0.6946666836738586\n", + "Batch Loss = 1.341482, Accuracy = 0.6679999828338623\n", + "Batch Loss = 1.600628, Accuracy = 0.5933333039283752\n", + "Batch Loss = 1.374022, Accuracy = 0.6545857787132263\n", + "Batch Loss = 1.590493, Accuracy = 0.5239999890327454\n", + "Batch Loss = 1.433335, Accuracy = 0.5946666598320007\n", + "Batch Loss = 1.254257, Accuracy = 0.7226666808128357\n", + "Batch Loss = 1.820678, Accuracy = 0.527999997138977\n", + "Batch Loss = 1.411729, Accuracy = 0.6471893787384033\n", + "Batch Loss = 1.417269, Accuracy = 0.6359999775886536\n", + "Batch Loss = 1.373504, Accuracy = 0.6613333225250244\n", + "Batch Loss = 1.343409, Accuracy = 0.7046666741371155\n", + "Batch Loss = 1.269480, Accuracy = 0.7366666793823242\n", + "Batch Loss = 1.339166, Accuracy = 0.7196745276451111\n", + "Batch Loss = 1.368126, Accuracy = 0.6393333077430725\n", + "Batch Loss = 1.349553, Accuracy = 0.722000002861023\n", + "Batch Loss = 1.236530, Accuracy = 0.7179999947547913\n", + "Batch Loss = 1.221019, Accuracy = 0.7566666603088379\n", + "Batch Loss = 1.282286, Accuracy = 0.7315088510513306\n", + "Batch Loss = 1.303923, Accuracy = 0.6853333115577698\n", + "Batch Loss = 1.247506, Accuracy = 0.7239999771118164\n", + "Batch Loss = 1.207210, Accuracy = 0.7039999961853027\n", + "Batch Loss = 1.257670, Accuracy = 0.7253333330154419\n", + "Batch Loss = 1.269979, Accuracy = 0.7071005702018738\n", + "Batch Loss = 1.260590, Accuracy = 0.7279999852180481\n", + "Batch Loss = 1.195216, Accuracy = 0.7799999713897705\n", + "Batch Loss = 1.232077, Accuracy = 0.7386666536331177\n", + "Batch Loss = 1.155753, Accuracy = 0.7806666493415833\n", + "Batch Loss = 1.236396, Accuracy = 0.7707100510597229\n", + "Batch Loss = 1.224076, Accuracy = 0.7726666927337646\n", + "Batch Loss = 1.159579, Accuracy = 0.8086666464805603\n", + "Batch Loss = 1.169154, Accuracy = 0.7419999837875366\n", + "Batch Loss = 1.218150, Accuracy = 0.7480000257492065\n", + "Batch Loss = 1.221550, Accuracy = 0.7374260425567627\n", + "Batch Loss = 1.184118, Accuracy = 0.7620000243186951\n", + "Batch Loss = 1.130956, Accuracy = 0.8046666383743286\n", + "Batch Loss = 1.168662, Accuracy = 0.7393333315849304\n", + "Batch Loss = 1.131838, Accuracy = 0.7733333110809326\n", + "Batch Loss = 1.206504, Accuracy = 0.7559171319007874\n", + "Batch Loss = 1.133658, Accuracy = 0.8233333230018616\n", + "Batch Loss = 1.112805, Accuracy = 0.8119999766349792\n", + "Batch Loss = 1.134827, Accuracy = 0.7413333058357239\n", + "Batch Loss = 1.053966, Accuracy = 0.843999981880188\n", + "Batch Loss = 1.131961, Accuracy = 0.7995561957359314\n", + "Batch Loss = 1.084540, Accuracy = 0.8226666450500488\n", + "Batch Loss = 1.055858, Accuracy = 0.8386666774749756\n", + "Batch Loss = 1.082902, Accuracy = 0.7746666669845581\n", + "Batch Loss = 1.069604, Accuracy = 0.8253333568572998\n", + "Batch Loss = 1.094777, Accuracy = 0.8254438042640686\n", + "Batch Loss = 1.025982, Accuracy = 0.8500000238418579\n", + "Batch Loss = 1.038732, Accuracy = 0.8413333296775818\n", + "Batch Loss = 1.059510, Accuracy = 0.7900000214576721\n", + "Batch Loss = 1.040493, Accuracy = 0.8566666841506958\n", + "Batch Loss = 1.095273, Accuracy = 0.8254438042640686\n", + "Batch Loss = 0.965999, Accuracy = 0.8700000047683716\n", + "Batch Loss = 0.993554, Accuracy = 0.8579999804496765\n", + "Batch Loss = 1.088169, Accuracy = 0.7853333353996277\n", + "Batch Loss = 0.965077, Accuracy = 0.8793333172798157\n", + "Batch Loss = 1.037169, Accuracy = 0.8491124510765076\n", "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", "\n" ] @@ -618,7 +620,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -630,11 +632,11 @@ "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", "INFO:tensorflow:Restoring parameters from /home/alexandre/Documents/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n", - "Batch Loss = 0.866899, Accuracy = 0.9213333129882812\n", - "Batch Loss = 0.937756, Accuracy = 0.8640000224113464\n", - "Batch Loss = 1.042669, Accuracy = 0.7886666655540466\n", - "Batch Loss = 0.863248, Accuracy = 0.9206666946411133\n", - "Batch Loss = 0.916062, Accuracy = 0.8831360936164856\n", + "Batch Loss = 0.942640, Accuracy = 0.8813333511352539\n", + "Batch Loss = 0.914275, Accuracy = 0.8820000290870667\n", + "Batch Loss = 1.038205, Accuracy = 0.8100000023841858\n", + "Batch Loss = 0.846154, Accuracy = 0.9353333115577698\n", + "Batch Loss = 0.918082, Accuracy = 0.8957100510597229\n", " * Serving Flask app \"neuraxle.api.flask\" (lazy loading)\n", " * Environment: production\n", " WARNING: This is a development server. Do not use it in a production deployment.\n", @@ -647,11 +649,8 @@ "output_type": "stream", "text": [ " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", - "127.0.0.1 - - [04/Nov/2019 20:01:56] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", - "127.0.0.1 - - [04/Nov/2019 20:02:33] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", - "127.0.0.1 - - [04/Nov/2019 20:03:06] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", - "127.0.0.1 - - [04/Nov/2019 20:03:55] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", - "127.0.0.1 - - [04/Nov/2019 20:09:05] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" + "127.0.0.1 - - [04/Nov/2019 20:56:45] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", + "127.0.0.1 - - [04/Nov/2019 20:57:08] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" ] } ], From b0a06547875793a881c35203e027301d88b786de Mon Sep 17 00:00:00 2001 From: guillaume-chevalier Date: Mon, 4 Nov 2019 21:01:08 -0500 Subject: [PATCH 18/30] Added things to make it work. --- 1_train_and_save_LSTM.ipynb | 733 +++++++++++++++++++++ 2_call_rest_api_and_eval.ipynb | 264 ++++++++ pipeline.py | 5 +- steps/custom_json_decoder_for_2darray.py | 3 + steps/lstm_rnn_tensorflow_model_wrapper.py | 5 +- 5 files changed, 1005 insertions(+), 5 deletions(-) create mode 100644 1_train_and_save_LSTM.ipynb create mode 100644 2_call_rest_api_and_eval.ipynb diff --git a/1_train_and_save_LSTM.ipynb b/1_train_and_save_LSTM.ipynb new file mode 100644 index 0000000..85bd97c --- /dev/null +++ b/1_train_and_save_LSTM.ipynb @@ -0,0 +1,733 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import os\n", + "import numpy as np\n", + "import tensorflow as tf\n", + "\n", + "from neuraxle.api.flask import FlaskRestApiWrapper\n", + "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", + "from neuraxle.hyperparams.space import HyperparameterSamples\n", + "from neuraxle.steps.encoding import OneHotEncoder\n", + "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", + "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", + "\n", + "# TODO: move in a package neuraxle-tensorflow \n", + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", + "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", + "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", + "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, load_X, load_y, \\\n", + " TRAIN_FILE_NAME, TEST_FILE_NAME\n", + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", + "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Download Data" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py\t steps\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__\t venv\n", + "cache\t\t\t\tLSTM_files\t README.md\n", + "call_api.py\t\t\tnew.py\t\t requirements.txt\n", + "data\t\t\t\told.py\t\t savers\n", + "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/data\n", + " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", + " __MACOSX\t 'UCI HAR Dataset'\n", + "\n", + "Downloading...\n", + "Dataset already downloaded. Did not download twice.\n", + "\n", + "Extracting...\n", + "Dataset already extracted. Did not extract twice.\n", + "\n", + "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/data\n", + " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", + " __MACOSX\t 'UCI HAR Dataset'\n", + "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py\t steps\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__\t venv\n", + "cache\t\t\t\tLSTM_files\t README.md\n", + "call_api.py\t\t\tnew.py\t\t requirements.txt\n", + "data\t\t\t\told.py\t\t savers\n", + "\n", + "Dataset is now located at: data/UCI HAR Dataset/\n" + ] + } + ], + "source": [ + "# Note: Linux bash commands start with a \"!\" inside those \"ipython notebook\" cells\n", + "\n", + "DATA_PATH = \"data/\"\n", + "\n", + "!pwd && ls\n", + "os.chdir(DATA_PATH)\n", + "!pwd && ls\n", + "\n", + "!python download_dataset.py\n", + "\n", + "!pwd && ls\n", + "os.chdir(\"..\")\n", + "!pwd && ls\n", + "\n", + "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", + "print(\"\\n\" + \"Dataset is now located at: \" + DATASET_PATH)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load data" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some useful info to get an insight on dataset's shape and normalisation:\n", + "(X shape, y shape, every X's mean, every X's standard deviation)\n", + "(7352, 128, 9) (7352, 1) 0.10206611 0.40216514\n", + "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" + ] + } + ], + "source": [ + "# Load \"X\" (the neural network's training and testing inputs)\n", + "\n", + "X_train = load_X(X_train_signals_paths)\n", + "# X_test = load_X(X_test_signals_paths)\n", + "\n", + "# Load \"y\" (the neural network's training and testing outputs)\n", + "\n", + "y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME)\n", + "# y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME)\n", + "\n", + "y_train = load_y(y_train_path)\n", + "# y_test = load_y(y_test_path)\n", + "\n", + "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", + "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", + "print(X_train.shape, y_train.shape, np.mean(X_train), np.std(X_train))\n", + "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# LSTM RNN Model Forward" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def tf_model_forward(pred_name, name_x, name_y, hyperparams):\n", + " # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters.\n", + " # Moreover, two LSTM cells are stacked which adds deepness to the neural network.\n", + " # Note, some code of this notebook is inspired from an slightly different\n", + " # RNN architecture used on another dataset, some of the credits goes to\n", + " # \"aymericdamien\" under the MIT license.\n", + " # (NOTE: This step could be greatly optimised by shaping the dataset once\n", + " # input shape: (batch_size, n_steps, n_input)\n", + "\n", + " # Graph input/output\n", + " x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_inputs']], name=name_x)\n", + " y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name=name_y)\n", + "\n", + " # Graph weights\n", + " weights = {\n", + " 'hidden': tf.Variable(\n", + " tf.random_normal([hyperparams['n_inputs'], hyperparams['n_hidden']])\n", + " ), # Hidden layer weights\n", + " 'out': tf.Variable(\n", + " tf.random_normal([hyperparams['n_hidden'], hyperparams['n_classes']], mean=1.0)\n", + " )\n", + " }\n", + "\n", + " biases = {\n", + " 'hidden': tf.Variable(\n", + " tf.random_normal([hyperparams['n_hidden']])\n", + " ),\n", + " 'out': tf.Variable(\n", + " tf.random_normal([hyperparams['n_classes']])\n", + " )\n", + " }\n", + "\n", + " data_inputs = tf.transpose(\n", + " x,\n", + " [1, 0, 2]) # permute n_steps and batch_size\n", + "\n", + " # Reshape to prepare input to hidden activation\n", + " data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_inputs']])\n", + " # new shape: (n_steps*batch_size, n_input)\n", + "\n", + " # ReLU activation, thanks to Yu Zhao for adding this improvement here:\n", + " _X = tf.nn.relu(\n", + " tf.matmul(data_inputs, weights['hidden']) + biases['hidden']\n", + " )\n", + "\n", + " # Split data because rnn cell needs a list of inputs for the RNN inner loop\n", + " _X = tf.split(_X, hyperparams['n_steps'], 0)\n", + " # new shape: n_steps * (batch_size, n_hidden)\n", + "\n", + " # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow\n", + " lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", + " lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", + " lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True)\n", + "\n", + " # Get LSTM cell output\n", + " outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32)\n", + "\n", + " # Get last time step's output feature for a \"many-to-one\" style classifier,\n", + " # as in the image describing RNNs at the top of this page\n", + " lstm_last_output = outputs[-1]\n", + "\n", + " # Linear activation\n", + " pred = tf.matmul(lstm_last_output, weights['out']) + biases['out']\n", + " return tf.identity(pred, name=pred_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neuraxle RNN TensorFlow Model Step" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "LSTM_RNN_VARIABLE_SCOPE = \"lstm_rnn\"\n", + "X_NAME = 'x'\n", + "Y_NAME = 'y'\n", + "PRED_NAME = 'pred'\n", + "\n", + "N_HIDDEN = 32\n", + "N_STEPS = 128\n", + "N_INPUTS = 9\n", + "LAMBDA_LOSS_AMOUNT = 0.0015\n", + "LEARNING_RATE = 0.0025\n", + "N_CLASSES = 6\n", + "BATCH_SIZE = 1500\n", + "\n", + "class ClassificationRNNTensorFlowModel(BaseStep):\n", + " HYPERPARAMS = HyperparameterSamples({\n", + " 'n_steps': N_STEPS, # 128 timesteps per series\n", + " 'n_inputs': N_INPUTS, # 9 input parameters per timestep\n", + " 'n_hidden': N_HIDDEN, # Hidden layer num of features\n", + " 'n_classes': N_CLASSES, # Total classes (should go up, or should go down)\n", + " 'learning_rate': LEARNING_RATE,\n", + " 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT,\n", + " 'batch_size': BATCH_SIZE\n", + " })\n", + "\n", + " def __init__(\n", + " self\n", + " ):\n", + " BaseStep.__init__(\n", + " self,\n", + " hyperparams=ClassificationRNNTensorFlowModel.HYPERPARAMS,\n", + " savers=[TensorflowV1StepSaver()]\n", + " )\n", + "\n", + " self.graph = None\n", + " self.sess = None\n", + " self.l2 = None\n", + " self.cost = None\n", + " self.optimizer = None\n", + " self.correct_pred = None\n", + " self.accuracy = None\n", + " self.test_losses = None\n", + " self.test_accuracies = None\n", + " self.train_losses = None\n", + " self.train_accuracies = None\n", + "\n", + " def strip(self):\n", + " self.sess = None\n", + " self.graph = None\n", + " self.l2 = None\n", + " self.cost = None\n", + " self.optimizer = None\n", + " self.correct_pred = None\n", + " self.accuracy = None\n", + "\n", + " def setup(self) -> BaseStep:\n", + " if self.is_initialized:\n", + " return self\n", + "\n", + " self.create_graph()\n", + "\n", + " with self.graph.as_default():\n", + " # Launch the graph\n", + " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", + "\n", + " pred = tf_model_forward(PRED_NAME, X_NAME, Y_NAME, self.hyperparams)\n", + "\n", + " # Loss, optimizer and evaluation\n", + " # L2 loss prevents this overkill neural network to overfit the data\n", + "\n", + " l2 = self.hyperparams['lambda_loss_amount'] * sum(\n", + " tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables()\n", + " )\n", + "\n", + " # Softmax loss\n", + " self.cost = tf.reduce_mean(\n", + " tf.nn.softmax_cross_entropy_with_logits(\n", + " labels=self.get_y_placeholder(),\n", + " logits=pred\n", + " )\n", + " ) + l2\n", + "\n", + " # Adam Optimizer\n", + " self.optimizer = tf.train.AdamOptimizer(\n", + " learning_rate=self.hyperparams['learning_rate']\n", + " ).minimize(self.cost)\n", + "\n", + " self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(Y_NAME), 1))\n", + " self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32))\n", + "\n", + " # To keep track of training's performance\n", + " self.test_losses = []\n", + " self.test_accuracies = []\n", + " self.train_losses = []\n", + " self.train_accuracies = []\n", + "\n", + " self.create_session()\n", + "\n", + " self.is_initialized = True\n", + "\n", + " return self\n", + "\n", + " def create_graph(self):\n", + " self.graph = tf.Graph()\n", + "\n", + " def create_session(self):\n", + " self.sess = tf.Session(config=tf.ConfigProto(log_device_placement=True), graph=self.graph)\n", + " init = tf.global_variables_initializer()\n", + " self.sess.run(init)\n", + "\n", + " def get_tensor_by_name(self, name):\n", + " return self.graph.get_tensor_by_name(\"{0}/{1}:0\".format(LSTM_RNN_VARIABLE_SCOPE, name))\n", + "\n", + " def get_graph(self):\n", + " return self.graph\n", + "\n", + " def get_session(self):\n", + " return self.sess\n", + "\n", + " def get_x_placeholder(self):\n", + " return self.get_tensor_by_name(X_NAME)\n", + "\n", + " def get_y_placeholder(self):\n", + " return self.get_tensor_by_name(Y_NAME)\n", + "\n", + " def teardown(self):\n", + " if self.sess is not None:\n", + " self.sess.close()\n", + " self.is_initialized = False\n", + "\n", + " def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep':\n", + " if not isinstance(data_inputs, np.ndarray):\n", + " data_inputs = np.array(data_inputs)\n", + "\n", + " if not isinstance(expected_outputs, np.ndarray):\n", + " expected_outputs = np.array(expected_outputs)\n", + "\n", + " if expected_outputs.shape != (len(data_inputs), self.hyperparams['n_classes']):\n", + " expected_outputs = np.reshape(expected_outputs, (len(data_inputs), self.hyperparams['n_classes']))\n", + "\n", + " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", + " _, loss, acc = self.sess.run(\n", + " [self.optimizer, self.cost, self.accuracy],\n", + " feed_dict={\n", + " self.get_x_placeholder(): data_inputs,\n", + " self.get_y_placeholder(): expected_outputs\n", + " }\n", + " )\n", + "\n", + " self.train_losses.append(loss)\n", + " self.train_accuracies.append(acc)\n", + "\n", + " print(\"Batch Loss = \" + \"{:.6f}\".format(loss) + \", Accuracy = {}\".format(acc))\n", + "\n", + " self.is_invalidated = True\n", + "\n", + " return self\n", + "\n", + " def transform(self, data_inputs):\n", + " if not isinstance(data_inputs, np.ndarray):\n", + " data_inputs = np.array(data_inputs)\n", + "\n", + " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", + " outputs = self.sess.run(\n", + " [self.get_tensor_by_name(PRED_NAME)],\n", + " feed_dict={\n", + " self.get_x_placeholder(): data_inputs\n", + " }\n", + " )[0]\n", + " return outputs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Neuraxle Pipeline " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline):\n", + " def __init__(self):\n", + " MiniBatchSequentialPipeline.__init__(self, [\n", + " OutputTransformerWrapper(OneHotEncoder(nb_columns=N_CLASSES, name='one_hot_encoded_label')),\n", + " ClassificationRNNTensorFlowModel(),\n", + " Joiner(batch_size=BATCH_SIZE)\n", + " ])\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Train Pipeline " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:\n", + "The TensorFlow contrib module will not be included in TensorFlow 2.0.\n", + "For more information, please see:\n", + " * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n", + " * https://github.com/tensorflow/addons\n", + " * https://github.com/tensorflow/io (for I/O related ops)\n", + "If you depend on functionality not listed there, please file an issue.\n", + "\n", + "WARNING:tensorflow:From :51: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.\n", + "WARNING:tensorflow:From :53: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "This class is equivalent as tf.keras.layers.StackedRNNCells, and will be replaced by that in Tensorflow 2.0.\n", + "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Please use `layer.add_weight` method instead.\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Call initializer instance with the dtype argument instead of passing it to the constructor\n", + "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "\n", + "Future major versions of TensorFlow will allow gradients to flow\n", + "into the labels input on backprop by default.\n", + "\n", + "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n", + "\n", + "Device mapping:\n", + "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", + "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", + "\n", + "Batch Loss = 3.241702, Accuracy = 0.2486666738986969\n", + "Batch Loss = 2.910203, Accuracy = 0.1679999977350235\n", + "Batch Loss = 2.725292, Accuracy = 0.1586666703224182\n", + "Batch Loss = 2.542361, Accuracy = 0.2866666615009308\n", + "Batch Loss = 2.473011, Accuracy = 0.2869822382926941\n", + "Batch Loss = 2.330014, Accuracy = 0.37466666102409363\n", + "Batch Loss = 2.148370, Accuracy = 0.5326666831970215\n", + "Batch Loss = 2.044745, Accuracy = 0.5440000295639038\n", + "Batch Loss = 1.965893, Accuracy = 0.5566666722297668\n", + "Batch Loss = 1.962404, Accuracy = 0.543639063835144\n", + "Batch Loss = 1.842139, Accuracy = 0.5946666598320007\n", + "Batch Loss = 1.706470, Accuracy = 0.6213333606719971\n", + "Batch Loss = 1.632022, Accuracy = 0.5893333554267883\n", + "Batch Loss = 1.551357, Accuracy = 0.6299999952316284\n", + "Batch Loss = 1.610076, Accuracy = 0.5806213021278381\n", + "Batch Loss = 1.548283, Accuracy = 0.6039999723434448\n", + "Batch Loss = 1.422438, Accuracy = 0.6480000019073486\n", + "Batch Loss = 1.444853, Accuracy = 0.6000000238418579\n", + "Batch Loss = 1.360228, Accuracy = 0.6866666674613953\n", + "Batch Loss = 1.450967, Accuracy = 0.5828402638435364\n", + "Batch Loss = 1.455857, Accuracy = 0.6286666393280029\n", + "Batch Loss = 1.380381, Accuracy = 0.6706666946411133\n", + "Batch Loss = 1.301196, Accuracy = 0.6286666393280029\n", + "Batch Loss = 1.271451, Accuracy = 0.7459999918937683\n", + "Batch Loss = 1.330271, Accuracy = 0.6294378638267517\n", + "Batch Loss = 1.370623, Accuracy = 0.596666693687439\n", + "Batch Loss = 1.304938, Accuracy = 0.6673333048820496\n", + "Batch Loss = 1.239836, Accuracy = 0.6326666474342346\n", + "Batch Loss = 1.230559, Accuracy = 0.7213333249092102\n", + "Batch Loss = 1.297570, Accuracy = 0.6508875489234924\n", + "Batch Loss = 1.295553, Accuracy = 0.6679999828338623\n", + "Batch Loss = 1.245163, Accuracy = 0.7173333168029785\n", + "Batch Loss = 1.223890, Accuracy = 0.6766666769981384\n", + "Batch Loss = 1.170235, Accuracy = 0.7599999904632568\n", + "Batch Loss = 1.287478, Accuracy = 0.6723372936248779\n", + "Batch Loss = 1.262810, Accuracy = 0.699999988079071\n", + "Batch Loss = 1.199744, Accuracy = 0.7519999742507935\n", + "Batch Loss = 1.208227, Accuracy = 0.6840000152587891\n", + "Batch Loss = 1.125371, Accuracy = 0.7746666669845581\n", + "Batch Loss = 1.273762, Accuracy = 0.6886094808578491\n", + "Batch Loss = 1.220099, Accuracy = 0.7266666889190674\n", + "Batch Loss = 1.161350, Accuracy = 0.7599999904632568\n", + "Batch Loss = 1.176977, Accuracy = 0.7133333086967468\n", + "Batch Loss = 1.089378, Accuracy = 0.7866666913032532\n", + "Batch Loss = 1.245091, Accuracy = 0.7026627063751221\n", + "Batch Loss = 1.172715, Accuracy = 0.7726666927337646\n", + "Batch Loss = 1.113400, Accuracy = 0.7886666655540466\n", + "Batch Loss = 1.121949, Accuracy = 0.7446666955947876\n", + "Batch Loss = 1.084712, Accuracy = 0.7886666655540466\n", + "Batch Loss = 1.200617, Accuracy = 0.7211538553237915\n", + "Batch Loss = 1.125846, Accuracy = 0.7753333449363708\n", + "Batch Loss = 1.075384, Accuracy = 0.8059999942779541\n", + "Batch Loss = 1.073960, Accuracy = 0.753333330154419\n", + "Batch Loss = 1.115330, Accuracy = 0.777999997138977\n", + "Batch Loss = 1.206975, Accuracy = 0.7048816680908203\n", + "Batch Loss = 1.086149, Accuracy = 0.8073333501815796\n", + "Batch Loss = 1.090850, Accuracy = 0.8066666722297668\n", + "Batch Loss = 1.065366, Accuracy = 0.7526666522026062\n", + "Batch Loss = 1.053179, Accuracy = 0.8133333325386047\n", + "Batch Loss = 1.278356, Accuracy = 0.6930473446846008\n", + "Batch Loss = 1.098301, Accuracy = 0.7893333435058594\n", + "Batch Loss = 0.966477, Accuracy = 0.8633333444595337\n", + "Batch Loss = 1.134416, Accuracy = 0.7559999823570251\n", + "Batch Loss = 1.027541, Accuracy = 0.8119999766349792\n", + "Batch Loss = 1.040835, Accuracy = 0.8232248425483704\n", + "Batch Loss = 1.051602, Accuracy = 0.8173333406448364\n", + "Batch Loss = 1.040935, Accuracy = 0.7853333353996277\n", + "Batch Loss = 1.011296, Accuracy = 0.8013333082199097\n", + "Batch Loss = 0.946583, Accuracy = 0.8379999995231628\n", + "Batch Loss = 1.061627, Accuracy = 0.793639063835144\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "HumanActivityRecognitionPipeline\n", + "(\n", + "\tHumanActivityRecognitionPipeline(\n", + "\tname=HumanActivityRecognitionPipeline,\n", + "\thyperparameters=HyperparameterSamples()\n", + ")(\n", + "\t\t[('OutputTransformerWrapper',\n", + " OutputTransformerWrapper(\n", + "\twrapped=OneHotEncoder(\n", + "\tname=one_hot_encoded_label,\n", + "\thyperparameters=HyperparameterSamples()\n", + "),\n", + "\thyperparameters=HyperparameterSamples()\n", + ")),\n", + " ('ClassificationRNNTensorFlowModel',\n", + " ClassificationRNNTensorFlowModel(\n", + "\tname=ClassificationRNNTensorFlowModel,\n", + "\thyperparameters=HyperparameterSamples([('n_steps', 128),\n", + " ('n_inputs', 9),\n", + " ('n_hidden', 32),\n", + " ('n_classes', 6),\n", + " ('learning_rate', 0.0025),\n", + " ('lambda_loss_amount', 0.0015),\n", + " ('batch_size', 1500)])\n", + ")),\n", + " ('Joiner', Joiner(\n", + "\tname=Joiner,\n", + "\thyperparameters=HyperparameterSamples()\n", + "))]\t\n", + ")\n", + ")" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "training_data_count = len(X_train)\n", + "training_iters = training_data_count * 3\n", + "\n", + "pipeline = HumanActivityRecognitionPipeline()\n", + "\n", + "no_iter = int(math.floor(training_iters / BATCH_SIZE))\n", + "for _ in range(no_iter):\n", + " pipeline, outputs = pipeline.fit_transform(X_train, y_train)\n", + "\n", + "pipeline.save(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", + "\n", + "pipeline.teardown()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Serve Rest Api" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:\n", + "The TensorFlow contrib module will not be included in TensorFlow 2.0.\n", + "For more information, please see:\n", + " * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n", + " * https://github.com/tensorflow/addons\n", + " * https://github.com/tensorflow/io (for I/O related ops)\n", + "If you depend on functionality not listed there, please file an issue.\n", + "\n", + "WARNING:tensorflow:From :51: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.\n", + "WARNING:tensorflow:From :53: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "This class is equivalent as tf.keras.layers.StackedRNNCells, and will be replaced by that in Tensorflow 2.0.\n", + "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Please use `layer.add_weight` method instead.\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "Call initializer instance with the dtype argument instead of passing it to the constructor\n", + "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", + "Instructions for updating:\n", + "\n", + "Future major versions of TensorFlow will allow gradients to flow\n", + "into the labels input on backprop by default.\n", + "\n", + "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n", + "\n", + "Device mapping:\n", + "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", + "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", + "\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:50: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "\n", + "INFO:tensorflow:Restoring parameters from /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n" + ] + } + ], + "source": [ + "pipeline = HumanActivityRecognitionPipeline()\n", + "\n", + "pipeline = pipeline.load(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", + "\n", + "# pipeline, outputs = pipeline.fit_transform(X_train, y_train) # we could train further more here for instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " * Serving Flask app \"neuraxle.api.flask\" (lazy loading)\n", + " * Environment: production\n", + " WARNING: This is a development server. Do not use it in a production deployment.\n", + " Use a production WSGI server instead.\n", + " * Debug mode: off\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n" + ] + } + ], + "source": [ + "app = FlaskRestApiWrapper(\n", + " json_decoder=CustomJSONDecoderFor2DArray(),\n", + " wrapped=pipeline,\n", + " json_encoder=CustomJSONEncoderOfOutputs()\n", + ").get_app()\n", + "\n", + "app.run(debug=False, port=5000)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "lstm_har", + "language": "python", + "name": "lstm_har" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/2_call_rest_api_and_eval.ipynb b/2_call_rest_api_and_eval.ipynb new file mode 100644 index 0000000..45bad08 --- /dev/null +++ b/2_call_rest_api_and_eval.ipynb @@ -0,0 +1,264 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Imports " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import os\n", + "import json\n", + "import urllib\n", + "\n", + "import numpy as np\n", + "import tensorflow as tf\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib\n", + "from sklearn import metrics\n", + "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep, NonFittableMixin\n", + "from neuraxle.api.flask import FlaskRestApiWrapper\n", + "from neuraxle.hyperparams.space import HyperparameterSamples\n", + "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner, Pipeline\n", + "from neuraxle.steps.encoding import OneHotEncoder\n", + "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", + "\n", + "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", + " TRAIN_FILE_NAME, TEST_FILE_NAME, LABELS\n", + "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n", + "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", + "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", + "from savers.tensorflow1_step_saver import TensorflowV1StepSaver # TODO: move in a package neuraxle-tensorflow." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Read Data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some useful info to get an insight on dataset's shape and normalisation:\n", + "(X shape, y shape, every X's mean, every X's standard deviation)\n", + "(2947, 128, 9) (2947, 1) 0.09913992 0.39567086\n", + "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" + ] + } + ], + "source": [ + "DATA_PATH = \"data/\"\n", + "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", + "\n", + "# X_train = load_X(X_train_signals_paths)\n", + "X_test = load_X(X_test_signals_paths)\n", + "\n", + "# y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME)\n", + "y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME)\n", + "\n", + "# y_train = load_y(y_train_path)\n", + "y_test = load_y(y_test_path)\n", + "\n", + "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", + "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", + "print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test))\n", + "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# API Caller " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "class APICaller(NonFittableMixin, BaseStep):\n", + " def __init__(self, url):\n", + " BaseStep.__init__(self)\n", + " self.url = url\n", + " \n", + " def transform(self, data_inputs):\n", + " data = json.dumps(data_inputs.tolist()).encode('utf8')\n", + " req = urllib.request.Request(\n", + " 'http://127.0.0.1:5000/',\n", + " method=\"GET\",\n", + " headers={'content-type': 'application/json'},\n", + " data=data\n", + " )\n", + " response = urllib.request.urlopen(req)\n", + " test_predictions = json.loads(response.read())\n", + " return np.array(test_predictions['predictions'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Call Rest Api " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "URLError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mConnectionRefusedError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mdo_open\u001b[0;34m(self, http_class, req, **http_conn_args)\u001b[0m\n\u001b[1;32m 1317\u001b[0m h.request(req.get_method(), req.selector, req.data, headers,\n\u001b[0;32m-> 1318\u001b[0;31m encode_chunked=req.has_header('Transfer-encoding'))\n\u001b[0m\u001b[1;32m 1319\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mOSError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# timeout error\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, body, headers, encode_chunked)\u001b[0m\n\u001b[1;32m 1238\u001b[0m \u001b[0;34m\"\"\"Send a complete request to the server.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1239\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1240\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36m_send_request\u001b[0;34m(self, method, url, body, headers, encode_chunked)\u001b[0m\n\u001b[1;32m 1284\u001b[0m \u001b[0mbody\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_encode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'body'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1285\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mendheaders\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1286\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36mendheaders\u001b[0;34m(self, message_body, encode_chunked)\u001b[0m\n\u001b[1;32m 1233\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mCannotSendHeader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1234\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send_output\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmessage_body\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1235\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36m_send_output\u001b[0;34m(self, message_body, encode_chunked)\u001b[0m\n\u001b[1;32m 1025\u001b[0m \u001b[0;32mdel\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1026\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1027\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 963\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_open\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 964\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 965\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36mconnect\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 935\u001b[0m self.sock = self._create_connection(\n\u001b[0;32m--> 936\u001b[0;31m (self.host,self.port), self.timeout, self.source_address)\n\u001b[0m\u001b[1;32m 937\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetsockopt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msocket\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mIPPROTO_TCP\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msocket\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTCP_NODELAY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/socket.py\u001b[0m in \u001b[0;36mcreate_connection\u001b[0;34m(address, timeout, source_address)\u001b[0m\n\u001b[1;32m 723\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0merr\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 724\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 725\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/socket.py\u001b[0m in \u001b[0;36mcreate_connection\u001b[0;34m(address, timeout, source_address)\u001b[0m\n\u001b[1;32m 712\u001b[0m \u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msource_address\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 713\u001b[0;31m \u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msa\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 714\u001b[0m \u001b[0;31m# Break explicitly a reference cycle\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mConnectionRefusedError\u001b[0m: [Errno 111] Connection refused", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mURLError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mAPICaller\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"http://localhost:5000/\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m ])\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0my_pred\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX_test\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my_pred\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0mcontext\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mExecutionContext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_from_root\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mExecutionMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTRANSFORM\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcache_folder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 73\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_transform_core\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 75\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36m_transform_core\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstep_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstep\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msteps_left_to_do\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 249\u001b[0m \u001b[0msub_context\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 250\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstep\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandle_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msub_context\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 251\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 252\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/base.py\u001b[0m in \u001b[0;36mhandle_transform\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 805\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;32mreturn\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mtransformed\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mcontainer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 806\u001b[0m \"\"\"\n\u001b[0;32m--> 807\u001b[0;31m \u001b[0mout\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 808\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_data_inputs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 809\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m )\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0murllib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0murlopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0mtest_predictions\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtest_predictions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'predictions'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36murlopen\u001b[0;34m(url, data, timeout, cafile, capath, cadefault, context)\u001b[0m\n\u001b[1;32m 221\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0mopener\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_opener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 223\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mopener\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 224\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 225\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minstall_opener\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mopener\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mopen\u001b[0;34m(self, fullurl, data, timeout)\u001b[0m\n\u001b[1;32m 524\u001b[0m \u001b[0mreq\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmeth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 525\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 526\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_open\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 527\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 528\u001b[0m \u001b[0;31m# post-process response\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36m_open\u001b[0;34m(self, req, data)\u001b[0m\n\u001b[1;32m 542\u001b[0m \u001b[0mprotocol\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 543\u001b[0m result = self._call_chain(self.handle_open, protocol, protocol +\n\u001b[0;32m--> 544\u001b[0;31m '_open', req)\n\u001b[0m\u001b[1;32m 545\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 546\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36m_call_chain\u001b[0;34m(self, chain, kind, meth_name, *args)\u001b[0m\n\u001b[1;32m 502\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mhandler\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mhandlers\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 503\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhandler\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmeth_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 504\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 505\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 506\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mhttp_open\u001b[0;34m(self, req)\u001b[0m\n\u001b[1;32m 1344\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1345\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mhttp_open\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1346\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdo_open\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhttp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclient\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mHTTPConnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1347\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1348\u001b[0m \u001b[0mhttp_request\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mAbstractHTTPHandler\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdo_request_\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mdo_open\u001b[0;34m(self, http_class, req, **http_conn_args)\u001b[0m\n\u001b[1;32m 1318\u001b[0m encode_chunked=req.has_header('Transfer-encoding'))\n\u001b[1;32m 1319\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mOSError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# timeout error\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1320\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mURLError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1321\u001b[0m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetresponse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1322\u001b[0m \u001b[0;32mexcept\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mURLError\u001b[0m: " + ] + } + ], + "source": [ + "p = Pipeline([\n", + " APICaller(url=\"http://localhost:5000/\")\n", + "])\n", + "y_pred = p.transform(X_test)\n", + "print(y_pred)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# (Inline plots: )\n", + "%matplotlib inline\n", + "\n", + "font = {\n", + " 'family' : 'Bitstream Vera Sans',\n", + " 'weight' : 'bold',\n", + " 'size' : 18\n", + "}\n", + "matplotlib.rc('font', **font)\n", + "\n", + "# Results\n", + "\n", + "predictions = y_pred.argmax(1)\n", + "n_classes = 6\n", + "\n", + "print(\"\")\n", + "print(\"Precision: {}%\".format(100*metrics.precision_score(y_test, predictions, average=\"weighted\")))\n", + "print(\"Recall: {}%\".format(100*metrics.recall_score(y_test, predictions, average=\"weighted\")))\n", + "print(\"f1_score: {}%\".format(100*metrics.f1_score(y_test, predictions, average=\"weighted\")))\n", + "\n", + "print(\"\")\n", + "print(\"Confusion Matrix:\")\n", + "confusion_matrix = metrics.confusion_matrix(y_test, predictions)\n", + "print(confusion_matrix)\n", + "normalised_confusion_matrix = np.array(confusion_matrix, dtype=np.float32)/np.sum(confusion_matrix)*100\n", + "\n", + "print(\"\")\n", + "print(\"Confusion matrix (normalised to % of total test data):\")\n", + "print(normalised_confusion_matrix)\n", + "print(\"Note: training and testing data is not equally distributed amongst classes, \")\n", + "print(\"so it is normal that more than a 6th of the data is correctly classifier in the last category.\")\n", + "\n", + "# Plot Results:\n", + "width = 12\n", + "height = 12\n", + "plt.figure(figsize=(width, height))\n", + "plt.imshow(\n", + " normalised_confusion_matrix,\n", + " interpolation='nearest',\n", + " cmap=plt.cm.rainbow\n", + ")\n", + "plt.title(\"Confusion matrix \\n(normalised to % of total test data)\")\n", + "plt.colorbar()\n", + "tick_marks = np.arange(n_classes)\n", + "plt.xticks(tick_marks, LABELS, rotation=90)\n", + "plt.yticks(tick_marks, LABELS)\n", + "plt.tight_layout()\n", + "plt.ylabel('True label')\n", + "plt.xlabel('Predicted label')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "p.teardown()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "lstm_har", + "language": "python", + "name": "lstm_har" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/pipeline.py b/pipeline.py index 96ae597..fd342ac 100644 --- a/pipeline.py +++ b/pipeline.py @@ -1,7 +1,8 @@ from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner +from neuraxle.steps.encoding import OneHotEncoder +from neuraxle.steps.output_handlers import OutputTransformerWrapper + from steps.lstm_rnn_tensorflow_model_wrapper import ClassificationRNNTensorFlowModel, N_CLASSES, BATCH_SIZE -from steps.one_hot_encoder import OneHotEncoder -from steps.transform_expected_output_wrapper import OutputTransformerWrapper # TODO: wrap by a validation split wrapper as issue #174 diff --git a/steps/custom_json_decoder_for_2darray.py b/steps/custom_json_decoder_for_2darray.py index 0d60898..f74f047 100644 --- a/steps/custom_json_decoder_for_2darray.py +++ b/steps/custom_json_decoder_for_2darray.py @@ -13,4 +13,7 @@ def decode(self, data_inputs): :param data_inputs: json object :return: np array for data inputs """ + print(type(data_inputs)) + print(len(data_inputs)) + print(type(data_inputs)[0]) return np.array(data_inputs) diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index 0d4d53d..97e6a4c 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -1,11 +1,11 @@ import numpy as np import tensorflow as tf - from neuraxle.base import BaseStep from neuraxle.hyperparams.space import HyperparameterSamples +from neuraxle.steps.encoding import OneHotEncoder + from savers.tensorflow1_step_saver import TensorflowV1StepSaver from steps.lstm_rnn_tensorflow_model import tf_model_forward -from steps.one_hot_encoder import OneHotEncoder LSTM_RNN_VARIABLE_SCOPE = "lstm_rnn" X_NAME = 'x' @@ -80,7 +80,6 @@ def setup(self) -> BaseStep: with self.graph.as_default(): # Launch the graph with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): - pred = tf_model_forward(PRED_NAME, X_NAME, Y_NAME, self.hyperparams) # Loss, optimizer and evaluation From e200823932de83147bd552fb6291449bdb3e3c3b Mon Sep 17 00:00:00 2001 From: guillaume-chevalier Date: Mon, 4 Nov 2019 22:35:25 -0500 Subject: [PATCH 19/30] remove print bug --- steps/custom_json_decoder_for_2darray.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/steps/custom_json_decoder_for_2darray.py b/steps/custom_json_decoder_for_2darray.py index f74f047..0d60898 100644 --- a/steps/custom_json_decoder_for_2darray.py +++ b/steps/custom_json_decoder_for_2darray.py @@ -13,7 +13,4 @@ def decode(self, data_inputs): :param data_inputs: json object :return: np array for data inputs """ - print(type(data_inputs)) - print(len(data_inputs)) - print(type(data_inputs)[0]) return np.array(data_inputs) From 16a88561e6d4c3663cd7ce4b61186a11e4c997fb Mon Sep 17 00:00:00 2001 From: guillaume-chevalier Date: Mon, 4 Nov 2019 22:44:55 -0500 Subject: [PATCH 20/30] Improved examples --- 1_train_and_save_LSTM.ipynb | 232 ++++++++++++++------------------- 2_call_rest_api_and_eval.ipynb | 125 +++++++++++++----- 2 files changed, 190 insertions(+), 167 deletions(-) diff --git a/1_train_and_save_LSTM.ipynb b/1_train_and_save_LSTM.ipynb index 85bd97c..907690f 100644 --- a/1_train_and_save_LSTM.ipynb +++ b/1_train_and_save_LSTM.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -44,20 +44,20 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py\t steps\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__\t venv\n", - "cache\t\t\t\tLSTM_files\t README.md\n", - "call_api.py\t\t\tnew.py\t\t requirements.txt\n", - "data\t\t\t\told.py\t\t savers\n", - "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/data\n", + "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py old.py\t\t savers\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t pipeline.py\t steps\n", + "Call-API.ipynb\t\t\tLSTM_files\t __pycache__\t venv\n", + "call_api.py\t\t\tLSTM_new.ipynb\t README.md\n", + "data\t\t\t\tnew.py\t\t requirements.txt\n", + "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/data\n", " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", "\n", @@ -67,15 +67,15 @@ "Extracting...\n", "Dataset already extracted. Did not extract twice.\n", "\n", - "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/data\n", + "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/data\n", " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", - "/home/gui/Documents/GIT/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py\t steps\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__\t venv\n", - "cache\t\t\t\tLSTM_files\t README.md\n", - "call_api.py\t\t\tnew.py\t\t requirements.txt\n", - "data\t\t\t\told.py\t\t savers\n", + "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py old.py\t\t savers\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t pipeline.py\t steps\n", + "Call-API.ipynb\t\t\tLSTM_files\t __pycache__\t venv\n", + "call_api.py\t\t\tLSTM_new.ipynb\t README.md\n", + "data\t\t\t\tnew.py\t\t requirements.txt\n", "\n", "Dataset is now located at: data/UCI HAR Dataset/\n" ] @@ -109,7 +109,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -152,7 +152,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -231,7 +231,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -415,7 +415,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -438,7 +438,9 @@ { "cell_type": "code", "execution_count": 7, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [ { "name": "stdout", @@ -461,10 +463,10 @@ "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Please use `layer.add_weight` method instead.\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Call initializer instance with the dtype argument instead of passing it to the constructor\n", "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", @@ -479,77 +481,77 @@ "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", - "Batch Loss = 3.241702, Accuracy = 0.2486666738986969\n", - "Batch Loss = 2.910203, Accuracy = 0.1679999977350235\n", - "Batch Loss = 2.725292, Accuracy = 0.1586666703224182\n", - "Batch Loss = 2.542361, Accuracy = 0.2866666615009308\n", - "Batch Loss = 2.473011, Accuracy = 0.2869822382926941\n", - "Batch Loss = 2.330014, Accuracy = 0.37466666102409363\n", - "Batch Loss = 2.148370, Accuracy = 0.5326666831970215\n", - "Batch Loss = 2.044745, Accuracy = 0.5440000295639038\n", - "Batch Loss = 1.965893, Accuracy = 0.5566666722297668\n", - "Batch Loss = 1.962404, Accuracy = 0.543639063835144\n", - "Batch Loss = 1.842139, Accuracy = 0.5946666598320007\n", - "Batch Loss = 1.706470, Accuracy = 0.6213333606719971\n", - "Batch Loss = 1.632022, Accuracy = 0.5893333554267883\n", - "Batch Loss = 1.551357, Accuracy = 0.6299999952316284\n", - "Batch Loss = 1.610076, Accuracy = 0.5806213021278381\n", - "Batch Loss = 1.548283, Accuracy = 0.6039999723434448\n", - "Batch Loss = 1.422438, Accuracy = 0.6480000019073486\n", - "Batch Loss = 1.444853, Accuracy = 0.6000000238418579\n", - "Batch Loss = 1.360228, Accuracy = 0.6866666674613953\n", - "Batch Loss = 1.450967, Accuracy = 0.5828402638435364\n", - "Batch Loss = 1.455857, Accuracy = 0.6286666393280029\n", - "Batch Loss = 1.380381, Accuracy = 0.6706666946411133\n", - "Batch Loss = 1.301196, Accuracy = 0.6286666393280029\n", - "Batch Loss = 1.271451, Accuracy = 0.7459999918937683\n", - "Batch Loss = 1.330271, Accuracy = 0.6294378638267517\n", - "Batch Loss = 1.370623, Accuracy = 0.596666693687439\n", - "Batch Loss = 1.304938, Accuracy = 0.6673333048820496\n", - "Batch Loss = 1.239836, Accuracy = 0.6326666474342346\n", - "Batch Loss = 1.230559, Accuracy = 0.7213333249092102\n", - "Batch Loss = 1.297570, Accuracy = 0.6508875489234924\n", - "Batch Loss = 1.295553, Accuracy = 0.6679999828338623\n", - "Batch Loss = 1.245163, Accuracy = 0.7173333168029785\n", - "Batch Loss = 1.223890, Accuracy = 0.6766666769981384\n", - "Batch Loss = 1.170235, Accuracy = 0.7599999904632568\n", - "Batch Loss = 1.287478, Accuracy = 0.6723372936248779\n", - "Batch Loss = 1.262810, Accuracy = 0.699999988079071\n", - "Batch Loss = 1.199744, Accuracy = 0.7519999742507935\n", - "Batch Loss = 1.208227, Accuracy = 0.6840000152587891\n", - "Batch Loss = 1.125371, Accuracy = 0.7746666669845581\n", - "Batch Loss = 1.273762, Accuracy = 0.6886094808578491\n", - "Batch Loss = 1.220099, Accuracy = 0.7266666889190674\n", - "Batch Loss = 1.161350, Accuracy = 0.7599999904632568\n", - "Batch Loss = 1.176977, Accuracy = 0.7133333086967468\n", - "Batch Loss = 1.089378, Accuracy = 0.7866666913032532\n", - "Batch Loss = 1.245091, Accuracy = 0.7026627063751221\n", - "Batch Loss = 1.172715, Accuracy = 0.7726666927337646\n", - "Batch Loss = 1.113400, Accuracy = 0.7886666655540466\n", - "Batch Loss = 1.121949, Accuracy = 0.7446666955947876\n", - "Batch Loss = 1.084712, Accuracy = 0.7886666655540466\n", - "Batch Loss = 1.200617, Accuracy = 0.7211538553237915\n", - "Batch Loss = 1.125846, Accuracy = 0.7753333449363708\n", - "Batch Loss = 1.075384, Accuracy = 0.8059999942779541\n", - "Batch Loss = 1.073960, Accuracy = 0.753333330154419\n", - "Batch Loss = 1.115330, Accuracy = 0.777999997138977\n", - "Batch Loss = 1.206975, Accuracy = 0.7048816680908203\n", - "Batch Loss = 1.086149, Accuracy = 0.8073333501815796\n", - "Batch Loss = 1.090850, Accuracy = 0.8066666722297668\n", - "Batch Loss = 1.065366, Accuracy = 0.7526666522026062\n", - "Batch Loss = 1.053179, Accuracy = 0.8133333325386047\n", - "Batch Loss = 1.278356, Accuracy = 0.6930473446846008\n", - "Batch Loss = 1.098301, Accuracy = 0.7893333435058594\n", - "Batch Loss = 0.966477, Accuracy = 0.8633333444595337\n", - "Batch Loss = 1.134416, Accuracy = 0.7559999823570251\n", - "Batch Loss = 1.027541, Accuracy = 0.8119999766349792\n", - "Batch Loss = 1.040835, Accuracy = 0.8232248425483704\n", - "Batch Loss = 1.051602, Accuracy = 0.8173333406448364\n", - "Batch Loss = 1.040935, Accuracy = 0.7853333353996277\n", - "Batch Loss = 1.011296, Accuracy = 0.8013333082199097\n", - "Batch Loss = 0.946583, Accuracy = 0.8379999995231628\n", - "Batch Loss = 1.061627, Accuracy = 0.793639063835144\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "Batch Loss = 2.876983, Accuracy = 0.20266667008399963\n", + "Batch Loss = 2.422258, Accuracy = 0.1393333375453949\n", + "Batch Loss = 2.341008, Accuracy = 0.3019999861717224\n", + "Batch Loss = 2.229099, Accuracy = 0.3840000033378601\n", + "Batch Loss = 2.204831, Accuracy = 0.459319531917572\n", + "Batch Loss = 2.161258, Accuracy = 0.44733333587646484\n", + "Batch Loss = 2.008523, Accuracy = 0.5326666831970215\n", + "Batch Loss = 1.936510, Accuracy = 0.492000013589859\n", + "Batch Loss = 1.889814, Accuracy = 0.5640000104904175\n", + "Batch Loss = 1.925490, Accuracy = 0.5044378638267517\n", + "Batch Loss = 1.901193, Accuracy = 0.45266667008399963\n", + "Batch Loss = 1.823743, Accuracy = 0.5026666522026062\n", + "Batch Loss = 1.856515, Accuracy = 0.5546666383743286\n", + "Batch Loss = 1.781088, Accuracy = 0.6653333306312561\n", + "Batch Loss = 1.788599, Accuracy = 0.5739644765853882\n", + "Batch Loss = 1.765126, Accuracy = 0.5773333311080933\n", + "Batch Loss = 1.715384, Accuracy = 0.5860000252723694\n", + "Batch Loss = 1.694299, Accuracy = 0.6066666841506958\n", + "Batch Loss = 1.629456, Accuracy = 0.6166666746139526\n", + "Batch Loss = 1.659033, Accuracy = 0.5798816680908203\n", + "Batch Loss = 1.690838, Accuracy = 0.5299999713897705\n", + "Batch Loss = 1.546839, Accuracy = 0.6359999775886536\n", + "Batch Loss = 1.502047, Accuracy = 0.6713333129882812\n", + "Batch Loss = 1.516301, Accuracy = 0.6119999885559082\n", + "Batch Loss = 1.472707, Accuracy = 0.6797337532043457\n", + "Batch Loss = 1.473756, Accuracy = 0.6140000224113464\n", + "Batch Loss = 1.435259, Accuracy = 0.6420000195503235\n", + "Batch Loss = 1.357141, Accuracy = 0.6926666498184204\n", + "Batch Loss = 1.298965, Accuracy = 0.7059999704360962\n", + "Batch Loss = 1.342825, Accuracy = 0.6767751574516296\n", + "Batch Loss = 1.246462, Accuracy = 0.7273333072662354\n", + "Batch Loss = 1.204010, Accuracy = 0.746666669845581\n", + "Batch Loss = 1.221481, Accuracy = 0.7213333249092102\n", + "Batch Loss = 1.134928, Accuracy = 0.7746666669845581\n", + "Batch Loss = 1.173433, Accuracy = 0.7536982297897339\n", + "Batch Loss = 1.493877, Accuracy = 0.6880000233650208\n", + "Batch Loss = 1.092508, Accuracy = 0.7873333096504211\n", + "Batch Loss = 1.183987, Accuracy = 0.731333315372467\n", + "Batch Loss = 1.287978, Accuracy = 0.7333333492279053\n", + "Batch Loss = 1.353160, Accuracy = 0.7004438042640686\n", + "Batch Loss = 1.258987, Accuracy = 0.6980000138282776\n", + "Batch Loss = 1.120263, Accuracy = 0.7739999890327454\n", + "Batch Loss = 1.157481, Accuracy = 0.7213333249092102\n", + "Batch Loss = 1.016302, Accuracy = 0.831333339214325\n", + "Batch Loss = 1.225947, Accuracy = 0.7204142212867737\n", + "Batch Loss = 1.158564, Accuracy = 0.7620000243186951\n", + "Batch Loss = 1.083358, Accuracy = 0.7940000295639038\n", + "Batch Loss = 1.149199, Accuracy = 0.7233333587646484\n", + "Batch Loss = 1.003003, Accuracy = 0.8633333444595337\n", + "Batch Loss = 1.134901, Accuracy = 0.7803254723548889\n", + "Batch Loss = 1.140388, Accuracy = 0.765333354473114\n", + "Batch Loss = 1.061487, Accuracy = 0.8199999928474426\n", + "Batch Loss = 1.074304, Accuracy = 0.7480000257492065\n", + "Batch Loss = 1.014286, Accuracy = 0.8326666951179504\n", + "Batch Loss = 1.077505, Accuracy = 0.7847633361816406\n", + "Batch Loss = 1.020065, Accuracy = 0.8053333163261414\n", + "Batch Loss = 1.056145, Accuracy = 0.8066666722297668\n", + "Batch Loss = 1.143748, Accuracy = 0.718666672706604\n", + "Batch Loss = 1.218218, Accuracy = 0.7706666588783264\n", + "Batch Loss = 1.041671, Accuracy = 0.8195266127586365\n", + "Batch Loss = 1.523212, Accuracy = 0.7006666660308838\n", + "Batch Loss = 1.661112, Accuracy = 0.7006666660308838\n", + "Batch Loss = 1.367053, Accuracy = 0.7459999918937683\n", + "Batch Loss = 1.472857, Accuracy = 0.6653333306312561\n", + "Batch Loss = 1.244812, Accuracy = 0.7315088510513306\n", + "Batch Loss = 1.302771, Accuracy = 0.7273333072662354\n", + "Batch Loss = 1.324543, Accuracy = 0.7106666564941406\n", + "Batch Loss = 1.442056, Accuracy = 0.6573333144187927\n", + "Batch Loss = 1.196929, Accuracy = 0.8273333311080933\n", + "Batch Loss = 1.161574, Accuracy = 0.7988165616989136\n", + "WARNING:tensorflow:From /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", "\n" ] }, @@ -625,44 +627,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "WARNING:tensorflow:\n", - "The TensorFlow contrib module will not be included in TensorFlow 2.0.\n", - "For more information, please see:\n", - " * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n", - " * https://github.com/tensorflow/addons\n", - " * https://github.com/tensorflow/io (for I/O related ops)\n", - "If you depend on functionality not listed there, please file an issue.\n", - "\n", - "WARNING:tensorflow:From :51: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :53: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "This class is equivalent as tf.keras.layers.StackedRNNCells, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Please use `layer.add_weight` method instead.\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Call initializer instance with the dtype argument instead of passing it to the constructor\n", - "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "\n", - "Future major versions of TensorFlow will allow gradients to flow\n", - "into the labels input on backprop by default.\n", - "\n", - "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n", - "\n", "Device mapping:\n", "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:50: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", - "\n", - "INFO:tensorflow:Restoring parameters from /home/gui/Documents/GIT/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n" + "INFO:tensorflow:Restoring parameters from /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n" ] } ], @@ -694,7 +663,8 @@ "name": "stderr", "output_type": "stream", "text": [ - " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n" + " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", + "127.0.0.1 - - [04/Nov/2019 22:38:52] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" ] } ], @@ -711,9 +681,9 @@ ], "metadata": { "kernelspec": { - "display_name": "lstm_har", + "display_name": "lstm_har_2", "language": "python", - "name": "lstm_har" + "name": "lstm_har_2" }, "language_info": { "codemirror_mode": { diff --git a/2_call_rest_api_and_eval.ipynb b/2_call_rest_api_and_eval.ipynb index 45bad08..802e308 100644 --- a/2_call_rest_api_and_eval.ipynb +++ b/2_call_rest_api_and_eval.ipynb @@ -124,36 +124,16 @@ "metadata": {}, "outputs": [ { - "ename": "URLError", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mConnectionRefusedError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mdo_open\u001b[0;34m(self, http_class, req, **http_conn_args)\u001b[0m\n\u001b[1;32m 1317\u001b[0m h.request(req.get_method(), req.selector, req.data, headers,\n\u001b[0;32m-> 1318\u001b[0;31m encode_chunked=req.has_header('Transfer-encoding'))\n\u001b[0m\u001b[1;32m 1319\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mOSError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# timeout error\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, body, headers, encode_chunked)\u001b[0m\n\u001b[1;32m 1238\u001b[0m \u001b[0;34m\"\"\"Send a complete request to the server.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1239\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheaders\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1240\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36m_send_request\u001b[0;34m(self, method, url, body, headers, encode_chunked)\u001b[0m\n\u001b[1;32m 1284\u001b[0m \u001b[0mbody\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_encode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'body'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1285\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mendheaders\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1286\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36mendheaders\u001b[0;34m(self, message_body, encode_chunked)\u001b[0m\n\u001b[1;32m 1233\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mCannotSendHeader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1234\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_send_output\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmessage_body\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mencode_chunked\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mencode_chunked\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1235\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36m_send_output\u001b[0;34m(self, message_body, encode_chunked)\u001b[0m\n\u001b[1;32m 1025\u001b[0m \u001b[0;32mdel\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1026\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1027\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36msend\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m 963\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mauto_open\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 964\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 965\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/http/client.py\u001b[0m in \u001b[0;36mconnect\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 935\u001b[0m self.sock = self._create_connection(\n\u001b[0;32m--> 936\u001b[0;31m (self.host,self.port), self.timeout, self.source_address)\n\u001b[0m\u001b[1;32m 937\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msetsockopt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msocket\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mIPPROTO_TCP\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msocket\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTCP_NODELAY\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/socket.py\u001b[0m in \u001b[0;36mcreate_connection\u001b[0;34m(address, timeout, source_address)\u001b[0m\n\u001b[1;32m 723\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0merr\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 724\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 725\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/socket.py\u001b[0m in \u001b[0;36mcreate_connection\u001b[0;34m(address, timeout, source_address)\u001b[0m\n\u001b[1;32m 712\u001b[0m \u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msource_address\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 713\u001b[0;31m \u001b[0msock\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconnect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msa\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 714\u001b[0m \u001b[0;31m# Break explicitly a reference cycle\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mConnectionRefusedError\u001b[0m: [Errno 111] Connection refused", - "\nDuring handling of the above exception, another exception occurred:\n", - "\u001b[0;31mURLError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mAPICaller\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"http://localhost:5000/\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m ])\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0my_pred\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX_test\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my_pred\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0mcontext\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mExecutionContext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_from_root\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mExecutionMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTRANSFORM\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcache_folder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 73\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_transform_core\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 75\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36m_transform_core\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstep_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstep\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msteps_left_to_do\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 249\u001b[0m \u001b[0msub_context\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 250\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstep\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandle_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msub_context\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 251\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 252\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/GIT/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/base.py\u001b[0m in \u001b[0;36mhandle_transform\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 805\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;32mreturn\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mtransformed\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mcontainer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 806\u001b[0m \"\"\"\n\u001b[0;32m--> 807\u001b[0;31m \u001b[0mout\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 808\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_data_inputs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 809\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m )\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0murllib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0murlopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0mtest_predictions\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtest_predictions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'predictions'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36murlopen\u001b[0;34m(url, data, timeout, cafile, capath, cadefault, context)\u001b[0m\n\u001b[1;32m 221\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0mopener\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_opener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 223\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mopener\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 224\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 225\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minstall_opener\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mopener\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mopen\u001b[0;34m(self, fullurl, data, timeout)\u001b[0m\n\u001b[1;32m 524\u001b[0m \u001b[0mreq\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmeth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 525\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 526\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_open\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 527\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 528\u001b[0m \u001b[0;31m# post-process response\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36m_open\u001b[0;34m(self, req, data)\u001b[0m\n\u001b[1;32m 542\u001b[0m \u001b[0mprotocol\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 543\u001b[0m result = self._call_chain(self.handle_open, protocol, protocol +\n\u001b[0;32m--> 544\u001b[0;31m '_open', req)\n\u001b[0m\u001b[1;32m 545\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 546\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36m_call_chain\u001b[0;34m(self, chain, kind, meth_name, *args)\u001b[0m\n\u001b[1;32m 502\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mhandler\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mhandlers\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 503\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhandler\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmeth_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 504\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 505\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 506\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mhttp_open\u001b[0;34m(self, req)\u001b[0m\n\u001b[1;32m 1344\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1345\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mhttp_open\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1346\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdo_open\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhttp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclient\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mHTTPConnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1347\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1348\u001b[0m \u001b[0mhttp_request\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mAbstractHTTPHandler\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdo_request_\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mdo_open\u001b[0;34m(self, http_class, req, **http_conn_args)\u001b[0m\n\u001b[1;32m 1318\u001b[0m encode_chunked=req.has_header('Transfer-encoding'))\n\u001b[1;32m 1319\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mOSError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# timeout error\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1320\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mURLError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1321\u001b[0m \u001b[0mr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mh\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetresponse\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1322\u001b[0m \u001b[0;32mexcept\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mURLError\u001b[0m: " + "name": "stdout", + "output_type": "stream", + "text": [ + "[[-1.00376022 -3.22574663 -1.74762106 -0.1013099 0.61165339 -5.02736664]\n", + " [-0.90527415 -3.20171666 -1.81338 0.0932436 0.75021946 -4.88879108]\n", + " [-0.89485669 -3.16932821 -1.80476213 0.04580438 0.77750677 -4.89109993]\n", + " ...\n", + " [ 4.41215801 6.99871826 2.78054214 1.71619391 0.72097886 2.2889092 ]\n", + " [ 3.51318192 6.10547161 1.43138313 2.56595802 1.21157002 0.62227172]\n", + " [ 3.96033239 5.9378233 3.445261 0.99932408 1.05689049 -0.02206901]]\n" ] } ], @@ -174,9 +154,59 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Precision: 73.67085664926599%\n", + "Recall: 72.54835425856804%\n", + "f1_score: 72.63862952734833%\n", + "\n", + "Confusion Matrix:\n", + "[[285 128 67 9 7 0]\n", + " [ 73 334 60 4 0 0]\n", + " [115 76 211 13 5 0]\n", + " [ 3 25 0 419 44 0]\n", + " [ 6 11 5 131 379 0]\n", + " [ 0 27 0 0 0 510]]\n", + "\n", + "Confusion matrix (normalised to % of total test data):\n", + "[[ 9.670852 4.3434 2.2734985 0.3053953 0.2375297 0. ]\n", + " [ 2.4770954 11.333559 2.0359688 0.13573125 0. 0. ]\n", + " [ 3.9022737 2.578894 7.1598234 0.44112659 0.16966406 0. ]\n", + " [ 0.10179844 0.8483203 0. 14.217849 1.4930438 0. ]\n", + " [ 0.20359688 0.37326095 0.16966406 4.4451985 12.860537 0. ]\n", + " [ 0. 0.916186 0. 0. 0. 17.305735 ]]\n", + "Note: training and testing data is not equally distributed amongst classes, \n", + "so it is normal that more than a 6th of the data is correctly classifier in the last category.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ "# (Inline plots: )\n", "%matplotlib inline\n", @@ -232,9 +262,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "Pipeline\n", + "(\n", + "\tPipeline(\n", + "\tname=Pipeline,\n", + "\thyperparameters=HyperparameterSamples()\n", + ")(\n", + "\t\t[('APICaller',\n", + " APICaller(\n", + "\tname=APICaller,\n", + "\thyperparameters=HyperparameterSamples()\n", + "))]\t\n", + ")\n", + ")" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "p.teardown()" ] @@ -242,9 +295,9 @@ ], "metadata": { "kernelspec": { - "display_name": "lstm_har", + "display_name": "lstm_har_2", "language": "python", - "name": "lstm_har" + "name": "lstm_har_2" }, "language_info": { "codemirror_mode": { From 67144b98e8c49a7b21dce6049bb127b5667408a7 Mon Sep 17 00:00:00 2001 From: guillaume-chevalier Date: Mon, 4 Nov 2019 23:08:48 -0500 Subject: [PATCH 21/30] update README temporarily for clarity of the demo of the prototype. --- Call-API.ipynb | 333 ------------------- LSTM_files/LSTM_16_0.png | Bin 77480 -> 0 bytes LSTM_files/LSTM_18_1.png | Bin 43286 -> 0 bytes LSTM_new.ipynb | 695 --------------------------------------- README.md | 689 +------------------------------------- new.py | 85 ----- old.py | 275 ---------------- 7 files changed, 10 insertions(+), 2067 deletions(-) delete mode 100644 Call-API.ipynb delete mode 100644 LSTM_files/LSTM_16_0.png delete mode 100644 LSTM_files/LSTM_18_1.png delete mode 100644 LSTM_new.ipynb delete mode 100644 new.py delete mode 100644 old.py diff --git a/Call-API.ipynb b/Call-API.ipynb deleted file mode 100644 index f65e593..0000000 --- a/Call-API.ipynb +++ /dev/null @@ -1,333 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Install " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import math\n", - "import os\n", - "import numpy as np\n", - "import tensorflow as tf\n", - "import matplotlib.pyplot as plt\n", - "import matplotlib\n", - "\n", - "from sklearn import metrics\n", - "\n", - "import json\n", - "import urllib\n", - "\n", - "from neuraxle.base import NonFittableMixin\n", - "\n", - "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", - " TRAIN_FILE_NAME, TEST_FILE_NAME, LABELS\n", - "\n", - "from neuraxle.api.flask import FlaskRestApiWrapper\n", - "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", - "\n", - "from neuraxle.hyperparams.space import HyperparameterSamples\n", - "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", - "from neuraxle.steps.encoding import OneHotEncoder\n", - "\n", - "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n", - "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", - "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", - "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", - "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", - "\n", - "# TODO: move in a package neuraxle-tensorflow \n", - "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", - "from neuraxle.pipeline import Pipeline\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Read Data" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Some useful info to get an insight on dataset's shape and normalisation:\n", - "(X shape, y shape, every X's mean, every X's standard deviation)\n", - "(2947, 128, 9) (2947, 1) 0.09913992 0.39567086\n", - "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" - ] - } - ], - "source": [ - "DATA_PATH = \"data/\"\n", - "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", - "\n", - "# Load \"X\" (the neural network's training and testing inputs)\n", - "\n", - "# X_train = load_X(X_train_signals_paths)\n", - "X_test = load_X(X_test_signals_paths)\n", - "\n", - "# Load \"y\" (the neural network's training and testing outputs)\n", - "\n", - "# y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME)\n", - "y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME)\n", - "\n", - "# y_train = load_y(y_train_path)\n", - "y_test = load_y(y_test_path)\n", - "\n", - "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", - "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", - "print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test))\n", - "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# API Caller " - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "class APICaller(NonFittableMixin, BaseStep):\n", - " def __init__(self, url):\n", - " BaseStep.__init__(self)\n", - " self.url = url\n", - " \n", - " def transform(self, data_inputs):\n", - " data = json.dumps(np.array(data_inputs).tolist()).encode('utf8')\n", - " req = urllib.request.Request(\n", - " 'http://127.0.0.1:5000/',\n", - " method=\"GET\",\n", - " headers={'content-type': 'application/json'},\n", - " data=data\n", - " )\n", - " response = urllib.request.urlopen(req)\n", - " test_predictions = json.loads(response.read())\n", - " return np.array(test_predictions['predictions'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Call Rest Api " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[-0.03531179 0.18158735 -3.0826726 3.00903678 5.91984081 -2.97888589]\n", - " [-0.02582957 0.31272477 -3.01284409 2.68027282 6.0538106 -3.13478899]\n", - " [ 0.12298633 0.45438862 -2.9635911 2.63191962 6.16904402 -3.11883259]\n", - " ...\n", - " [ 5.13892221 9.45673847 4.57333708 -0.56252116 4.21247244 1.09177899]\n", - " [ 4.97646046 8.32038403 4.04000902 0.38786072 3.45109177 0.72300702]\n", - " [ 4.45698881 7.74218273 4.54759359 -0.06746368 2.63403916 0.53912646]]\n" - ] - } - ], - "source": [ - "p = Pipeline([\n", - " APICaller(url=\"http://localhost:5000/\")\n", - "])\n", - "y_pred = p.transform(X_test)\n", - "print(y_pred)\n", - "# TODO: \n", - "# y_test = y_test.argmax(1) ???? is this already made?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Plot " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", - "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", - "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Precision: 79.75777133050042%\n", - "Recall: 79.33491686460808%\n", - "f1_score: 79.35276493576643%\n", - "\n", - "Confusion Matrix:\n", - "[[358 50 40 14 34 0]\n", - " [ 62 361 48 0 0 0]\n", - " [117 31 271 0 1 0]\n", - " [ 1 4 1 363 122 0]\n", - " [ 3 3 0 61 465 0]\n", - " [ 0 0 17 0 0 520]]\n", - "\n", - "Confusion matrix (normalised to % of total test data):\n", - "[[12.147947 1.6966406 1.3573124 0.4750594 1.1537156 0. ]\n", - " [ 2.1038344 12.249745 1.628775 0. 0. 0. ]\n", - " [ 3.970139 1.0519172 9.195793 0. 0.03393281 0. ]\n", - " [ 0.03393281 0.13573125 0.03393281 12.317612 4.1398034 0. ]\n", - " [ 0.10179844 0.10179844 0. 2.0699017 15.778758 0. ]\n", - " [ 0. 0. 0.5768578 0. 0. 17.645063 ]]\n", - "Note: training and testing data is not equally distributed amongst classes, \n", - "so it is normal that more than a 6th of the data is correctly classifier in the last category.\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "# (Inline plots: )\n", - "%matplotlib inline\n", - "\n", - "font = {\n", - " 'family' : 'Bitstream Vera Sans',\n", - " 'weight' : 'bold',\n", - " 'size' : 18\n", - "}\n", - "matplotlib.rc('font', **font)\n", - "\n", - "# Results\n", - "\n", - "predictions = y_pred.argmax(1)\n", - "n_classes = 6\n", - "\n", - "print(\"\")\n", - "print(\"Precision: {}%\".format(100*metrics.precision_score(y_test, predictions, average=\"weighted\")))\n", - "print(\"Recall: {}%\".format(100*metrics.recall_score(y_test, predictions, average=\"weighted\")))\n", - "print(\"f1_score: {}%\".format(100*metrics.f1_score(y_test, predictions, average=\"weighted\")))\n", - "\n", - "print(\"\")\n", - "print(\"Confusion Matrix:\")\n", - "confusion_matrix = metrics.confusion_matrix(y_test, predictions)\n", - "print(confusion_matrix)\n", - "normalised_confusion_matrix = np.array(confusion_matrix, dtype=np.float32)/np.sum(confusion_matrix)*100\n", - "\n", - "print(\"\")\n", - "print(\"Confusion matrix (normalised to % of total test data):\")\n", - "print(normalised_confusion_matrix)\n", - "print(\"Note: training and testing data is not equally distributed amongst classes, \")\n", - "print(\"so it is normal that more than a 6th of the data is correctly classifier in the last category.\")\n", - "\n", - "# Plot Results:\n", - "width = 12\n", - "height = 12\n", - "plt.figure(figsize=(width, height))\n", - "plt.imshow(\n", - " normalised_confusion_matrix,\n", - " interpolation='nearest',\n", - " cmap=plt.cm.rainbow\n", - ")\n", - "plt.title(\"Confusion matrix \\n(normalised to % of total test data)\")\n", - "plt.colorbar()\n", - "tick_marks = np.arange(n_classes)\n", - "plt.xticks(tick_marks, LABELS, rotation=90)\n", - "plt.yticks(tick_marks, LABELS)\n", - "plt.tight_layout()\n", - "plt.ylabel('True label')\n", - "plt.xlabel('Predicted label')\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Pipeline\n", - "(\n", - "\tPipeline(\n", - "\tname=Pipeline,\n", - "\thyperparameters=HyperparameterSamples()\n", - ")(\n", - "\t\t[('APICaller',\n", - " APICaller(\n", - "\tname=APICaller,\n", - "\thyperparameters=HyperparameterSamples()\n", - "))]\t\n", - ")\n", - ")" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "p.teardown()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Human Activity Recognition", - "language": "python", - "name": "human-activity-recognition" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/LSTM_files/LSTM_16_0.png b/LSTM_files/LSTM_16_0.png deleted file mode 100644 index 140f52083ef67a7d40ee660d44f39da8e798c4e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77480 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%0A9mbdyGOBXjM0S4+e@pfi}AW07Ai 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 diff --git a/LSTM_new.ipynb b/LSTM_new.ipynb deleted file mode 100644 index 9c380bf..0000000 --- a/LSTM_new.ipynb +++ /dev/null @@ -1,695 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Imports" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import math\n", - "import os\n", - "import numpy as np\n", - "import tensorflow as tf\n", - "\n", - "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", - " TRAIN_FILE_NAME, TEST_FILE_NAME\n", - "from neuraxle.api.flask import FlaskRestApiWrapper\n", - "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", - "from neuraxle.hyperparams.space import HyperparameterSamples\n", - "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", - "from neuraxle.steps.encoding import OneHotEncoder\n", - "\n", - "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n", - "from neuraxle.api.flask import JSONDataBodyDecoder\n", - "from neuraxle.api.flask import JSONDataResponseEncoder\n", - "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", - "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", - "\n", - "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", - "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", - "\n", - "# TODO: move in a package neuraxle-tensorflow \n", - "from savers.tensorflow1_step_saver import TensorflowV1StepSaver" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Download Data" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "cache\t\tdata_reading.py new.py README.md\t\tvenv\n", - "Call-API.ipynb\tLICENSE\t\t old.py requirements.txt\n", - "call_api.py\tLSTM_files\t pipeline.py savers\n", - "data\t\tLSTM_new.ipynb\t __pycache__ steps\n", - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", - " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", - " __MACOSX\t 'UCI HAR Dataset'\n", - "\n", - "Downloading...\n", - "Dataset already downloaded. Did not download twice.\n", - "\n", - "Extracting...\n", - "Dataset already extracted. Did not extract twice.\n", - "\n", - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", - " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", - " __MACOSX\t 'UCI HAR Dataset'\n", - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "cache\t\tdata_reading.py new.py README.md\t\tvenv\n", - "Call-API.ipynb\tLICENSE\t\t old.py requirements.txt\n", - "call_api.py\tLSTM_files\t pipeline.py savers\n", - "data\t\tLSTM_new.ipynb\t __pycache__ steps\n", - "\n", - "Dataset is now located at: data/UCI HAR Dataset/\n" - ] - } - ], - "source": [ - "# Note: Linux bash commands start with a \"!\" inside those \"ipython notebook\" cells\n", - "\n", - "DATA_PATH = \"data/\"\n", - "\n", - "!pwd && ls\n", - "os.chdir(DATA_PATH)\n", - "!pwd && ls\n", - "\n", - "!python download_dataset.py\n", - "\n", - "!pwd && ls\n", - "os.chdir(\"..\")\n", - "!pwd && ls\n", - "\n", - "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", - "print(\"\\n\" + \"Dataset is now located at: \" + DATASET_PATH)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Load data" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Some useful info to get an insight on dataset's shape and normalisation:\n", - "(X shape, y shape, every X's mean, every X's standard deviation)\n", - "(2947, 128, 9) (2947, 1) 0.09913992 0.39567086\n", - "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" - ] - } - ], - "source": [ - "# Load \"X\" (the neural network's training and testing inputs)\n", - "\n", - "X_train = load_X(X_train_signals_paths)\n", - "X_test = load_X(X_test_signals_paths)\n", - "\n", - "# Load \"y\" (the neural network's training and testing outputs)\n", - "\n", - "y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME)\n", - "y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME)\n", - "\n", - "y_train = load_y(y_train_path)\n", - "y_test = load_y(y_test_path)\n", - "\n", - "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", - "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", - "print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test))\n", - "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# LSTM RNN Model Forward" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "def tf_model_forward(pred_name, name_x, name_y, hyperparams):\n", - " # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters.\n", - " # Moreover, two LSTM cells are stacked which adds deepness to the neural network.\n", - " # Note, some code of this notebook is inspired from an slightly different\n", - " # RNN architecture used on another dataset, some of the credits goes to\n", - " # \"aymericdamien\" under the MIT license.\n", - " # (NOTE: This step could be greatly optimised by shaping the dataset once\n", - " # input shape: (batch_size, n_steps, n_input)\n", - "\n", - " # Graph input/output\n", - " x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_inputs']], name=name_x)\n", - " y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name=name_y)\n", - "\n", - " # Graph weights\n", - " weights = {\n", - " 'hidden': tf.Variable(\n", - " tf.random_normal([hyperparams['n_inputs'], hyperparams['n_hidden']])\n", - " ), # Hidden layer weights\n", - " 'out': tf.Variable(\n", - " tf.random_normal([hyperparams['n_hidden'], hyperparams['n_classes']], mean=1.0)\n", - " )\n", - " }\n", - "\n", - " biases = {\n", - " 'hidden': tf.Variable(\n", - " tf.random_normal([hyperparams['n_hidden']])\n", - " ),\n", - " 'out': tf.Variable(\n", - " tf.random_normal([hyperparams['n_classes']])\n", - " )\n", - " }\n", - "\n", - " data_inputs = tf.transpose(\n", - " x,\n", - " [1, 0, 2]) # permute n_steps and batch_size\n", - "\n", - " # Reshape to prepare input to hidden activation\n", - " data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_inputs']])\n", - " # new shape: (n_steps*batch_size, n_input)\n", - "\n", - " # ReLU activation, thanks to Yu Zhao for adding this improvement here:\n", - " _X = tf.nn.relu(\n", - " tf.matmul(data_inputs, weights['hidden']) + biases['hidden']\n", - " )\n", - "\n", - " # Split data because rnn cell needs a list of inputs for the RNN inner loop\n", - " _X = tf.split(_X, hyperparams['n_steps'], 0)\n", - " # new shape: n_steps * (batch_size, n_hidden)\n", - "\n", - " # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow\n", - " lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", - " lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", - " lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True)\n", - "\n", - " # Get LSTM cell output\n", - " outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32)\n", - "\n", - " # Get last time step's output feature for a \"many-to-one\" style classifier,\n", - " # as in the image describing RNNs at the top of this page\n", - " lstm_last_output = outputs[-1]\n", - "\n", - " # Linear activation\n", - " pred = tf.matmul(lstm_last_output, weights['out']) + biases['out']\n", - " return tf.identity(pred, name=pred_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Neuraxle RNN TensorFlow Model Step" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "LSTM_RNN_VARIABLE_SCOPE = \"lstm_rnn\"\n", - "X_NAME = 'x'\n", - "Y_NAME = 'y'\n", - "PRED_NAME = 'pred'\n", - "\n", - "N_HIDDEN = 32\n", - "N_STEPS = 128\n", - "N_INPUTS = 9\n", - "LAMBDA_LOSS_AMOUNT = 0.0015\n", - "LEARNING_RATE = 0.0025\n", - "N_CLASSES = 6\n", - "BATCH_SIZE = 1500\n", - "\n", - "class ClassificationRNNTensorFlowModel(BaseStep):\n", - " HYPERPARAMS = HyperparameterSamples({\n", - " 'n_steps': N_STEPS, # 128 timesteps per series\n", - " 'n_inputs': N_INPUTS, # 9 input parameters per timestep\n", - " 'n_hidden': N_HIDDEN, # Hidden layer num of features\n", - " 'n_classes': N_CLASSES, # Total classes (should go up, or should go down)\n", - " 'learning_rate': LEARNING_RATE,\n", - " 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT,\n", - " 'batch_size': BATCH_SIZE\n", - " })\n", - "\n", - " def __init__(\n", - " self\n", - " ):\n", - " BaseStep.__init__(\n", - " self,\n", - " hyperparams=ClassificationRNNTensorFlowModel.HYPERPARAMS,\n", - " savers=[TensorflowV1StepSaver()]\n", - " )\n", - "\n", - " self.graph = None\n", - " self.sess = None\n", - " self.l2 = None\n", - " self.cost = None\n", - " self.optimizer = None\n", - " self.correct_pred = None\n", - " self.accuracy = None\n", - " self.test_losses = None\n", - " self.test_accuracies = None\n", - " self.train_losses = None\n", - " self.train_accuracies = None\n", - "\n", - " def strip(self):\n", - " self.sess = None\n", - " self.graph = None\n", - " self.l2 = None\n", - " self.cost = None\n", - " self.optimizer = None\n", - " self.correct_pred = None\n", - " self.accuracy = None\n", - "\n", - " def setup(self) -> BaseStep:\n", - " if self.is_initialized:\n", - " return self\n", - "\n", - " self.create_graph()\n", - "\n", - " with self.graph.as_default():\n", - " # Launch the graph\n", - " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", - "\n", - " pred = tf_model_forward(PRED_NAME, X_NAME, Y_NAME, self.hyperparams)\n", - "\n", - " # Loss, optimizer and evaluation\n", - " # L2 loss prevents this overkill neural network to overfit the data\n", - "\n", - " l2 = self.hyperparams['lambda_loss_amount'] * sum(\n", - " tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables()\n", - " )\n", - "\n", - " # Softmax loss\n", - " self.cost = tf.reduce_mean(\n", - " tf.nn.softmax_cross_entropy_with_logits(\n", - " labels=self.get_y_placeholder(),\n", - " logits=pred\n", - " )\n", - " ) + l2\n", - "\n", - " # Adam Optimizer\n", - " self.optimizer = tf.train.AdamOptimizer(\n", - " learning_rate=self.hyperparams['learning_rate']\n", - " ).minimize(self.cost)\n", - "\n", - " self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(Y_NAME), 1))\n", - " self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32))\n", - "\n", - " # To keep track of training's performance\n", - " self.test_losses = []\n", - " self.test_accuracies = []\n", - " self.train_losses = []\n", - " self.train_accuracies = []\n", - "\n", - " self.create_session()\n", - "\n", - " self.is_initialized = True\n", - "\n", - " return self\n", - "\n", - " def create_graph(self):\n", - " self.graph = tf.Graph()\n", - "\n", - " def create_session(self):\n", - " self.sess = tf.Session(config=tf.ConfigProto(log_device_placement=True), graph=self.graph)\n", - " init = tf.global_variables_initializer()\n", - " self.sess.run(init)\n", - "\n", - " def get_tensor_by_name(self, name):\n", - " return self.graph.get_tensor_by_name(\"{0}/{1}:0\".format(LSTM_RNN_VARIABLE_SCOPE, name))\n", - "\n", - " def get_graph(self):\n", - " return self.graph\n", - "\n", - " def get_session(self):\n", - " return self.sess\n", - "\n", - " def get_x_placeholder(self):\n", - " return self.get_tensor_by_name(X_NAME)\n", - "\n", - " def get_y_placeholder(self):\n", - " return self.get_tensor_by_name(Y_NAME)\n", - "\n", - " def teardown(self):\n", - " if self.sess is not None:\n", - " self.sess.close()\n", - " self.is_initialized = False\n", - "\n", - " def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep':\n", - " if not isinstance(data_inputs, np.ndarray):\n", - " data_inputs = np.array(data_inputs)\n", - "\n", - " if not isinstance(expected_outputs, np.ndarray):\n", - " expected_outputs = np.array(expected_outputs)\n", - "\n", - " if expected_outputs.shape != (len(data_inputs), self.hyperparams['n_classes']):\n", - " expected_outputs = np.reshape(expected_outputs, (len(data_inputs), self.hyperparams['n_classes']))\n", - "\n", - " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", - " _, loss, acc = self.sess.run(\n", - " [self.optimizer, self.cost, self.accuracy],\n", - " feed_dict={\n", - " self.get_x_placeholder(): data_inputs,\n", - " self.get_y_placeholder(): expected_outputs\n", - " }\n", - " )\n", - "\n", - " self.train_losses.append(loss)\n", - " self.train_accuracies.append(acc)\n", - "\n", - " print(\"Batch Loss = \" + \"{:.6f}\".format(loss) + \", Accuracy = {}\".format(acc))\n", - "\n", - " self.is_invalidated = True\n", - "\n", - " return self\n", - "\n", - " def transform(self, data_inputs):\n", - " if not isinstance(data_inputs, np.ndarray):\n", - " data_inputs = np.array(data_inputs)\n", - "\n", - " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", - " outputs = self.sess.run(\n", - " [self.get_tensor_by_name(PRED_NAME)],\n", - " feed_dict={\n", - " self.get_x_placeholder(): data_inputs\n", - " }\n", - " )[0]\n", - " return outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Neuraxle Pipeline " - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline):\n", - " def __init__(self):\n", - " MiniBatchSequentialPipeline.__init__(self, [\n", - " OutputTransformerWrapper(OneHotEncoder(nb_columns=N_CLASSES, name='one_hot_encoded_label')),\n", - " ClassificationRNNTensorFlowModel(),\n", - " Joiner(batch_size=BATCH_SIZE)\n", - " ])\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Train Pipeline " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING:tensorflow:\n", - "The TensorFlow contrib module will not be included in TensorFlow 2.0.\n", - "For more information, please see:\n", - " * https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md\n", - " * https://github.com/tensorflow/addons\n", - " * https://github.com/tensorflow/io (for I/O related ops)\n", - "If you depend on functionality not listed there, please file an issue.\n", - "\n", - "WARNING:tensorflow:From :51: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :53: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "This class is equivalent as tf.keras.layers.StackedRNNCells, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Please use `layer.add_weight` method instead.\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Call initializer instance with the dtype argument instead of passing it to the constructor\n", - "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "\n", - "Future major versions of TensorFlow will allow gradients to flow\n", - "into the labels input on backprop by default.\n", - "\n", - "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n", - "\n", - "Device mapping:\n", - "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", - "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", - "\n", - "Batch Loss = 2.689994, Accuracy = 0.1599999964237213\n", - "Batch Loss = 2.481512, Accuracy = 0.1106666699051857\n", - "Batch Loss = 2.218577, Accuracy = 0.3606666624546051\n", - "Batch Loss = 2.114502, Accuracy = 0.4593333303928375\n", - "Batch Loss = 2.015675, Accuracy = 0.4504437744617462\n", - "Batch Loss = 1.959644, Accuracy = 0.5580000281333923\n", - "Batch Loss = 1.832221, Accuracy = 0.5666666626930237\n", - "Batch Loss = 1.679870, Accuracy = 0.5979999899864197\n", - "Batch Loss = 1.707634, Accuracy = 0.6466666460037231\n", - "Batch Loss = 1.635818, Accuracy = 0.6205621361732483\n", - "Batch Loss = 1.556575, Accuracy = 0.640666663646698\n", - "Batch Loss = 1.418897, Accuracy = 0.6946666836738586\n", - "Batch Loss = 1.341482, Accuracy = 0.6679999828338623\n", - "Batch Loss = 1.600628, Accuracy = 0.5933333039283752\n", - "Batch Loss = 1.374022, Accuracy = 0.6545857787132263\n", - "Batch Loss = 1.590493, Accuracy = 0.5239999890327454\n", - "Batch Loss = 1.433335, Accuracy = 0.5946666598320007\n", - "Batch Loss = 1.254257, Accuracy = 0.7226666808128357\n", - "Batch Loss = 1.820678, Accuracy = 0.527999997138977\n", - "Batch Loss = 1.411729, Accuracy = 0.6471893787384033\n", - "Batch Loss = 1.417269, Accuracy = 0.6359999775886536\n", - "Batch Loss = 1.373504, Accuracy = 0.6613333225250244\n", - "Batch Loss = 1.343409, Accuracy = 0.7046666741371155\n", - "Batch Loss = 1.269480, Accuracy = 0.7366666793823242\n", - "Batch Loss = 1.339166, Accuracy = 0.7196745276451111\n", - "Batch Loss = 1.368126, Accuracy = 0.6393333077430725\n", - "Batch Loss = 1.349553, Accuracy = 0.722000002861023\n", - "Batch Loss = 1.236530, Accuracy = 0.7179999947547913\n", - "Batch Loss = 1.221019, Accuracy = 0.7566666603088379\n", - "Batch Loss = 1.282286, Accuracy = 0.7315088510513306\n", - "Batch Loss = 1.303923, Accuracy = 0.6853333115577698\n", - "Batch Loss = 1.247506, Accuracy = 0.7239999771118164\n", - "Batch Loss = 1.207210, Accuracy = 0.7039999961853027\n", - "Batch Loss = 1.257670, Accuracy = 0.7253333330154419\n", - "Batch Loss = 1.269979, Accuracy = 0.7071005702018738\n", - "Batch Loss = 1.260590, Accuracy = 0.7279999852180481\n", - "Batch Loss = 1.195216, Accuracy = 0.7799999713897705\n", - "Batch Loss = 1.232077, Accuracy = 0.7386666536331177\n", - "Batch Loss = 1.155753, Accuracy = 0.7806666493415833\n", - "Batch Loss = 1.236396, Accuracy = 0.7707100510597229\n", - "Batch Loss = 1.224076, Accuracy = 0.7726666927337646\n", - "Batch Loss = 1.159579, Accuracy = 0.8086666464805603\n", - "Batch Loss = 1.169154, Accuracy = 0.7419999837875366\n", - "Batch Loss = 1.218150, Accuracy = 0.7480000257492065\n", - "Batch Loss = 1.221550, Accuracy = 0.7374260425567627\n", - "Batch Loss = 1.184118, Accuracy = 0.7620000243186951\n", - "Batch Loss = 1.130956, Accuracy = 0.8046666383743286\n", - "Batch Loss = 1.168662, Accuracy = 0.7393333315849304\n", - "Batch Loss = 1.131838, Accuracy = 0.7733333110809326\n", - "Batch Loss = 1.206504, Accuracy = 0.7559171319007874\n", - "Batch Loss = 1.133658, Accuracy = 0.8233333230018616\n", - "Batch Loss = 1.112805, Accuracy = 0.8119999766349792\n", - "Batch Loss = 1.134827, Accuracy = 0.7413333058357239\n", - "Batch Loss = 1.053966, Accuracy = 0.843999981880188\n", - "Batch Loss = 1.131961, Accuracy = 0.7995561957359314\n", - "Batch Loss = 1.084540, Accuracy = 0.8226666450500488\n", - "Batch Loss = 1.055858, Accuracy = 0.8386666774749756\n", - "Batch Loss = 1.082902, Accuracy = 0.7746666669845581\n", - "Batch Loss = 1.069604, Accuracy = 0.8253333568572998\n", - "Batch Loss = 1.094777, Accuracy = 0.8254438042640686\n", - "Batch Loss = 1.025982, Accuracy = 0.8500000238418579\n", - "Batch Loss = 1.038732, Accuracy = 0.8413333296775818\n", - "Batch Loss = 1.059510, Accuracy = 0.7900000214576721\n", - "Batch Loss = 1.040493, Accuracy = 0.8566666841506958\n", - "Batch Loss = 1.095273, Accuracy = 0.8254438042640686\n", - "Batch Loss = 0.965999, Accuracy = 0.8700000047683716\n", - "Batch Loss = 0.993554, Accuracy = 0.8579999804496765\n", - "Batch Loss = 1.088169, Accuracy = 0.7853333353996277\n", - "Batch Loss = 0.965077, Accuracy = 0.8793333172798157\n", - "Batch Loss = 1.037169, Accuracy = 0.8491124510765076\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", - "\n" - ] - }, - { - "data": { - "text/plain": [ - "HumanActivityRecognitionPipeline\n", - "(\n", - "\tHumanActivityRecognitionPipeline(\n", - "\tname=HumanActivityRecognitionPipeline,\n", - "\thyperparameters=HyperparameterSamples()\n", - ")(\n", - "\t\t[('OutputTransformerWrapper',\n", - " OutputTransformerWrapper(\n", - "\twrapped=OneHotEncoder(\n", - "\tname=one_hot_encoded_label,\n", - "\thyperparameters=HyperparameterSamples()\n", - "),\n", - "\thyperparameters=HyperparameterSamples()\n", - ")),\n", - " ('ClassificationRNNTensorFlowModel',\n", - " ClassificationRNNTensorFlowModel(\n", - "\tname=ClassificationRNNTensorFlowModel,\n", - "\thyperparameters=HyperparameterSamples([('n_steps', 128),\n", - " ('n_inputs', 9),\n", - " ('n_hidden', 32),\n", - " ('n_classes', 6),\n", - " ('learning_rate', 0.0025),\n", - " ('lambda_loss_amount', 0.0015),\n", - " ('batch_size', 1500)])\n", - ")),\n", - " ('Joiner', Joiner(\n", - "\tname=Joiner,\n", - "\thyperparameters=HyperparameterSamples()\n", - "))]\t\n", - ")\n", - ")" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "training_data_count = len(X_train)\n", - "training_iters = training_data_count * 3\n", - "\n", - "pipeline = HumanActivityRecognitionPipeline()\n", - "\n", - "no_iter = int(math.floor(training_iters / BATCH_SIZE))\n", - "for _ in range(no_iter):\n", - " pipeline, outputs = pipeline.fit_transform(X_train, y_train)\n", - "\n", - "pipeline.save(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", - "\n", - "pipeline.teardown()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Serve Rest Api" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Device mapping:\n", - "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", - "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", - "\n", - "INFO:tensorflow:Restoring parameters from /home/alexandre/Documents/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n", - "Batch Loss = 0.942640, Accuracy = 0.8813333511352539\n", - "Batch Loss = 0.914275, Accuracy = 0.8820000290870667\n", - "Batch Loss = 1.038205, Accuracy = 0.8100000023841858\n", - "Batch Loss = 0.846154, Accuracy = 0.9353333115577698\n", - "Batch Loss = 0.918082, Accuracy = 0.8957100510597229\n", - " * Serving Flask app \"neuraxle.api.flask\" (lazy loading)\n", - " * Environment: production\n", - " WARNING: This is a development server. Do not use it in a production deployment.\n", - " Use a production WSGI server instead.\n", - " * Debug mode: off\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", - "127.0.0.1 - - [04/Nov/2019 20:56:45] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n", - "127.0.0.1 - - [04/Nov/2019 20:57:08] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" - ] - } - ], - "source": [ - "pipeline = HumanActivityRecognitionPipeline()\n", - "\n", - "pipeline = pipeline.load(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", - "\n", - "pipeline, outputs = pipeline.fit_transform(X_train, y_train)\n", - "\n", - "app = FlaskRestApiWrapper(\n", - " json_decoder=CustomJSONDecoderFor2DArray(),\n", - " wrapped=pipeline,\n", - " json_encoder=CustomJSONEncoderOfOutputs()\n", - ").get_app()\n", - "\n", - "app.run(debug=False, port=5000)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Human Activity Recognition", - "language": "python", - "name": "human-activity-recognition" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.8" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/README.md b/README.md index c17a4b3..99e4c6a 100644 --- a/README.md +++ b/README.md @@ -1,686 +1,17 @@ +# Full example of LSTM training in TensorFlow, and REST API serving & calling -# LSTMs for Human Activity Recognition +## Instructions: -Human Activity Recognition (HAR) using smartphones dataset and an LSTM RNN. Classifying the type of movement amongst six categories: -- WALKING, -- WALKING_UPSTAIRS, -- WALKING_DOWNSTAIRS, -- SITTING, -- STANDING, -- LAYING. +The first notebook will create a model, save it, then delete the local variable, reload the model completely, and serve its predictions as a REST API on localhost ([http://127.0.0.1:5000/](http://127.0.0.1:5000/)). -Compared to a classical approach, using a Recurrent Neural Networks (RNN) with Long Short-Term Memory cells (LSTMs) require no or almost no feature engineering. Data can be fed directly into the neural network who acts like a black box, modeling the problem correctly. [Other research](https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.names) on the activity recognition dataset can use a big amount of feature engineering, which is rather a signal processing approach combined with classical data science techniques. The approach here is rather very simple in terms of how much was the data preprocessed. +1. Read `1_train_and_save_LSTM.ipynb` and execute all the code. Once fully executed, the notebook will have trained and serialized the neural net to a local `./cache;`, folder which is reused at the end of the notebook to launch the REST API. The last two cells of this notebook that loads the model and serve it could as well be executed as a `.py` file instead of as a notebook. +2. Read `2_call_rest_api_and_eval.ipynb` and while the first notebook is running and has reached the end of the code, execute all the code of the second notebook. The code of the second notebook reads the test data on disks, serializes it to JSON, and calls the REST API on the localhost of the machine. Ideally you'd open your ports to the world so as to use a public IP address, or an URL with a DNS pointing to the machine. +3. If you want more information, dive into the local `.py` files for some more reading on how things works. -Let's use Google's neat Deep Learning library, TensorFlow, demonstrating the usage of an LSTM, a type of Artificial Neural Network that can process sequential data / time series. +Note: you may install all the requirements.txt before starting. -## Video dataset overview +## Future improvements -Follow this link to see a video of the 6 activities recorded in the experiment with one of the participants: +Note that in the `requirements.txt`, an URL to a yet-unreleased version of Neuraxle is used as we needed to apply some changes for the example to fully work with TensorFlow. We will soon release those changes as `neuraxle==0.2.2` on PyPI. This means that soon, it will be possible to update this `requirements.txt` to use an official version hosted on the Python Package Index (PyPI), installable with `pip install neuraxle==0.2.2`. Once released, the entry for Neuraxle in the `requirements.txt` will look like `neuraxle==0.2.2` as it'll finally be an officially released version. -

- -

[Watch video]
-

- -## Details about the input data - -I will be using an LSTM on the data to learn (as a cellphone attached on the waist) to recognise the type of activity that the user is doing. The dataset's description goes like this: - -> The sensor signals (accelerometer and gyroscope) were pre-processed by applying noise filters and then sampled in fixed-width sliding windows of 2.56 sec and 50% overlap (128 readings/window). The sensor acceleration signal, which has gravitational and body motion components, was separated using a Butterworth low-pass filter into body acceleration and gravity. The gravitational force is assumed to have only low frequency components, therefore a filter with 0.3 Hz cutoff frequency was used. - -That said, I will use the almost raw data: only the gravity effect has been filtered out of the accelerometer as a preprocessing step for another 3D feature as an input to help learning. If you'd ever want to extract the gravity by yourself, you could fork my code on using a [Butterworth Low-Pass Filter (LPF) in Python](https://github.com/guillaume-chevalier/filtering-stft-and-laplace-transform) and edit it to have the right cutoff frequency of 0.3 Hz which is a good frequency for activity recognition from body sensors. - -## What is an RNN? - -As explained in [this article](http://karpathy.github.io/2015/05/21/rnn-effectiveness/), an RNN takes many input vectors to process them and output other vectors. It can be roughly pictured like in the image below, imagining each rectangle has a vectorial depth and other special hidden quirks in the image below. **In our case, the "many to one" architecture is used**: we accept time series of [feature vectors](https://www.quora.com/What-do-samples-features-time-steps-mean-in-LSTM/answer/Guillaume-Chevalier-2) (one vector per [time step](https://www.quora.com/What-do-samples-features-time-steps-mean-in-LSTM/answer/Guillaume-Chevalier-2)) to convert them to a probability vector at the output for classification. Note that a "one to one" architecture would be a standard feedforward neural network. - -> -> http://karpathy.github.io/2015/05/21/rnn-effectiveness/ - -## What is an LSTM? - -An LSTM is an improved RNN. It is more complex, but easier to train, avoiding what is called the vanishing gradient problem. I recommend [this article](http://colah.github.io/posts/2015-08-Understanding-LSTMs/) for you to learn more on LSTMs. - - -## Results - -Scroll on! Nice visuals awaits. - - -```python -# All Includes - -import numpy as np -import matplotlib -import matplotlib.pyplot as plt -import tensorflow as tf # Version 1.0.0 (some previous versions are used in past commits) -from sklearn import metrics - -import os -``` - - -```python -# Useful Constants - -# Those are separate normalised input features for the neural network -INPUT_SIGNAL_TYPES = [ - "body_acc_x_", - "body_acc_y_", - "body_acc_z_", - "body_gyro_x_", - "body_gyro_y_", - "body_gyro_z_", - "total_acc_x_", - "total_acc_y_", - "total_acc_z_" -] - -# Output classes to learn how to classify -LABELS = [ - "WALKING", - "WALKING_UPSTAIRS", - "WALKING_DOWNSTAIRS", - "SITTING", - "STANDING", - "LAYING" -] - -``` - -## Let's start by downloading the data: - - -```python -# Note: Linux bash commands start with a "!" inside those "ipython notebook" cells - -DATA_PATH = "data/" - -!pwd && ls -os.chdir(DATA_PATH) -!pwd && ls - -!python download_dataset.py - -!pwd && ls -os.chdir("..") -!pwd && ls - -DATASET_PATH = DATA_PATH + "UCI HAR Dataset/" -print("\n" + "Dataset is now located at: " + DATASET_PATH) - -``` - - /home/ubuntu/pynb/LSTM-Human-Activity-Recognition - data LSTM_files LSTM_OLD.ipynb README.md - LICENSE LSTM.ipynb lstm.py screenlog.0 - /home/ubuntu/pynb/LSTM-Human-Activity-Recognition/data - download_dataset.py source.txt - - Downloading... - --2017-05-24 01:49:53-- https://archive.ics.uci.edu/ml/machine-learning-databases/00240/UCI%20HAR%20Dataset.zip - Resolving archive.ics.uci.edu (archive.ics.uci.edu)... 128.195.10.249 - Connecting to archive.ics.uci.edu (archive.ics.uci.edu)|128.195.10.249|:443... connected. - HTTP request sent, awaiting response... 200 OK - Length: 60999314 (58M) [application/zip] - Saving to: ‘UCI HAR Dataset.zip’ - - 100%[======================================>] 60,999,314 1.69MB/s in 38s - - 2017-05-24 01:50:31 (1.55 MB/s) - ‘UCI HAR Dataset.zip’ saved [60999314/60999314] - - Downloading done. - - Extracting... - Extracting successfully done to /home/ubuntu/pynb/LSTM-Human-Activity-Recognition/data/UCI HAR Dataset. - /home/ubuntu/pynb/LSTM-Human-Activity-Recognition/data - download_dataset.py __MACOSX source.txt UCI HAR Dataset UCI HAR Dataset.zip - /home/ubuntu/pynb/LSTM-Human-Activity-Recognition - data LSTM_files LSTM_OLD.ipynb README.md - LICENSE LSTM.ipynb lstm.py screenlog.0 - - Dataset is now located at: data/UCI HAR Dataset/ - - -## Preparing dataset: - - -```python -TRAIN = "train/" -TEST = "test/" - - -# Load "X" (the neural network's training and testing inputs) - -def load_X(X_signals_paths): - X_signals = [] - - for signal_type_path in X_signals_paths: - file = open(signal_type_path, 'r') - # Read dataset from disk, dealing with text files' syntax - X_signals.append( - [np.array(serie, dtype=np.float32) for serie in [ - row.replace(' ', ' ').strip().split(' ') for row in file - ]] - ) - file.close() - - return np.transpose(np.array(X_signals), (1, 2, 0)) - -X_train_signals_paths = [ - DATASET_PATH + TRAIN + "Inertial Signals/" + signal + "train.txt" for signal in INPUT_SIGNAL_TYPES -] -X_test_signals_paths = [ - DATASET_PATH + TEST + "Inertial Signals/" + signal + "test.txt" for signal in INPUT_SIGNAL_TYPES -] - -X_train = load_X(X_train_signals_paths) -X_test = load_X(X_test_signals_paths) - - -# Load "y" (the neural network's training and testing outputs) - -def load_y(y_path): - file = open(y_path, 'r') - # Read dataset from disk, dealing with text file's syntax - y_ = np.array( - [elem for elem in [ - row.replace(' ', ' ').strip().split(' ') for row in file - ]], - dtype=np.int32 - ) - file.close() - - # Substract 1 to each output class for friendly 0-based indexing - return y_ - 1 - -y_train_path = DATASET_PATH + TRAIN + "y_train.txt" -y_test_path = DATASET_PATH + TEST + "y_test.txt" - -y_train = load_y(y_train_path) -y_test = load_y(y_test_path) - -``` - -## Additionnal Parameters: - -Here are some core parameter definitions for the training. - -For example, the whole neural network's structure could be summarised by enumerating those parameters and the fact that two LSTM are used one on top of another (stacked) output-to-input as hidden layers through time steps. - - -```python -# Input Data - -training_data_count = len(X_train) # 7352 training series (with 50% overlap between each serie) -test_data_count = len(X_test) # 2947 testing series -n_steps = len(X_train[0]) # 128 timesteps per series -n_input = len(X_train[0][0]) # 9 input parameters per timestep - - -# LSTM Neural Network's internal structure - -n_hidden = 32 # Hidden layer num of features -n_classes = 6 # Total classes (should go up, or should go down) - - -# Training - -learning_rate = 0.0025 -lambda_loss_amount = 0.0015 -training_iters = training_data_count * 300 # Loop 300 times on the dataset -batch_size = 1500 -display_iter = 30000 # To show test set accuracy during training - - -# Some debugging info - -print("Some useful info to get an insight on dataset's shape and normalisation:") -print("(X shape, y shape, every X's mean, every X's standard deviation)") -print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test)) -print("The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.") - -``` - - Some useful info to get an insight on dataset's shape and normalisation: - (X shape, y shape, every X's mean, every X's standard deviation) - (2947, 128, 9) (2947, 1) 0.0991399 0.395671 - The dataset is therefore properly normalised, as expected, but not yet one-hot encoded. - - -## Utility functions for training: - - -```python -def LSTM_RNN(_X, _weights, _biases): - # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. - # Moreover, two LSTM cells are stacked which adds deepness to the neural network. - # Note, some code of this notebook is inspired from an slightly different - # RNN architecture used on another dataset, some of the credits goes to - # "aymericdamien" under the MIT license. - - # (NOTE: This step could be greatly optimised by shaping the dataset once - # input shape: (batch_size, n_steps, n_input) - _X = tf.transpose(_X, [1, 0, 2]) # permute n_steps and batch_size - # Reshape to prepare input to hidden activation - _X = tf.reshape(_X, [-1, n_input]) - # new shape: (n_steps*batch_size, n_input) - - # ReLU activation, thanks to Yu Zhao for adding this improvement here: - _X = tf.nn.relu(tf.matmul(_X, _weights['hidden']) + _biases['hidden']) - # Split data because rnn cell needs a list of inputs for the RNN inner loop - _X = tf.split(_X, n_steps, 0) - # new shape: n_steps * (batch_size, n_hidden) - - # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow - lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True) - lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True) - lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) - # Get LSTM cell output - outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) - - # Get last time step's output feature for a "many-to-one" style classifier, - # as in the image describing RNNs at the top of this page - lstm_last_output = outputs[-1] - - # Linear activation - return tf.matmul(lstm_last_output, _weights['out']) + _biases['out'] - - -def extract_batch_size(_train, step, batch_size): - # Function to fetch a "batch_size" amount of data from "(X|y)_train" data. - - shape = list(_train.shape) - shape[0] = batch_size - batch_s = np.empty(shape) - - for i in range(batch_size): - # Loop index - index = ((step-1)*batch_size + i) % len(_train) - batch_s[i] = _train[index] - - return batch_s - - -def one_hot(y_, n_classes=n_classes): - # Function to encode neural one-hot output labels from number indexes - # e.g.: - # one_hot(y_=[[5], [0], [3]], n_classes=6): - # return [[0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0]] - - y_ = y_.reshape(len(y_)) - return np.eye(n_classes)[np.array(y_, dtype=np.int32)] # Returns FLOATS - -``` - -## Let's get serious and build the neural network: - - -```python - -# Graph input/output -x = tf.placeholder(tf.float32, [None, n_steps, n_input]) -y = tf.placeholder(tf.float32, [None, n_classes]) - -# Graph weights -weights = { - 'hidden': tf.Variable(tf.random_normal([n_input, n_hidden])), # Hidden layer weights - 'out': tf.Variable(tf.random_normal([n_hidden, n_classes], mean=1.0)) -} -biases = { - 'hidden': tf.Variable(tf.random_normal([n_hidden])), - 'out': tf.Variable(tf.random_normal([n_classes])) -} - -pred = LSTM_RNN(x, weights, biases) - -# Loss, optimizer and evaluation -l2 = lambda_loss_amount * sum( - tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() -) # L2 loss prevents this overkill neural network to overfit the data -cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=pred)) + l2 # Softmax loss -optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # Adam Optimizer - -correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1)) -accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) - -``` - -## Hooray, now train the neural network: - - -```python -# To keep track of training's performance -test_losses = [] -test_accuracies = [] -train_losses = [] -train_accuracies = [] - -# Launch the graph -sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) -init = tf.global_variables_initializer() -sess.run(init) - -# Perform Training steps with "batch_size" amount of example data at each loop -step = 1 -while step * batch_size <= training_iters: - batch_xs = extract_batch_size(X_train, step, batch_size) - batch_ys = one_hot(extract_batch_size(y_train, step, batch_size)) - - # Fit training using batch data - _, loss, acc = sess.run( - [optimizer, cost, accuracy], - feed_dict={ - x: batch_xs, - y: batch_ys - } - ) - train_losses.append(loss) - train_accuracies.append(acc) - - # Evaluate network only at some steps for faster training: - if (step*batch_size % display_iter == 0) or (step == 1) or (step * batch_size > training_iters): - - # To not spam console, show training accuracy/loss in this "if" - print("Training iter #" + str(step*batch_size) + \ - ": Batch Loss = " + "{:.6f}".format(loss) + \ - ", Accuracy = {}".format(acc)) - - # Evaluation on the test set (no learning made here - just evaluation for diagnosis) - loss, acc = sess.run( - [cost, accuracy], - feed_dict={ - x: X_test, - y: one_hot(y_test) - } - ) - test_losses.append(loss) - test_accuracies.append(acc) - print("PERFORMANCE ON TEST SET: " + \ - "Batch Loss = {}".format(loss) + \ - ", Accuracy = {}".format(acc)) - - step += 1 - -print("Optimization Finished!") - -# Accuracy for test data - -one_hot_predictions, accuracy, final_loss = sess.run( - [pred, accuracy, cost], - feed_dict={ - x: X_test, - y: one_hot(y_test) - } -) - -test_losses.append(final_loss) -test_accuracies.append(accuracy) - -print("FINAL RESULT: " + \ - "Batch Loss = {}".format(final_loss) + \ - ", Accuracy = {}".format(accuracy)) - -``` - - WARNING:tensorflow:From :9: initialize_all_variables (from tensorflow.python.ops.variables) is deprecated and will be removed after 2017-03-02. - Instructions for updating: - Use `tf.global_variables_initializer` instead. - Training iter #1500: Batch Loss = 5.416760, Accuracy = 0.15266665816307068 - PERFORMANCE ON TEST SET: Batch Loss = 4.880829811096191, Accuracy = 0.05632847175002098 - Training iter #30000: Batch Loss = 3.031930, Accuracy = 0.607333242893219 - PERFORMANCE ON TEST SET: Batch Loss = 3.0515167713165283, Accuracy = 0.6067186594009399 - Training iter #60000: Batch Loss = 2.672764, Accuracy = 0.7386666536331177 - PERFORMANCE ON TEST SET: Batch Loss = 2.780435085296631, Accuracy = 0.7027485370635986 - Training iter #90000: Batch Loss = 2.378301, Accuracy = 0.8366667032241821 - PERFORMANCE ON TEST SET: Batch Loss = 2.6019773483276367, Accuracy = 0.7617915868759155 - Training iter #120000: Batch Loss = 2.127290, Accuracy = 0.9066667556762695 - PERFORMANCE ON TEST SET: Batch Loss = 2.3625404834747314, Accuracy = 0.8116728663444519 - Training iter #150000: Batch Loss = 1.929805, Accuracy = 0.9380000233650208 - PERFORMANCE ON TEST SET: Batch Loss = 2.306251049041748, Accuracy = 0.8276212215423584 - Training iter #180000: Batch Loss = 1.971904, Accuracy = 0.9153333902359009 - PERFORMANCE ON TEST SET: Batch Loss = 2.0835530757904053, Accuracy = 0.8771631121635437 - Training iter #210000: Batch Loss = 1.860249, Accuracy = 0.8613333702087402 - PERFORMANCE ON TEST SET: Batch Loss = 1.9994492530822754, Accuracy = 0.8788597583770752 - Training iter #240000: Batch Loss = 1.626292, Accuracy = 0.9380000233650208 - PERFORMANCE ON TEST SET: Batch Loss = 1.879166603088379, Accuracy = 0.8944689035415649 - Training iter #270000: Batch Loss = 1.582758, Accuracy = 0.9386667013168335 - PERFORMANCE ON TEST SET: Batch Loss = 2.0341007709503174, Accuracy = 0.8361043930053711 - Training iter #300000: Batch Loss = 1.620352, Accuracy = 0.9306666851043701 - PERFORMANCE ON TEST SET: Batch Loss = 1.8185184001922607, Accuracy = 0.8639293313026428 - Training iter #330000: Batch Loss = 1.474394, Accuracy = 0.9693333506584167 - PERFORMANCE ON TEST SET: Batch Loss = 1.7638503313064575, Accuracy = 0.8747878670692444 - Training iter #360000: Batch Loss = 1.406998, Accuracy = 0.9420000314712524 - PERFORMANCE ON TEST SET: Batch Loss = 1.5946787595748901, Accuracy = 0.902273416519165 - Training iter #390000: Batch Loss = 1.362515, Accuracy = 0.940000057220459 - PERFORMANCE ON TEST SET: Batch Loss = 1.5285792350769043, Accuracy = 0.9046487212181091 - Training iter #420000: Batch Loss = 1.252860, Accuracy = 0.9566667079925537 - PERFORMANCE ON TEST SET: Batch Loss = 1.4635565280914307, Accuracy = 0.9107565879821777 - Training iter #450000: Batch Loss = 1.190078, Accuracy = 0.9553333520889282 - ... - PERFORMANCE ON TEST SET: Batch Loss = 0.42567864060401917, Accuracy = 0.9324736595153809 - Training iter #2070000: Batch Loss = 0.342763, Accuracy = 0.9326667189598083 - PERFORMANCE ON TEST SET: Batch Loss = 0.4292983412742615, Accuracy = 0.9273836612701416 - Training iter #2100000: Batch Loss = 0.259442, Accuracy = 0.9873334169387817 - PERFORMANCE ON TEST SET: Batch Loss = 0.44131210446357727, Accuracy = 0.9273836612701416 - Training iter #2130000: Batch Loss = 0.284630, Accuracy = 0.9593333601951599 - PERFORMANCE ON TEST SET: Batch Loss = 0.46982717514038086, Accuracy = 0.9093992710113525 - Training iter #2160000: Batch Loss = 0.299012, Accuracy = 0.9686667323112488 - PERFORMANCE ON TEST SET: Batch Loss = 0.48389002680778503, Accuracy = 0.9138105511665344 - Training iter #2190000: Batch Loss = 0.287106, Accuracy = 0.9700000286102295 - PERFORMANCE ON TEST SET: Batch Loss = 0.4670214056968689, Accuracy = 0.9216151237487793 - Optimization Finished! - FINAL RESULT: Batch Loss = 0.45611169934272766, Accuracy = 0.9165252447128296 - - -## Training is good, but having visual insight is even better: - -Okay, let's plot this simply in the notebook for now. - - -```python -# (Inline plots: ) -%matplotlib inline - -font = { - 'family' : 'Bitstream Vera Sans', - 'weight' : 'bold', - 'size' : 18 -} -matplotlib.rc('font', **font) - -width = 12 -height = 12 -plt.figure(figsize=(width, height)) - -indep_train_axis = np.array(range(batch_size, (len(train_losses)+1)*batch_size, batch_size)) -plt.plot(indep_train_axis, np.array(train_losses), "b--", label="Train losses") -plt.plot(indep_train_axis, np.array(train_accuracies), "g--", label="Train accuracies") - -indep_test_axis = np.append( - np.array(range(batch_size, len(test_losses)*display_iter, display_iter)[:-1]), - [training_iters] -) -plt.plot(indep_test_axis, np.array(test_losses), "b-", label="Test losses") -plt.plot(indep_test_axis, np.array(test_accuracies), "g-", label="Test accuracies") - -plt.title("Training session's progress over iterations") -plt.legend(loc='upper right', shadow=True) -plt.ylabel('Training Progress (Loss or Accuracy values)') -plt.xlabel('Training iteration') - -plt.show() -``` - - -![LSTM Training Testing Comparison Curve](LSTM_files/LSTM_16_0.png) - - -## And finally, the multi-class confusion matrix and metrics! - - -```python -# Results - -predictions = one_hot_predictions.argmax(1) - -print("Testing Accuracy: {}%".format(100*accuracy)) - -print("") -print("Precision: {}%".format(100*metrics.precision_score(y_test, predictions, average="weighted"))) -print("Recall: {}%".format(100*metrics.recall_score(y_test, predictions, average="weighted"))) -print("f1_score: {}%".format(100*metrics.f1_score(y_test, predictions, average="weighted"))) - -print("") -print("Confusion Matrix:") -confusion_matrix = metrics.confusion_matrix(y_test, predictions) -print(confusion_matrix) -normalised_confusion_matrix = np.array(confusion_matrix, dtype=np.float32)/np.sum(confusion_matrix)*100 - -print("") -print("Confusion matrix (normalised to % of total test data):") -print(normalised_confusion_matrix) -print("Note: training and testing data is not equally distributed amongst classes, ") -print("so it is normal that more than a 6th of the data is correctly classifier in the last category.") - -# Plot Results: -width = 12 -height = 12 -plt.figure(figsize=(width, height)) -plt.imshow( - normalised_confusion_matrix, - interpolation='nearest', - cmap=plt.cm.rainbow -) -plt.title("Confusion matrix \n(normalised to % of total test data)") -plt.colorbar() -tick_marks = np.arange(n_classes) -plt.xticks(tick_marks, LABELS, rotation=90) -plt.yticks(tick_marks, LABELS) -plt.tight_layout() -plt.ylabel('True label') -plt.xlabel('Predicted label') -plt.show() -``` - - Testing Accuracy: 91.65252447128296% - - Precision: 91.76286479743305% - Recall: 91.65252799457076% - f1_score: 91.6437546304815% - - Confusion Matrix: - [[466 2 26 0 2 0] - [ 5 441 25 0 0 0] - [ 1 0 419 0 0 0] - [ 1 1 0 396 87 6] - [ 2 1 0 87 442 0] - [ 0 0 0 0 0 537]] - - Confusion matrix (normalised to % of total test data): - [[ 15.81269073 0.06786563 0.88225317 0. 0.06786563 0. ] - [ 0.16966406 14.96437073 0.84832031 0. 0. 0. ] - [ 0.03393281 0. 14.21784878 0. 0. 0. ] - [ 0.03393281 0.03393281 0. 13.43739319 2.95215464 - 0.20359688] - [ 0.06786563 0.03393281 0. 2.95215464 14.99830341 0. ] - [ 0. 0. 0. 0. 0. 18.22192001]] - Note: training and testing data is not equally distributed amongst classes, - so it is normal that more than a 6th of the data is correctly classifier in the last category. - - - -![Confusion Matrix](LSTM_files/LSTM_18_1.png) - - - -```python -sess.close() -``` - -## Conclusion - -Outstandingly, **the final accuracy is of 91%**! And it can peak to values such as 93.25%, at some moments of luck during the training, depending on how the neural network's weights got initialized at the start of the training, randomly. - -This means that the neural networks is almost always able to correctly identify the movement type! Remember, the phone is attached on the waist and each series to classify has just a 128 sample window of two internal sensors (a.k.a. 2.56 seconds at 50 FPS), so it amazes me how those predictions are extremely accurate given this small window of context and raw data. I've validated and re-validated that there is no important bug, and the community used and tried this code a lot. (Note: be sure to report something in the issue tab if you find bugs, otherwise [Quora](https://www.quora.com/), [StackOverflow](https://stackoverflow.com/questions/tagged/tensorflow?sort=votes&pageSize=50), and other [StackExchange](https://stackexchange.com/sites#science) sites are the places for asking questions.) - -I specially did not expect such good results for guessing between the labels "SITTING" and "STANDING". Those are seemingly almost the same thing from the point of view of a device placed at waist level according to how the dataset was originally gathered. Thought, it is still possible to see a little cluster on the matrix between those classes, which drifts away just a bit from the identity. This is great. - -It is also possible to see that there was a slight difficulty in doing the difference between "WALKING", "WALKING_UPSTAIRS" and "WALKING_DOWNSTAIRS". Obviously, those activities are quite similar in terms of movements. - -I also tried my code without the gyroscope, using only the 3D accelerometer's 6 features (and not changing the training hyperparameters), and got an accuracy of 87%. In general, gyroscopes consumes more power than accelerometers, so it is preferable to turn them off. - - -## Improvements - -In [another open-source repository of mine](https://github.com/guillaume-chevalier/HAR-stacked-residual-bidir-LSTMs), the accuracy is pushed up to nearly 94% using a special deep LSTM architecture which combines the concepts of bidirectional RNNs, residual connections, and stacked cells. This architecture is also tested on another similar activity dataset. It resembles the nice architecture used in "[Google’s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation](https://arxiv.org/pdf/1609.08144.pdf)", without an attention mechanism, and with just the encoder part - as a "many to one" architecture instead of a "many to many" to be adapted to the Human Activity Recognition (HAR) problem. I also worked more on the problem and came up with the [LARNN](https://github.com/guillaume-chevalier/Linear-Attention-Recurrent-Neural-Network), however it's complicated for just a little gain. Thus the current, original activity recognition project is simply better to use for its outstanding simplicity. - -If you want to learn more about deep learning, I have also built a list of the learning ressources for deep learning which have revealed to be the most useful to me [here](https://github.com/guillaume-chevalier/Awesome-Deep-Learning-Resources). - -I also have made even more improvements as seen just below with the few lines of code for easier usage and for reaching an even better score. Note this this is still an ongoing project, [subscribe here](https://www.neuraxio.com/en/time-series-solution) to learn more. - -### Even better and easily usable in the cloud - -So I'm planning to release an easily-usable cloud solution that can be used as follow to classify or forecast your time series, and this isn't limited to Human Activity Recognition data: - -[![](https://www.neuraxio.com/images/neuraxio-ts-solution.png)](https://www.neuraxio.com/en/time-series-solution) - -Visit [the Neuraxio's Time Series Solution](https://www.neuraxio.com/en/time-series-solution) product page for more information. - -## References - -The [dataset](https://archive.ics.uci.edu/ml/datasets/Human+Activity+Recognition+Using+Smartphones) can be found on the UCI Machine Learning Repository: - -> Davide Anguita, Alessandro Ghio, Luca Oneto, Xavier Parra and Jorge L. Reyes-Ortiz. A Public Domain Dataset for Human Activity Recognition Using Smartphones. 21th European Symposium on Artificial Neural Networks, Computational Intelligence and Machine Learning, ESANN 2013. Bruges, Belgium 24-26 April 2013. - -The RNN image for "many-to-one" is taken from Karpathy's post: - -> Andrej Karpathy, The Unreasonable Effectiveness of Recurrent Neural Networks, 2015, -> http://karpathy.github.io/2015/05/21/rnn-effectiveness/ - -## Citation - -Copyright (c) 2016 Guillaume Chevalier. To cite my code, you can point to the URL of the GitHub repository, for example: - -> Guillaume Chevalier, LSTMs for Human Activity Recognition, 2016, -> https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition - -My code is available for free and even for private usage for anyone under the [MIT License](https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition/blob/master/LICENSE), however I ask to cite for using the code. - -## Extra links - -### Connect with me - -- [LinkedIn](https://ca.linkedin.com/in/chevalierg) -- [Twitter](https://twitter.com/guillaume_che) -- [GitHub](https://github.com/guillaume-chevalier/) -- [Quora](https://www.quora.com/profile/Guillaume-Chevalier-2) -- [YouTube](https://www.youtube.com/c/GuillaumeChevalier) -- [Machine Learning Consulting](https://www.neuraxio.com/en/) - -### Liked this project? Did it help you? Leave a [star](https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition/stargazers), [fork](https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition/network/members) and share the love! - -This activity recognition project has been seen in: - -- [Hacker News 1st page](https://news.ycombinator.com/item?id=13049143) -- [Awesome TensorFlow](https://github.com/jtoy/awesome-tensorflow#tutorials) -- [TensorFlow World](https://github.com/astorfi/TensorFlow-World#some-useful-tutorials) -- And more. - ---- - - - -```python -# Let's convert this notebook to a README automatically for the GitHub project's title page: -!jupyter nbconvert --to markdown LSTM.ipynb -!mv LSTM.md README.md -``` - - [NbConvertApp] Converting notebook LSTM.ipynb to markdown - [NbConvertApp] Support files will be in LSTM_files/ - [NbConvertApp] Making directory LSTM_files - [NbConvertApp] Making directory LSTM_files - [NbConvertApp] Writing 38654 bytes to LSTM.md +You may keep up to date by cloning a new version of the project later. Visit the current [LSTM REST API serving demo project's page](https://github.com/Neuraxio/LSTM-Human-Activity-Recognition) to get updates as they are coded. diff --git a/new.py b/new.py deleted file mode 100644 index 1b96659..0000000 --- a/new.py +++ /dev/null @@ -1,85 +0,0 @@ -# Those are separate normalised input features for the neural network -import math -import os -import numpy as np - -from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \ - TRAIN_FILE_NAME, TEST_FILE_NAME -from neuraxle.api.flask import FlaskRestApiWrapper -from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode -from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE -from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray -from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs - - -def main(): - # Load "X" (the neural network's training and testing inputs) - - X_train = load_X(X_train_signals_paths) - X_test = load_X(X_test_signals_paths) - - # Load "y" (the neural network's training and testing outputs) - - y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME) - y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME) - - y_train = load_y(y_train_path) - y_test = load_y(y_test_path) - - print("Some useful info to get an insight on dataset's shape and normalisation:") - print("(X shape, y shape, every X's mean, every X's standard deviation)") - print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test)) - print("The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.") - - training_data_count = len(X_train) - training_iters = training_data_count * 3 - - pipeline = HumanActivityRecognitionPipeline() - - no_iter = int(math.floor(training_iters / BATCH_SIZE)) - for _ in range(no_iter): - pipeline, outputs = pipeline.fit_transform(X_train, y_train) - - pipeline.save(ExecutionContext.create_from_root( pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER)) - - pipeline.teardown() - - -def serve_rest_api(): - # Load "X" (the neural network's training and testing inputs) - - X_train = load_X(X_train_signals_paths) - X_test = load_X(X_test_signals_paths) - - # Load "y" (the neural network's training and testing outputs) - - y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME) - y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME) - - y_train = load_y(y_train_path) - y_test = load_y(y_test_path) - - pipeline = HumanActivityRecognitionPipeline() - - pipeline = pipeline.load( - ExecutionContext.create_from_root( - pipeline, - ExecutionMode.FIT, - DEFAULT_CACHE_FOLDER - ) - ) - - pipeline, outputs = pipeline.fit_transform(X_train, y_train) - - # Easy REST API deployment. - app = FlaskRestApiWrapper( - json_decoder=CustomJSONDecoderFor2DArray(), - wrapped=pipeline, - json_encoder=CustomJSONEncoderOfOutputs() - ).get_app() - - app.run(debug=False, port=5000) - - -if __name__ == '__main__': - serve_rest_api() diff --git a/old.py b/old.py deleted file mode 100644 index c99e002..0000000 --- a/old.py +++ /dev/null @@ -1,275 +0,0 @@ -import numpy as np -import tensorflow as tf - -from steps.one_hot_encoder import OneHotEncoder - -# Those are separate normalised input features for the neural network -INPUT_SIGNAL_TYPES = [ - "body_acc_x_", - "body_acc_y_", - "body_acc_z_", - "body_gyro_x_", - "body_gyro_y_", - "body_gyro_z_", - "total_acc_x_", - "total_acc_y_", - "total_acc_z_" -] - -# Output classes to learn how to classify -LABELS = [ - "WALKING", - "WALKING_UPSTAIRS", - "WALKING_DOWNSTAIRS", - "SITTING", - "STANDING", - "LAYING" -] - -DATA_PATH = "data/" -DATASET_PATH = DATA_PATH + "UCI HAR Dataset/" - -TRAIN = "train/" -TEST = "test/" - -X_train_signals_paths = [ - DATASET_PATH + TRAIN + "Inertial Signals/" + signal + "train.txt" for signal in INPUT_SIGNAL_TYPES -] - -X_test_signals_paths = [ - DATASET_PATH + TEST + "Inertial Signals/" + signal + "test.txt" for signal in INPUT_SIGNAL_TYPES -] - - -def main(): - def load_X(X_signals_paths): - X_signals = [] - - for signal_type_path in X_signals_paths: - file = open(signal_type_path, 'r') - # Read dataset from disk, dealing with text files' syntax - X_signals.append( - [np.array(serie, dtype=np.float32) for serie in [ - row.replace(' ', ' ').strip().split(' ') for row in file - ]] - ) - file.close() - - return np.transpose(np.array(X_signals), (1, 2, 0)) - - def load_y(y_path): - file = open(y_path, 'r') - # Read dataset from disk, dealing with text file's syntax - y_ = np.array( - [elem for elem in [ - row.replace(' ', ' ').strip().split(' ') for row in file - ]], - dtype=np.int32 - ) - file.close() - - # Substract 1 to each output class for friendly 0-based indexing - return y_ - 1 - - def LSTM_RNN(_X, _weights, _biases): - # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. - # Moreover, two LSTM cells are stacked which adds deepness to the neural network. - # Note, some code of this notebook is inspired from an slightly different - # RNN architecture used on another dataset, some of the credits goes to - # "aymericdamien" under the MIT license. - - # (NOTE: This step could be greatly optimised by shaping the dataset once - # input shape: (batch_size, n_steps, n_input) - _X = tf.transpose(_X, [1, 0, 2]) # permute n_steps and batch_size - # Reshape to prepare input to hidden activation - _X = tf.reshape(_X, [-1, n_input]) - # new shape: (n_steps*batch_size, n_input) - - # ReLU activation, thanks to Yu Zhao for adding this improvement here: - _X = tf.nn.relu(tf.matmul(_X, _weights['hidden']) + _biases['hidden']) - # Split data because rnn cell needs a list of inputs for the RNN inner loop - _X = tf.split(_X, n_steps, 0) - # new shape: n_steps * (batch_size, n_hidden) - - # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow - lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True) - lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0, state_is_tuple=True) - lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) - # Get LSTM cell output - outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) - - # Get last time step's output feature for a "many-to-one" style classifier, - # as in the image describing RNNs at the top of this page - lstm_last_output = outputs[-1] - - # Linear activation - return tf.matmul(lstm_last_output, _weights['out']) + _biases['out'] - - def extract_batch_size(_train, step, batch_size): - # Function to fetch a "batch_size" amount of data from "(X|y)_train" data. - - shape = list(_train.shape) - shape[0] = batch_size - batch_s = np.empty(shape) - - for i in range(batch_size): - # Loop index - index = ((step - 1) * batch_size + i) % len(_train) - batch_s[i] = _train[index] - - return batch_s - - # Load "X" (the neural network's training and testing inputs) - - X_train = load_X(X_train_signals_paths) - X_test = load_X(X_test_signals_paths) - - # Load "y" (the neural network's training and testing outputs) - - y_train_path = DATASET_PATH + TRAIN + "y_train.txt" - y_test_path = DATASET_PATH + TEST + "y_test.txt" - - y_train = load_y(y_train_path) - y_test = load_y(y_test_path) - - # Input Data - - training_data_count = len(X_train) # 7352 training series (with 50% overlap between each serie) - test_data_count = len(X_test) # 2947 testing series - n_steps = len(X_train[0]) # 128 timesteps per series - n_input = len(X_train[0][0]) # 9 input parameters per timestep - - # LSTM Neural Network's internal structure - - n_hidden = 32 # Hidden layer num of features - n_classes = 6 # Total classes (should go up, or should go down) - - # Training - - learning_rate = 0.0025 - lambda_loss_amount = 0.0015 - training_iters = training_data_count * 300 # Loop 300 times on the dataset - batch_size = 1500 - display_iter = 30000 # To show test set accuracy during training - - # Some debugging info - - print("Some useful info to get an insight on dataset's shape and normalisation:") - print("(X shape, y shape, every X's mean, every X's standard deviation)") - print(X_test.shape, y_test.shape, np.mean(X_test), np.std(X_test)) - print("The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.") - - # Graph input/output - x = tf.placeholder(tf.float32, [None, n_steps, n_input]) - y = tf.placeholder(tf.float32, [None, n_classes]) - - # Graph weights - weights = { - 'hidden': tf.Variable(tf.random_normal([n_input, n_hidden])), # Hidden layer weights - 'out': tf.Variable(tf.random_normal([n_hidden, n_classes], mean=1.0)) - } - biases = { - 'hidden': tf.Variable(tf.random_normal([n_hidden])), - 'out': tf.Variable(tf.random_normal([n_classes])) - } - - pred = LSTM_RNN(x, weights, biases) - - # Loss, optimizer and evaluation - l2 = lambda_loss_amount * sum( - tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() - ) # L2 loss prevents this overkill neural network to overfit the data - cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y, logits=pred)) + l2 # Softmax loss - optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # Adam Optimizer - - correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) - accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) - - # To keep track of training's performance - test_losses = [] - test_accuracies = [] - train_losses = [] - train_accuracies = [] - - # Launch the graph - sess = tf.InteractiveSession(config=tf.ConfigProto(log_device_placement=True)) - init = tf.global_variables_initializer() - sess.run(init) - - # Perform Training steps with "batch_size" amount of example data at each loop - step = 1 - while step * batch_size <= training_iters: - batch_xs = extract_batch_size(X_train, step, batch_size) - batch_size_train = extract_batch_size(y_train, step, batch_size) - - batch_ys = OneHotEncoder( - nb_columns=n_classes, - name='batch_ys' - ).transform(batch_size_train) - - # Fit training using batch data - _, loss, acc = sess.run( - [optimizer, cost, accuracy], - feed_dict={ - x: batch_xs, - y: batch_ys - } - ) - train_losses.append(loss) - train_accuracies.append(acc) - - # Evaluate network only at some steps for faster training: - if (step * batch_size % display_iter == 0) or (step == 1) or (step * batch_size > training_iters): - # To not spam console, show training accuracy/loss in this "if" - print("Training iter #" + str(step * batch_size) + \ - ": Batch Loss = " + "{:.6f}".format(loss) + \ - ", Accuracy = {}".format(acc)) - - # Evaluation on the test set (no learning made here - just evaluation for diagnosis) - one_hot_encoded_y_test = OneHotEncoder( - nb_columns=n_classes, - name='one_hot_encoded_y_test' - ).transform(y_test) - - loss, acc = sess.run( - [cost, accuracy], - feed_dict={ - x: X_test, - y: one_hot_encoded_y_test - } - ) - test_losses.append(loss) - test_accuracies.append(acc) - print("PERFORMANCE ON TEST SET: " + \ - "Batch Loss = {}".format(loss) + \ - ", Accuracy = {}".format(acc)) - - step += 1 - - print("Optimization Finished!") - - # Accuracy for test data - - one_host_encoded_y_test = OneHotEncoder( - nb_columns=n_classes, - name='one_hot_predictions' - ).transform(y_test) - - one_hot_predictions, accuracy, final_loss = sess.run( - [pred, accuracy, cost], - feed_dict={ - x: X_test, - y: one_host_encoded_y_test - } - ) - - test_losses.append(final_loss) - test_accuracies.append(accuracy) - - print("FINAL RESULT: " + \ - "Batch Loss = {}".format(final_loss) + \ - ", Accuracy = {}".format(accuracy)) - - -if __name__ == '__main__': - main() From 2fceeb6fbb70cacbaca0b2d95064bd2da8320907 Mon Sep 17 00:00:00 2001 From: guillaume-chevalier Date: Tue, 5 Nov 2019 00:49:32 -0500 Subject: [PATCH 22/30] Clean code a bit --- 2_call_rest_api_and_eval.ipynb | 4 ++-- call_api.py | 0 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 call_api.py diff --git a/2_call_rest_api_and_eval.ipynb b/2_call_rest_api_and_eval.ipynb index 802e308..416bde7 100644 --- a/2_call_rest_api_and_eval.ipynb +++ b/2_call_rest_api_and_eval.ipynb @@ -101,7 +101,7 @@ " def transform(self, data_inputs):\n", " data = json.dumps(data_inputs.tolist()).encode('utf8')\n", " req = urllib.request.Request(\n", - " 'http://127.0.0.1:5000/',\n", + " self.url,\n", " method=\"GET\",\n", " headers={'content-type': 'application/json'},\n", " data=data\n", @@ -139,7 +139,7 @@ ], "source": [ "p = Pipeline([\n", - " APICaller(url=\"http://localhost:5000/\")\n", + " APICaller(url=\"http://127.0.0.1:5000/\")\n", "])\n", "y_pred = p.transform(X_test)\n", "print(y_pred)" diff --git a/call_api.py b/call_api.py deleted file mode 100644 index e69de29..0000000 From 1c74c614e32e29f682dce928187f1aee2683d4b5 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Wed, 20 Nov 2019 12:50:32 -0500 Subject: [PATCH 23/30] Fix neuraxle.steps.numpy OneHotEncoder imports --- 1_train_and_save_LSTM.ipynb | 219 ++++++++++++--------- 2_call_rest_api_and_eval.ipynb | 115 +++-------- pipeline.py | 2 +- steps/lstm_rnn_tensorflow_model_wrapper.py | 2 +- 4 files changed, 154 insertions(+), 184 deletions(-) diff --git a/1_train_and_save_LSTM.ipynb b/1_train_and_save_LSTM.ipynb index 907690f..18a76ab 100644 --- a/1_train_and_save_LSTM.ipynb +++ b/1_train_and_save_LSTM.ipynb @@ -21,7 +21,7 @@ "from neuraxle.api.flask import FlaskRestApiWrapper\n", "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", "from neuraxle.hyperparams.space import HyperparameterSamples\n", - "from neuraxle.steps.encoding import OneHotEncoder\n", + "from neuraxle.steps.numpy import OneHotEncoder\n", "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", "\n", @@ -51,13 +51,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py old.py\t\t savers\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t pipeline.py\t steps\n", - "Call-API.ipynb\t\t\tLSTM_files\t __pycache__\t venv\n", - "call_api.py\t\t\tLSTM_new.ipynb\t README.md\n", - "data\t\t\t\tnew.py\t\t requirements.txt\n", - "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/data\n", + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py README.md\t venv\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t requirements.txt\n", + "cache\t\t\t\tpipeline.py\t savers\n", + "data\t\t\t\t__pycache__\t steps\n", + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", "\n", @@ -67,15 +66,14 @@ "Extracting...\n", "Dataset already extracted. Did not extract twice.\n", "\n", - "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/data\n", + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", - "/home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py old.py\t\t savers\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t pipeline.py\t steps\n", - "Call-API.ipynb\t\t\tLSTM_files\t __pycache__\t venv\n", - "call_api.py\t\t\tLSTM_new.ipynb\t README.md\n", - "data\t\t\t\tnew.py\t\t requirements.txt\n", + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py README.md\t venv\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t requirements.txt\n", + "cache\t\t\t\tpipeline.py\t savers\n", + "data\t\t\t\t__pycache__\t steps\n", "\n", "Dataset is now located at: data/UCI HAR Dataset/\n" ] @@ -463,10 +461,10 @@ "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Please use `layer.add_weight` method instead.\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Call initializer instance with the dtype argument instead of passing it to the constructor\n", "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", @@ -481,77 +479,77 @@ "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", - "Batch Loss = 2.876983, Accuracy = 0.20266667008399963\n", - "Batch Loss = 2.422258, Accuracy = 0.1393333375453949\n", - "Batch Loss = 2.341008, Accuracy = 0.3019999861717224\n", - "Batch Loss = 2.229099, Accuracy = 0.3840000033378601\n", - "Batch Loss = 2.204831, Accuracy = 0.459319531917572\n", - "Batch Loss = 2.161258, Accuracy = 0.44733333587646484\n", - "Batch Loss = 2.008523, Accuracy = 0.5326666831970215\n", - "Batch Loss = 1.936510, Accuracy = 0.492000013589859\n", - "Batch Loss = 1.889814, Accuracy = 0.5640000104904175\n", - "Batch Loss = 1.925490, Accuracy = 0.5044378638267517\n", - "Batch Loss = 1.901193, Accuracy = 0.45266667008399963\n", - "Batch Loss = 1.823743, Accuracy = 0.5026666522026062\n", - "Batch Loss = 1.856515, Accuracy = 0.5546666383743286\n", - "Batch Loss = 1.781088, Accuracy = 0.6653333306312561\n", - "Batch Loss = 1.788599, Accuracy = 0.5739644765853882\n", - "Batch Loss = 1.765126, Accuracy = 0.5773333311080933\n", - "Batch Loss = 1.715384, Accuracy = 0.5860000252723694\n", - "Batch Loss = 1.694299, Accuracy = 0.6066666841506958\n", - "Batch Loss = 1.629456, Accuracy = 0.6166666746139526\n", - "Batch Loss = 1.659033, Accuracy = 0.5798816680908203\n", - "Batch Loss = 1.690838, Accuracy = 0.5299999713897705\n", - "Batch Loss = 1.546839, Accuracy = 0.6359999775886536\n", - "Batch Loss = 1.502047, Accuracy = 0.6713333129882812\n", - "Batch Loss = 1.516301, Accuracy = 0.6119999885559082\n", - "Batch Loss = 1.472707, Accuracy = 0.6797337532043457\n", - "Batch Loss = 1.473756, Accuracy = 0.6140000224113464\n", - "Batch Loss = 1.435259, Accuracy = 0.6420000195503235\n", - "Batch Loss = 1.357141, Accuracy = 0.6926666498184204\n", - "Batch Loss = 1.298965, Accuracy = 0.7059999704360962\n", - "Batch Loss = 1.342825, Accuracy = 0.6767751574516296\n", - "Batch Loss = 1.246462, Accuracy = 0.7273333072662354\n", - "Batch Loss = 1.204010, Accuracy = 0.746666669845581\n", - "Batch Loss = 1.221481, Accuracy = 0.7213333249092102\n", - "Batch Loss = 1.134928, Accuracy = 0.7746666669845581\n", - "Batch Loss = 1.173433, Accuracy = 0.7536982297897339\n", - "Batch Loss = 1.493877, Accuracy = 0.6880000233650208\n", - "Batch Loss = 1.092508, Accuracy = 0.7873333096504211\n", - "Batch Loss = 1.183987, Accuracy = 0.731333315372467\n", - "Batch Loss = 1.287978, Accuracy = 0.7333333492279053\n", - "Batch Loss = 1.353160, Accuracy = 0.7004438042640686\n", - "Batch Loss = 1.258987, Accuracy = 0.6980000138282776\n", - "Batch Loss = 1.120263, Accuracy = 0.7739999890327454\n", - "Batch Loss = 1.157481, Accuracy = 0.7213333249092102\n", - "Batch Loss = 1.016302, Accuracy = 0.831333339214325\n", - "Batch Loss = 1.225947, Accuracy = 0.7204142212867737\n", - "Batch Loss = 1.158564, Accuracy = 0.7620000243186951\n", - "Batch Loss = 1.083358, Accuracy = 0.7940000295639038\n", - "Batch Loss = 1.149199, Accuracy = 0.7233333587646484\n", - "Batch Loss = 1.003003, Accuracy = 0.8633333444595337\n", - "Batch Loss = 1.134901, Accuracy = 0.7803254723548889\n", - "Batch Loss = 1.140388, Accuracy = 0.765333354473114\n", - "Batch Loss = 1.061487, Accuracy = 0.8199999928474426\n", - "Batch Loss = 1.074304, Accuracy = 0.7480000257492065\n", - "Batch Loss = 1.014286, Accuracy = 0.8326666951179504\n", - "Batch Loss = 1.077505, Accuracy = 0.7847633361816406\n", - "Batch Loss = 1.020065, Accuracy = 0.8053333163261414\n", - "Batch Loss = 1.056145, Accuracy = 0.8066666722297668\n", - "Batch Loss = 1.143748, Accuracy = 0.718666672706604\n", - "Batch Loss = 1.218218, Accuracy = 0.7706666588783264\n", - "Batch Loss = 1.041671, Accuracy = 0.8195266127586365\n", - "Batch Loss = 1.523212, Accuracy = 0.7006666660308838\n", - "Batch Loss = 1.661112, Accuracy = 0.7006666660308838\n", - "Batch Loss = 1.367053, Accuracy = 0.7459999918937683\n", - "Batch Loss = 1.472857, Accuracy = 0.6653333306312561\n", - "Batch Loss = 1.244812, Accuracy = 0.7315088510513306\n", - "Batch Loss = 1.302771, Accuracy = 0.7273333072662354\n", - "Batch Loss = 1.324543, Accuracy = 0.7106666564941406\n", - "Batch Loss = 1.442056, Accuracy = 0.6573333144187927\n", - "Batch Loss = 1.196929, Accuracy = 0.8273333311080933\n", - "Batch Loss = 1.161574, Accuracy = 0.7988165616989136\n", - "WARNING:tensorflow:From /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "Batch Loss = 3.256458, Accuracy = 0.15733332931995392\n", + "Batch Loss = 2.619813, Accuracy = 0.18199999630451202\n", + "Batch Loss = 2.512498, Accuracy = 0.21466666460037231\n", + "Batch Loss = 2.398641, Accuracy = 0.24799999594688416\n", + "Batch Loss = 2.349453, Accuracy = 0.4164201319217682\n", + "Batch Loss = 2.238119, Accuracy = 0.35600000619888306\n", + "Batch Loss = 2.184882, Accuracy = 0.335999995470047\n", + "Batch Loss = 2.010944, Accuracy = 0.6306666731834412\n", + "Batch Loss = 1.971244, Accuracy = 0.5373333096504211\n", + "Batch Loss = 2.006573, Accuracy = 0.4822485148906708\n", + "Batch Loss = 1.963192, Accuracy = 0.47466665506362915\n", + "Batch Loss = 1.868868, Accuracy = 0.527999997138977\n", + "Batch Loss = 1.719967, Accuracy = 0.5806666612625122\n", + "Batch Loss = 1.672469, Accuracy = 0.6726666688919067\n", + "Batch Loss = 1.709138, Accuracy = 0.5828402638435364\n", + "Batch Loss = 1.632946, Accuracy = 0.621999979019165\n", + "Batch Loss = 1.594946, Accuracy = 0.6233333349227905\n", + "Batch Loss = 1.526639, Accuracy = 0.6513333320617676\n", + "Batch Loss = 1.473576, Accuracy = 0.6366666555404663\n", + "Batch Loss = 1.526323, Accuracy = 0.601331353187561\n", + "Batch Loss = 1.413392, Accuracy = 0.6766666769981384\n", + "Batch Loss = 1.422748, Accuracy = 0.6859999895095825\n", + "Batch Loss = 1.429600, Accuracy = 0.6660000085830688\n", + "Batch Loss = 1.270950, Accuracy = 0.7139999866485596\n", + "Batch Loss = 1.369228, Accuracy = 0.6619822382926941\n", + "Batch Loss = 1.280670, Accuracy = 0.7080000042915344\n", + "Batch Loss = 1.290532, Accuracy = 0.7120000123977661\n", + "Batch Loss = 1.235535, Accuracy = 0.6899999976158142\n", + "Batch Loss = 1.216868, Accuracy = 0.7553333044052124\n", + "Batch Loss = 1.284065, Accuracy = 0.6952662467956543\n", + "Batch Loss = 1.221238, Accuracy = 0.7386666536331177\n", + "Batch Loss = 1.210181, Accuracy = 0.7580000162124634\n", + "Batch Loss = 1.202590, Accuracy = 0.6733333468437195\n", + "Batch Loss = 1.122101, Accuracy = 0.8019999861717224\n", + "Batch Loss = 1.253165, Accuracy = 0.7485207319259644\n", + "Batch Loss = 1.181665, Accuracy = 0.7480000257492065\n", + "Batch Loss = 1.145692, Accuracy = 0.7773333191871643\n", + "Batch Loss = 1.191469, Accuracy = 0.7046666741371155\n", + "Batch Loss = 1.035143, Accuracy = 0.8566666841506958\n", + "Batch Loss = 1.179767, Accuracy = 0.7781065106391907\n", + "Batch Loss = 1.139800, Accuracy = 0.8033333420753479\n", + "Batch Loss = 1.092379, Accuracy = 0.7933333516120911\n", + "Batch Loss = 1.131772, Accuracy = 0.7266666889190674\n", + "Batch Loss = 1.021260, Accuracy = 0.8393333554267883\n", + "Batch Loss = 1.110702, Accuracy = 0.7921597361564636\n", + "Batch Loss = 1.092661, Accuracy = 0.8026666641235352\n", + "Batch Loss = 1.051368, Accuracy = 0.8119999766349792\n", + "Batch Loss = 1.109515, Accuracy = 0.7720000147819519\n", + "Batch Loss = 1.061533, Accuracy = 0.8259999752044678\n", + "Batch Loss = 1.074168, Accuracy = 0.7995561957359314\n", + "Batch Loss = 1.050956, Accuracy = 0.7986666560173035\n", + "Batch Loss = 1.037090, Accuracy = 0.8153333067893982\n", + "Batch Loss = 1.079096, Accuracy = 0.7726666927337646\n", + "Batch Loss = 0.998904, Accuracy = 0.8460000157356262\n", + "Batch Loss = 1.058527, Accuracy = 0.790680468082428\n", + "Batch Loss = 0.958898, Accuracy = 0.8726666569709778\n", + "Batch Loss = 1.017526, Accuracy = 0.8100000023841858\n", + "Batch Loss = 1.093624, Accuracy = 0.7586666941642761\n", + "Batch Loss = 0.847462, Accuracy = 0.9319999814033508\n", + "Batch Loss = 0.975213, Accuracy = 0.8454142212867737\n", + "Batch Loss = 0.937760, Accuracy = 0.8613333106040955\n", + "Batch Loss = 0.894541, Accuracy = 0.8826666474342346\n", + "Batch Loss = 1.015310, Accuracy = 0.8119999766349792\n", + "Batch Loss = 0.792434, Accuracy = 0.9426666498184204\n", + "Batch Loss = 0.918421, Accuracy = 0.8801774978637695\n", + "Batch Loss = 0.856438, Accuracy = 0.9086666703224182\n", + "Batch Loss = 0.831793, Accuracy = 0.903333306312561\n", + "Batch Loss = 0.975429, Accuracy = 0.8486666679382324\n", + "Batch Loss = 0.891167, Accuracy = 0.8913333415985107\n", + "Batch Loss = 0.865273, Accuracy = 0.8905325531959534\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", "\n" ] }, @@ -631,7 +629,7 @@ "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", - "INFO:tensorflow:Restoring parameters from /home/gui/Documents/GIT/refactor_lstm/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n" + "INFO:tensorflow:Restoring parameters from /home/alexandre/Documents/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n" ] } ], @@ -664,7 +662,44 @@ "output_type": "stream", "text": [ " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", - "127.0.0.1 - - [04/Nov/2019 22:38:52] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" + "[2019-11-20 12:43:33,783] ERROR in app: Exception on / [GET]\n", + "Traceback (most recent call last):\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/app.py\", line 1949, in full_dispatch_request\n", + " rv = self.dispatch_request()\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/app.py\", line 1935, in dispatch_request\n", + " return self.view_functions[rule.endpoint](**req.view_args)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask_restful/__init__.py\", line 458, in wrapper\n", + " resp = resource(*args, **kwargs)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/views.py\", line 89, in view\n", + " return self.dispatch_request(*args, **kwargs)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask_restful/__init__.py\", line 573, in dispatch_request\n", + " resp = meth(*args, **kwargs)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/api/flask.py\", line 136, in get\n", + " return wrapped.transform(request.get_json())\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\", line 74, in transform\n", + " data_container = self._transform_core(data_container, context)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\", line 250, in _transform_core\n", + " data_container = step.handle_transform(data_container, sub_context)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/base.py\", line 807, in handle_transform\n", + " out = self.transform(data_container.data_inputs)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/api/flask.py\", line 63, in transform\n", + " return jsonify(self.encode(data_inputs))\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/json/__init__.py\", line 370, in jsonify\n", + " dumps(data, indent=indent, separators=separators) + \"\\n\",\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/json/__init__.py\", line 211, in dumps\n", + " rv = _json.dumps(obj, **kwargs)\n", + " File \"/usr/lib/python3.6/json/__init__.py\", line 238, in dumps\n", + " **kw).encode(obj)\n", + " File \"/usr/lib/python3.6/json/encoder.py\", line 199, in encode\n", + " chunks = self.iterencode(o, _one_shot=True)\n", + " File \"/usr/lib/python3.6/json/encoder.py\", line 257, in iterencode\n", + " return _iterencode(o, 0)\n", + " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/json/__init__.py\", line 100, in default\n", + " return _json.JSONEncoder.default(self, o)\n", + " File \"/usr/lib/python3.6/json/encoder.py\", line 180, in default\n", + " o.__class__.__name__)\n", + "TypeError: Object of type 'ndarray' is not JSON serializable\n", + "127.0.0.1 - - [20/Nov/2019 12:43:33] \"\u001b[1m\u001b[35mGET / HTTP/1.1\u001b[0m\" 500 -\n" ] } ], @@ -681,9 +716,9 @@ ], "metadata": { "kernelspec": { - "display_name": "lstm_har_2", + "display_name": "demo", "language": "python", - "name": "lstm_har_2" + "name": "demo" }, "language_info": { "codemirror_mode": { diff --git a/2_call_rest_api_and_eval.ipynb b/2_call_rest_api_and_eval.ipynb index 416bde7..f876121 100644 --- a/2_call_rest_api_and_eval.ipynb +++ b/2_call_rest_api_and_eval.ipynb @@ -27,7 +27,7 @@ "from neuraxle.api.flask import FlaskRestApiWrapper\n", "from neuraxle.hyperparams.space import HyperparameterSamples\n", "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner, Pipeline\n", - "from neuraxle.steps.encoding import OneHotEncoder\n", + "from neuraxle.steps.numpy import OneHotEncoder\n", "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", "\n", "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, X_test_signals_paths, load_X, load_y, \\\n", @@ -124,16 +124,24 @@ "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "[[-1.00376022 -3.22574663 -1.74762106 -0.1013099 0.61165339 -5.02736664]\n", - " [-0.90527415 -3.20171666 -1.81338 0.0932436 0.75021946 -4.88879108]\n", - " [-0.89485669 -3.16932821 -1.80476213 0.04580438 0.77750677 -4.89109993]\n", - " ...\n", - " [ 4.41215801 6.99871826 2.78054214 1.71619391 0.72097886 2.2889092 ]\n", - " [ 3.51318192 6.10547161 1.43138313 2.56595802 1.21157002 0.62227172]\n", - " [ 3.96033239 5.9378233 3.445261 0.99932408 1.05689049 -0.02206901]]\n" + "ename": "HTTPError", + "evalue": "HTTP Error 500: INTERNAL SERVER ERROR", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mHTTPError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mAPICaller\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"http://127.0.0.1:5000/\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m ])\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0my_pred\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX_test\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my_pred\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0mcontext\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mExecutionContext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_from_root\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mExecutionMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTRANSFORM\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcache_folder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 73\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_transform_core\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 75\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36m_transform_core\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstep_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstep\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msteps_left_to_do\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 249\u001b[0m \u001b[0msub_context\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 250\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstep\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandle_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msub_context\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 251\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 252\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/base.py\u001b[0m in \u001b[0;36mhandle_transform\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 805\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;32mreturn\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mtransformed\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mcontainer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 806\u001b[0m \"\"\"\n\u001b[0;32m--> 807\u001b[0;31m \u001b[0mout\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 808\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_data_inputs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 809\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m )\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0murllib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0murlopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0mtest_predictions\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtest_predictions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'predictions'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36murlopen\u001b[0;34m(url, data, timeout, cafile, capath, cadefault, context)\u001b[0m\n\u001b[1;32m 221\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0mopener\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_opener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 223\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mopener\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 224\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 225\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minstall_opener\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mopener\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mopen\u001b[0;34m(self, fullurl, data, timeout)\u001b[0m\n\u001b[1;32m 530\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mprocessor\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprocess_response\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprotocol\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 531\u001b[0m \u001b[0mmeth\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprocessor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmeth_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 532\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmeth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 533\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 534\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mhttp_response\u001b[0;34m(self, request, response)\u001b[0m\n\u001b[1;32m 640\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m200\u001b[0m \u001b[0;34m<=\u001b[0m \u001b[0mcode\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m300\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 641\u001b[0m response = self.parent.error(\n\u001b[0;32m--> 642\u001b[0;31m 'http', request, response, code, msg, hdrs)\n\u001b[0m\u001b[1;32m 643\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 644\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36merror\u001b[0;34m(self, proto, *args)\u001b[0m\n\u001b[1;32m 568\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhttp_err\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 569\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mdict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'default'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'http_error_default'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0morig_args\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 570\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_call_chain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 571\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 572\u001b[0m \u001b[0;31m# XXX probably also want an abstract factory that knows when it makes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36m_call_chain\u001b[0;34m(self, chain, kind, meth_name, *args)\u001b[0m\n\u001b[1;32m 502\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mhandler\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mhandlers\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 503\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhandler\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmeth_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 504\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 505\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 506\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mhttp_error_default\u001b[0;34m(self, req, fp, code, msg, hdrs)\u001b[0m\n\u001b[1;32m 648\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mHTTPDefaultErrorHandler\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mBaseHandler\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 649\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mhttp_error_default\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhdrs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 650\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mHTTPError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfull_url\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhdrs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 651\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 652\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mHTTPRedirectHandler\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mBaseHandler\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mHTTPError\u001b[0m: HTTP Error 500: INTERNAL SERVER ERROR" ] } ], @@ -154,59 +162,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", - "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", - "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Precision: 73.67085664926599%\n", - "Recall: 72.54835425856804%\n", - "f1_score: 72.63862952734833%\n", - "\n", - "Confusion Matrix:\n", - "[[285 128 67 9 7 0]\n", - " [ 73 334 60 4 0 0]\n", - " [115 76 211 13 5 0]\n", - " [ 3 25 0 419 44 0]\n", - " [ 6 11 5 131 379 0]\n", - " [ 0 27 0 0 0 510]]\n", - "\n", - "Confusion matrix (normalised to % of total test data):\n", - "[[ 9.670852 4.3434 2.2734985 0.3053953 0.2375297 0. ]\n", - " [ 2.4770954 11.333559 2.0359688 0.13573125 0. 0. ]\n", - " [ 3.9022737 2.578894 7.1598234 0.44112659 0.16966406 0. ]\n", - " [ 0.10179844 0.8483203 0. 14.217849 1.4930438 0. ]\n", - " [ 0.20359688 0.37326095 0.16966406 4.4451985 12.860537 0. ]\n", - " [ 0. 0.916186 0. 0. 0. 17.305735 ]]\n", - "Note: training and testing data is not equally distributed amongst classes, \n", - "so it is normal that more than a 6th of the data is correctly classifier in the last category.\n" - ] - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# (Inline plots: )\n", "%matplotlib inline\n", @@ -262,32 +220,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Pipeline\n", - "(\n", - "\tPipeline(\n", - "\tname=Pipeline,\n", - "\thyperparameters=HyperparameterSamples()\n", - ")(\n", - "\t\t[('APICaller',\n", - " APICaller(\n", - "\tname=APICaller,\n", - "\thyperparameters=HyperparameterSamples()\n", - "))]\t\n", - ")\n", - ")" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "p.teardown()" ] @@ -295,9 +230,9 @@ ], "metadata": { "kernelspec": { - "display_name": "lstm_har_2", + "display_name": "demo", "language": "python", - "name": "lstm_har_2" + "name": "demo" }, "language_info": { "codemirror_mode": { diff --git a/pipeline.py b/pipeline.py index fd342ac..a8c00f7 100644 --- a/pipeline.py +++ b/pipeline.py @@ -1,5 +1,5 @@ from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner -from neuraxle.steps.encoding import OneHotEncoder +from neuraxle.steps.numpy import OneHotEncoder from neuraxle.steps.output_handlers import OutputTransformerWrapper from steps.lstm_rnn_tensorflow_model_wrapper import ClassificationRNNTensorFlowModel, N_CLASSES, BATCH_SIZE diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py index 97e6a4c..2a54aa7 100644 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ b/steps/lstm_rnn_tensorflow_model_wrapper.py @@ -2,7 +2,7 @@ import tensorflow as tf from neuraxle.base import BaseStep from neuraxle.hyperparams.space import HyperparameterSamples -from neuraxle.steps.encoding import OneHotEncoder +from neuraxle.steps.numpy import OneHotEncoder from savers.tensorflow1_step_saver import TensorflowV1StepSaver from steps.lstm_rnn_tensorflow_model import tf_model_forward From e4d2c99a8124b3be9350100da5d488a8199069fc Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Wed, 20 Nov 2019 13:16:28 -0500 Subject: [PATCH 24/30] Rerun notebooks with fixed neuraxle imports --- 1_train_and_save_LSTM.ipynb | 183 +++++++++++++-------------------- 2_call_rest_api_and_eval.ipynb | 113 +++++++++++++++----- 2 files changed, 162 insertions(+), 134 deletions(-) diff --git a/1_train_and_save_LSTM.ipynb b/1_train_and_save_LSTM.ipynb index 18a76ab..e5007ab 100644 --- a/1_train_and_save_LSTM.ipynb +++ b/1_train_and_save_LSTM.ipynb @@ -479,76 +479,76 @@ "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", "\n", - "Batch Loss = 3.256458, Accuracy = 0.15733332931995392\n", - "Batch Loss = 2.619813, Accuracy = 0.18199999630451202\n", - "Batch Loss = 2.512498, Accuracy = 0.21466666460037231\n", - "Batch Loss = 2.398641, Accuracy = 0.24799999594688416\n", - "Batch Loss = 2.349453, Accuracy = 0.4164201319217682\n", - "Batch Loss = 2.238119, Accuracy = 0.35600000619888306\n", - "Batch Loss = 2.184882, Accuracy = 0.335999995470047\n", - "Batch Loss = 2.010944, Accuracy = 0.6306666731834412\n", - "Batch Loss = 1.971244, Accuracy = 0.5373333096504211\n", - "Batch Loss = 2.006573, Accuracy = 0.4822485148906708\n", - "Batch Loss = 1.963192, Accuracy = 0.47466665506362915\n", - "Batch Loss = 1.868868, Accuracy = 0.527999997138977\n", - "Batch Loss = 1.719967, Accuracy = 0.5806666612625122\n", - "Batch Loss = 1.672469, Accuracy = 0.6726666688919067\n", - "Batch Loss = 1.709138, Accuracy = 0.5828402638435364\n", - "Batch Loss = 1.632946, Accuracy = 0.621999979019165\n", - "Batch Loss = 1.594946, Accuracy = 0.6233333349227905\n", - "Batch Loss = 1.526639, Accuracy = 0.6513333320617676\n", - "Batch Loss = 1.473576, Accuracy = 0.6366666555404663\n", - "Batch Loss = 1.526323, Accuracy = 0.601331353187561\n", - "Batch Loss = 1.413392, Accuracy = 0.6766666769981384\n", - "Batch Loss = 1.422748, Accuracy = 0.6859999895095825\n", - "Batch Loss = 1.429600, Accuracy = 0.6660000085830688\n", - "Batch Loss = 1.270950, Accuracy = 0.7139999866485596\n", - "Batch Loss = 1.369228, Accuracy = 0.6619822382926941\n", - "Batch Loss = 1.280670, Accuracy = 0.7080000042915344\n", - "Batch Loss = 1.290532, Accuracy = 0.7120000123977661\n", - "Batch Loss = 1.235535, Accuracy = 0.6899999976158142\n", - "Batch Loss = 1.216868, Accuracy = 0.7553333044052124\n", - "Batch Loss = 1.284065, Accuracy = 0.6952662467956543\n", - "Batch Loss = 1.221238, Accuracy = 0.7386666536331177\n", - "Batch Loss = 1.210181, Accuracy = 0.7580000162124634\n", - "Batch Loss = 1.202590, Accuracy = 0.6733333468437195\n", - "Batch Loss = 1.122101, Accuracy = 0.8019999861717224\n", - "Batch Loss = 1.253165, Accuracy = 0.7485207319259644\n", - "Batch Loss = 1.181665, Accuracy = 0.7480000257492065\n", - "Batch Loss = 1.145692, Accuracy = 0.7773333191871643\n", - "Batch Loss = 1.191469, Accuracy = 0.7046666741371155\n", - "Batch Loss = 1.035143, Accuracy = 0.8566666841506958\n", - "Batch Loss = 1.179767, Accuracy = 0.7781065106391907\n", - "Batch Loss = 1.139800, Accuracy = 0.8033333420753479\n", - "Batch Loss = 1.092379, Accuracy = 0.7933333516120911\n", - "Batch Loss = 1.131772, Accuracy = 0.7266666889190674\n", - "Batch Loss = 1.021260, Accuracy = 0.8393333554267883\n", - "Batch Loss = 1.110702, Accuracy = 0.7921597361564636\n", - "Batch Loss = 1.092661, Accuracy = 0.8026666641235352\n", - "Batch Loss = 1.051368, Accuracy = 0.8119999766349792\n", - "Batch Loss = 1.109515, Accuracy = 0.7720000147819519\n", - "Batch Loss = 1.061533, Accuracy = 0.8259999752044678\n", - "Batch Loss = 1.074168, Accuracy = 0.7995561957359314\n", - "Batch Loss = 1.050956, Accuracy = 0.7986666560173035\n", - "Batch Loss = 1.037090, Accuracy = 0.8153333067893982\n", - "Batch Loss = 1.079096, Accuracy = 0.7726666927337646\n", - "Batch Loss = 0.998904, Accuracy = 0.8460000157356262\n", - "Batch Loss = 1.058527, Accuracy = 0.790680468082428\n", - "Batch Loss = 0.958898, Accuracy = 0.8726666569709778\n", - "Batch Loss = 1.017526, Accuracy = 0.8100000023841858\n", - "Batch Loss = 1.093624, Accuracy = 0.7586666941642761\n", - "Batch Loss = 0.847462, Accuracy = 0.9319999814033508\n", - "Batch Loss = 0.975213, Accuracy = 0.8454142212867737\n", - "Batch Loss = 0.937760, Accuracy = 0.8613333106040955\n", - "Batch Loss = 0.894541, Accuracy = 0.8826666474342346\n", - "Batch Loss = 1.015310, Accuracy = 0.8119999766349792\n", - "Batch Loss = 0.792434, Accuracy = 0.9426666498184204\n", - "Batch Loss = 0.918421, Accuracy = 0.8801774978637695\n", - "Batch Loss = 0.856438, Accuracy = 0.9086666703224182\n", - "Batch Loss = 0.831793, Accuracy = 0.903333306312561\n", - "Batch Loss = 0.975429, Accuracy = 0.8486666679382324\n", - "Batch Loss = 0.891167, Accuracy = 0.8913333415985107\n", - "Batch Loss = 0.865273, Accuracy = 0.8905325531959534\n", + "Batch Loss = 2.664519, Accuracy = 0.20133332908153534\n", + "Batch Loss = 2.283946, Accuracy = 0.2933333218097687\n", + "Batch Loss = 2.120373, Accuracy = 0.33399999141693115\n", + "Batch Loss = 2.025987, Accuracy = 0.44066667556762695\n", + "Batch Loss = 1.993905, Accuracy = 0.39423078298568726\n", + "Batch Loss = 1.912767, Accuracy = 0.44066667556762695\n", + "Batch Loss = 1.801165, Accuracy = 0.527999997138977\n", + "Batch Loss = 1.695085, Accuracy = 0.6100000143051147\n", + "Batch Loss = 1.674571, Accuracy = 0.5960000157356262\n", + "Batch Loss = 1.673713, Accuracy = 0.6050295829772949\n", + "Batch Loss = 1.625562, Accuracy = 0.6520000100135803\n", + "Batch Loss = 1.561000, Accuracy = 0.6313333511352539\n", + "Batch Loss = 1.542400, Accuracy = 0.5699999928474426\n", + "Batch Loss = 1.491684, Accuracy = 0.6600000262260437\n", + "Batch Loss = 1.481272, Accuracy = 0.6878698468208313\n", + "Batch Loss = 1.433280, Accuracy = 0.6806666851043701\n", + "Batch Loss = 1.350184, Accuracy = 0.7333333492279053\n", + "Batch Loss = 1.346786, Accuracy = 0.7066666483879089\n", + "Batch Loss = 1.266333, Accuracy = 0.7360000014305115\n", + "Batch Loss = 1.265888, Accuracy = 0.7544378638267517\n", + "Batch Loss = 1.262501, Accuracy = 0.7233333587646484\n", + "Batch Loss = 1.204190, Accuracy = 0.7606666684150696\n", + "Batch Loss = 1.170833, Accuracy = 0.7393333315849304\n", + "Batch Loss = 1.144998, Accuracy = 0.7866666913032532\n", + "Batch Loss = 1.190787, Accuracy = 0.7692307829856873\n", + "Batch Loss = 1.197734, Accuracy = 0.7453333139419556\n", + "Batch Loss = 1.149767, Accuracy = 0.7826666831970215\n", + "Batch Loss = 1.120700, Accuracy = 0.7366666793823242\n", + "Batch Loss = 1.062440, Accuracy = 0.8299999833106995\n", + "Batch Loss = 1.097159, Accuracy = 0.8062130212783813\n", + "Batch Loss = 1.120843, Accuracy = 0.7873333096504211\n", + "Batch Loss = 1.070200, Accuracy = 0.8119999766349792\n", + "Batch Loss = 1.127003, Accuracy = 0.7440000176429749\n", + "Batch Loss = 0.972900, Accuracy = 0.8666666746139526\n", + "Batch Loss = 1.006018, Accuracy = 0.8387573957443237\n", + "Batch Loss = 1.033235, Accuracy = 0.8013333082199097\n", + "Batch Loss = 1.033802, Accuracy = 0.8106666803359985\n", + "Batch Loss = 1.034655, Accuracy = 0.7733333110809326\n", + "Batch Loss = 0.944360, Accuracy = 0.8606666922569275\n", + "Batch Loss = 0.969663, Accuracy = 0.8653846383094788\n", + "Batch Loss = 1.029325, Accuracy = 0.800000011920929\n", + "Batch Loss = 0.971321, Accuracy = 0.8339999914169312\n", + "Batch Loss = 1.032927, Accuracy = 0.7973333597183228\n", + "Batch Loss = 0.887862, Accuracy = 0.8886666893959045\n", + "Batch Loss = 0.880777, Accuracy = 0.8786982297897339\n", + "Batch Loss = 1.174494, Accuracy = 0.7699999809265137\n", + "Batch Loss = 0.892749, Accuracy = 0.8726666569709778\n", + "Batch Loss = 0.981043, Accuracy = 0.7786666750907898\n", + "Batch Loss = 1.076179, Accuracy = 0.859333336353302\n", + "Batch Loss = 1.022293, Accuracy = 0.8394970297813416\n", + "Batch Loss = 0.902665, Accuracy = 0.8693333268165588\n", + "Batch Loss = 0.907600, Accuracy = 0.8673333525657654\n", + "Batch Loss = 1.087252, Accuracy = 0.7433333396911621\n", + "Batch Loss = 1.061295, Accuracy = 0.8006666898727417\n", + "Batch Loss = 0.903294, Accuracy = 0.8727810382843018\n", + "Batch Loss = 0.884878, Accuracy = 0.8799999952316284\n", + "Batch Loss = 0.937158, Accuracy = 0.8533333539962769\n", + "Batch Loss = 0.969752, Accuracy = 0.7826666831970215\n", + "Batch Loss = 0.843873, Accuracy = 0.9039999842643738\n", + "Batch Loss = 0.929935, Accuracy = 0.8698225021362305\n", + "Batch Loss = 0.868710, Accuracy = 0.8866666555404663\n", + "Batch Loss = 0.847875, Accuracy = 0.8939999938011169\n", + "Batch Loss = 0.934327, Accuracy = 0.8220000267028809\n", + "Batch Loss = 0.918672, Accuracy = 0.878000020980835\n", + "Batch Loss = 0.924883, Accuracy = 0.8594674468040466\n", + "Batch Loss = 0.871912, Accuracy = 0.8740000128746033\n", + "Batch Loss = 0.811406, Accuracy = 0.9073333144187927\n", + "Batch Loss = 0.887143, Accuracy = 0.8293333053588867\n", + "Batch Loss = 0.763929, Accuracy = 0.9393333196640015\n", + "Batch Loss = 0.851500, Accuracy = 0.9001479148864746\n", "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", "\n" ] @@ -662,44 +662,7 @@ "output_type": "stream", "text": [ " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", - "[2019-11-20 12:43:33,783] ERROR in app: Exception on / [GET]\n", - "Traceback (most recent call last):\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/app.py\", line 1949, in full_dispatch_request\n", - " rv = self.dispatch_request()\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/app.py\", line 1935, in dispatch_request\n", - " return self.view_functions[rule.endpoint](**req.view_args)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask_restful/__init__.py\", line 458, in wrapper\n", - " resp = resource(*args, **kwargs)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/views.py\", line 89, in view\n", - " return self.dispatch_request(*args, **kwargs)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask_restful/__init__.py\", line 573, in dispatch_request\n", - " resp = meth(*args, **kwargs)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/api/flask.py\", line 136, in get\n", - " return wrapped.transform(request.get_json())\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\", line 74, in transform\n", - " data_container = self._transform_core(data_container, context)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\", line 250, in _transform_core\n", - " data_container = step.handle_transform(data_container, sub_context)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/base.py\", line 807, in handle_transform\n", - " out = self.transform(data_container.data_inputs)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/api/flask.py\", line 63, in transform\n", - " return jsonify(self.encode(data_inputs))\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/json/__init__.py\", line 370, in jsonify\n", - " dumps(data, indent=indent, separators=separators) + \"\\n\",\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/json/__init__.py\", line 211, in dumps\n", - " rv = _json.dumps(obj, **kwargs)\n", - " File \"/usr/lib/python3.6/json/__init__.py\", line 238, in dumps\n", - " **kw).encode(obj)\n", - " File \"/usr/lib/python3.6/json/encoder.py\", line 199, in encode\n", - " chunks = self.iterencode(o, _one_shot=True)\n", - " File \"/usr/lib/python3.6/json/encoder.py\", line 257, in iterencode\n", - " return _iterencode(o, 0)\n", - " File \"/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/flask/json/__init__.py\", line 100, in default\n", - " return _json.JSONEncoder.default(self, o)\n", - " File \"/usr/lib/python3.6/json/encoder.py\", line 180, in default\n", - " o.__class__.__name__)\n", - "TypeError: Object of type 'ndarray' is not JSON serializable\n", - "127.0.0.1 - - [20/Nov/2019 12:43:33] \"\u001b[1m\u001b[35mGET / HTTP/1.1\u001b[0m\" 500 -\n" + "127.0.0.1 - - [20/Nov/2019 13:14:45] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" ] } ], @@ -716,9 +679,9 @@ ], "metadata": { "kernelspec": { - "display_name": "demo", + "display_name": "venv", "language": "python", - "name": "demo" + "name": "venv" }, "language_info": { "codemirror_mode": { diff --git a/2_call_rest_api_and_eval.ipynb b/2_call_rest_api_and_eval.ipynb index f876121..290ff1d 100644 --- a/2_call_rest_api_and_eval.ipynb +++ b/2_call_rest_api_and_eval.ipynb @@ -124,24 +124,16 @@ "metadata": {}, "outputs": [ { - "ename": "HTTPError", - "evalue": "HTTP Error 500: INTERNAL SERVER ERROR", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mHTTPError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mAPICaller\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"http://127.0.0.1:5000/\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m ])\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0my_pred\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mX_test\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my_pred\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0mcontext\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mExecutionContext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcreate_from_root\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mExecutionMode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTRANSFORM\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcache_folder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 73\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 74\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_transform_core\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 75\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/pipeline.py\u001b[0m in \u001b[0;36m_transform_core\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 248\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mstep_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstep\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msteps_left_to_do\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 249\u001b[0m \u001b[0msub_context\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 250\u001b[0;31m \u001b[0mdata_container\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstep\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandle_transform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msub_context\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 251\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 252\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/neuraxle/base.py\u001b[0m in \u001b[0;36mhandle_transform\u001b[0;34m(self, data_container, context)\u001b[0m\n\u001b[1;32m 805\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;32mreturn\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mtransformed\u001b[0m \u001b[0mdata\u001b[0m \u001b[0mcontainer\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 806\u001b[0m \"\"\"\n\u001b[0;32m--> 807\u001b[0;31m \u001b[0mout\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata_inputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 808\u001b[0m \u001b[0mdata_container\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mset_data_inputs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 809\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m\u001b[0m in \u001b[0;36mtransform\u001b[0;34m(self, data_inputs)\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 13\u001b[0m )\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0murllib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0murlopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 15\u001b[0m \u001b[0mtest_predictions\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresponse\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 16\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtest_predictions\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'predictions'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36murlopen\u001b[0;34m(url, data, timeout, cafile, capath, cadefault, context)\u001b[0m\n\u001b[1;32m 221\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 222\u001b[0m \u001b[0mopener\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_opener\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 223\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mopener\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtimeout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 224\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 225\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0minstall_opener\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mopener\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mopen\u001b[0;34m(self, fullurl, data, timeout)\u001b[0m\n\u001b[1;32m 530\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mprocessor\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprocess_response\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprotocol\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 531\u001b[0m \u001b[0mmeth\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprocessor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmeth_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 532\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmeth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 533\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 534\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mhttp_response\u001b[0;34m(self, request, response)\u001b[0m\n\u001b[1;32m 640\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m200\u001b[0m \u001b[0;34m<=\u001b[0m \u001b[0mcode\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m300\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 641\u001b[0m response = self.parent.error(\n\u001b[0;32m--> 642\u001b[0;31m 'http', request, response, code, msg, hdrs)\n\u001b[0m\u001b[1;32m 643\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 644\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresponse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36merror\u001b[0;34m(self, proto, *args)\u001b[0m\n\u001b[1;32m 568\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhttp_err\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 569\u001b[0m \u001b[0margs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mdict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'default'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'http_error_default'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0morig_args\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 570\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_call_chain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 571\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 572\u001b[0m \u001b[0;31m# XXX probably also want an abstract factory that knows when it makes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36m_call_chain\u001b[0;34m(self, chain, kind, meth_name, *args)\u001b[0m\n\u001b[1;32m 502\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mhandler\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mhandlers\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 503\u001b[0m \u001b[0mfunc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhandler\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmeth_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 504\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 505\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 506\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m/usr/lib/python3.6/urllib/request.py\u001b[0m in \u001b[0;36mhttp_error_default\u001b[0;34m(self, req, fp, code, msg, hdrs)\u001b[0m\n\u001b[1;32m 648\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mHTTPDefaultErrorHandler\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mBaseHandler\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 649\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mhttp_error_default\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreq\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhdrs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 650\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mHTTPError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfull_url\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhdrs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 651\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 652\u001b[0m \u001b[0;32mclass\u001b[0m \u001b[0mHTTPRedirectHandler\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mBaseHandler\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mHTTPError\u001b[0m: HTTP Error 500: INTERNAL SERVER ERROR" + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 2.07112408 0.09142101 0.45754826 4.80191326 8.39756203 -1.65806925]\n", + " [ 1.95728445 -0.02661133 0.57281792 4.96968269 8.64764977 -1.52020741]\n", + " [ 1.99783969 0.04387677 0.59818095 5.06699944 8.73121643 -1.54111767]\n", + " ...\n", + " [ 2.27837229 6.34097433 1.04619193 -1.33004761 -1.27574241 -1.44660664]\n", + " [ 2.44670582 6.67506218 0.52507615 -2.08214211 -0.20692641 -0.51537234]\n", + " [ 3.03775263 5.70212603 0.31813109 -1.95930123 -0.19588709 -0.17824771]]\n" ] } ], @@ -162,9 +154,59 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n", + "findfont: Font family ['Bitstream Vera Sans'] not found. Falling back to DejaVu Sans.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Precision: 85.16945732166661%\n", + "Recall: 84.49270444519851%\n", + "f1_score: 84.45214093239125%\n", + "\n", + "Confusion Matrix:\n", + "[[401 82 13 0 0 0]\n", + " [ 24 432 14 0 0 1]\n", + " [ 68 40 312 0 0 0]\n", + " [ 4 26 0 356 105 0]\n", + " [ 4 9 0 67 452 0]\n", + " [ 0 0 0 0 0 537]]\n", + "\n", + "Confusion matrix (normalised to % of total test data):\n", + "[[13.607058 2.7824907 0.44112659 0. 0. 0. ]\n", + " [ 0.8143875 14.658976 0.4750594 0. 0. 0.03393281]\n", + " [ 2.3074312 1.3573124 10.587037 0. 0. 0. ]\n", + " [ 0.13573125 0.88225317 0. 12.080082 3.5629456 0. ]\n", + " [ 0.13573125 0.3053953 0. 2.2734985 15.337631 0. ]\n", + " [ 0. 0. 0. 0. 0. 18.22192 ]]\n", + "Note: training and testing data is not equally distributed amongst classes, \n", + "so it is normal that more than a 6th of the data is correctly classifier in the last category.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ "# (Inline plots: )\n", "%matplotlib inline\n", @@ -220,9 +262,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "Pipeline\n", + "(\n", + "\tPipeline(\n", + "\tname=Pipeline,\n", + "\thyperparameters=HyperparameterSamples()\n", + ")(\n", + "\t\t[('APICaller',\n", + " APICaller(\n", + "\tname=APICaller,\n", + "\thyperparameters=HyperparameterSamples()\n", + "))]\t\n", + ")\n", + ")" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "p.teardown()" ] @@ -230,9 +295,9 @@ ], "metadata": { "kernelspec": { - "display_name": "demo", + "display_name": "venv", "language": "python", - "name": "demo" + "name": "venv" }, "language_info": { "codemirror_mode": { From 6856be6d8702483848b0e9d5f9588e7cc1ea9c45 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Wed, 20 Nov 2019 13:34:50 -0500 Subject: [PATCH 25/30] Add tensorflow-gpu in requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index e74a09a..398f195 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ tensorflow==1.15 +tensorflow-gpu==1.15 conv==0.2 git+git://github.com/alexbrillant/Neuraxle@one-hot-encoder-step#egg=Neuraxle From d6aa64162aa4ee7dba2a9d81ab00ebd56b389b15 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Mon, 6 Jan 2020 19:13:18 -0500 Subject: [PATCH 26/30] Clean Example Using Neuraxle-Tensorflow --- 1_train_and_save_LSTM.ipynb | 487 +++++++++------------ model.py | 149 +++++++ savers/__init__.py | 0 savers/tensorflow1_step_saver.py | 85 ---- steps/lstm_rnn_tensorflow_model.py | 67 --- steps/lstm_rnn_tensorflow_model_wrapper.py | 208 --------- steps/transform_expected_output_wrapper.py | 32 -- 7 files changed, 361 insertions(+), 667 deletions(-) create mode 100644 model.py delete mode 100644 savers/__init__.py delete mode 100644 savers/tensorflow1_step_saver.py delete mode 100644 steps/lstm_rnn_tensorflow_model.py delete mode 100644 steps/lstm_rnn_tensorflow_model_wrapper.py delete mode 100644 steps/transform_expected_output_wrapper.py diff --git a/1_train_and_save_LSTM.ipynb b/1_train_and_save_LSTM.ipynb index e5007ab..99228ce 100644 --- a/1_train_and_save_LSTM.ipynb +++ b/1_train_and_save_LSTM.ipynb @@ -20,19 +20,18 @@ "\n", "from neuraxle.api.flask import FlaskRestApiWrapper\n", "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", + "from neuraxle.data_container import DataContainer\n", "from neuraxle.hyperparams.space import HyperparameterSamples\n", "from neuraxle.steps.numpy import OneHotEncoder\n", "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", + "from neuraxle_tensorflow.tensorflow_v1 import TensorflowV1ModelStep\n", "\n", - "# TODO: move in a package neuraxle-tensorflow \n", - "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", + "\n", "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, load_X, load_y, \\\n", - " TRAIN_FILE_NAME, TEST_FILE_NAME\n", - "from savers.tensorflow1_step_saver import TensorflowV1StepSaver\n", - "from pipeline import HumanActivityRecognitionPipeline, BATCH_SIZE\n" + " TRAIN_FILE_NAME, TEST_FILE_NAME" ] }, { @@ -52,10 +51,10 @@ "output_type": "stream", "text": [ "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py README.md\t venv\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t requirements.txt\n", - "cache\t\t\t\tpipeline.py\t savers\n", - "data\t\t\t\t__pycache__\t steps\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py steps\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__ venv\n", + "cache\t\t\t\tmodel.py\t README.md\n", + "data\t\t\t\tneuraxle_tensorflow requirements.txt\n", "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", @@ -70,10 +69,10 @@ " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", " __MACOSX\t 'UCI HAR Dataset'\n", "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py README.md\t venv\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t requirements.txt\n", - "cache\t\t\t\tpipeline.py\t savers\n", - "data\t\t\t\t__pycache__\t steps\n", + "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py steps\n", + "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__ venv\n", + "cache\t\t\t\tmodel.py\t README.md\n", + "data\t\t\t\tneuraxle_tensorflow requirements.txt\n", "\n", "Dataset is now located at: data/UCI HAR Dataset/\n" ] @@ -145,7 +144,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# LSTM RNN Model Forward" + "# Create LSTM RNN Tensorflow Graph" ] }, { @@ -154,7 +153,7 @@ "metadata": {}, "outputs": [], "source": [ - "def tf_model_forward(pred_name, name_x, name_y, hyperparams):\n", + "def create_graph(step: TensorflowV1ModelStep):\n", " # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters.\n", " # Moreover, two LSTM cells are stacked which adds deepness to the neural network.\n", " # Note, some code of this notebook is inspired from an slightly different\n", @@ -164,34 +163,35 @@ " # input shape: (batch_size, n_steps, n_input)\n", "\n", " # Graph input/output\n", - " x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_inputs']], name=name_x)\n", - " y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name=name_y)\n", + " data_inputs = tf.placeholder(tf.float32, [None, step.hyperparams['n_steps'], step.hyperparams['n_inputs']],\n", + " name='data_inputs')\n", + " expected_outputs = tf.placeholder(tf.float32, [None, step.hyperparams['n_classes']], name='expected_outputs')\n", "\n", " # Graph weights\n", " weights = {\n", " 'hidden': tf.Variable(\n", - " tf.random_normal([hyperparams['n_inputs'], hyperparams['n_hidden']])\n", + " tf.random_normal([step.hyperparams['n_inputs'], step.hyperparams['n_hidden']])\n", " ), # Hidden layer weights\n", " 'out': tf.Variable(\n", - " tf.random_normal([hyperparams['n_hidden'], hyperparams['n_classes']], mean=1.0)\n", + " tf.random_normal([step.hyperparams['n_hidden'], step.hyperparams['n_classes']], mean=1.0)\n", " )\n", " }\n", "\n", " biases = {\n", " 'hidden': tf.Variable(\n", - " tf.random_normal([hyperparams['n_hidden']])\n", + " tf.random_normal([step.hyperparams['n_hidden']])\n", " ),\n", " 'out': tf.Variable(\n", - " tf.random_normal([hyperparams['n_classes']])\n", + " tf.random_normal([step.hyperparams['n_classes']])\n", " )\n", " }\n", "\n", " data_inputs = tf.transpose(\n", - " x,\n", + " data_inputs,\n", " [1, 0, 2]) # permute n_steps and batch_size\n", "\n", " # Reshape to prepare input to hidden activation\n", - " data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_inputs']])\n", + " data_inputs = tf.reshape(data_inputs, [-1, step.hyperparams['n_inputs']])\n", " # new shape: (n_steps*batch_size, n_input)\n", "\n", " # ReLU activation, thanks to Yu Zhao for adding this improvement here:\n", @@ -200,12 +200,12 @@ " )\n", "\n", " # Split data because rnn cell needs a list of inputs for the RNN inner loop\n", - " _X = tf.split(_X, hyperparams['n_steps'], 0)\n", + " _X = tf.split(_X, step.hyperparams['n_steps'], 0)\n", " # new shape: n_steps * (batch_size, n_hidden)\n", "\n", " # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow\n", - " lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", - " lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", + " lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(step.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", + " lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(step.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True)\n", " lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True)\n", "\n", " # Get LSTM cell output\n", @@ -216,15 +216,14 @@ " lstm_last_output = outputs[-1]\n", "\n", " # Linear activation\n", - " pred = tf.matmul(lstm_last_output, weights['out']) + biases['out']\n", - " return tf.identity(pred, name=pred_name)" + " return tf.matmul(lstm_last_output, weights['out']) + biases['out']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Neuraxle RNN TensorFlow Model Step" + "# Create Optimizer" ] }, { @@ -233,175 +232,81 @@ "metadata": {}, "outputs": [], "source": [ - "LSTM_RNN_VARIABLE_SCOPE = \"lstm_rnn\"\n", - "X_NAME = 'x'\n", - "Y_NAME = 'y'\n", - "PRED_NAME = 'pred'\n", - "\n", - "N_HIDDEN = 32\n", - "N_STEPS = 128\n", - "N_INPUTS = 9\n", - "LAMBDA_LOSS_AMOUNT = 0.0015\n", - "LEARNING_RATE = 0.0025\n", - "N_CLASSES = 6\n", - "BATCH_SIZE = 1500\n", - "\n", - "class ClassificationRNNTensorFlowModel(BaseStep):\n", - " HYPERPARAMS = HyperparameterSamples({\n", - " 'n_steps': N_STEPS, # 128 timesteps per series\n", - " 'n_inputs': N_INPUTS, # 9 input parameters per timestep\n", - " 'n_hidden': N_HIDDEN, # Hidden layer num of features\n", - " 'n_classes': N_CLASSES, # Total classes (should go up, or should go down)\n", - " 'learning_rate': LEARNING_RATE,\n", - " 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT,\n", - " 'batch_size': BATCH_SIZE\n", - " })\n", - "\n", - " def __init__(\n", - " self\n", - " ):\n", - " BaseStep.__init__(\n", - " self,\n", - " hyperparams=ClassificationRNNTensorFlowModel.HYPERPARAMS,\n", - " savers=[TensorflowV1StepSaver()]\n", + "def create_optimizer(step: TensorflowV1ModelStep):\n", + " return tf.train.AdamOptimizer(learning_rate=step.hyperparams['learning_rate'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create Loss" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def create_loss(step: TensorflowV1ModelStep):\n", + " # Loss, optimizer and evaluation\n", + " # L2 loss prevents this overkill neural network to overfit the data\n", + " l2 = step.hyperparams['lambda_loss_amount'] * sum(tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables())\n", + "\n", + " # Softmax loss\n", + " return tf.reduce_mean(\n", + " tf.nn.softmax_cross_entropy_with_logits(\n", + " labels=step['expected_outputs'],\n", + " logits=step['output']\n", " )\n", - "\n", - " self.graph = None\n", - " self.sess = None\n", - " self.l2 = None\n", - " self.cost = None\n", - " self.optimizer = None\n", - " self.correct_pred = None\n", - " self.accuracy = None\n", - " self.test_losses = None\n", - " self.test_accuracies = None\n", - " self.train_losses = None\n", - " self.train_accuracies = None\n", - "\n", - " def strip(self):\n", - " self.sess = None\n", - " self.graph = None\n", - " self.l2 = None\n", - " self.cost = None\n", - " self.optimizer = None\n", - " self.correct_pred = None\n", - " self.accuracy = None\n", - "\n", + " ) + l2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create TensorflowV1ModelStep " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "class ClassificationRNNTensorFlowModel(TensorflowV1ModelStep):\n", " def setup(self) -> BaseStep:\n", - " if self.is_initialized:\n", - " return self\n", - "\n", - " self.create_graph()\n", - "\n", - " with self.graph.as_default():\n", - " # Launch the graph\n", - " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", + " TensorflowV1ModelStep.setup(self)\n", "\n", - " pred = tf_model_forward(PRED_NAME, X_NAME, Y_NAME, self.hyperparams)\n", - "\n", - " # Loss, optimizer and evaluation\n", - " # L2 loss prevents this overkill neural network to overfit the data\n", - "\n", - " l2 = self.hyperparams['lambda_loss_amount'] * sum(\n", - " tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables()\n", - " )\n", - "\n", - " # Softmax loss\n", - " self.cost = tf.reduce_mean(\n", - " tf.nn.softmax_cross_entropy_with_logits(\n", - " labels=self.get_y_placeholder(),\n", - " logits=pred\n", - " )\n", - " ) + l2\n", - "\n", - " # Adam Optimizer\n", - " self.optimizer = tf.train.AdamOptimizer(\n", - " learning_rate=self.hyperparams['learning_rate']\n", - " ).minimize(self.cost)\n", - "\n", - " self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(Y_NAME), 1))\n", - " self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32))\n", - "\n", - " # To keep track of training's performance\n", - " self.test_losses = []\n", - " self.test_accuracies = []\n", - " self.train_losses = []\n", - " self.train_accuracies = []\n", - "\n", - " self.create_session()\n", - "\n", - " self.is_initialized = True\n", + " self.losses = []\n", + " self.accuracies = []\n", "\n", " return self\n", "\n", - " def create_graph(self):\n", - " self.graph = tf.Graph()\n", - "\n", - " def create_session(self):\n", - " self.sess = tf.Session(config=tf.ConfigProto(log_device_placement=True), graph=self.graph)\n", - " init = tf.global_variables_initializer()\n", - " self.sess.run(init)\n", - "\n", - " def get_tensor_by_name(self, name):\n", - " return self.graph.get_tensor_by_name(\"{0}/{1}:0\".format(LSTM_RNN_VARIABLE_SCOPE, name))\n", - "\n", - " def get_graph(self):\n", - " return self.graph\n", + " def _will_process(self, data_container: DataContainer, context: ExecutionContext) -> ('BaseStep', DataContainer):\n", + " if not isinstance(data_container.data_inputs, np.ndarray):\n", + " data_container.data_inputs = np.array(data_container.data_inputs)\n", "\n", - " def get_session(self):\n", - " return self.sess\n", + " if data_container.expected_outputs is not None:\n", + " if not isinstance(data_container.expected_outputs, np.ndarray):\n", + " data_container.expected_outputs = np.array(data_container.expected_outputs)\n", "\n", - " def get_x_placeholder(self):\n", - " return self.get_tensor_by_name(X_NAME)\n", + " if data_container.expected_outputs.shape != (len(data_container.data_inputs), self.hyperparams['n_classes']):\n", + " data_container.expected_outputs = np.reshape(data_container.expected_outputs, (len(data_container.data_inputs), self.hyperparams['n_classes']))\n", "\n", - " def get_y_placeholder(self):\n", - " return self.get_tensor_by_name(Y_NAME)\n", + " return data_container, context\n", "\n", - " def teardown(self):\n", - " if self.sess is not None:\n", - " self.sess.close()\n", - " self.is_initialized = False\n", + " def _did_fit_transform(self, data_container: DataContainer, context: ExecutionContext) -> DataContainer:\n", + " accuracy = np.mean(np.argmax(data_container.data_inputs, axis=1) == np.argmax(data_container.expected_outputs, axis=1))\n", "\n", - " def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep':\n", - " if not isinstance(data_inputs, np.ndarray):\n", - " data_inputs = np.array(data_inputs)\n", + " self.accuracies.append(accuracy)\n", + " self.losses.append(self.loss)\n", "\n", - " if not isinstance(expected_outputs, np.ndarray):\n", - " expected_outputs = np.array(expected_outputs)\n", + " print(\"Batch Loss = \" + \"{:.6f}\".format(self.losses[-1]) + \", Accuracy = {}\".format(self.accuracies[-1]))\n", "\n", - " if expected_outputs.shape != (len(data_inputs), self.hyperparams['n_classes']):\n", - " expected_outputs = np.reshape(expected_outputs, (len(data_inputs), self.hyperparams['n_classes']))\n", - "\n", - " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", - " _, loss, acc = self.sess.run(\n", - " [self.optimizer, self.cost, self.accuracy],\n", - " feed_dict={\n", - " self.get_x_placeholder(): data_inputs,\n", - " self.get_y_placeholder(): expected_outputs\n", - " }\n", - " )\n", - "\n", - " self.train_losses.append(loss)\n", - " self.train_accuracies.append(acc)\n", - "\n", - " print(\"Batch Loss = \" + \"{:.6f}\".format(loss) + \", Accuracy = {}\".format(acc))\n", - "\n", - " self.is_invalidated = True\n", - "\n", - " return self\n", - "\n", - " def transform(self, data_inputs):\n", - " if not isinstance(data_inputs, np.ndarray):\n", - " data_inputs = np.array(data_inputs)\n", - "\n", - " with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE):\n", - " outputs = self.sess.run(\n", - " [self.get_tensor_by_name(PRED_NAME)],\n", - " feed_dict={\n", - " self.get_x_placeholder(): data_inputs\n", - " }\n", - " )[0]\n", - " return outputs" + " return data_container" ] }, { @@ -413,17 +318,40 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ + "N_HIDDEN = 32\n", + "N_STEPS = 128\n", + "N_INPUTS = 9\n", + "LAMBDA_LOSS_AMOUNT = 0.0015\n", + "LEARNING_RATE = 0.0025\n", + "N_CLASSES = 6\n", + "BATCH_SIZE = 1500\n", + "\n", + "\n", "class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline):\n", " def __init__(self):\n", " MiniBatchSequentialPipeline.__init__(self, [\n", " OutputTransformerWrapper(OneHotEncoder(nb_columns=N_CLASSES, name='one_hot_encoded_label')),\n", - " ClassificationRNNTensorFlowModel(),\n", + " ClassificationRNNTensorFlowModel(\n", + " create_graph=create_graph,\n", + " create_loss=create_loss,\n", + " create_optimizer=create_optimizer\n", + " ).set_hyperparams(\n", + " HyperparameterSamples({\n", + " 'n_steps': N_STEPS,\n", + " 'n_inputs': N_INPUTS, \n", + " 'n_hidden': N_HIDDEN,\n", + " 'n_classes': N_CLASSES,\n", + " 'learning_rate': LEARNING_RATE,\n", + " 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT,\n", + " 'batch_size': BATCH_SIZE\n", + " })\n", + " ),\n", " Joiner(batch_size=BATCH_SIZE)\n", - " ])\n" + " ])" ] }, { @@ -435,7 +363,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": { "scrolled": false }, @@ -444,6 +372,18 @@ "name": "stdout", "output_type": "stream", "text": [ + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:73: The name tf.variable_scope is deprecated. Please use tf.compat.v1.variable_scope instead.\n", + "\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:73: The name tf.AUTO_REUSE is deprecated. Please use tf.compat.v1.AUTO_REUSE instead.\n", + "\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:74: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.\n", + "\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:74: The name tf.ConfigProto is deprecated. Please use tf.compat.v1.ConfigProto instead.\n", + "\n", + "Device mapping:\n", + "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", + "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", + "\n", "WARNING:tensorflow:\n", "The TensorFlow contrib module will not be included in TensorFlow 2.0.\n", "For more information, please see:\n", @@ -452,13 +392,13 @@ " * https://github.com/tensorflow/io (for I/O related ops)\n", "If you depend on functionality not listed there, please file an issue.\n", "\n", - "WARNING:tensorflow:From :51: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :52: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :53: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :54: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "This class is equivalent as tf.keras.layers.StackedRNNCells, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :56: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :57: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", @@ -467,7 +407,7 @@ "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Call initializer instance with the dtype argument instead of passing it to the constructor\n", - "WARNING:tensorflow:From :78: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :10: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "\n", "Future major versions of TensorFlow will allow gradients to flow\n", @@ -475,81 +415,79 @@ "\n", "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n", "\n", - "Device mapping:\n", - "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", - "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:80: The name tf.global_variables_initializer is deprecated. Please use tf.compat.v1.global_variables_initializer instead.\n", "\n", - "Batch Loss = 2.664519, Accuracy = 0.20133332908153534\n", - "Batch Loss = 2.283946, Accuracy = 0.2933333218097687\n", - "Batch Loss = 2.120373, Accuracy = 0.33399999141693115\n", - "Batch Loss = 2.025987, Accuracy = 0.44066667556762695\n", - "Batch Loss = 1.993905, Accuracy = 0.39423078298568726\n", - "Batch Loss = 1.912767, Accuracy = 0.44066667556762695\n", - "Batch Loss = 1.801165, Accuracy = 0.527999997138977\n", - "Batch Loss = 1.695085, Accuracy = 0.6100000143051147\n", - "Batch Loss = 1.674571, Accuracy = 0.5960000157356262\n", - "Batch Loss = 1.673713, Accuracy = 0.6050295829772949\n", - "Batch Loss = 1.625562, Accuracy = 0.6520000100135803\n", - "Batch Loss = 1.561000, Accuracy = 0.6313333511352539\n", - "Batch Loss = 1.542400, Accuracy = 0.5699999928474426\n", - "Batch Loss = 1.491684, Accuracy = 0.6600000262260437\n", - "Batch Loss = 1.481272, Accuracy = 0.6878698468208313\n", - "Batch Loss = 1.433280, Accuracy = 0.6806666851043701\n", - "Batch Loss = 1.350184, Accuracy = 0.7333333492279053\n", - "Batch Loss = 1.346786, Accuracy = 0.7066666483879089\n", - "Batch Loss = 1.266333, Accuracy = 0.7360000014305115\n", - "Batch Loss = 1.265888, Accuracy = 0.7544378638267517\n", - "Batch Loss = 1.262501, Accuracy = 0.7233333587646484\n", - "Batch Loss = 1.204190, Accuracy = 0.7606666684150696\n", - "Batch Loss = 1.170833, Accuracy = 0.7393333315849304\n", - "Batch Loss = 1.144998, Accuracy = 0.7866666913032532\n", - "Batch Loss = 1.190787, Accuracy = 0.7692307829856873\n", - "Batch Loss = 1.197734, Accuracy = 0.7453333139419556\n", - "Batch Loss = 1.149767, Accuracy = 0.7826666831970215\n", - "Batch Loss = 1.120700, Accuracy = 0.7366666793823242\n", - "Batch Loss = 1.062440, Accuracy = 0.8299999833106995\n", - "Batch Loss = 1.097159, Accuracy = 0.8062130212783813\n", - "Batch Loss = 1.120843, Accuracy = 0.7873333096504211\n", - "Batch Loss = 1.070200, Accuracy = 0.8119999766349792\n", - "Batch Loss = 1.127003, Accuracy = 0.7440000176429749\n", - "Batch Loss = 0.972900, Accuracy = 0.8666666746139526\n", - "Batch Loss = 1.006018, Accuracy = 0.8387573957443237\n", - "Batch Loss = 1.033235, Accuracy = 0.8013333082199097\n", - "Batch Loss = 1.033802, Accuracy = 0.8106666803359985\n", - "Batch Loss = 1.034655, Accuracy = 0.7733333110809326\n", - "Batch Loss = 0.944360, Accuracy = 0.8606666922569275\n", - "Batch Loss = 0.969663, Accuracy = 0.8653846383094788\n", - "Batch Loss = 1.029325, Accuracy = 0.800000011920929\n", - "Batch Loss = 0.971321, Accuracy = 0.8339999914169312\n", - "Batch Loss = 1.032927, Accuracy = 0.7973333597183228\n", - "Batch Loss = 0.887862, Accuracy = 0.8886666893959045\n", - "Batch Loss = 0.880777, Accuracy = 0.8786982297897339\n", - "Batch Loss = 1.174494, Accuracy = 0.7699999809265137\n", - "Batch Loss = 0.892749, Accuracy = 0.8726666569709778\n", - "Batch Loss = 0.981043, Accuracy = 0.7786666750907898\n", - "Batch Loss = 1.076179, Accuracy = 0.859333336353302\n", - "Batch Loss = 1.022293, Accuracy = 0.8394970297813416\n", - "Batch Loss = 0.902665, Accuracy = 0.8693333268165588\n", - "Batch Loss = 0.907600, Accuracy = 0.8673333525657654\n", - "Batch Loss = 1.087252, Accuracy = 0.7433333396911621\n", - "Batch Loss = 1.061295, Accuracy = 0.8006666898727417\n", - "Batch Loss = 0.903294, Accuracy = 0.8727810382843018\n", - "Batch Loss = 0.884878, Accuracy = 0.8799999952316284\n", - "Batch Loss = 0.937158, Accuracy = 0.8533333539962769\n", - "Batch Loss = 0.969752, Accuracy = 0.7826666831970215\n", - "Batch Loss = 0.843873, Accuracy = 0.9039999842643738\n", - "Batch Loss = 0.929935, Accuracy = 0.8698225021362305\n", - "Batch Loss = 0.868710, Accuracy = 0.8866666555404663\n", - "Batch Loss = 0.847875, Accuracy = 0.8939999938011169\n", - "Batch Loss = 0.934327, Accuracy = 0.8220000267028809\n", - "Batch Loss = 0.918672, Accuracy = 0.878000020980835\n", - "Batch Loss = 0.924883, Accuracy = 0.8594674468040466\n", - "Batch Loss = 0.871912, Accuracy = 0.8740000128746033\n", - "Batch Loss = 0.811406, Accuracy = 0.9073333144187927\n", - "Batch Loss = 0.887143, Accuracy = 0.8293333053588867\n", - "Batch Loss = 0.763929, Accuracy = 0.9393333196640015\n", - "Batch Loss = 0.851500, Accuracy = 0.9001479148864746\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/savers/tensorflow1_step_saver.py:27: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "Batch Loss = 3.521425, Accuracy = 0.18533333333333332\n", + "Batch Loss = 2.740264, Accuracy = 0.258\n", + "Batch Loss = 2.427801, Accuracy = 0.436\n", + "Batch Loss = 2.392420, Accuracy = 0.38333333333333336\n", + "Batch Loss = 2.336757, Accuracy = 0.3727810650887574\n", + "Batch Loss = 2.210095, Accuracy = 0.406\n", + "Batch Loss = 2.081177, Accuracy = 0.4806666666666667\n", + "Batch Loss = 1.928595, Accuracy = 0.5286666666666666\n", + "Batch Loss = 1.908575, Accuracy = 0.526\n", + "Batch Loss = 1.926798, Accuracy = 0.5968934911242604\n", + "Batch Loss = 1.857221, Accuracy = 0.532\n", + "Batch Loss = 1.840149, Accuracy = 0.5313333333333333\n", + "Batch Loss = 1.801555, Accuracy = 0.5926666666666667\n", + "Batch Loss = 1.740426, Accuracy = 0.5833333333333334\n", + "Batch Loss = 1.795835, Accuracy = 0.5769230769230769\n", + "Batch Loss = 1.758576, Accuracy = 0.5346666666666666\n", + "Batch Loss = 1.689172, Accuracy = 0.582\n", + "Batch Loss = 1.681654, Accuracy = 0.5986666666666667\n", + "Batch Loss = 1.631148, Accuracy = 0.608\n", + "Batch Loss = 1.640699, Accuracy = 0.6161242603550295\n", + "Batch Loss = 1.642633, Accuracy = 0.556\n", + "Batch Loss = 1.651147, Accuracy = 0.576\n", + "Batch Loss = 1.580308, Accuracy = 0.6373333333333333\n", + "Batch Loss = 1.506242, Accuracy = 0.626\n", + "Batch Loss = 1.523232, Accuracy = 0.639792899408284\n", + "Batch Loss = 1.518811, Accuracy = 0.6606666666666666\n", + "Batch Loss = 1.418894, Accuracy = 0.6646666666666666\n", + "Batch Loss = 1.394570, Accuracy = 0.7066666666666667\n", + "Batch Loss = 1.344470, Accuracy = 0.6933333333333334\n", + "Batch Loss = 1.326845, Accuracy = 0.7078402366863905\n", + "Batch Loss = 1.364004, Accuracy = 0.6373333333333333\n", + "Batch Loss = 1.334362, Accuracy = 0.6793333333333333\n", + "Batch Loss = 1.253492, Accuracy = 0.7186666666666667\n", + "Batch Loss = 1.297910, Accuracy = 0.6966666666666667\n", + "Batch Loss = 1.258239, Accuracy = 0.716715976331361\n", + "Batch Loss = 1.304081, Accuracy = 0.6733333333333333\n", + "Batch Loss = 1.259897, Accuracy = 0.7026666666666667\n", + "Batch Loss = 1.208963, Accuracy = 0.7246666666666667\n", + "Batch Loss = 1.200220, Accuracy = 0.7346666666666667\n", + "Batch Loss = 1.236549, Accuracy = 0.735207100591716\n", + "Batch Loss = 1.264173, Accuracy = 0.708\n", + "Batch Loss = 1.256850, Accuracy = 0.7066666666666667\n", + "Batch Loss = 1.171723, Accuracy = 0.7506666666666667\n", + "Batch Loss = 1.232849, Accuracy = 0.7553333333333333\n", + "Batch Loss = 1.173469, Accuracy = 0.742603550295858\n", + "Batch Loss = 1.241831, Accuracy = 0.712\n", + "Batch Loss = 1.205076, Accuracy = 0.728\n", + "Batch Loss = 1.148691, Accuracy = 0.7706666666666667\n", + "Batch Loss = 1.141159, Accuracy = 0.7606666666666667\n", + "Batch Loss = 1.148656, Accuracy = 0.7618343195266272\n", + "Batch Loss = 1.189209, Accuracy = 0.758\n", + "Batch Loss = 1.154536, Accuracy = 0.7686666666666667\n", + "Batch Loss = 1.111722, Accuracy = 0.776\n", + "Batch Loss = 1.168701, Accuracy = 0.8013333333333333\n", + "Batch Loss = 1.105878, Accuracy = 0.7455621301775148\n", + "Batch Loss = 1.244627, Accuracy = 0.7733333333333333\n", + "Batch Loss = 1.159975, Accuracy = 0.7206666666666667\n", + "Batch Loss = 1.115686, Accuracy = 0.7486666666666667\n", + "Batch Loss = 1.177998, Accuracy = 0.7826666666666666\n", + "Batch Loss = 1.092668, Accuracy = 0.8084319526627219\n", + "Batch Loss = 1.123520, Accuracy = 0.79\n", + "Batch Loss = 1.089692, Accuracy = 0.7773333333333333\n", + "Batch Loss = 1.080361, Accuracy = 0.7906666666666666\n", + "Batch Loss = 1.055221, Accuracy = 0.8406666666666667\n", + "Batch Loss = 1.039549, Accuracy = 0.8054733727810651\n", + "Batch Loss = 1.094885, Accuracy = 0.8086666666666666\n", + "Batch Loss = 1.111347, Accuracy = 0.7833333333333333\n", + "Batch Loss = 1.034543, Accuracy = 0.7993333333333333\n", + "Batch Loss = 0.981616, Accuracy = 0.8593333333333333\n", + "Batch Loss = 1.002564, Accuracy = 0.8306213017751479\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:199: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", "\n" ] }, @@ -589,7 +527,7 @@ ")" ] }, - "execution_count": 7, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -604,7 +542,7 @@ "for _ in range(no_iter):\n", " pipeline, outputs = pipeline.fit_transform(X_train, y_train)\n", "\n", - "pipeline.save(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", + "pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER))\n", "\n", "pipeline.teardown()" ] @@ -618,7 +556,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -636,7 +574,7 @@ "source": [ "pipeline = HumanActivityRecognitionPipeline()\n", "\n", - "pipeline = pipeline.load(ExecutionContext.create_from_root(pipeline, ExecutionMode.FIT, DEFAULT_CACHE_FOLDER))\n", + "pipeline = pipeline.load(ExecutionContext(DEFAULT_CACHE_FOLDER))\n", "\n", "# pipeline, outputs = pipeline.fit_transform(X_train, y_train) # we could train further more here for instance." ] @@ -661,8 +599,7 @@ "name": "stderr", "output_type": "stream", "text": [ - " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n", - "127.0.0.1 - - [20/Nov/2019 13:14:45] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n" + " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n" ] } ], @@ -679,9 +616,9 @@ ], "metadata": { "kernelspec": { - "display_name": "venv", + "display_name": "Human Activity Recognition", "language": "python", - "name": "venv" + "name": "human-activity-recognition" }, "language_info": { "codemirror_mode": { diff --git a/model.py b/model.py new file mode 100644 index 0000000..af7847b --- /dev/null +++ b/model.py @@ -0,0 +1,149 @@ +import numpy as np +from neuraxle.base import BaseStep, ExecutionContext +from neuraxle.data_container import DataContainer +from neuraxle.hyperparams.space import HyperparameterSamples +import tensorflow as tf + +from neuraxle_tensorflow.tensorflow_v1 import TensorflowV1ModelStep + +N_HIDDEN = 32 +N_STEPS = 128 +N_INPUTS = 9 +LAMBDA_LOSS_AMOUNT = 0.0015 +LEARNING_RATE = 0.0025 +N_CLASSES = 6 +BATCH_SIZE = 1500 + + +def create_graph(step: TensorflowV1ModelStep): + # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. + # Moreover, two LSTM cells are stacked which adds deepness to the neural network. + # Note, some code of this notebook is inspired from an slightly different + # RNN architecture used on another dataset, some of the credits goes to + # "aymericdamien" under the MIT license. + # (NOTE: This step could be greatly optimised by shaping the dataset once + # input shape: (batch_size, n_steps, n_input) + + # Graph input/output + data_inputs = tf.placeholder(tf.float32, [None, step.hyperparams['n_steps'], step.hyperparams['n_inputs']], + name='data_inputs') + expected_outputs = tf.placeholder(tf.float32, [None, step.hyperparams['n_classes']], name='expected_outputs') + + # Graph weights + weights = { + 'hidden': tf.Variable( + tf.random_normal([step.hyperparams['n_inputs'], step.hyperparams['n_hidden']]) + ), # Hidden layer weights + 'out': tf.Variable( + tf.random_normal([step.hyperparams['n_hidden'], step.hyperparams['n_classes']], mean=1.0) + ) + } + + biases = { + 'hidden': tf.Variable( + tf.random_normal([step.hyperparams['n_hidden']]) + ), + 'out': tf.Variable( + tf.random_normal([step.hyperparams['n_classes']]) + ) + } + + data_inputs = tf.transpose( + data_inputs, + [1, 0, 2]) # permute n_steps and batch_size + + # Reshape to prepare input to hidden activation + data_inputs = tf.reshape(data_inputs, [-1, step.hyperparams['n_inputs']]) + # new shape: (n_steps*batch_size, n_input) + + # ReLU activation, thanks to Yu Zhao for adding this improvement here: + _X = tf.nn.relu( + tf.matmul(data_inputs, weights['hidden']) + biases['hidden'] + ) + + # Split data because rnn cell needs a list of inputs for the RNN inner loop + _X = tf.split(_X, step.hyperparams['n_steps'], 0) + # new shape: n_steps * (batch_size, n_hidden) + + # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow + lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(step.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(step.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) + + # Get LSTM cell output + outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) + + # Get last time step's output feature for a "many-to-one" style classifier, + # as in the image describing RNNs at the top of this page + lstm_last_output = outputs[-1] + + # Linear activation + return tf.matmul(lstm_last_output, weights['out']) + biases['out'] + + +def create_optimizer(step: TensorflowV1ModelStep): + return tf.train.AdamOptimizer(learning_rate=step.hyperparams['learning_rate']) + + +def create_loss(step: TensorflowV1ModelStep): + # Loss, optimizer and evaluation + # L2 loss prevents this overkill neural network to overfit the data + l2 = step.hyperparams['lambda_loss_amount'] * sum(tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables()) + + # Softmax loss + return tf.reduce_mean( + tf.nn.softmax_cross_entropy_with_logits( + labels=step['expected_outputs'], + logits=step['output'] + ) + ) + l2 + + +class ClassificationRNNTensorFlowModel(TensorflowV1ModelStep): + def setup(self) -> BaseStep: + TensorflowV1ModelStep.setup(self) + + self.losses = [] + self.accuracies = [] + + return self + + def _will_process(self, data_container: DataContainer, context: ExecutionContext) -> ('BaseStep', DataContainer): + if not isinstance(data_container.data_inputs, np.ndarray): + data_container.data_inputs = np.array(data_container.data_inputs) + + if data_container.expected_outputs is not None: + if not isinstance(data_container.expected_outputs, np.ndarray): + data_container.expected_outputs = np.array(data_container.expected_outputs) + + if data_container.expected_outputs.shape != (len(data_container.data_inputs), self.hyperparams['n_classes']): + data_container.expected_outputs = np.reshape(data_container.expected_outputs, (len(data_container.data_inputs), self.hyperparams['n_classes'])) + + return data_container, context + + def _did_fit_transform(self, data_container: DataContainer, context: ExecutionContext) -> DataContainer: + accuracy = np.mean(np.argmax(data_container.data_inputs, axis=1) == np.argmax(data_container.expected_outputs, axis=1)) + + self.accuracies.append(accuracy) + self.losses.append(self.loss) + + print("Batch Loss = " + "{:.6f}".format(self.losses[-1]) + ", Accuracy = {}".format(self.accuracies[-1])) + + return data_container + + +model_step = ClassificationRNNTensorFlowModel( + create_graph=create_graph, + create_loss=create_loss, + create_optimizer=create_optimizer +).set_hyperparams( + HyperparameterSamples({ + 'n_steps': N_STEPS, # 128 timesteps per series + 'n_inputs': N_INPUTS, # 9 input parameters per timestep + 'n_hidden': N_HIDDEN, # Hidden layer num of features + 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) + 'learning_rate': LEARNING_RATE, + 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT, + 'batch_size': BATCH_SIZE + }) +) diff --git a/savers/__init__.py b/savers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/savers/tensorflow1_step_saver.py b/savers/tensorflow1_step_saver.py deleted file mode 100644 index cae9f6a..0000000 --- a/savers/tensorflow1_step_saver.py +++ /dev/null @@ -1,85 +0,0 @@ -import os -import tensorflow as tf - -from neuraxle.base import BaseSaver - - -class TensorflowV1StepSaver(BaseSaver): - """ - Step saver for a tensorflow Session using tf.train.Saver(). - It saves, or restores the tf.Session() checkpoint at the context path using the step name as file name. - - .. seealso:: - `Using the saved model format `_ - """ - - def save_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep': - """ - Save a step that is using tf.train.Saver(). - - :param step: step to save - :type step: BaseStep - :param context: execution context to save from - :type context: ExecutionContext - :return: saved step - """ - with step.get_graph().as_default(): - saver = tf.train.Saver() - saver.save( - step.get_session(), - self._get_saved_model_path(context, step) - ) - - step.strip() - - return step - - def load_step(self, step: 'BaseStep', context: 'ExecutionContext') -> 'BaseStep': - """ - Load a step that is using tensorflow using tf.train.Saver(). - - :param step: step to load - :type step: BaseStep - :param context: execution context to load from - :type context: ExecutionContext - :return: loaded step - """ - step.is_initialized = False - step.setup() - with step.get_graph().as_default(): - saver = tf.train.Saver() - saver.restore( - step.get_session(), - self._get_saved_model_path(context, step) - ) - - return step - - def can_load(self, step: 'BaseStep', context: 'ExecutionContext'): - """ - Returns whether or not we can load. - - :param step: step to load - :type step: BaseStep - :param context: execution context to load from - :type context: ExecutionContext - :return: loaded step - """ - meta_exists = os.path.exists(os.path.join(context.get_path(), "{0}.ckpt.meta".format(step.get_name()))) - index_exists = os.path.exists(os.path.join(context.get_path(), "{0}.ckpt.index".format(step.get_name()))) - return meta_exists and index_exists - - def _get_saved_model_path(self, context, step): - """ - Returns the saved model path using the given execution context, and step name. - - :param step: step to load - :type step: BaseStep - :param context: execution context to load from - :type context: ExecutionContext - :return: loaded step - """ - return os.path.join( - context.get_path(), - "{0}.ckpt".format(step.get_name()) - ) diff --git a/steps/lstm_rnn_tensorflow_model.py b/steps/lstm_rnn_tensorflow_model.py deleted file mode 100644 index 1dbcbd8..0000000 --- a/steps/lstm_rnn_tensorflow_model.py +++ /dev/null @@ -1,67 +0,0 @@ -import tensorflow as tf - - -def tf_model_forward(pred_name, name_x, name_y, hyperparams): - # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. - # Moreover, two LSTM cells are stacked which adds deepness to the neural network. - # Note, some code of this notebook is inspired from an slightly different - # RNN architecture used on another dataset, some of the credits goes to - # "aymericdamien" under the MIT license. - # (NOTE: This step could be greatly optimised by shaping the dataset once - # input shape: (batch_size, n_steps, n_input) - - # Graph input/output - x = tf.placeholder(tf.float32, [None, hyperparams['n_steps'], hyperparams['n_inputs']], name=name_x) - y = tf.placeholder(tf.float32, [None, hyperparams['n_classes']], name=name_y) - - # Graph weights - weights = { - 'hidden': tf.Variable( - tf.random_normal([hyperparams['n_inputs'], hyperparams['n_hidden']]) - ), # Hidden layer weights - 'out': tf.Variable( - tf.random_normal([hyperparams['n_hidden'], hyperparams['n_classes']], mean=1.0) - ) - } - - biases = { - 'hidden': tf.Variable( - tf.random_normal([hyperparams['n_hidden']]) - ), - 'out': tf.Variable( - tf.random_normal([hyperparams['n_classes']]) - ) - } - - data_inputs = tf.transpose( - x, - [1, 0, 2]) # permute n_steps and batch_size - - # Reshape to prepare input to hidden activation - data_inputs = tf.reshape(data_inputs, [-1, hyperparams['n_inputs']]) - # new shape: (n_steps*batch_size, n_input) - - # ReLU activation, thanks to Yu Zhao for adding this improvement here: - _X = tf.nn.relu( - tf.matmul(data_inputs, weights['hidden']) + biases['hidden'] - ) - - # Split data because rnn cell needs a list of inputs for the RNN inner loop - _X = tf.split(_X, hyperparams['n_steps'], 0) - # new shape: n_steps * (batch_size, n_hidden) - - # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow - lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) - lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) - lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) - - # Get LSTM cell output - outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) - - # Get last time step's output feature for a "many-to-one" style classifier, - # as in the image describing RNNs at the top of this page - lstm_last_output = outputs[-1] - - # Linear activation - pred = tf.matmul(lstm_last_output, weights['out']) + biases['out'] - return tf.identity(pred, name=pred_name) diff --git a/steps/lstm_rnn_tensorflow_model_wrapper.py b/steps/lstm_rnn_tensorflow_model_wrapper.py deleted file mode 100644 index 2a54aa7..0000000 --- a/steps/lstm_rnn_tensorflow_model_wrapper.py +++ /dev/null @@ -1,208 +0,0 @@ -import numpy as np -import tensorflow as tf -from neuraxle.base import BaseStep -from neuraxle.hyperparams.space import HyperparameterSamples -from neuraxle.steps.numpy import OneHotEncoder - -from savers.tensorflow1_step_saver import TensorflowV1StepSaver -from steps.lstm_rnn_tensorflow_model import tf_model_forward - -LSTM_RNN_VARIABLE_SCOPE = "lstm_rnn" -X_NAME = 'x' -Y_NAME = 'y' -PRED_NAME = 'pred' - -N_HIDDEN = 32 -N_STEPS = 128 -N_INPUTS = 9 -LAMBDA_LOSS_AMOUNT = 0.0015 -LEARNING_RATE = 0.0025 -N_CLASSES = 6 -BATCH_SIZE = 1500 - - -class ClassificationRNNTensorFlowModel(BaseStep): - HYPERPARAMS = HyperparameterSamples({ - 'n_steps': N_STEPS, # 128 timesteps per series - 'n_inputs': N_INPUTS, # 9 input parameters per timestep - 'n_hidden': N_HIDDEN, # Hidden layer num of features - 'n_classes': N_CLASSES, # Total classes (should go up, or should go down) - 'learning_rate': LEARNING_RATE, - 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT, - 'batch_size': BATCH_SIZE - }) - - def __init__( - self, - # TODO: replace with issue 174 - # - X_test=None, - y_test=None - ): - BaseStep.__init__( - self, - hyperparams=ClassificationRNNTensorFlowModel.HYPERPARAMS, - savers=[TensorflowV1StepSaver()] - ) - - # TODO: replace with issue 174 - # - self.y_test = y_test - self.X_test = X_test - - self.graph = None - self.sess = None - self.l2 = None - self.cost = None - self.optimizer = None - self.correct_pred = None - self.accuracy = None - self.test_losses = None - self.test_accuracies = None - self.train_losses = None - self.train_accuracies = None - - def strip(self): - self.sess = None - self.graph = None - self.l2 = None - self.cost = None - self.optimizer = None - self.correct_pred = None - self.accuracy = None - - def setup(self) -> BaseStep: - if self.is_initialized: - return self - - self.create_graph() - - with self.graph.as_default(): - # Launch the graph - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): - pred = tf_model_forward(PRED_NAME, X_NAME, Y_NAME, self.hyperparams) - - # Loss, optimizer and evaluation - # L2 loss prevents this overkill neural network to overfit the data - - l2 = self.hyperparams['lambda_loss_amount'] * sum( - tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables() - ) - - # Softmax loss - self.cost = tf.reduce_mean( - tf.nn.softmax_cross_entropy_with_logits( - labels=self.get_y_placeholder(), - logits=pred - ) - ) + l2 - - # Adam Optimizer - self.optimizer = tf.train.AdamOptimizer( - learning_rate=self.hyperparams['learning_rate'] - ).minimize(self.cost) - - self.correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(self.get_tensor_by_name(Y_NAME), 1)) - self.accuracy = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32)) - - # To keep track of training's performance - self.test_losses = [] - self.test_accuracies = [] - self.train_losses = [] - self.train_accuracies = [] - - self.create_session() - - self.is_initialized = True - - return self - - def create_graph(self): - self.graph = tf.Graph() - - def create_session(self): - self.sess = tf.Session(config=tf.ConfigProto(log_device_placement=True), graph=self.graph) - init = tf.global_variables_initializer() - self.sess.run(init) - - def get_tensor_by_name(self, name): - return self.graph.get_tensor_by_name("{0}/{1}:0".format(LSTM_RNN_VARIABLE_SCOPE, name)) - - def get_graph(self): - return self.graph - - def get_session(self): - return self.sess - - def get_x_placeholder(self): - return self.get_tensor_by_name(X_NAME) - - def get_y_placeholder(self): - return self.get_tensor_by_name(Y_NAME) - - def teardown(self): - if self.sess is not None: - self.sess.close() - self.is_initialized = False - - def fit(self, data_inputs, expected_outputs=None) -> 'BaseStep': - if not isinstance(data_inputs, np.ndarray): - data_inputs = np.array(data_inputs) - - if not isinstance(expected_outputs, np.ndarray): - expected_outputs = np.array(expected_outputs) - - if expected_outputs.shape != (len(data_inputs), self.hyperparams['n_classes']): - expected_outputs = np.reshape(expected_outputs, (len(data_inputs), self.hyperparams['n_classes'])) - - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): - _, loss, acc = self.sess.run( - [self.optimizer, self.cost, self.accuracy], - feed_dict={ - self.get_x_placeholder(): data_inputs, - self.get_y_placeholder(): expected_outputs - } - ) - - self.train_losses.append(loss) - self.train_accuracies.append(acc) - - print("Batch Loss = " + "{:.6f}".format(loss) + ", Accuracy = {}".format(acc)) - - self.is_invalidated = True - - return self - - def transform(self, data_inputs): - if not isinstance(data_inputs, np.ndarray): - data_inputs = np.array(data_inputs) - - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): - outputs = self.sess.run( - [self.get_tensor_by_name(PRED_NAME)], - feed_dict={ - self.get_x_placeholder(): data_inputs - } - )[0] - return outputs - - def _evaluate_on_test_set(self): - one_hot_encoded_y_test = OneHotEncoder( - nb_columns=self.hyperparams['n_classes'], - name='one_hot_encoded_y_test' - ).transform(self.y_test) - - with tf.variable_scope(LSTM_RNN_VARIABLE_SCOPE, reuse=tf.AUTO_REUSE): - loss, acc = self.sess.run( - [self.cost, self.accuracy], - feed_dict={ - self.get_x_placeholder(): self.X_test, - self.get_y_placeholder(): one_hot_encoded_y_test - } - ) - - self.test_losses.append(loss) - self.test_accuracies.append(acc) - print("PERFORMANCE ON TEST SET: " + \ - "Batch Loss = {}".format(loss) + \ - ", Accuracy = {}".format(acc)) diff --git a/steps/transform_expected_output_wrapper.py b/steps/transform_expected_output_wrapper.py deleted file mode 100644 index 1cca900..0000000 --- a/steps/transform_expected_output_wrapper.py +++ /dev/null @@ -1,32 +0,0 @@ -from neuraxle.base import NonFittableMixin, MetaStepMixin, BaseStep, DataContainer, ExecutionContext - - -class OutputTransformerWrapper(NonFittableMixin, MetaStepMixin, BaseStep): - """ - Transform expected output wrapper step that can sends the expected_outputs to the wrapped step - so that it can transform the expected outputs. - """ - def __init__(self, wrapped): - NonFittableMixin.__init__(self) - MetaStepMixin.__init__(self, wrapped) - BaseStep.__init__(self) - - def handle_transform(self, data_container: DataContainer, context: ExecutionContext) -> DataContainer: - new_expected_outputs_data_container = self.wrapped.handle_transform( - DataContainer( - current_ids=data_container.current_ids, - data_inputs=data_container.expected_outputs, - expected_outputs=None - ), - context.push(self.wrapped) - ) - - data_container.set_expected_outputs(new_expected_outputs_data_container.data_inputs) - - current_ids = self.hash(data_container) - data_container.set_current_ids(current_ids) - - return data_container - - def transform(self, data_inputs): - raise NotImplementedError('must be used inside a pipeline') From cfdd5457cdce54fbe7a8f6b8764bf7fcddb71eb8 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 11 Jan 2020 15:00:29 -0500 Subject: [PATCH 27/30] Wip Use Deep Learning Pipeline --- .gitignore | 1 + data_reading.py | 24 +++++++ requirements.txt | 2 +- steps/forma_data.py | 26 +++++++ train_and_save.py | 171 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 steps/forma_data.py create mode 100644 train_and_save.py diff --git a/.gitignore b/.gitignore index 9c8bea1..dd70090 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ steps/one_hot_encoder.py steps/transform_expected_output_only_wrapper.py venv/** cache/** +neuraxle_tensorflow/** diff --git a/data_reading.py b/data_reading.py index 3b633c5..042e863 100644 --- a/data_reading.py +++ b/data_reading.py @@ -1,3 +1,5 @@ +import os + import numpy as np INPUT_SIGNAL_TYPES = [ @@ -68,3 +70,25 @@ def load_y(y_path): # Substract 1 to each output class for friendly 0-based indexing return y_ - 1 + + +def load_data(): + # Load "X" (the neural network's training and testing inputs) + + X_train = load_X(X_train_signals_paths) + # X_test = load_X(X_test_signals_paths) + + # Load "y" (the neural network's training and testing outputs) + + y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME) + # y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME) + + y_train = load_y(y_train_path) + # y_test = load_y(y_test_path) + + print("Some useful info to get an insight on dataset's shape and normalisation:") + print("(data_inputs shape, expected_outputs shape, every data input mean, every data input standard deviation)") + print(X_train.shape, y_train.shape, np.mean(X_train), np.std(X_train)) + print("The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.") + + return X_train, y_train diff --git a/requirements.txt b/requirements.txt index 398f195..5371ab0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ tensorflow==1.15 tensorflow-gpu==1.15 conv==0.2 -git+git://github.com/alexbrillant/Neuraxle@one-hot-encoder-step#egg=Neuraxle +-e git://github.com/alexbrillant/Neuraxle.git@a270fe2b2f73c9350d76fcf4b6f058b764a8c8f7#egg=neuraxle diff --git a/steps/forma_data.py b/steps/forma_data.py new file mode 100644 index 0000000..1107045 --- /dev/null +++ b/steps/forma_data.py @@ -0,0 +1,26 @@ +import numpy as np +from neuraxle.base import BaseStep, NonFittableMixin +from neuraxle.steps.output_handlers import InputAndOutputTransformerMixin + + +class FormatData(NonFittableMixin, InputAndOutputTransformerMixin, BaseStep): + def __init__(self, n_classes): + NonFittableMixin.__init__(self) + InputAndOutputTransformerMixin.__init__(self) + BaseStep.__init__(self) + self.n_classes = n_classes + + def transform(self, data_inputs): + data_inputs, expected_outputs = data_inputs + + if not isinstance(data_inputs, np.ndarray): + data_inputs = np.array(data_inputs) + + if expected_outputs is not None: + if not isinstance(expected_outputs, np.ndarray): + expected_outputs = np.array(expected_outputs) + + if expected_outputs.shape != (len(data_inputs), self.n_classes): + expected_outputs = np.reshape(expected_outputs, (len(data_inputs), self.n_classes)) + + return data_inputs, expected_outputs diff --git a/train_and_save.py b/train_and_save.py new file mode 100644 index 0000000..ed4eef8 --- /dev/null +++ b/train_and_save.py @@ -0,0 +1,171 @@ +import matplotlib.pyplot as plt +import numpy as np +import tensorflow as tf +from neuraxle.api import DeepLearningPipeline +from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER +from neuraxle.hyperparams.space import HyperparameterSamples +from neuraxle.pipeline import Pipeline +from neuraxle.steps.numpy import OneHotEncoder +from neuraxle.steps.output_handlers import OutputTransformerWrapper +from sklearn.metrics import accuracy_score + +from data_reading import load_data +from neuraxle_tensorflow.tensorflow_v1 import TensorflowV1ModelStep +from steps.forma_data import FormatData + + +def create_graph(step: TensorflowV1ModelStep): + # Function returns a tensorflow LSTM (RNN) artificial neural network from given parameters. + # Moreover, two LSTM cells are stacked which adds deepness to the neural network. + # Note, some code of this notebook is inspired from an slightly different + # RNN architecture used on another dataset, some of the credits goes to + # "aymericdamien" under the MIT license. + # (NOTE: This step could be greatly optimised by shaping the dataset once + # input shape: (batch_size, n_steps, n_input) + + # Graph input/output + data_inputs = tf.placeholder(tf.float32, [None, step.hyperparams['n_steps'], step.hyperparams['n_inputs']], + name='data_inputs') + expected_outputs = tf.placeholder(tf.float32, [None, step.hyperparams['n_classes']], name='expected_outputs') + + # Graph weights + weights = { + 'hidden': tf.Variable( + tf.random_normal([step.hyperparams['n_inputs'], step.hyperparams['n_hidden']]) + ), # Hidden layer weights + 'out': tf.Variable( + tf.random_normal([step.hyperparams['n_hidden'], step.hyperparams['n_classes']], mean=1.0) + ) + } + + biases = { + 'hidden': tf.Variable( + tf.random_normal([step.hyperparams['n_hidden']]) + ), + 'out': tf.Variable( + tf.random_normal([step.hyperparams['n_classes']]) + ) + } + + data_inputs = tf.transpose( + data_inputs, + [1, 0, 2]) # permute n_steps and batch_size + + # Reshape to prepare input to hidden activation + data_inputs = tf.reshape(data_inputs, [-1, step.hyperparams['n_inputs']]) + # new shape: (n_steps*batch_size, n_input) + + # ReLU activation, thanks to Yu Zhao for adding this improvement here: + _X = tf.nn.relu( + tf.matmul(data_inputs, weights['hidden']) + biases['hidden'] + ) + + # Split data because rnn cell needs a list of inputs for the RNN inner loop + _X = tf.split(_X, step.hyperparams['n_steps'], 0) + # new shape: n_steps * (batch_size, n_hidden) + + # Define two stacked LSTM cells (two recurrent layers deep) with tensorflow + lstm_cell_1 = tf.contrib.rnn.BasicLSTMCell(step.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cell_2 = tf.contrib.rnn.BasicLSTMCell(step.hyperparams['n_hidden'], forget_bias=1.0, state_is_tuple=True) + lstm_cells = tf.contrib.rnn.MultiRNNCell([lstm_cell_1, lstm_cell_2], state_is_tuple=True) + + # Get LSTM cell output + outputs, states = tf.contrib.rnn.static_rnn(lstm_cells, _X, dtype=tf.float32) + + # Get last time step's output feature for a "many-to-one" style classifier, + # as in the image describing RNNs at the top of this page + lstm_last_output = outputs[-1] + + # Linear activation + return tf.matmul(lstm_last_output, weights['out']) + biases['out'] + + +def create_optimizer(step: TensorflowV1ModelStep): + return tf.train.AdamOptimizer(learning_rate=step.hyperparams['learning_rate']) + + +def create_loss(step: TensorflowV1ModelStep): + # Loss, optimizer and evaluation + # L2 loss prevents this overkill neural network to overfit the data + l2 = step.hyperparams['lambda_loss_amount'] * sum(tf.nn.l2_loss(tf_var) for tf_var in tf.trainable_variables()) + + # Softmax loss + return tf.reduce_mean( + tf.nn.softmax_cross_entropy_with_logits( + labels=step['expected_outputs'], + logits=step['output'] + ) + ) + l2 + + +def accuracy_score_classification(data_inputs, expected_outputs): + accuracy = np.mean(np.argmax(data_inputs, axis=1) == np.argmax(expected_outputs, axis=1)) + return accuracy + + +class HumanActivityRecognitionPipeline(DeepLearningPipeline): + N_HIDDEN = 32 + N_STEPS = 128 + N_INPUTS = 9 + LAMBDA_LOSS_AMOUNT = 0.0015 + LEARNING_RATE = 0.0025 + N_CLASSES = 6 + BATCH_SIZE = 1500 + EPOCHS = 14 + + def __init__(self): + super().__init__( + Pipeline([ + OutputTransformerWrapper(OneHotEncoder(nb_columns=self.N_CLASSES, name='one_hot_encoded_label')), + FormatData(n_classes=self.N_CLASSES), + TensorflowV1ModelStep( + create_graph=create_graph, + create_loss=create_loss, + create_optimizer=create_optimizer + ).set_hyperparams( + HyperparameterSamples({ + 'n_steps': self.N_STEPS, # 128 timesteps per series + 'n_inputs': self.N_INPUTS, # 9 input parameters per timestep + 'n_hidden': self.N_HIDDEN, # Hidden layer num of features + 'n_classes': self.N_CLASSES, # Total classes (should go up, or should go down) + 'learning_rate': self.LEARNING_RATE, + 'lambda_loss_amount': self.LAMBDA_LOSS_AMOUNT, + 'batch_size': self.BATCH_SIZE + }) + ) + ]), + validation_size=0.15, + batch_size=self.BATCH_SIZE, + batch_metrics={'accuracy': accuracy_score}, + shuffle_in_each_epoch_at_train=True, + n_epochs=self.EPOCHS, + epochs_metrics={'accuracy': accuracy_score}, + scoring_function=accuracy_score + ) + + +def main(): + pipeline = HumanActivityRecognitionPipeline() + + data_inputs, expected_outputs = load_data() + pipeline, outputs = pipeline.fit_transform(data_inputs, expected_outputs) + + accuracies = pipeline.get_epoch_metric_train('accuracy') + plt.plot(range(len(accuracies)), accuracies) + plt.xlabel('epochs') + plt.xlabel('accuracy') + plt.title('Training accuracy') + + accuracies = pipeline.get_epoch_metric_validation('accuracy') + plt.plot(range(len(accuracies)), accuracies) + plt.xlabel('epochs') + plt.xlabel('accuracy') + plt.title('Validation accuracy') + plt.show() + + pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER)) + pipeline.teardown() + + +if __name__ == '__main__': + main() From 09e8e09316db3690e5774ca8a866568643ce1292 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 11 Jan 2020 15:15:02 -0500 Subject: [PATCH 28/30] Add Accuracy Metric Plotting With Deep Learning Pipeline --- train_and_save.py | 88 ++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/train_and_save.py b/train_and_save.py index ed4eef8..24b917f 100644 --- a/train_and_save.py +++ b/train_and_save.py @@ -7,7 +7,6 @@ from neuraxle.pipeline import Pipeline from neuraxle.steps.numpy import OneHotEncoder from neuraxle.steps.output_handlers import OutputTransformerWrapper -from sklearn.metrics import accuracy_score from data_reading import load_data from neuraxle_tensorflow.tensorflow_v1 import TensorflowV1ModelStep @@ -98,12 +97,7 @@ def create_loss(step: TensorflowV1ModelStep): ) + l2 -def accuracy_score_classification(data_inputs, expected_outputs): - accuracy = np.mean(np.argmax(data_inputs, axis=1) == np.argmax(expected_outputs, axis=1)) - return accuracy - - -class HumanActivityRecognitionPipeline(DeepLearningPipeline): +class HumanActivityRecognitionPipeline(Pipeline): N_HIDDEN = 32 N_STEPS = 128 N_INPUTS = 9 @@ -114,58 +108,66 @@ class HumanActivityRecognitionPipeline(DeepLearningPipeline): EPOCHS = 14 def __init__(self): - super().__init__( - Pipeline([ - OutputTransformerWrapper(OneHotEncoder(nb_columns=self.N_CLASSES, name='one_hot_encoded_label')), - FormatData(n_classes=self.N_CLASSES), - TensorflowV1ModelStep( - create_graph=create_graph, - create_loss=create_loss, - create_optimizer=create_optimizer - ).set_hyperparams( - HyperparameterSamples({ - 'n_steps': self.N_STEPS, # 128 timesteps per series - 'n_inputs': self.N_INPUTS, # 9 input parameters per timestep - 'n_hidden': self.N_HIDDEN, # Hidden layer num of features - 'n_classes': self.N_CLASSES, # Total classes (should go up, or should go down) - 'learning_rate': self.LEARNING_RATE, - 'lambda_loss_amount': self.LAMBDA_LOSS_AMOUNT, - 'batch_size': self.BATCH_SIZE - }) - ) - ]), - validation_size=0.15, - batch_size=self.BATCH_SIZE, - batch_metrics={'accuracy': accuracy_score}, - shuffle_in_each_epoch_at_train=True, - n_epochs=self.EPOCHS, - epochs_metrics={'accuracy': accuracy_score}, - scoring_function=accuracy_score - ) + super().__init__([ + OutputTransformerWrapper(OneHotEncoder(nb_columns=self.N_CLASSES, name='one_hot_encoded_label')), + FormatData(n_classes=self.N_CLASSES), + TensorflowV1ModelStep( + create_graph=create_graph, + create_loss=create_loss, + create_optimizer=create_optimizer + ).set_hyperparams( + HyperparameterSamples({ + 'n_steps': self.N_STEPS, # 128 timesteps per series + 'n_inputs': self.N_INPUTS, # 9 input parameters per timestep + 'n_hidden': self.N_HIDDEN, # Hidden layer num of features + 'n_classes': self.N_CLASSES, # Total classes (should go up, or should go down) + 'learning_rate': self.LEARNING_RATE, + 'lambda_loss_amount': self.LAMBDA_LOSS_AMOUNT, + 'batch_size': self.BATCH_SIZE + }) + ) + ]) + + +def accuracy_score_classification(data_inputs, expected_outputs): + accuracy = np.mean(np.argmax(data_inputs, axis=1) == np.argmax(expected_outputs, axis=1)) + return accuracy def main(): - pipeline = HumanActivityRecognitionPipeline() + pipeline = DeepLearningPipeline( + HumanActivityRecognitionPipeline(), + validation_size=0.15, + batch_size=HumanActivityRecognitionPipeline.BATCH_SIZE, + batch_metrics={'accuracy': accuracy_score_classification}, + shuffle_in_each_epoch_at_train=True, + n_epochs=HumanActivityRecognitionPipeline.EPOCHS, + epochs_metrics={'accuracy': accuracy_score_classification}, + scoring_function=accuracy_score_classification + ) data_inputs, expected_outputs = load_data() pipeline, outputs = pipeline.fit_transform(data_inputs, expected_outputs) + plot_metrics(pipeline) + + pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER)) + pipeline.teardown() + + +def plot_metrics(pipeline): accuracies = pipeline.get_epoch_metric_train('accuracy') plt.plot(range(len(accuracies)), accuracies) - plt.xlabel('epochs') - plt.xlabel('accuracy') - plt.title('Training accuracy') accuracies = pipeline.get_epoch_metric_validation('accuracy') plt.plot(range(len(accuracies)), accuracies) + plt.xlabel('epochs') plt.xlabel('accuracy') - plt.title('Validation accuracy') + plt.title('Model Accuracy') + plt.legend(['training', 'validation'], loc='upper left') plt.show() - pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER)) - pipeline.teardown() - if __name__ == '__main__': main() From 866619bf212d242e17d419f86132f397c1e26c84 Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sat, 11 Jan 2020 15:44:41 -0500 Subject: [PATCH 29/30] Extract plotting function to a file --- plotting.py | 15 +++++++++++++++ train_and_save.py | 18 ++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 plotting.py diff --git a/plotting.py b/plotting.py new file mode 100644 index 0000000..76db06d --- /dev/null +++ b/plotting.py @@ -0,0 +1,15 @@ +from matplotlib import pyplot as plt + + +def plot_metric(pipeline, metric_name, xlabel, ylabel, title): + accuracies = pipeline.get_epoch_metric_train(metric_name) + plt.plot(range(len(accuracies)), accuracies) + + accuracies = pipeline.get_epoch_metric_validation(metric_name) + plt.plot(range(len(accuracies)), accuracies) + + plt.xlabel(xlabel) + plt.xlabel(ylabel) + plt.title(title) + plt.legend(['training', 'validation'], loc='upper left') + plt.show() \ No newline at end of file diff --git a/train_and_save.py b/train_and_save.py index 24b917f..f6fc6c0 100644 --- a/train_and_save.py +++ b/train_and_save.py @@ -1,4 +1,3 @@ -import matplotlib.pyplot as plt import numpy as np import tensorflow as tf from neuraxle.api import DeepLearningPipeline @@ -10,6 +9,7 @@ from data_reading import load_data from neuraxle_tensorflow.tensorflow_v1 import TensorflowV1ModelStep +from plotting import plot_metric from steps.forma_data import FormatData @@ -149,25 +149,11 @@ def main(): data_inputs, expected_outputs = load_data() pipeline, outputs = pipeline.fit_transform(data_inputs, expected_outputs) - plot_metrics(pipeline) + plot_metric(pipeline, metric_name='accuracy', xlabel='epoch', ylabel='accuracy', title='Model Accuracy') pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER)) pipeline.teardown() -def plot_metrics(pipeline): - accuracies = pipeline.get_epoch_metric_train('accuracy') - plt.plot(range(len(accuracies)), accuracies) - - accuracies = pipeline.get_epoch_metric_validation('accuracy') - plt.plot(range(len(accuracies)), accuracies) - - plt.xlabel('epochs') - plt.xlabel('accuracy') - plt.title('Model Accuracy') - plt.legend(['training', 'validation'], loc='upper left') - plt.show() - - if __name__ == '__main__': main() From cb08c95649de09abafbbd6039c04f45ae6a1072e Mon Sep 17 00:00:00 2001 From: alexbrillant Date: Sun, 12 Jan 2020 16:37:49 -0500 Subject: [PATCH 30/30] Wip update notebook --- 1_train_and_save_LSTM.ipynb | 521 ++++++++++++++---------------------- plotting.py | 16 +- requirements.txt | 2 +- train_and_save.py | 8 +- 4 files changed, 222 insertions(+), 325 deletions(-) diff --git a/1_train_and_save_LSTM.ipynb b/1_train_and_save_LSTM.ipynb index 99228ce..55cf140 100644 --- a/1_train_and_save_LSTM.ipynb +++ b/1_train_and_save_LSTM.ipynb @@ -13,131 +13,46 @@ "metadata": {}, "outputs": [], "source": [ - "import math\n", - "import os\n", "import numpy as np\n", "import tensorflow as tf\n", - "\n", - "from neuraxle.api.flask import FlaskRestApiWrapper\n", - "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER, ExecutionMode, BaseStep\n", - "from neuraxle.data_container import DataContainer\n", + "from neuraxle.api import DeepLearningPipeline\n", + "from neuraxle.base import ExecutionContext, DEFAULT_CACHE_FOLDER\n", "from neuraxle.hyperparams.space import HyperparameterSamples\n", + "from neuraxle.pipeline import Pipeline\n", "from neuraxle.steps.numpy import OneHotEncoder\n", - "from neuraxle.pipeline import MiniBatchSequentialPipeline, Joiner\n", "from neuraxle.steps.output_handlers import OutputTransformerWrapper\n", - "from neuraxle_tensorflow.tensorflow_v1 import TensorflowV1ModelStep\n", - "\n", - "from steps.custom_json_decoder_for_2darray import CustomJSONDecoderFor2DArray\n", - "from steps.custom_json_encoder_of_outputs import CustomJSONEncoderOfOutputs\n", "\n", - "from data_reading import DATASET_PATH, TRAIN, TEST, X_train_signals_paths, load_X, load_y, \\\n", - " TRAIN_FILE_NAME, TEST_FILE_NAME" + "from data_reading import load_data\n", + "from neuraxle_tensorflow.tensorflow_v1 import TensorflowV1ModelStep\n", + "from plotting import plot_metric\n", + "from steps.forma_data import FormatData" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Download Data" + "# Load Data" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py steps\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__ venv\n", - "cache\t\t\t\tmodel.py\t README.md\n", - "data\t\t\t\tneuraxle_tensorflow requirements.txt\n", - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", - " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", - " __MACOSX\t 'UCI HAR Dataset'\n", - "\n", - "Downloading...\n", - "Dataset already downloaded. Did not download twice.\n", - "\n", - "Extracting...\n", - "Dataset already extracted. Did not extract twice.\n", - "\n", - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/data\n", - " download_dataset.py source.txt\t 'UCI HAR Dataset.zip'\n", - " __MACOSX\t 'UCI HAR Dataset'\n", - "/home/alexandre/Documents/LSTM-Human-Activity-Recognition\n", - "1_train_and_save_LSTM.ipynb\tdata_reading.py pipeline.py steps\n", - "2_call_rest_api_and_eval.ipynb\tLICENSE\t\t __pycache__ venv\n", - "cache\t\t\t\tmodel.py\t README.md\n", - "data\t\t\t\tneuraxle_tensorflow requirements.txt\n", - "\n", - "Dataset is now located at: data/UCI HAR Dataset/\n" - ] - } - ], - "source": [ - "# Note: Linux bash commands start with a \"!\" inside those \"ipython notebook\" cells\n", - "\n", - "DATA_PATH = \"data/\"\n", - "\n", - "!pwd && ls\n", - "os.chdir(DATA_PATH)\n", - "!pwd && ls\n", - "\n", - "!python download_dataset.py\n", - "\n", - "!pwd && ls\n", - "os.chdir(\"..\")\n", - "!pwd && ls\n", - "\n", - "DATASET_PATH = DATA_PATH + \"UCI HAR Dataset/\"\n", - "print(\"\\n\" + \"Dataset is now located at: \" + DATASET_PATH)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Load data" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Some useful info to get an insight on dataset's shape and normalisation:\n", - "(X shape, y shape, every X's mean, every X's standard deviation)\n", + "(data_inputs shape, expected_outputs shape, every data input mean, every data input standard deviation)\n", "(7352, 128, 9) (7352, 1) 0.10206611 0.40216514\n", "The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\n" ] } ], "source": [ - "# Load \"X\" (the neural network's training and testing inputs)\n", - "\n", - "X_train = load_X(X_train_signals_paths)\n", - "# X_test = load_X(X_test_signals_paths)\n", - "\n", - "# Load \"y\" (the neural network's training and testing outputs)\n", - "\n", - "y_train_path = os.path.join(DATASET_PATH, TRAIN, TRAIN_FILE_NAME)\n", - "# y_test_path = os.path.join(DATASET_PATH, TEST, TEST_FILE_NAME)\n", - "\n", - "y_train = load_y(y_train_path)\n", - "# y_test = load_y(y_test_path)\n", - "\n", - "print(\"Some useful info to get an insight on dataset's shape and normalisation:\")\n", - "print(\"(X shape, y shape, every X's mean, every X's standard deviation)\")\n", - "print(X_train.shape, y_train.shape, np.mean(X_train), np.std(X_train))\n", - "print(\"The dataset is therefore properly normalised, as expected, but not yet one-hot encoded.\")" + "data_inputs, expected_outputs = load_data()" ] }, { @@ -149,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -228,7 +143,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -245,7 +160,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -267,118 +182,124 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Create TensorflowV1ModelStep " + "# Create Neuraxle Pipeline " ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "class ClassificationRNNTensorFlowModel(TensorflowV1ModelStep):\n", - " def setup(self) -> BaseStep:\n", - " TensorflowV1ModelStep.setup(self)\n", - "\n", - " self.losses = []\n", - " self.accuracies = []\n", - "\n", - " return self\n", - "\n", - " def _will_process(self, data_container: DataContainer, context: ExecutionContext) -> ('BaseStep', DataContainer):\n", - " if not isinstance(data_container.data_inputs, np.ndarray):\n", - " data_container.data_inputs = np.array(data_container.data_inputs)\n", - "\n", - " if data_container.expected_outputs is not None:\n", - " if not isinstance(data_container.expected_outputs, np.ndarray):\n", - " data_container.expected_outputs = np.array(data_container.expected_outputs)\n", - "\n", - " if data_container.expected_outputs.shape != (len(data_container.data_inputs), self.hyperparams['n_classes']):\n", - " data_container.expected_outputs = np.reshape(data_container.expected_outputs, (len(data_container.data_inputs), self.hyperparams['n_classes']))\n", - "\n", - " return data_container, context\n", - "\n", - " def _did_fit_transform(self, data_container: DataContainer, context: ExecutionContext) -> DataContainer:\n", - " accuracy = np.mean(np.argmax(data_container.data_inputs, axis=1) == np.argmax(data_container.expected_outputs, axis=1))\n", - "\n", - " self.accuracies.append(accuracy)\n", - " self.losses.append(self.loss)\n", - "\n", - " print(\"Batch Loss = \" + \"{:.6f}\".format(self.losses[-1]) + \", Accuracy = {}\".format(self.accuracies[-1]))\n", + "class HumanActivityRecognitionPipeline(Pipeline):\n", + " N_HIDDEN = 32\n", + " N_STEPS = 128\n", + " N_INPUTS = 9\n", + " LAMBDA_LOSS_AMOUNT = 0.0015\n", + " LEARNING_RATE = 0.0025\n", + " N_CLASSES = 6\n", + " BATCH_SIZE = 1500\n", + " EPOCHS = 14\n", "\n", - " return data_container" + " def __init__(self):\n", + " super().__init__([\n", + " OutputTransformerWrapper(OneHotEncoder(nb_columns=self.N_CLASSES, name='one_hot_encoded_label')),\n", + " FormatData(n_classes=self.N_CLASSES),\n", + " TensorflowV1ModelStep(\n", + " create_graph=create_graph,\n", + " create_loss=create_loss,\n", + " create_optimizer=create_optimizer\n", + " ).set_hyperparams(\n", + " HyperparameterSamples({\n", + " 'n_steps': self.N_STEPS, # 128 timesteps per series\n", + " 'n_inputs': self.N_INPUTS, # 9 input parameters per timestep\n", + " 'n_hidden': self.N_HIDDEN, # Hidden layer num of features\n", + " 'n_classes': self.N_CLASSES, # Total classes (should go up, or should go down)\n", + " 'learning_rate': self.LEARNING_RATE,\n", + " 'lambda_loss_amount': self.LAMBDA_LOSS_AMOUNT,\n", + " 'batch_size': self.BATCH_SIZE\n", + " })\n", + " )\n", + " ])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Neuraxle Pipeline " + "# Create scoring metric" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "N_HIDDEN = 32\n", - "N_STEPS = 128\n", - "N_INPUTS = 9\n", - "LAMBDA_LOSS_AMOUNT = 0.0015\n", - "LEARNING_RATE = 0.0025\n", - "N_CLASSES = 6\n", - "BATCH_SIZE = 1500\n", - "\n", - "\n", - "class HumanActivityRecognitionPipeline(MiniBatchSequentialPipeline):\n", - " def __init__(self):\n", - " MiniBatchSequentialPipeline.__init__(self, [\n", - " OutputTransformerWrapper(OneHotEncoder(nb_columns=N_CLASSES, name='one_hot_encoded_label')),\n", - " ClassificationRNNTensorFlowModel(\n", - " create_graph=create_graph,\n", - " create_loss=create_loss,\n", - " create_optimizer=create_optimizer\n", - " ).set_hyperparams(\n", - " HyperparameterSamples({\n", - " 'n_steps': N_STEPS,\n", - " 'n_inputs': N_INPUTS, \n", - " 'n_hidden': N_HIDDEN,\n", - " 'n_classes': N_CLASSES,\n", - " 'learning_rate': LEARNING_RATE,\n", - " 'lambda_loss_amount': LAMBDA_LOSS_AMOUNT,\n", - " 'batch_size': BATCH_SIZE\n", - " })\n", - " ),\n", - " Joiner(batch_size=BATCH_SIZE)\n", - " ])" + "def accuracy_score_classification(data_inputs, expected_outputs):\n", + " accuracy = np.mean(np.argmax(data_inputs, axis=1) == np.argmax(expected_outputs, axis=1))\n", + " return accuracy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Train Pipeline " + "# Create deep learning pipeline" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": { "scrolled": false }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/src/neuraxle/neuraxle/pipeline.py:347: UserWarning: Replacing MiniBatchSequentialPipeline[Joiner].batch_size by MiniBatchSequentialPipeline.batch_size.\n", + " 'Replacing {}[{}].batch_size by {}.batch_size.'.format(self.name, step.name, self.name))\n" + ] + } + ], + "source": [ + "pipeline = DeepLearningPipeline(\n", + " HumanActivityRecognitionPipeline(),\n", + " validation_size=0.15,\n", + " batch_size=HumanActivityRecognitionPipeline.BATCH_SIZE,\n", + " batch_metrics={'accuracy': accuracy_score_classification},\n", + " shuffle_in_each_epoch_at_train=True,\n", + " n_epochs=HumanActivityRecognitionPipeline.EPOCHS,\n", + " epochs_metrics={'accuracy': accuracy_score_classification},\n", + " scoring_function=accuracy_score_classification\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Train" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:73: The name tf.variable_scope is deprecated. Please use tf.compat.v1.variable_scope instead.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:82: The name tf.variable_scope is deprecated. Please use tf.compat.v1.variable_scope instead.\n", "\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:73: The name tf.AUTO_REUSE is deprecated. Please use tf.compat.v1.AUTO_REUSE instead.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:82: The name tf.AUTO_REUSE is deprecated. Please use tf.compat.v1.AUTO_REUSE instead.\n", "\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:74: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:83: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.\n", "\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:74: The name tf.ConfigProto is deprecated. Please use tf.compat.v1.ConfigProto instead.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:83: The name tf.ConfigProto is deprecated. Please use tf.compat.v1.ConfigProto instead.\n", "\n", "Device mapping:\n", "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", @@ -392,13 +313,13 @@ " * https://github.com/tensorflow/io (for I/O related ops)\n", "If you depend on functionality not listed there, please file an issue.\n", "\n", - "WARNING:tensorflow:From :52: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :52: BasicLSTMCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "This class is equivalent as tf.keras.layers.LSTMCell, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :54: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :54: MultiRNNCell.__init__ (from tensorflow.python.ops.rnn_cell_impl) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "This class is equivalent as tf.keras.layers.StackedRNNCells, and will be replaced by that in Tensorflow 2.0.\n", - "WARNING:tensorflow:From :57: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :57: static_rnn (from tensorflow.python.ops.rnn) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Please use `keras.layers.RNN(cell, unroll=True)`, which is equivalent to this API\n", "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:735: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.\n", @@ -407,7 +328,7 @@ "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/tensorflow_core/python/ops/rnn_cell_impl.py:739: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Call initializer instance with the dtype argument instead of passing it to the constructor\n", - "WARNING:tensorflow:From :10: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", + "WARNING:tensorflow:From :10: softmax_cross_entropy_with_logits (from tensorflow.python.ops.nn_ops) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "\n", "Future major versions of TensorFlow will allow gradients to flow\n", @@ -415,194 +336,162 @@ "\n", "See `tf.nn.softmax_cross_entropy_with_logits_v2`.\n", "\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:80: The name tf.global_variables_initializer is deprecated. Please use tf.compat.v1.global_variables_initializer instead.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:95: The name tf.global_variables_initializer is deprecated. Please use tf.compat.v1.global_variables_initializer instead.\n", "\n", - "Batch Loss = 3.521425, Accuracy = 0.18533333333333332\n", - "Batch Loss = 2.740264, Accuracy = 0.258\n", - "Batch Loss = 2.427801, Accuracy = 0.436\n", - "Batch Loss = 2.392420, Accuracy = 0.38333333333333336\n", - "Batch Loss = 2.336757, Accuracy = 0.3727810650887574\n", - "Batch Loss = 2.210095, Accuracy = 0.406\n", - "Batch Loss = 2.081177, Accuracy = 0.4806666666666667\n", - "Batch Loss = 1.928595, Accuracy = 0.5286666666666666\n", - "Batch Loss = 1.908575, Accuracy = 0.526\n", - "Batch Loss = 1.926798, Accuracy = 0.5968934911242604\n", - "Batch Loss = 1.857221, Accuracy = 0.532\n", - "Batch Loss = 1.840149, Accuracy = 0.5313333333333333\n", - "Batch Loss = 1.801555, Accuracy = 0.5926666666666667\n", - "Batch Loss = 1.740426, Accuracy = 0.5833333333333334\n", - "Batch Loss = 1.795835, Accuracy = 0.5769230769230769\n", - "Batch Loss = 1.758576, Accuracy = 0.5346666666666666\n", - "Batch Loss = 1.689172, Accuracy = 0.582\n", - "Batch Loss = 1.681654, Accuracy = 0.5986666666666667\n", - "Batch Loss = 1.631148, Accuracy = 0.608\n", - "Batch Loss = 1.640699, Accuracy = 0.6161242603550295\n", - "Batch Loss = 1.642633, Accuracy = 0.556\n", - "Batch Loss = 1.651147, Accuracy = 0.576\n", - "Batch Loss = 1.580308, Accuracy = 0.6373333333333333\n", - "Batch Loss = 1.506242, Accuracy = 0.626\n", - "Batch Loss = 1.523232, Accuracy = 0.639792899408284\n", - "Batch Loss = 1.518811, Accuracy = 0.6606666666666666\n", - "Batch Loss = 1.418894, Accuracy = 0.6646666666666666\n", - "Batch Loss = 1.394570, Accuracy = 0.7066666666666667\n", - "Batch Loss = 1.344470, Accuracy = 0.6933333333333334\n", - "Batch Loss = 1.326845, Accuracy = 0.7078402366863905\n", - "Batch Loss = 1.364004, Accuracy = 0.6373333333333333\n", - "Batch Loss = 1.334362, Accuracy = 0.6793333333333333\n", - "Batch Loss = 1.253492, Accuracy = 0.7186666666666667\n", - "Batch Loss = 1.297910, Accuracy = 0.6966666666666667\n", - "Batch Loss = 1.258239, Accuracy = 0.716715976331361\n", - "Batch Loss = 1.304081, Accuracy = 0.6733333333333333\n", - "Batch Loss = 1.259897, Accuracy = 0.7026666666666667\n", - "Batch Loss = 1.208963, Accuracy = 0.7246666666666667\n", - "Batch Loss = 1.200220, Accuracy = 0.7346666666666667\n", - "Batch Loss = 1.236549, Accuracy = 0.735207100591716\n", - "Batch Loss = 1.264173, Accuracy = 0.708\n", - "Batch Loss = 1.256850, Accuracy = 0.7066666666666667\n", - "Batch Loss = 1.171723, Accuracy = 0.7506666666666667\n", - "Batch Loss = 1.232849, Accuracy = 0.7553333333333333\n", - "Batch Loss = 1.173469, Accuracy = 0.742603550295858\n", - "Batch Loss = 1.241831, Accuracy = 0.712\n", - "Batch Loss = 1.205076, Accuracy = 0.728\n", - "Batch Loss = 1.148691, Accuracy = 0.7706666666666667\n", - "Batch Loss = 1.141159, Accuracy = 0.7606666666666667\n", - "Batch Loss = 1.148656, Accuracy = 0.7618343195266272\n", - "Batch Loss = 1.189209, Accuracy = 0.758\n", - "Batch Loss = 1.154536, Accuracy = 0.7686666666666667\n", - "Batch Loss = 1.111722, Accuracy = 0.776\n", - "Batch Loss = 1.168701, Accuracy = 0.8013333333333333\n", - "Batch Loss = 1.105878, Accuracy = 0.7455621301775148\n", - "Batch Loss = 1.244627, Accuracy = 0.7733333333333333\n", - "Batch Loss = 1.159975, Accuracy = 0.7206666666666667\n", - "Batch Loss = 1.115686, Accuracy = 0.7486666666666667\n", - "Batch Loss = 1.177998, Accuracy = 0.7826666666666666\n", - "Batch Loss = 1.092668, Accuracy = 0.8084319526627219\n", - "Batch Loss = 1.123520, Accuracy = 0.79\n", - "Batch Loss = 1.089692, Accuracy = 0.7773333333333333\n", - "Batch Loss = 1.080361, Accuracy = 0.7906666666666666\n", - "Batch Loss = 1.055221, Accuracy = 0.8406666666666667\n", - "Batch Loss = 1.039549, Accuracy = 0.8054733727810651\n", - "Batch Loss = 1.094885, Accuracy = 0.8086666666666666\n", - "Batch Loss = 1.111347, Accuracy = 0.7833333333333333\n", - "Batch Loss = 1.034543, Accuracy = 0.7993333333333333\n", - "Batch Loss = 0.981616, Accuracy = 0.8593333333333333\n", - "Batch Loss = 1.002564, Accuracy = 0.8306213017751479\n", - "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:199: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:206: The name tf.get_variable is deprecated. Please use tf.compat.v1.get_variable instead.\n", "\n" ] - }, + } + ], + "source": [ + "pipeline, outputs = pipeline.fit_transform(data_inputs, expected_outputs)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualize Accuracy" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { "data": { + "image/png": "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\n", "text/plain": [ - "HumanActivityRecognitionPipeline\n", - "(\n", - "\tHumanActivityRecognitionPipeline(\n", - "\tname=HumanActivityRecognitionPipeline,\n", - "\thyperparameters=HyperparameterSamples()\n", - ")(\n", - "\t\t[('OutputTransformerWrapper',\n", - " OutputTransformerWrapper(\n", - "\twrapped=OneHotEncoder(\n", - "\tname=one_hot_encoded_label,\n", - "\thyperparameters=HyperparameterSamples()\n", - "),\n", - "\thyperparameters=HyperparameterSamples()\n", - ")),\n", - " ('ClassificationRNNTensorFlowModel',\n", - " ClassificationRNNTensorFlowModel(\n", - "\tname=ClassificationRNNTensorFlowModel,\n", - "\thyperparameters=HyperparameterSamples([('n_steps', 128),\n", - " ('n_inputs', 9),\n", - " ('n_hidden', 32),\n", - " ('n_classes', 6),\n", - " ('learning_rate', 0.0025),\n", - " ('lambda_loss_amount', 0.0015),\n", - " ('batch_size', 1500)])\n", - ")),\n", - " ('Joiner', Joiner(\n", - "\tname=Joiner,\n", - "\thyperparameters=HyperparameterSamples()\n", - "))]\t\n", - ")\n", - ")" + "
" ] }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } ], "source": [ - "training_data_count = len(X_train)\n", - "training_iters = training_data_count * 3\n", + "accuracies_train = pipeline.get_epoch_metric_train('accuracy')\n", + "accuracies_validation = pipeline.get_epoch_metric_validation('accuracy')\n", "\n", - "pipeline = HumanActivityRecognitionPipeline()\n", - "\n", - "no_iter = int(math.floor(training_iters / BATCH_SIZE))\n", - "for _ in range(no_iter):\n", - " pipeline, outputs = pipeline.fit_transform(X_train, y_train)\n", - "\n", - "pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER))\n", - "\n", - "pipeline.teardown()" + "plot_metric(accuracies_train, accuracies_validation, xlabel='epoch', ylabel='accuracy', title='Model Accuracy')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Serve Rest Api" + "# Visualize Loss" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Device mapping:\n", - "/job:localhost/replica:0/task:0/device:XLA_CPU:0 -> device: XLA_CPU device\n", - "/job:localhost/replica:0/task:0/device:XLA_GPU:0 -> device: XLA_GPU device\n", - "\n", - "INFO:tensorflow:Restoring parameters from /home/alexandre/Documents/LSTM-Human-Activity-Recognition/cache/HumanActivityRecognitionPipeline/ClassificationRNNTensorFlowModel/ClassificationRNNTensorFlowModel.ckpt\n" - ] + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } ], "source": [ - "pipeline = HumanActivityRecognitionPipeline()\n", - "\n", - "pipeline = pipeline.load(ExecutionContext(DEFAULT_CACHE_FOLDER))\n", - "\n", - "# pipeline, outputs = pipeline.fit_transform(X_train, y_train) # we could train further more here for instance." + "loss = pipeline.get_step_by_name('TensorflowV1ModelStep').loss\n", + "plot_metric(loss, xlabel='batch', ylabel='softmax_cross_entropy_with_logits', title='softmax_cross_entropy_with_logits')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Save pipeline" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " * Serving Flask app \"neuraxle.api.flask\" (lazy loading)\n", - " * Environment: production\n", - " WARNING: This is a development server. Do not use it in a production deployment.\n", - " Use a production WSGI server instead.\n", - " * Debug mode: off\n" + "WARNING:tensorflow:From /home/alexandre/Documents/LSTM-Human-Activity-Recognition/neuraxle_tensorflow/tensorflow_v1.py:231: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", + "\n" ] }, { - "name": "stderr", - "output_type": "stream", - "text": [ - " * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n" + "ename": "AttributeError", + "evalue": "'DeepLearningPipeline' object has no attribute 'steps_as_tuple'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/IPython/core/formatters.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 700\u001b[0m \u001b[0mtype_pprinters\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype_printers\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 701\u001b[0m deferred_pprinters=self.deferred_printers)\n\u001b[0;32m--> 702\u001b[0;31m \u001b[0mprinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpretty\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 703\u001b[0m \u001b[0mprinter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mflush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 704\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mstream\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgetvalue\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/IPython/lib/pretty.py\u001b[0m in \u001b[0;36mpretty\u001b[0;34m(self, obj)\u001b[0m\n\u001b[1;32m 400\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mobject\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 401\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcls\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__dict__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'__repr__'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 402\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_repr_pprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcycle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 403\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 404\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0m_default_pprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcycle\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/IPython/lib/pretty.py\u001b[0m in \u001b[0;36m_repr_pprint\u001b[0;34m(obj, p, cycle)\u001b[0m\n\u001b[1;32m 695\u001b[0m \u001b[0;34m\"\"\"A pprint that just redirects to the normal repr function.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 696\u001b[0m \u001b[0;31m# Find newlines and replace them with p.break_()\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 697\u001b[0;31m \u001b[0moutput\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrepr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 698\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0midx\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0moutput_line\u001b[0m \u001b[0;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplitlines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 699\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0midx\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/src/neuraxle/neuraxle/base.py\u001b[0m in \u001b[0;36m__repr__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2817\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"(\\n\\t\\t\"\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mpprint\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msteps_as_tuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2818\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m\"\\t\\n)\"\u001b[0m\u001b[0;31m \u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2819\u001b[0;31m \u001b[0;34m+\u001b[0m \u001b[0;34m\"\\n)\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2820\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2821\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0moutput\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: 'DeepLearningPipeline' object has no attribute 'steps_as_tuple'" + ] + } + ], + "source": [ + "pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Serve Rest Api" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "module '__main__' has no attribute 'ClassificationRNNTensorFlowModel'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mpipeline\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mHumanActivityRecognitionPipeline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mpipeline\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpipeline\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mExecutionContext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mDEFAULT_CACHE_FOLDER\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;31m# pipeline, outputs = pipeline.fit_transform(X_train, y_train) # we could train further more here for instance.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/src/neuraxle/neuraxle/base.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(self, context)\u001b[0m\n\u001b[1;32m 1263\u001b[0m \u001b[0;31m# Each saver unstrips the step a bit more if needed\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1264\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msaver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcan_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloaded_self\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1265\u001b[0;31m \u001b[0mloaded_self\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msaver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload_step\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloaded_self\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1266\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1267\u001b[0m warnings.warn(\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/src/neuraxle/neuraxle/base.py\u001b[0m in \u001b[0;36mload_step\u001b[0;34m(self, step, context)\u001b[0m\n\u001b[1;32m 2047\u001b[0m \u001b[0;31m# Load each sub step with their savers\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2048\u001b[0m \u001b[0msub_step_to_load\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mIdentity\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstep_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msavers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msavers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2049\u001b[0;31m \u001b[0msub_step\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msub_step_to_load\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2050\u001b[0m \u001b[0mstep\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msteps_as_tuple\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstep_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0msub_step\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2051\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/src/neuraxle/neuraxle/base.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(self, context)\u001b[0m\n\u001b[1;32m 1263\u001b[0m \u001b[0;31m# Each saver unstrips the step a bit more if needed\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1264\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0msaver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcan_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloaded_self\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1265\u001b[0;31m \u001b[0mloaded_self\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msaver\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload_step\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mloaded_self\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1266\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1267\u001b[0m warnings.warn(\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/src/neuraxle/neuraxle/base.py\u001b[0m in \u001b[0;36mload_step\u001b[0;34m(self, step, context)\u001b[0m\n\u001b[1;32m 279\u001b[0m \u001b[0;34m:\u001b[0m\u001b[0;32mreturn\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 280\u001b[0m \"\"\"\n\u001b[0;32m--> 281\u001b[0;31m \u001b[0mloaded_step\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_create_step_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcontext\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 282\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 283\u001b[0m \u001b[0;31m# we need to keep the current steps in memory because they have been deleted before saving...\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/joblib/numpy_pickle.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(filename, mmap_mode)\u001b[0m\n\u001b[1;32m 603\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mload_compatibility\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 604\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 605\u001b[0;31m \u001b[0mobj\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_unpickle\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfilename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmmap_mode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 606\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 607\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mobj\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Documents/LSTM-Human-Activity-Recognition/venv/lib/python3.6/site-packages/joblib/numpy_pickle.py\u001b[0m in \u001b[0;36m_unpickle\u001b[0;34m(fobj, filename, mmap_mode)\u001b[0m\n\u001b[1;32m 527\u001b[0m \u001b[0mobj\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 528\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 529\u001b[0;31m \u001b[0mobj\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0munpickler\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 530\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0munpickler\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcompat_mode\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 531\u001b[0m warnings.warn(\"The file '%s' has been generated with a \"\n", + "\u001b[0;32m/usr/lib/python3.6/pickle.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1048\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mEOFError\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1049\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbytes_types\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1050\u001b[0;31m \u001b[0mdispatch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1051\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0m_Stop\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mstopinst\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1052\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mstopinst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/pickle.py\u001b[0m in \u001b[0;36mload_global\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 1336\u001b[0m \u001b[0mmodule\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"utf-8\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1337\u001b[0m \u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreadline\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"utf-8\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1338\u001b[0;31m \u001b[0mklass\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfind_class\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodule\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1339\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mklass\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1340\u001b[0m \u001b[0mdispatch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mGLOBAL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload_global\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/lib/python3.6/pickle.py\u001b[0m in \u001b[0;36mfind_class\u001b[0;34m(self, module, name)\u001b[0m\n\u001b[1;32m 1390\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0m_getattribute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodules\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mmodule\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1391\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1392\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmodules\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mmodule\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1393\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1394\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mload_reduce\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAttributeError\u001b[0m: module '__main__' has no attribute 'ClassificationRNNTensorFlowModel'" ] } ], + "source": [ + "pipeline = HumanActivityRecognitionPipeline()\n", + "\n", + "pipeline = pipeline.load(ExecutionContext(DEFAULT_CACHE_FOLDER))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "app = FlaskRestApiWrapper(\n", " json_decoder=CustomJSONDecoderFor2DArray(),\n", diff --git a/plotting.py b/plotting.py index 76db06d..96280d5 100644 --- a/plotting.py +++ b/plotting.py @@ -1,15 +1,17 @@ from matplotlib import pyplot as plt -def plot_metric(pipeline, metric_name, xlabel, ylabel, title): - accuracies = pipeline.get_epoch_metric_train(metric_name) - plt.plot(range(len(accuracies)), accuracies) +def plot_metric(metric_train, metric_validation=None, xlabel='x', ylabel='y', title='Metric'): + plt.plot(range(len(metric_train)), metric_train) - accuracies = pipeline.get_epoch_metric_validation(metric_name) - plt.plot(range(len(accuracies)), accuracies) + legend = ['training'] + if metric_validation is not None: + plt.plot(range(len(metric_validation)), metric_validation) + legend.append('validation') plt.xlabel(xlabel) plt.xlabel(ylabel) plt.title(title) - plt.legend(['training', 'validation'], loc='upper left') - plt.show() \ No newline at end of file + + plt.legend(legend, loc='upper left') + plt.show() diff --git a/requirements.txt b/requirements.txt index 5371ab0..3e11ae1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ tensorflow==1.15 tensorflow-gpu==1.15 conv==0.2 --e git://github.com/alexbrillant/Neuraxle.git@a270fe2b2f73c9350d76fcf4b6f058b764a8c8f7#egg=neuraxle +-e git://github.com/alexbrillant/Neuraxle.git@bb9bba79dbbf93c9b60c6c5036e9d6bf6021e5c5#egg=neuraxle diff --git a/train_and_save.py b/train_and_save.py index f6fc6c0..91e22a1 100644 --- a/train_and_save.py +++ b/train_and_save.py @@ -149,7 +149,13 @@ def main(): data_inputs, expected_outputs = load_data() pipeline, outputs = pipeline.fit_transform(data_inputs, expected_outputs) - plot_metric(pipeline, metric_name='accuracy', xlabel='epoch', ylabel='accuracy', title='Model Accuracy') + accuracies_train = pipeline.get_epoch_metric_train('accuracy') + accuracies_validation = pipeline.get_epoch_metric_validation('accuracy') + + plot_metric(accuracies_train, accuracies_validation, xlabel='epoch', ylabel='accuracy', title='Model Accuracy') + + loss = pipeline.get_step_by_name('TensorflowV1ModelStep').loss + plot_metric(loss, xlabel='batch', ylabel='softmax_cross_entropy_with_logits', title='softmax_cross_entropy_with_logits') pipeline.save(ExecutionContext(DEFAULT_CACHE_FOLDER)) pipeline.teardown()