You are on page 1of 22

5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API

hon API | Link…

1 Free Upgrade
Search
to Premium

Ahmed Gad 19 2 6

Complete Guide to Build ConvNet


HTTP-Based Application using
TensorFlow and Flask RESTful Python
API
Published on May 1, 2018

This tutorial takes you along the steps required to create a convolutional neural network
(CNN/ConvNet) using TensorFlow and get it into production by allowing remote access via
a HTTP-based application using Flask RESTful API.

In this tutorial, a CNN is to be built using TensorFlow NN (tf.nn) module. The CNN model
architecture is created and trained and tested against the CIFAR10 dataset. To make the
model remotely accessible, a Flask Web application is created using Python to receive an
uploaded image and return its classification label using HTTP. Anaconda3 is used in
addition to TensorFlow on Windows with CPU support. This tutorial assumes you have a
basic understanding of CNN such as layers, strides, and padding. Also knowledge about
Python is required.

The tutorial is summarized into the following steps:

1. Preparing the Environment by Installing Python, TensorFlow, PyCharm, and Flask


API.

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 1/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

1 Free Upgrade
Search
2. Downloading and Preparing the CIFAR-10 Dataset. to Premium

Ahmed Gad 19 2 6
3. Building the CNN Computational Graph using TensorFlow.

4. Training the CNN.

5. Saving the Trained CNN Model.

6. Preparing the Test Data and Restoring the Trained CNN Model.

7. Testing the Trained CNN Model.

8. Building the Flask Web Application.

9. Upload an Image using HTML Form.

10. Creating Helper HTML, JavaScript, and CSS Files.

11. HTTP-Based Remote Accessing the Trained Model for Prediction.

1. Installing Python, TensorFlow, PyCharm, and Flask API

Before starting building the project, it is required to prepare its environment. Python is the
first tool to start installing because the environment is fully dependent on it. If you already
have the environment prepared, you can skip the first step.

1.1 Anaconda/Python Installation

It is possible to install the native Python distribution but it is recommended to use an all-in-
one package such as Anaconda because it does some stuff for you. In this project, Anaconda
3 is used. For Windows, the executable file can be downloaded from
https://www.anaconda.com/download/#windows. It could be installed easily.

To ensure Anaconda3 is installed properly, the CMD command (where python) could be
issued as in figure 1. If Anaconda3 is installed properly, its installation path will appear in
the command output.

Figure 1

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 2/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
1.2 TensorFlow Installation 1 Free Upgrade
Search
to Premium

After installation
Ahmed GadPython using Anaconda3, next is to install TensorFlow (TF).
19 This tutorial
2 6

uses TF on Windows with CPU support. The installation instructions are found in this page
https://www.tensorflow.org/install/install_windows. This YouTube video might be helpful
(https://youtu.be/MsONo20MRVU).

TF installation steps are as follows:

1) Creating a conda environment for TF by invoking this command:

C:> conda create -n tensorflow pip python=3.5

This creates an empty folder holding the virtual environment (venv) for TF installation. The
venv is located under Anaconda3 installation directory in this location
(\Anaconda3\envs\tensorflow).

2) Activating the venv for TensorFlow installation using this command:

C:> activate tensorflow

The above command tells that we are inside the venv and any library installation will be
inside it. The command prompt is expected to be changed after this command to be
(tensorflow)C:>. After getting into the directory, we are ready to install the library.

3) After activating the venv, the CPU-only version of Windows TensorFlow could be
installed by issuing this command:

(tensorflow)C:> pip install --ignore-installed --upgrade tensorflow

To test whether TF is installed properly, we can try to import it as in figure 2. But remember
before importing TF, its venv must be activated. Testing it from the CMD, we need to issue
the python command in order to be able to interact with Python. Because no error occurred
in the import line, TF is successfully installed.

Figure 2

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 3/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

1 Free Upgrade
Search
to Premium

Ahmed Gad
1.3 PyCharm Python IDE Installation 19 2 6

For this project, it is recommended to use a Python IDE rather than entering commands in
CMD. The IDE used in this tutorial is PyCharm. Its Windows executable file could be
downloaded from this page
https://www.jetbrains.com/pycharm/download/#section=windows. Its installation
instructions are pretty simple.

After downloading and installation PyCharm Python IDE, next is to link it with TF. This is
done by setting its Python interpreter to the installed Python under the TF venv as in figure
3. This is done by opening the settings of the IDE and choosing the Project interpreter to the
python.exe file installed inside the TF venv.

Figure 3

1.4 Flask Installation

The last tool to get installed is the Flask RESTful API. It is a library to be installed using
pip/conda installer under the TF venv using the following CMD command:

C:> pip install Flask-API

If not already installed, NumPy and SciPy should be installed inside the venv in order to be
able to read and manipulate images.

By installing Anaconda (Python), TensorFlow, PyCharm, and Flask, we are ready to start
building the project.

2. Downloading and Preparing the CIFAR-10 Dataset

The Python version of the 10 classes CIFAR dataset (CIFAR-10) could be downloaded from
this page https://www.cs.toronto.edu/~kriz/cifar.html. The dataset contains 60,000 images
divided into training and testing data. There are five files holding the training data where
each file has 10,000 images. The images are RGB of size 32x32x3. The training files are
named data_batch_1, data_batch_2, and so on. There is a single file holding the test data
named test_batch with 10,000 images. A metadata file named batches.meta is available
that holds the dataset classes labels which are airplane automobile bird cat deer dog
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 4/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
that holds the dataset classes labels which are airplane, automobile, bird, cat, deer, dog,
1 Free Upgrade
Search frog, horse, ship, and truck. to Premium

Ahmed Gad 19 2 6
Because each file in the dataset is a binary file, it should be decoded in order to retrieve the
actual image data. For such reason, a function called unpickle_patch is created to do such
job defined as follows:

def unpickle_patch(file):
    """
    Decoding the binary file.
    :param file:File to decode it data.
    :return: Dictionary of the file holding details including input data a
    """
    patch_bin_file = open(file, 'rb')#Reading the binary file.
    patch_dict = pickle.load(patch_bin_file, encoding='bytes')#Loading the
    return patch_dict#Returning the dictionary.

The method accepts the binary file name and returns a dictionary holding details about such
file. The dictionary holds the data for all 10,000 samples within the file in addition to their
labels.

In order to decode the entire training data, a new function called get_dataset_images is
created. That function accepts the dataset path and works only on the training data. As a
result, it filters the files under this path and returns only files starting with data_batch_. The
testing data is prepared later after building and training the CNN.

For each training file, it is decoded by calling the unpickle_patch function. Based on the
dictionary returned by such function, the get_dataset_images function returns both the
images data and their class labels. The images data is retrieved from the ‘data’ key and their
class labels are retrieved from the ‘labels’ key.

Because the images data are saved as 1D vector, it should be reshaped to be of 3 dimensions.
This is because TensorFlow accepts the images of such shape. For such reason, the
get_dataset_images function accepts the number of rows/columns in addition to the number
of channels in each image as arguments.

The implementation of such function is as follows:

def get_dataset_images(dataset_path, im_dim=32, num_channels=3):


    """

    This function accepts the dataset path, reads the data, and returns it
    :param dataset_path:Path of the CIFAR10 dataset binary files.
    :param im_dim:Number of rows and columns in each image. The image is e
    :param num_channels:Number of color channels in the image.
    :return:Returns the input data after being reshaped and output labels.
    """
    num_files = 5#Number of training binary files in the CIFAR10 dataset.
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 5/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

Search     images_per_file = 10000#Number of samples withing each


1 binary file. Free Upgrade
to Premium
    files_names = os.listdir(patches_dir)#Listing the binary files in the
   Ahmed
""" Gad 19 2 6

    Creating an empty array to hold the entire training data after being r
    The dataset has 5 binary files holding the data. Each binary file has
    Each sample has a total of 3,072 pixels. These pixels are reshaped to
    Finally, the entire dataset has 50,000 samples and each sample of shap
    """
    dataset_array = numpy.zeros(shape=(num_files * images_per_file, im_dim
    #Creating an empty array to hold the labels of each input sample. Its
    dataset_labels = numpy.zeros(shape=(num_files * images_per_file), dtyp

    index = 0#Index variable to count number of training binary files bein
    for file_name in files_names:

        """
        Because the CIFAR10 directory does not only contain the desired tr

        Training files start by 'data_batch_' which is used to test whethe

        """
        if file_name[0:len(file_name) - 1] == "data_batch_":

            print("Working on : ", file_name)


            """

            Appending the path of the binary files to the name of the curr
            Then the complete path of the binary file is used to decoded t

            """
            data_dict = unpickle_patch(dataset_path+file_name)

            """
            Returning the data using its key 'data' in the dictionary.

            Character b is used before the key to tell it is binary string


            """

            images_data = data_dict[b"data"]


            #Reshaping all samples in the current binary file to be of 32x

            images_data_reshaped = numpy.reshape(images_data, newshape=(le


            #Appending the data of the current file after being reshaped.

            dataset_array[index * images_per_file:(index + 1) * images_per


            #Appening the labels of the current file.

            dataset_labels[index * images_per_file:(index + 1) * images_pe


            index = index + 1#Incrementing the counter of the processed tr

    return dataset_array, dataset_labels#Returning the training input data

By preparing the training data, we can build and train the CNN model using TF.

3. Building the CNN Computational Graph using TensorFlow

The computational graph of the CNN is created inside a function called create_CNN. It
creates a stack of convolution (conv), ReLU, max pooling, dropout, and fully connected
(FC) layers and returns the results of the last fully connected layer. The output of each layer
is the input to the next layer. This requires consistency between the sizes of outputs and
inputs of neighboring layers. Note that for each conv, ReLU, and max pooling layers, there
are some of parameters to get specified such as strides across each dimension and padding.
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 6/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
are some of parameters to get specified such as strides across each dimension and padding.
1 Free Upgrade
Search
to Premium

defAhmed Gad
create_CNN(input_data, num_classes, keep_prop): 19 2 6

    """
    Builds the CNN architecture by stacking conv, relu, pool, dropout, and

    :param input_data:patch data to be processed.


    :param num_classes:Number of classes in the dataset. It helps determin

    :param keep_prop:probability of dropping neurons in the dropout layer.


    :return: last fully connected layer.

    """
    #Preparing the first convolution layer.

    filters1, conv_layer1 = create_conv_layer(input_data=input_data, filte


    """

    Applying ReLU activation function over the conv layer output.
    It returns a new array of the same shape as the input array.

    """

    relu_layer1 = tensorflow.nn.relu(conv_layer1)


    print("Size of relu1 result : ", relu_layer1.shape)

    """
    Max pooling is applied to the ReLU layer result to achieve translation

    It returns a new array of a different shape from the the input array r
    """

    max_pooling_layer1 = tensorflow.nn.max_pool(value=relu_layer1,


                                                ksize=[1, 2, 2, 1],

                                                strides=[1, 1, 1, 1],


                                                padding="VALID")

    print("Size of maxpool1 result : ", max_pooling_layer1.shape)

    #Similar to the previous conv-relu-pool layers, new layers are just st
    #Conv layer with 3 filters and each filter is of sisze of 5x5.

    filters2, conv_layer2 = create_conv_layer(input_data=max_pooling_layer


    relu_layer2 = tensorflow.nn.relu(conv_layer2)

    print("Size of relu2 result : ", relu_layer2.shape)


    max_pooling_layer2 = tensorflow.nn.max_pool(value=relu_layer2,

                                                ksize=[1, 2, 2, 1],


                                                strides=[1, 1, 1, 1],

                                                padding="VALID")
    print("Size of maxpool2 result : ", max_pooling_layer2.shape)

    #Conv layer with 2 filters and a filter sisze of 5x5.

    filters3, conv_layer3 = create_conv_layer(input_data=max_pooling_layer


    relu_layer3 = tensorflow.nn.relu(conv_layer3)

    print("Size of relu3 result : ", relu_layer3.shape)


    max_pooling_layer3 = tensorflow.nn.max_pool(value=relu_layer3,

                                                ksize=[1, 2, 2, 1],


                                                strides=[1, 1, 1, 1],

                                                padding="VALID")
    print("Size of maxpool3 result : ", max_pooling_layer3.shape)

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 7/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

    #Adding dropout layer before the fully connected layers


1 Free Upgrade
to avoid overf
Search
to Premium
    flattened_layer = dropout_flatten_layer(previous_layer=max_pooling_lay
Ahmed Gad 19 2 6

    #First fully connected (FC) layer. It accepts the result of the dropou
    fc_resultl = fc_layer(flattened_layer=flattened_layer, num_inputs=flat

                          num_outputs=200)
    #Second fully connected layer accepting the output of the previous ful

    fc_result2 = fc_layer(flattened_layer=fc_resultl, num_inputs=fc_result


                          num_outputs=num_classes)

    print("Fully connected layer results : ", fc_result2)

    return fc_result2#Returning the result of the last FC layer.

Because the convolution layer applies the convolution operation between the input data and
the set of filters used, the create_CNN function accepts the input data as an input argument.
Such data is what returned by the get_dataset_images function. The convolution layer is
created using the create_conv_layer function. The create_conv_layer function accepts the
input data, filter size, and number of filters and returns the result of convolving the input
data with the set of filters. The set of filters have their size set according to the depth of the
input images. The create_conv_layer is defined as follows:

def create_conv_layer(input_data, filter_size, num_filters):


    """

    Builds the CNN convolution (conv) layer.


    :param input_data:patch data to be processed.

    :param filter_size:#Number of rows and columns of each filter. It is e


    :param num_filters:Number of filters.

    :return:The last fully connected layer of the network.


    """

    """
    Preparing the filters of the conv layer by specifiying its shape.

    Number of channels in both input image and each filter must match.
    Because number of channels is specified in the shape of the input imag

    """
    filters = tensorflow.Variable(tensorflow.truncated_normal(shape=(filte

                                                              stddev=0.05)
    print("Size of conv filters bank : ", filters.shape)

    """

    Building the convolution layer by specifying the input data, filters,
    Padding value of 'VALID' means the some borders of the input image wil

    """
    conv_layer = tensorflow.nn.conv2d(input=input_data,

                                      filter=filters,
                                      strides=[1, 1, 1, 1],

                                      padding="VALID")
    print("Size of conv result : ", conv_layer.shape)

    return filters, conv_layer#Returing the filters and the convolution la

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 8/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

1 Free Upgrade
Search
to Premium

Ahmed Gad 19 2 6
Another argument is the probability of keeping neurons in the dropout layer. It specifies how
much neurons are dropped by the dropout layer. The dropout layer is implemented using
the dropout_flatten_layer function as shown below. Such function returns a flattened array
that will be the input to the fully connected layer.

def dropout_flatten_layer(previous_layer, keep_prop):

    """
    Applying the dropout layer.

    :param previous_layer: Result of the previous layer to the dropout lay
    :param keep_prop: Probability of keeping neurons.

    :return: flattened array.


    """

    dropout = tensorflow.nn.dropout(x=previous_layer, keep_prob=keep_prop)

    num_features = dropout.get_shape()[1:].num_elements()


    layer = tensorflow.reshape(previous_layer, shape=(-1, num_features))#F

    return layer

Because the last FC layer should have number of output neurons equal to the number of
dataset classes, the number of dataset classes is used as another input argument to the
create_CNN function. The fully connected layer is created using the fc_layer function. Such
function accepts the flattened result of the dropout layer, the number of features in such
flattened result, and number of output neurons from such FC layer. Based on number of
inputs and outputs, a weights tensor is created which get then multiplied by the flattened
layer to get the returned result of the FC layer.

def fc_layer(flattened_layer, num_inputs, num_outputs):


    """

    uilds a fully connected (FC) layer.


    :param flattened_layer: Previous layer after being flattened.

    :param num_inputs: Number of inputs in the previous layer.


    :param num_outputs: Number of outputs to be returned in such FC layer.

    :return:
    """

    #Preparing the set of weights for the FC layer. It depends on the numb
    fc_weights = tensorflow.Variable(tensorflow.truncated_normal(shape=(nu

                                                              stddev=0.05)
    #Matrix multiplication between the flattened array and the set of weig

    fc_resultl = tensorflow.matmul(flattened_layer, fc_weights)


    return fc_resultl#Output of the FC layer (result of matrix multiplicat

The computational graph after being visualized using TensorBoard is shown in figure 4.

Figure 4
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 9/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
g
1 Free Upgrade
Search
to Premium

Ahmed Gad 19 2 6

4. Training the CNN


https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 10/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

1 Free Upgrade
Search
to Premium
After building the computational graph of the CNN, next is to train it against the previously
preparedAhmed
training
Gaddata. The training is done according to the following code.
19The code
2 starts6

by preparing the path of the dataset and preparing it into a placeholder. Note that the path
should be changed to be suitable to your system. Then it calls the previously discussed
functions. The predictions of the trained CNN is used to measure the cost of the network
which is to be minimized using the gradient descent optimizer. Note: some of the tensors
have a name which is helpful for retrieving such tensors later when testing the CNN.

#Nnumber of classes in the dataset. Used to specify number of outputs in t


num_datatset_classes = 10

#Number of rows & columns in each input image. The image is expected to be
im_dim = 32

#Number of channels in rach input image. Used to reshape the images and sp
num_channels = 3

#Directory at which the training binary files of the CIFAR10 dataset are s
patches_dir = "C:\\Users\\Dell\\Downloads\\Compressed\\cifar-10-python\\ci
#Reading the CIFAR10 training binary files and returning the input data an
dataset_array, dataset_labels = get_dataset_images(dataset_path=patches_di
print("Size of data : ", dataset_array.shape)

"""
Input tensor to hold the data read above. It is the entry point of the com
The given name of 'data_tensor' is useful for retreiving it when restoring
"""

data_tensor = tensorflow.placeholder(tensorflow.float32, shape=[None, im_d


"""
Tensor to hold the outputs label.
The name "label_tensor" is used for accessing the tensor when tesing the s

"""
label_tensor = tensorflow.placeholder(tensorflow.float32, shape=[None], na

#The probability of dropping neurons in the dropout layer. It is given a n


keep_prop = tensorflow.Variable(initial_value=0.5, name="keep_prop")

#Building the CNN architecure and returning the last layer which is the fu
fc_result2 = create_CNN(input_data=data_tensor, num_classes=num_datatset_c

"""
Predicitions probabilities of the CNN for each training sample.

Each sample has a probability for each of the 10 classes in the dataset.
Such tensor is given a name for accessing it later.
"""
softmax_propabilities = tensorflow.nn.softmax(fc_result2, name="softmax_pr

"""
Predicitions labels of the CNN for each training sample.
Th i t l i l ifi d th l f th hi h t b bilit
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 11/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
The input sample is classified as the class of the highest probability.
1 Free Upgrade
Search
axis=1 indicates that maximum of values in the second axis is to be return
to Premium

"""
Ahmed Gad 19 2 6
softmax_predictions = tensorflow.argmax(softmax_propabilities, axis=1)

#Cross entropy of the CNN based on its calculated probabilities.


cross_entropy = tensorflow.nn.softmax_cross_entropy_with_logits(logits=ten
                                                                labels=lab
#Summarizing the cross entropy into a single value (cost) to be minimized
cost = tensorflow.reduce_mean(cross_entropy)
#Minimizng the network cost using the Gradient Descent optimizer with a le
error = tensorflow.train.GradientDescentOptimizer(learning_rate=.01).minim

#Creating a new TensorFlow Session to process the computational graph.


sess = tensorflow.Session()
#Wiriting summary of the graph to visualize it using TensorBoard.
tensorflow.summary.FileWriter(logdir="./log/", graph=sess.graph)

#Initializing the variables of the graph.


sess.run(tensorflow.global_variables_initializer())

"""

Because it may be impossible to feed the complete data to the CNN on norma
A percent of traning samples is used to create each path. Samples for each
"""
num_patches = 5#Number of patches

for patch_num in numpy.arange(num_patches):


    print("Patch : ", str(patch_num))
    percent = 80 #percent of samples to be included in each path.
    #Getting the input-output data of the current path.

    shuffled_data, shuffled_labels = get_patch(data=dataset_array, labels=


    #Data required for cnn operation. 1)Input Images, 2)Output Labels, and
    cnn_feed_dict = {data_tensor: shuffled_data,
                     label_tensor: shuffled_labels,

                     keep_prop: 0.5}


    """
    Training the CNN based on the current patch.

    CNN error is used as input in the run to minimize it.


    SoftMax predictions are returned to compute the classification accurac
    """
    softmax_predictions_, _ = sess.run([softmax_predictions, error], feed_

    #Calculating number of correctly classified samples.


    correct = numpy.array(numpy.where(softmax_predictions_ == shuffled_lab
    correct = correct.size
    print("Correct predictions/", str(percent * 50000/100), ' : ', correct

Rather than feeding the entire training data to the CNN, the data is divided into set of
patches and patch by patch will feed the network using a loop. Each patch contains subset of
the training data. The patches are returned using the get_patch function. Such function
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 12/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

accepts the input data, labels, and percent of samples to be returned from
1 such data. It then Free Upgrade
Search
to Premium
returns subset of the data according to the input percent.
Ahmed Gad 19 2 6

def get_patch(data, labels, percent=70):


    """
    Returning patch to train the CNN.

    :param data: Complete input data after being encoded and reshaped.
    :param labels: Labels of the entire dataset.
    :param percent: Percent of samples to get returned in each patch.
    :return: Subset of the data (patch) to train the CNN model.

    """
    #Using the percent of samples per patch to return the actual number of
    num_elements = numpy.uint32(percent*data.shape[0]/100)
    shuffled_labels = labels#Temporary variable to hold the data after bei

    numpy.random.shuffle(shuffled_labels)#Randomly reordering the labels.


    """
    The previously specified percent of the data is returned starting from
    The labels indices are also used to return their corresponding input i

    """
    return data[shuffled_labels[:num_elements], :, :, :], shuffled_labels[

5. Saving the Trained CNN Model

After training the CNN, the model is saved for reuse later for testing it in another Python
script. You should also change the path where the model is saved to be suitable to your
system.

#Saving the model after being trained.


saver = tensorflow.train.Saver()
save_model_path = "C:\\model\\"
save_path = saver.save(sess=sess, save_path=save_model_path+"model.ckpt")
print("Model saved in : ", save_path)

6. Preparing the Test Data and Restoring the Trained CNN Model

Before testing the trained model, it is required to prepare the test data and restore the
previously trained model. Test data preparation is similar to what happened with the training
data except that there is just a single binary file to be decoded. The test file is decoded
according to the modified get_dataset_images function. This function calls the
unpickle_patch function exactly as what done before with training data.

def get_dataset_images(test_path_path, im_dim=32, num_channels=3):

    """
    Similar to the one used in training except that there is just a single
"""
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 13/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
   
1 Free Upgrade
Search
    print("Working on testing patch") to Premium

    data_dict = unpickle_patch(test_path_path)


Ahmed Gad 19 2 6
    images_data = data_dict[b"data"]
    dataset_array = numpy.reshape(images_data, newshape=(len(images_data),

    return dataset_array, data_dict[b"labels"]

7. Testing the Trained CNN Model.

After preparing the test data and restoring the trained model, we can start testing the model
according to the following code. What worth mentioning is that our goal is to just return the
network predictions for the input samples. This is why the TF session runs to return just the
predictions. When training the CNN, the session runs to minimize the cost. In testing, we are
not interested in minimizing the cost anymore. Another interesting point is that the keep
probability of the dropout layer is now set to 1. That means do not drop any node. This is
because we are just using the pre-trained model after settling on what nodes to drop. Now
we just use what the model did before and not interested in making modification to it by
dropping other nodes.

#Dataset path containing the testing binary file to be decoded.


patches_dir = "C:\\Users\\Dell\\Downloads\\Compressed\\cifar-10-python\\ci
dataset_array, dataset_labels = get_dataset_images(test_path_path=patches_
print("Size of data : ", dataset_array.shape)

sess = tensorflow.Session()

#Restoring the previously saved trained model.


saved_model_path = 'C:\\Users\\Dell\\Desktop\\model\\'
saver = tensorflow.train.import_meta_graph(saved_model_path+'model.ckpt.me
saver.restore(sess=sess, save_path=saved_model_path+'model.ckpt')

#Initalizing the varaibales.


sess.run(tensorflow.global_variables_initializer())

graph = tensorflow.get_default_graph()

"""
Restoring previous created tensors in the training phase based on their gi
Some of such tensors will be assigned the testing input data and their out

Others are helpful in assessing the model prediction accuracy (softmax_pro


"""
softmax_propabilities = graph.get_tensor_by_name(name="softmax_probs:0")
softmax_predictions = tensorflow.argmax(softmax_propabilities, axis=1)

data_tensor = graph.get_tensor_by_name(name="data_tensor:0")
label_tensor = graph.get_tensor_by_name(name="label_tensor:0")
keep_prop = graph.get_tensor_by_name(name="keep_prop:0")

#keep prop is equal to 1 because there is no more interest to remove neuro


https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 14/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
#keep_prop is equal to 1 because there is no more interest to remove neuro
1 Free Upgrade
Search feed_dict_testing = {data_tensor: dataset_array, to Premium

                     label_tensor: dataset_labels,


Ahmed Gad 19 2 6
                     keep_prop: 1.0}
#Running the session to predict the outcomes of the testing samples.
softmax_propabilities_, softmax_predictions_ = sess.run([softmax_propabili
                                                      feed_dict=feed_dict_

#Assessing the model accuracy by counting number of correctly classified s


correct = numpy.array(numpy.where(softmax_predictions_ == dataset_labels))
correct = correct.size
print("Correct predictions/10,000 : ", correct)

8. Building the Flask Web Application

After training the CNN model, we can add it to a HTTP server and allow users to use it
online. User will upload an image using a HTTP client. The uploaded image will be received
by the HTTP server or more specifically by a Flask Web application. Such application will
predict the class label of the image based on the trained model and finally returns the class
label back to the HTTP client. Such discussion is summarized in figure 5.

Figure 5

import flask

#Creating a new Flask Web application. It accepts the package name.


app = flask.Flask("CIFAR10_Flask_Web_App")

"""

To activate the Web server to receive requests, the application must run.
A good practice is to check whether the file is whether the file called fr
If not, then it will run.
"""

if __name__ == "__main__":
    """
    In this example, the app will run based on the following properties:
host: localhost
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 15/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
    host: localhost
1 Free Upgrade
Search
    port: 7777 to Premium

    debug: flag set to True to return debugging information.


Ahmed Gad 19 2 6
    """
    app.run(host="localhost", port=7777, debug=True)

Currently, there is no functions the server provide. The first thing the server should do is to
allow the user to upload an image. When the user visits the root URL of the application, the
application do nothing. The application can redirect the user to an HTML page at which the
user could upload an image. To do that, the application has a function called redirect_upload
to redirect the user to a page for uploading an image. What lets this function to get executed
after the user visits the root of the app is the routing created using the following line:

app.add_url_rule(rule="/", endpoint="homepage", view_func=redirect_upload)

This line says that if the user visits the root of the app (marked as "/"), then the viewer
function (redirect_upload) will be called. Such function do nothing except rendering a
HTML page called upload_image.html. Such page is located under the special templates
directory of the server. A page inside the templates directory is rendered by calling the
render_template function. Note that there is an attribute called endpoint which makes it easy
to reuse the same route multiple times without hard coding it.

def redirect_upload():
    """
    A viewer function that redirects the Web application from the root to

    The HTML page is located under the /templates directory of the applica
    :return: HTML page used for uploading an image. It is 'upload_image.ht
    """
    return flask.render_template(template_name_or_list="upload_image.html"

"""
Creating a route between the homepage URL (http://localhost:7777) to a vie
Endpoint 'homepage' is used to make the route reusable without hard-coding

"""
app.add_url_rule(rule="/", endpoint="homepage", view_func=redirect_upload)

The screen of the HTML page rendered is shown in figure 6.

Figure 6

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 16/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

1 Free Upgrade
Search
to Premium

Ahmed Gad 19 2 6

Here is the HTML code of such page. It is a simple form that allows the user to upload an
image file. When submitting such form, a POST HTTP message is to be returned to the
URL http://localhost:7777/upload/.

<!DOCTYPE html>
<html lang="en">
<head>

    <link rel="stylesheet" type="text/css" href="{{url_for(endpoint='stati


    <meta charset="UTF-8">
    <title>Upload Image</title>
</head>

<body>
<form enctype="multipart/form-data" method="post" action="http://localhost
    <center>
    <h3>Select CIFAR10 image to predict its label.</h3>

    <input type="file" name="image_file" accept="image/*"><br>


    <input type="submit" value="Upload">
    </center>
</form>

</body>
</html>

After returning back to the server from the HTML form, the viewer function that is
associated with the URL specified in the form action attribute will be called which is the
upload_image function. Such function gets the image selected by the user and saves it to the
server.

def upload_image():
    """

    Viewer function that is called in response to getting to the 'http://l


    It uploads the selected image to the server.
    :return: redirects the application to a new page for predicting the cl
    """

    #Global variable to hold the name of the image file for reuse later in
    global secure_filename
    if flask.request.method == "POST":#Checking of the HTTP method initiat
        img_file = flask.request.files["image_file"]#Getting the file name

        secure_filename = werkzeug.secure_filename(img_file.filename)#Gett


        img_path = os.path.join(app.root_path, secure_filename)#Preparing
img file save(img path)#Saving the image in the specified path
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 17/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
        img_file.save(img_path)#Saving the image in the specified path.
1 Free Upgrade
Search         print("Image uploaded successfully.") to Premium
        """
Ahmed Gad 19 2 6
        After uploading the image file successfully, next is to predict th
        The application will fetch the URL that is tied to the HTML page r

        The URL is fetched using the endpoint 'predict'.


        """
        return flask.redirect(flask.url_for(endpoint="predict"))
    return "Image upload failed."

"""
Creating a route between the URL (http://localhost:7777/upload) to a viewe
Endpoint 'upload' is used to make the route reusable without hard-coding i
The set of HTTP method the viewer function is to respond to is added using

In this case, the function will just respond to requests of method of type
"""
app.add_url_rule(rule="/upload/", endpoint="upload", view_func=upload_imag

After uploading the image successfully to the server, we are ready to read the image and
predict its class label using the previously trained CNN model. For such reason, the
upload_image function redirects the application to the viewer function that is responsible for
predicting the class label of an image. Such viewer function is reached by its endpoint as
specified in this line:

return flask.redirect(flask.url_for(endpoint="predict"))

The method associated with endpoint="predict" will be called which is the CNN_predict
function. Such method reads the image and checks whether it matches the dimensions of the
CIFAR-10 dataset which is 32x32x3. If the image matches the specifications of the CIFAR-
10 dataset, then it will be passed to a function responsible for making prediction as in the
following line:

predicted_class = CIFAR10_CNN_Predict_Image.main(img)

The main function responsible for predicting the class label of an image is defined as shown
below. It restores the trained model and runs a session that returns the predicted class of the
image. The predicted class is returned back to the Flask Web application.

def CNN_predict():
    """
    Reads the uploaded image file and predicts its label using the saved p
    :return: Either an error if the image is not for CIFAR10 dataset or re

    """
    """
    Setting the previously created 'secure_filename' to global.

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 18/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
    This is because to be able invoke a global variable 1created in another
Free Upgrade
Search
to Premium
    """
   Ahmed
global secure_filename
Gad 19 2 6

    #Reading the image file from the path it was saved in previously.
    img = scipy.misc.imread(os.path.join(app.root_path, secure_filename))

    """
    Checking whether the image dimensions match the CIFAR10 specifications
    CIFAR10 images are RGB (i.e. they have 3 dimensions). It number of dim

    """
    if(img.ndim) == 3:
        """
        Checking if the number of rows and columns of the read image match

        """
        if img.shape[0] == img.shape[1] and img.shape[0] == 32:
            """
            Checking whether the last dimension of the image has just 3 ch

            """
            if img.shape[-1] == 3:
                """
                Passing all conditions above, the image is proved to be of

                This is why it is passed to the predictor.


                """
                predicted_class = CIFAR10_CNN_Predict_Image.main(img)

                """
                After predicting the class label of the input image, the p
                The HTML page is fetched from the /templates directory. Th
                """

                return flask.render_template(template_name_or_list="predic


            else:
                # If the image dimensions do not match the CIFAR10 specifi
                return flask.render_template(template_name_or_list="error.

        else:
            # If the image dimensions do not match the CIFAR10 specificati
            return flask.render_template(template_name_or_list="error.html
    return "An error occurred."#Returned if there is a different error oth

"""
Creating a route between the URL (http://localhost:7777/predict) to a view
Endpoint 'predict' is used to make the route reusable without hard-coding
"""

app.add_url_rule(rule="/predict/", endpoint="predict", view_func=CNN_predi

The main function responsible for predicting the class label of an image is defined as shown
below. It restores the trained model and runs a session that returns the predicted class of the
image. The predicted class is returned back to the Flask Web application.

def main(img):
https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 19/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
def main(img):
1 Free Upgrade
Search     """
to Premium
    The 'main' method accepts an input image array of size 32x32x3 and ret
Ahmed Gad 19 2 6
    :param img:RGB image of size 32x32x3.
    :return:Predicted class label.

    """
    #Dataset path containing a binary file with the labels of classes. Use
    patches_dir = "C:\\cifar-10-python\\cifar-10-batches-py\\"
    dataset_array = numpy.random.rand(1, 32, 32, 3)

    dataset_array[0, :, :, :] = img

    sess = tensorflow.Session()

    #Restoring the previously saved trained model.


    saved_model_path = 'C:\\model\\'
    saver = tensorflow.train.import_meta_graph(saved_model_path+'model.ckp
    saver.restore(sess=sess, save_path=saved_model_path+'model.ckpt')

    #Initalizing the varaibales.


    sess.run(tensorflow.global_variables_initializer())

    graph = tensorflow.get_default_graph()

    """
    Restoring previous created tensors in the training phase based on thei

    Some of such tensors will be assigned the testing input data and their
    Others are helpful in assessing the model prediction accuracy (softmax
    """
    softmax_propabilities = graph.get_tensor_by_name(name="softmax_probs:0

    softmax_predictions = tensorflow.argmax(softmax_propabilities, axis=1)


    data_tensor = graph.get_tensor_by_name(name="data_tensor:0")
    label_tensor = graph.get_tensor_by_name(name="label_tensor:0")
    keep_prop = graph.get_tensor_by_name(name="keep_prop:0")

    #keep_prop is equal to 1 because there is no more interest to remove n


    feed_dict_testing = {data_tensor: dataset_array,
                         keep_prop: 1.0}

    #Running the session to predict the outcomes of the testing samples.
    softmax_propabilities_, softmax_predictions_ = sess.run([softmax_propa
                                                          feed_dict=feed_d
    label_names_dict = unpickle_patch(patches_dir + "batches.meta")

    dataset_label_names = label_names_dict[b"label_names"]


    return dataset_label_names[softmax_predictions_[0]].decode('utf-8')

The returned class label of the image will be rendered on a new HTML page named
prediction_result.html as instructed by the CNN_predict function in this line as in figure 7.

Figure 7

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 20/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…

1 Free Upgrade
Search
to Premium

Ahmed Gad 19 2 6

Note that the Flask app uses the Jinja2 template engine that allows the HTML page to accept
input arguments. The input argument passed in this case is predicted_class=predicted_class.

return flask.render_template(template_name_or_list="prediction_result.html

The HTML code of such page is as follows.

<!DOCTYPE html>

<html lang="en">
<head>
    <link rel="stylesheet" type="text/css" href="{{url_for(endpoint='stati

    <script type="text/javascript" src="{{url_for(endpoint='static', filen


    <meta charset="UTF-8">
    <title>Prediction Result</title>
</head>

<body onload="show_alert('{{predicted_class}}')">
<center><h1>Predicted Class Label : <span>{{predicted_class}}</span></h1>
    <br>
    <a href="{{url_for(endpoint='homepage')}}"><span>Return to homepage</s

</center>
</body>
</html>

It is a template that is filled by the predicted class of the image which is passes as an
argument to the HTML page as in this part of the code:

<span>{{predicted_class}}</span>

For more information about the Flask RESTful API, you can visit such tutorial
https://www.tutorialspoint.com/flask/index.htm.

The complete project is available at Github in this link:


https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 21/22
5/15/2018 (1) Complete Guide to Build ConvNet HTTP-Based Application using TensorFlow and Flask RESTful Python API | Link…
p p j
Free Upgrade
Search https://github.com/ahmedfgad/CIFAR10CNNFlask 1
to Premium

Ahmed Gad 19 2
Report 6
this

19 Likes

+9

2 Comments

abu bakr Soliman 2w


Text Mining Research Assistant | Nile University

Thanks for your article. But I have a question.


You used Flask "run" to run your application, but I read that it's could be good for developing
mode but not for production as it could not handle multiple requests per time.
…see more
Like Reply 1 Like · 1 Reply
Follow Write an

Ahmed Gad 2w
KDnuggets Contributor, T.A. & Deep Learning | Machine Learning | Computer Vision Researcher

Thanks too much for your helpful comment. I made changes in the Github code
according to it.
As you said, the previous implementation can only be used in development but not in
production because server was opening a session for each new request. This wastes
…see more

Like Reply

Add a comment…

Ahmed Gad
KDnuggets Contributor, T.A. & Deep Learning | Machine Learning | Computer Vision Researcher

Follow

More from Ahmed Gad See all 13 articles

Building Convolutional Neural Derivation of Convolutional Neural Do New Technologies Kill their Introduc
Network using NumPy from Scratch Network from Fully Connected… Ancestors? Genetic
Ahmed Gad on LinkedIn Ahmed Gad on LinkedIn Ahmed Gad on LinkedIn Ahmed Ga

Select Language
t Questions?
Visit our Help Center. English (English)
munity Guidelines

cy & Terms
Manage your account and privacy.
feedback Go to your Settings.

dIn Corporation © 2018

https://www.linkedin.com/pulse/complete-guide-build-convnet-http-based-application-using-ahmed-gad/?irgwc=1 22/22

You might also like