Building a Web Application to Deploy Machine Learning Models

Source: Deep Learning on Medium

Creating the Web App Backend using Flask

Your Web app can be thought of as two parts — the “backend” and the “frontend”. Think of the “backend” application as the brains behind the operations — you might not see it, but the computations are all done here. This is where we define the logic of how an user will interact with your web application, and what happens to the user’s inputs. It is also where we will run our Machine Learning model. The “frontend” on the other hand, defines what the users see, touch and experience. This is like the face of your Web application and will be what your user interacts directly with.

In this section, we will define the backend — we’ll create the “brains” first, and then we’ll see how to fit in the “face” later. In particular, we will:

  1. Load our Machine Learning model;
  2. Define what happens when he uploads the photo in the main homepage; and
  3. Apply our Machine Learning model to the image and show the user the results in a separate “prediction” page.

In the next section, we will define how the user sees the two webpages — the homepage and the “prediction” page. But for now, let’s focus on the backend, and we’ll be using Flask as a framework to build it.

Flask is a simple web application framework that we can use to build the backend of web apps and get started quickly. In this tutorial, I will explain enough to create the web app around your Machine Learning model. Unfortunately, I will not be explaining every line of Flask code and structure; you can learn more about Flask here.

First, let’s install Flask. To do so, we first have to install pip3, which helps us to install other Python packages like Flask on our server. In your console, type

apt install python3-pip

Your console should look like this:

Once you click Enter, it will tell you the packages that will be installed. Type in “Y” to continue. Now that pip3 is installed, enter the command into your console:

pip3 install Flask

Your console should look like this:

Command to install Flask
How a successful installation looks like

While we’re installing Flask, be sure to install all the Python packages we had previously now on the server:

pip3 install keras
pip3 install tensorflow
pip3 install scikit-image

Now, we will organise our files in the server. We will createa Python file for our web backend, which we will call imgrec_webapp.py(using nano like we did earlier). We will then have two folders, one to store the uploaded images (which we will call uploads) and one to store the HTML files for your frontend (called templates). To create a folder, type mkdir folder_name into the console, where “folder_name” can be replaced by your desired name. So the two commands to type are:

mkdir uploads
mkdir templates

Now, we need to upload our model, my_cifar10_model.h5 into the server. Since the model is in my local computer, unfortunately, we can’t use the console to upload the model from my desktop. Instead, we will use Windows PowerShell. This is an app that you likely have installed already if you are using Windows:

When you’ve opened Windows Powershell, type in this command to upload your model, and be sure to replace the instructions in triangular brackets:

scp <where your model is stored>\my_cifar10_model.h5 <Droplet IP address>:~

In my case, I would type:

scp C:\Users\user\Desktop\my_cifar10_model.h5 root@157.230.156.140:~

If it is your first time connecting from Powershell, a prompt like this will appear. Simply click “yes”.

Type in your password, and once you’re done, it should look something like this:

And now your model is on the Droplet! Now, go back to the DigitalOcean console where you accessed your server previously. Alternatively, if you like the PowerShell interface, you can use PowerShell to connect to your server like this:

ssh root@<your Droplet IP>

In my case, it would be:

ssh root@157.230.156.140

And voila! You no longer need to go to DigitalOcean to get your server console. Once you’re in your console, type ls (which stands for list) to make sure that you’ve got all your files and folders:

What you should see if you’re using Powershell
What you should see if you’re using DigitalOcean console

Now that we’ve got everything set up, we can (finally) start coding our backend application. Open up the Python file you have created, imgrec_webapp.py. As usual, we will import some of the necessary functions that you will need later:

import os
from flask import Flask, request, redirect, url_for, render_template
from werkzeug.utils import secure_filename

Then, we create an instance of a Flask app which just one line of code:

app = Flask(__name__)

We’ll add more to the Flask app later. For now, let’s load our Machine Learning model that we’ve already saved in our pre-requisite tutorial my_cifar10_model.h5. We call the following functions and load the model like this:

from keras.models import load_model
from keras.backend import set_session
from skimage.transform import resize
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
print("Loading model")global sess
sess = tf.Session()
set_session(sess)
global model
model = load_model('my_cifar10_model.h5')
global graph
graph = tf.get_default_graph()

Now that we’ve got our model running, let’s add to our Flask web app. The first thing we want to do is state what the web application will do when it hits a certain URL. To define that, we use the@app.route() function. For my main page, I will use the default URL (i.e. I won’t have any additional “suffixes” to my URL) and so my first parameter will be '/'. An user will interact with my main page in two ways:

  1. Load the web page from the server into his browser
  2. Upload his image and send it to the server

This requires two different methods of interacting with this page: Interaction #1 will use “GET” (a method primarily for an user to request data from a resource), while interaction #2 will use “POST” (a method primarily for an user to send data to a server to create or update a resource). The code block below therefore says that if I’m at interaction #2, save the file in my “uploads” folder and go to the “prediction” page (and pass in the parameter filename). If not, I’m at interaction #1 and just render the webpage “index.html”.

@app.route('/', methods=['GET', 'POST'])
def main_page():
if request.method == 'POST':
file = request.files['file']
filename = secure_filename(file.filename)
file.save(os.path.join('uploads', filename))
return redirect(url_for('prediction', filename=filename))
return render_template('index.html')

Now, we have to create a “prediction” page to go to, and define what the Web app should do when an user is at that page. We use the @app.route()function again, and say that if an user is at the /prediction/<filename> subdirectory, pass in the <filename> into the Python function that I define for that page.

In this prediction page, I will:

  1. Read the image based on the filename, and store it as my_image
  2. Resize the image to 32x32x3, which is the format that my model reads it in, and store it as my_image_re
  3. Using the model, predict the probabilities that the image falls into the various classes, and put that under the variableprobabilities
  4. Find the label and the probability of the top three most probable classes, and put them under predictions
  5. Load the template of my webpage predict.html and give them the predictions in Step 4.

The five steps are reflected below:

@app.route('/prediction/<filename>')
def prediction(filename):
#Step 1
my_image = plt.imread(os.path.join('uploads', filename))
#Step 2
my_image_re = resize(my_image, (32,32,3))

#Step 3
with graph.as_default():
set_session(sess)
probabilities = model.predict(np.array( [my_image_re,] ))[0,:]
print(probabilities)
#Step 4
number_to_class = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
index = np.argsort(probabilities)
predictions = {
"class1":number_to_class[index[9]],
"class2":number_to_class[index[8]],
"class3":number_to_class[index[7]],
"prob1":probabilities[index[9]],
"prob2":probabilities[index[8]],
"prob3":probabilities[index[7]],
}
#Step 5
return render_template('predict.html', predictions=predictions)

All this should be pretty familiar, if you need a refresher, please visit the tutorial:

Lastly, we run the app with port 80, which is the standard port for accessing web pages. Type this at the end of your Python file:

app.run(host='0.0.0.0', port=80)

Now we have the backbones of our Web application. You might have noticed that we are still missing a few different pieces though. In particular, we’ve called on the HTML web pages like index.html and predict.html but we’ve not built them yet! We will do so in the next section.

Do also note that in this bare-bones tutorial, we’ve skipped many good practices. You may want to try incorporating some of these practices if you know how to (or Google it!):

  • Creating our Web app in an Anaconda environment (like we did in Anaconda Navigator previously)
  • Catching errors such as someone not uploading any file, or with a blank filename
  • Checking link extensions that people are uploading ‘.jpg’ files