Diff of /tensorflow_impl/cnn.py [000000] .. [74ff45]

Switch to side-by-side view

--- a
+++ b/tensorflow_impl/cnn.py
@@ -0,0 +1,341 @@
+import time
+import argparse
+
+import tensorflow as tf
+import numpy as np
+
+from utils import (shuffle_tensors, next_minibatch, get_labels,
+                   get_datasets, TensorBoardHandler, ModelSaver,
+                   check_processed_dir_existance)
+
+
+par = argparse.ArgumentParser(description="ECG Convolutional " +
+                                           "Neural Network implementation")
+
+par.add_argument("-lr", dest="learning_rate",
+                 type=float, default=0.001,
+                 help="Learning rate used by the model")
+
+par.add_argument("-e", dest="epochs",
+                 type=int, default=50,
+                 help="The number of epochs the model will train for")
+
+par.add_argument("-bs", dest="batch_size",
+                 type=int, default=32,
+                 help="The batch size of the model")
+
+par.add_argument("--display-step", dest="display_step",
+                 type=int, default=10,
+                 help="The display step")
+
+par.add_argument("--dropout", type=float, default=0.5,
+                 help="Dropout probability")
+
+par.add_argument("--restore", dest="restore_model",
+                 action="store_true", default=False,
+                 help="Restore the model previously saved")
+
+par.add_argument("--freeze", dest="freeze",
+                 action="store_true", default=False,
+                 help="Freezes the model")
+
+par.add_argument("--heart-diseases", nargs="+",
+                 dest="heart_diseases",
+                 default=["apnea-ecg", "svdb", "afdb"],
+                 choices=["apnea-ecg", "mitdb", "nsrdb", "svdb", "afdb"],
+                 help="Select the ECG diseases for the model")
+
+par.add_argument("--verbose", dest="verbose",
+                 action="store_true", default=False,
+                 help="Display information about minibatches")
+
+args = par.parse_args()
+
+# Parameters
+learning_rate = args.learning_rate
+epochs = args.epochs
+batch_size = args.batch_size
+display_step = args.display_step
+dropout = args.dropout
+restore_model = args.restore_model
+freeze = args.freeze
+heart_diseases = args.heart_diseases
+verbose = args.verbose
+
+# Network Parameters
+nr_inputs = 350 # changing this will also have to change the shape from wdense1
+nr_classes = len(heart_diseases)
+
+# TF Graph input
+x = tf.placeholder(tf.float32, shape=[None, nr_inputs], name="X_input")
+y = tf.placeholder(tf.float32, shape=[None, nr_classes], name="Y_classes")
+keep_prob = tf.placeholder(tf.float32)
+
+check_processed_dir_existance()
+
+
+class CNN(object):
+    weights = {
+        # 10x1 conv filter, 1 input, 64 outputs
+        'wconv1': tf.Variable(tf.random_normal([10, 1, 64])),
+        # 10x64 conv filter, 64 inputs, 128 outputs
+        'wconv2': tf.Variable(tf.random_normal([10, 64, 128])),
+        # 10x128 conv filter, 128 inputs, 128 outputs
+        'wconv3': tf.Variable(tf.random_normal([10, 128, 128])),
+        # 10x128 conv filter, 128 inputs, 256 outputs
+        'wconv4': tf.Variable(tf.random_normal([10, 128, 256])),
+        # fully connected, 1024 outputs
+        'wdense1': tf.Variable(tf.random_normal([5376, 1024])),
+        # fully connected, 1024 inputs, 2048 outputs
+        'wdense2': tf.Variable(tf.random_normal([1024, 2048])),
+        # 2048 inputs, class prediction
+        'wout': tf.Variable(tf.random_normal([2048, nr_classes]))
+    }
+
+    biases = {
+        'bconv1': tf.Variable(tf.random_normal([64])),
+        'bconv2': tf.Variable(tf.random_normal([128])),
+        'bconv3': tf.Variable(tf.random_normal([128])),
+        'bconv4': tf.Variable(tf.random_normal([256])),
+        'bdense1': tf.Variable(tf.random_normal([1024])),
+        'bdense2': tf.Variable(tf.random_normal([2048])),
+        'bout': tf.Variable(tf.random_normal([nr_classes]))
+    }
+
+    def __init__(self, weights=None, biases=None):
+        self.weights = weights if weights else self.weights
+        self.biases = biases if biases else self.biases
+        self.datasets = get_datasets(heart_diseases, nr_inputs)
+        self.label_data = get_labels(self.datasets)
+
+        self.saver = ModelSaver(save_dir="saved_models/cnn/")
+
+        logs_path = "tensorboard_data/cnn/"
+        self.tensorboard_handler = TensorBoardHandler(logs_path)
+        self.tensorboard_handler.add_histograms(self.weights)
+        self.tensorboard_handler.add_histograms(self.biases)
+
+        self.build()
+
+    def build(self):
+        dataset_len = []
+        for dataset in self.datasets:
+            dataset_len.append(len(dataset))
+
+        validation_size = int(0.1 * sum(dataset_len))
+
+        print("Validation size: {}".format(validation_size))
+        print("Total samples: {}".format(sum(dataset_len)))
+        print("Heart diseases: {}".format(', '.join(heart_diseases)))
+
+        # Shuffle the input, helps training
+        concat_dataset = np.concatenate(self.datasets)
+        concat_dataset, self.label_data = shuffle_tensors(concat_dataset, self.label_data)
+
+        # split training and testing sets
+        self.X_train, self.X_test = np.split(concat_dataset,
+                                             [len(concat_dataset)-validation_size])
+
+        self.Y_train, self.Y_test = np.split(self.label_data,
+                                             [len(self.label_data)-validation_size])
+
+        if verbose:
+            print("X_train shape: {}".format(self.X_train.shape))
+            print("Y_train shape: {}".format(self.Y_train.shape))
+            print("X_test shape: {}".format(self.X_test.shape))
+            print("Y_test shape: {}".format(self.Y_test.shape))
+
+    def train(self, x):
+        is_training = not freeze
+        # Reshape input so that we can feed it to the first conv layer
+        x = tf.reshape(x, shape=[-1, nr_inputs, 1])
+        
+        # Convolution Layer 1
+        conv1 = self.conv1d(x, self.weights['wconv1'], self.biases['bconv1'])
+        conv1 = self.maxpool1d(conv1)
+        # Batch Norm Layer 1
+        conv1 = tf.contrib.layers.batch_norm(conv1, is_training=is_training)
+
+        # Convolution Layer 2
+        conv2 = self.conv1d(conv1, self.weights['wconv2'], self.biases['bconv2'])
+        conv2 = self.maxpool1d(conv2)
+        # Batch Norm Layer 2
+        conv2 = tf.contrib.layers.batch_norm(conv2, is_training=is_training)
+
+        # Convolution Layer 3
+        conv3 = self.conv1d(conv2, self.weights['wconv3'], self.biases['bconv3'])
+        conv3 = self.maxpool1d(conv3)
+        # Batch Norm Layer 3
+        conv3 = tf.contrib.layers.batch_norm(conv3, is_training=is_training)
+
+        # Convolution Layer 4
+        conv4 = self.conv1d(conv3, self.weights['wconv4'], self.biases['bconv4'])
+        conv4 = self.maxpool1d(conv4)
+        # Batch Norm Layer 4
+        conv4 = tf.contrib.layers.batch_norm(conv4, is_training=is_training)
+
+        # Fully connected layer
+        # Reshape conv4 output to fit fully connected layer input
+        # shape_size is a cause for errors, it is determined using
+        # conv4.shape[1]*conv4.shape[2] and also has to be changed in weight definition
+        shape_size = conv4.shape[1] * conv4.shape[2]
+        fc1 = tf.reshape(conv4, [-1, shape_size])
+
+        # Fully connected layer 1
+        fc1 = tf.add(tf.matmul(fc1, self.weights['wdense1']), self.biases['bdense1'])
+        fc1 = tf.contrib.layers.batch_norm(fc1, is_training=is_training)
+        fc1 = tf.nn.relu(fc1)
+        fc1 = tf.nn.dropout(fc1, dropout)
+
+        # Fully connected layer 2
+        fc2 = tf.add(tf.matmul(fc1, self.weights['wdense2']), self.biases['bdense2'])
+        fc2 = tf.contrib.layers.batch_norm(fc2, is_training=is_training)
+        fc2 = tf.nn.relu(fc2)
+        fc2 = tf.nn.dropout(fc2, dropout)
+
+        # Output, class prediction
+        out = tf.add(tf.matmul(fc2, self.weights['wout']), self.biases['bout'])
+        return out
+
+    def conv1d(self, x, W, b, strides=1):
+        # conv1d needs a 3-D input([batch, in_width, in_channels]) and
+        # filter tensors([filter_width, in_channels, out_channels])
+        x = tf.nn.conv1d(x, W, stride=strides, padding='SAME')
+        x = tf.nn.bias_add(x, b)
+        return tf.nn.relu(x)
+
+    def maxpool1d(self, x, pool_size=2):
+        # [batch, height, width, channels] input type: tf.float32
+        return tf.contrib.keras.layers.MaxPool1D(pool_size=pool_size)(x)
+
+    def cost(self, pred):
+        softmax = tf.nn.softmax_cross_entropy_with_logits_v2(logits=pred,
+                                                             labels=y)
+        return tf.reduce_mean(softmax)
+
+    def optimizer(self, cost):
+        adam = tf.train.AdamOptimizer(learning_rate=learning_rate)
+        return adam.minimize(cost)
+
+    def evl(self, pred):
+        correct_pred = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1))
+        return tf.reduce_mean(tf.cast(correct_pred, tf.float32))
+
+    def get_data(self):
+        return (self.X_train, self.X_test,
+                self.Y_train, self.Y_test)
+
+
+# Construct model
+model = CNN()
+pred = model.train(x)
+
+# Define loss and optimizer
+cost = model.cost(pred)
+
+# Add scalar summary to cost tensor
+model.tensorboard_handler.add_scalar("training_loss", cost)
+
+# Create optimier
+optimizer = model.optimizer(cost)
+
+# Evaluate model
+accuracy = model.evl(pred)
+
+# Add scalar summary to accuracy tensor
+model.tensorboard_handler.add_scalar("training_accuracy", accuracy)
+# testing_acc = model.tensorboard_handler.add_scalar("testing_accuracy", accuracy)
+
+# Merge tensorboard data
+merged = model.tensorboard_handler.merge_all()
+
+# Initializing the variables
+init = tf.global_variables_initializer()
+
+X_train, X_test, Y_train, Y_test = model.get_data()
+
+
+# Launch the graph
+with tf.Session() as sess:
+    # Initialize the variables for the current session
+    sess.run(init)
+
+    # Add the graph to tensorboard writer
+    model.tensorboard_handler.writer.add_graph(sess.graph)
+    step = 1
+
+    # If restore_model flag True, restore the model
+    if restore_model:
+        model.saver.restore(sess)
+
+    # Set start time
+    total_time = time.time()
+    epoch_time = time.time()
+
+    print("-"*50)
+    # Train
+    for epoch in range(1, epochs):
+        for X_train_batch, Y_train_batch in next_minibatch(X_train, Y_train, batch_size):
+            sess.run(optimizer, feed_dict={x: X_train_batch,
+                                           y: Y_train_batch,
+                                           keep_prob: dropout})
+
+            # Once a few steps run the accuracy for the training model
+            if verbose and (step % display_step) == 0:
+                loss, acc = sess.run([cost, accuracy],
+                                     feed_dict={x: X_train,
+                                                y: Y_train,
+                                                keep_prob: 1.0})
+
+                print("Step: {}".format(step))
+                print("Training loss: {:.4f}".format(loss))
+                print("Training Accuracy: {:.4f}".format(acc))
+
+            step += 1
+
+        print("#"*50)
+        print("Epoch summary:")
+        print("Epoch: {}".format(epoch))
+        print("Training took: {0:.2f}s".format(time.time() - epoch_time))
+        summary, acc = sess.run([merged, accuracy],
+                                feed_dict={x: X_train,
+                                           y: Y_train,
+                                           keep_prob: 1.0})
+        print("Training accuracy: {0:.4f}".format(acc))
+
+        # Run testing accuracy
+        acc = sess.run(accuracy, feed_dict={x: X_test,
+                                            y: Y_test,
+                                            keep_prob: 1.0})
+        print("Testing accuracy: {0:.4f}".format(acc))
+        print("#"*50)
+
+        # write to log
+        model.tensorboard_handler.writer.add_summary(summary, epoch)
+
+        # Reset epoch time
+        epoch_time = time.time()
+
+    print("-"*50)
+
+    # Total training time
+    print("Total training time: {0:.2f}s".format(time.time() - total_time))
+    loss, acc = sess.run([cost, accuracy], feed_dict={x: X_train,
+                                                      y: Y_train,
+                                                      keep_prob: 1.0})
+    
+   
+    print("Training Accuracy: {0:.4f}".format(acc))
+    print("Training Loss: {0:.4f}".format(loss))
+
+    # If model not freezed, save the model
+    if not freeze:
+        model.saver.save(sess)
+
+    # Run testing accuracy
+    acc = sess.run(accuracy, feed_dict={x: X_test,
+                                        y: Y_test,
+                                        keep_prob: 1.0})
+    print("Testing Accuracy: {0:.4f}".format(acc))
+    print("-"*50)
+