Deploy a Predictive Model with Flask
By: Aaron Lee
Tags: Heroku, Flask, Data Science, Machine Learning

Deploy a Predictive Model with Flask

The purpose of this article is to show you a very simple ‘productionization’ of a machine learning model using Flask, Heroku, and GitHub. This article assumes a solid understanding of Python code and that you have already trained a Machine Learning model in Python but have not made a Flask app previously for this purpose. You will need access to GitHub and a Heroku account later; we will be creating a repository for our app and deploying to Heroku via the repo. Experience with HTML and Jinja is also nice to have, but not necessary.

Flask is a lightweight framework for web apps that is pretty straightforward to use, and well documented. It is also somewhat trivial to get it up and running on a cloud platform like Heroku.

I recently made a Flask app for one of my own models. I built a CNN model to predicts pneumonia outcomes using chest x-rays, and wanted to provide an interactive preliminary model for a non-technical presentation. That can be difficult to do using IDEs and notebooks, so I made a quick Flask app as a ‘proof of concept’ demonstration of my model making predictions.

My Model in Action

Before we start, you can take a look at the app I made using the same method to give you an idea of what we are making. The dataset I used for my project came from kaggle.com at the link below.

My Flask app is hosted on Heroku. The model is a CNN h5 model in keras, but most any ML model would work in the same way. If you can do a model.predict(), you have enough to make a prediction app.

Feel free to visit the app and upload chest x-rays (jpg or png) and predict whether they are ‘Normal’ or ‘Pneumonia’. You can use images from the dataset to feed into the model, or try your own.

Now let’s make a ‘no-frills’ app together.

Step 1: Make a Flask Folder Structure

We start by creating a virtual environment for our project (I recommend using Python 3.7). Use your normal workflow for pushing your project to GitHub. If we keep an organized repository, it will be a breeze to later deploy on Heroku through Github.

Below is a beginner friendly app structure that I use for students. More complex apps might require a more complex structure. Take the time to build out the folder structure before you begin. It’s a great way to visualize your project before you code. More about Flask folder structures can be found here.

My Project/

├── app.py

├── requirements.txt

├── Procfile

├── templates/

│ └── main.html

├── static/

│ ├── images/

│ ├── stylesheets/

│ └── fonts/

├── model/

│ └── my_model.pkl

Step2: Make a Basic Flask App

Start by installing the Flask library in your venv. The setup and install instructions on the Flask website can get you started. It is really important to refer to the Flask website to keep you out of trouble if you are unsure about any steps.

For now, we will start with a Hello World Flask app. I will just create a file named app.py (in the root of my folder structure as shown in Step 1). Insert the following code into app.py.

That was Flask’s ‘Hello World’ code. It imports the Flask class, creates a Flask object called app, and defines a route that returns ‘Hello, World!’. To run a flask app, just navigate to the ‘My Project’ folder in your CLI, and type

% flask run

This launches a builtin server, which is sufficient for development and troubleshooting, but not what you would use in production. Head over to the default app location http://127.0.0.1:5000/ in your browser, and you should see your message.

What is that code above my function?

We are making a web app. Everything is done through routes in Flask. You are essentially mapping URLs to actions. The thing that might be most confusing when you start using Flask is the line below.

@app.route(‘/’)

The @ symbol is just a Python decorator. A decorator is a fairly obscure bit of syntactic sugar in Python. It modifies functions; more specifically, it is a function that takes another function as a parameter and returns another function. If this is confusing… I agree with you. Fortunately, we don’t have to fully grasp it to use it.

What we need to know: The decorator links the relative URL (route) within our app to the function it is decorating. That means that the hello_world function is associated with the route (‘/’), which is the root of our web app.

When we run the app, we visit the root URL which ‘triggers’ the hello_world function without ever specifically making a call to the function. The hello_world function returns the message to the route, and we see it magically appear on our server at the root URL.

At times, routing URLs to achieve interactivity can be confusing and cumbersome. At other time, you can see the absolute genius of the whole thing.

Step 3: Load in your model

We created a folder in our file structure to hold our model. Depending on the type of model you made, there are different ways to store it. Maybe the easiest way is to use pickle. Pickling is just serializing an object in Python for later use. Pickling preserves the Python object in its current state.

Let’s say you have created a linear regression model in a coding session that has the object name LR_model. To pickle it, just do the following:

import pickle with open("my_model.pkl", 'wb') as file:

pickle.dump(LR_model, file)

A new file will now pop up in the current directory called my_model.pkl. This is the file that goes into folder named model shown in the Step 1 folder structure. In app.py, we can undo the pickling and retrieve the model by using the following.

import pickle with open('model/my_model.pkl', 'rb') as file:

model = pickle.load(file)

Let’s go ahead and integrate this into our app. After putting our pickled model into our folder structure, we can open it and make predictions that we can then output to the root of our Flask app.

In this hypothetical, the model takes in an array of length 3, makes a prediction, and returns that prediction to the user as a string.

What we changed:

Added lines 2,3,4,6,7, and 13.

Changed def hello_world to def index

to Return the prediction pred (cast as a string) instead of just “hello world” .

(cast as a string) instead of just . Added in line 16–17 because it is good practice.

Step 4: Add a Template

That was great, but we would like a little more than just a number on a webpage. Flask has a great tool to make complex web interfaces simple. Let’s look at how to use a Flask template.

If you are familiar with HTML (and CSS), you will feel right at home for this part. We need to make an HTML page. I will call mine index.html and I will place it in the templates folder (the default location for templates). This page will serve as my new ‘home page’ for my web app. In the index.html file, I place the following code:

index.html

To access my template, flask has a function called render_template that can ‘render’ an html file that is stored in the templates folder. When you return render_template from a function, it will be available at the designated route/URL (the one in the decorator) for the function. In the code below, index.html is made available at the root folder.

flask_app_step4.py

What we changed:

Added the render_template function to imports

Changed line 14 to return the render_template function call

With both files in place, the flask run command should now bring up the html template.

Now we can see the webpage, but we would also like to be able to display the prediction within the template as well. To do this, we change just two lines of code. First, change the render_template function to include the keyword argument for every value you wish to use in the template. These values will be available to display or manipulate on the webpage. For now, will just send a single value pred as a string.

What we changed:

added a kwarg to our render_template function (line 4 above)

Now switch over to your index.html template. We will add a little bit of Jinja template notation to our html. Jinja is a templating language to integrate python code into our html pages. The simplest implementation is shown below. When run, the flask app should now display the passed pred variable.

What we changed:

The only thing that changed is line 8. The {{ }} Jinja notation allows you to display the passed ‘variable’ of the same name sent from the render_template function anywhere within the html.

Jinja can do a ton more work for us, and I encourage you to check it out. We can implement Python-like structures like loops and conditions directly inside our HTML template, as well as implement many formatting features dynamically.

Step 5: Use a Web Form to Post Input for Your Model

This web app would not be very useful if we did not give the user a way to input data for prediction. In my own model, it was x-ray image data that the user submitted. For this model, we will just pass a few numbers.

Our hypothetical model will be a Linear Regression that determines housing prices. We will be sending the number of beds, baths, and square footage to this simple model to predict a price. All of the data will be displayed on a single page.

We will add a form to our HTML template.

What we changed:

Added a form tag on line 8 (closes on line 13). Specified method to POST and encoding type to multipart/form-data.

Added three number inputs to my form on lines 9–11 which will request a numerical input in a box

Added a submit button on line 12

Your page will look something like this.

Here we are using a <form> tag to contain user input elements. In the attributes for <form>, we specify the method and encoding type here. It is important that you keep these the same as shown. Within the form, we added three numerical inputs for the desired house characteristics, and we add a submit button to POST (submit) the information. As currently written, the form data will only POST when you click the submit button.

Now switch back over to app.py, so we can add a mechanism for accepting these submitted values into Python. The <form> tag’s method is set to POST. We are going to set up our route method to accept POST information to match. By default, routes only have the GET method unless otherwise specified.

What we changed:

Line 1 — We added request object to flask import to receive form data

Line 11 — We added POST to decorator to allow form submission

Line 13 — Added if statement to check if anything has been posted. If there has been a post, we grab the data and render the template. Otherwise, we render the template without it.

Lines 14 to 15 — We read the values from the form POST into python using the request object and a ‘dictionary like’ notation. The key used to access the value is the same as the name value on the form inputs in the template.

Line 17 — We now use the posted form data to feed the model and make a prediction (which is then sent back to the webpage through the returned render_template)

The basic functionality is there. A client would enter three numbers, click submit, and see a result.

Important

You should add some checks to validate your form data.

You should additionally make sure your prediction inputs are valid for your model before running the predict method.

Test your app doesn’t crash with a variety of form inputs.

You may want to look into the flask website’s use of Werkzeug to validate form inputs to make your app more secure and prevent hacking.

Step 6: Style It

I will leave this one to your personal preferences as it is time consuming and involves a lot of personal choices for your app/model. You don’t have to fully style it before deploying an app like this, and it really depends on your audience and reason for deployment.

The static folder

If you are adding files (images, stylesheets, scripts, datasets, etc.) to your webpages/templates, all those resources go in the static folder. The static folder is there for this purpose; you can serve up images, stylesheets, and scripts in the same way you do any website. Consider the rest of your folders inaccessible for the purposes of web design.

Simplistic styling for this model

Step 7: Ready Your App for Heroku

Our end goal is to move our project to the cloud. We need to add two files to our project to make it friendly for building on Heroku.

requirements.txt

This file goes into your root (see folder structure). It is a simple text file that lists all of your imports. When you build your app, Heroku will iterate through each line in your requirements.txt file to install the requisite libraries for your app.

Errors in your requirements file cause your app to crash during the build. It is also extremely difficult to troubleshoot, so make sure you have them all correct.

Below is the file I used for my project. It is the import and specific version number of every outside library I used.

Flask==1.1.2

Jinja2==2.11.2

keras

numpy==1.18.5

Werkzeug==1.0.1

requests

gunicorn

tensorflow==2.3.1

h5py==2.10

Pillow==8.0.1

To keep yourself out of trouble, go through each line and make sure you can do a pip install for each requirement to a local virtual environment.

% pip install Flask==1.1.2

Running those pip commands is what Heroku is going to do as well. If your machine has conflicts, or has to uninstall and reinstall different versions, make sure that you specify the correct final version of each library in requirements.txt.

Procfile

Create a file called Procfile and put it in the root directory as shown in the folder structure at the top of the article. DO NOT add an extension to make it Procfile.txt or anything else, it is just Procfile.

The Procfile just tells Heroku how to handle different processes for your project. For ours, simply place the following line inside your Procfile.

web: gunicorn app:app

Gunicorn is a Web Server Gateway Interface (WSGI) server used to manage our app. The name of our app is app, so if you called your app.py something else, you would have to change that here as well.

Github

Finally, push all of the content into a github repo. Make sure Procfile and requirements.txt are added to the commit. You are ready to deploy. Heroku plays really well with GitHub. You will have to give permission to Heroku for GitHub access, but Heroku will walk you through the process with user friendly prompts for adding the authentication.

Step 8: Deploy Your App on Heroku

Make an account and log in to Heroku. You can easily run an app like this with a free account, but of course they have upgrades for pay accounts.

Go to your Heroku Dashboard. Hit the “New” button and create a new app. Give your app a unique name. It must be unique across all Heroku. A new screen pops up. For ‘Deployment Method’ select ‘GitHub’

5. Search for the repo name of your app.

6. I recommend enabling automatic deploys. This means that every time you push the master branch of your project to GitHub, Heroku automatically builds and redeploys your app. It’s a real time saver.

Try Out Your App

If your build was successful, you will be able to open the link “Open App” on any computer. Your model is deployed, and you can forward the link to users.

Good Luck!