Deploying a Machine Learning Model as a Flask App on Heroku -Part 1

Olumide Akintoye
Analytics Vidhya
Published in
7 min readFeb 25, 2020
Photo by davisco on Unsplash

As a Data Scientist, i am sure the thought of communicating the results of your models to other people must have crossed your mind at some point. Well, if it has not, You are in the right place as this tutorial will give you the knowledge about doing that. As a data scientist, communicating your results to your supervisors or technical personnel for evaluation might not be an issue as it is pretty straightforward- You just save the model to a disk or share your Jupyter notebook to the person.

What if you have to share your results to someone with little or no technical skill, maybe a friend or a colleague who you think might find your work impressive. How do you go about it ?. Definitely, by thinking about this, we need some sort of interface to communicate between them and your work or some sort of web application. Doesn’t sound bad for this, right:)?

This Tutorial will be divided into two parts:

  1. Developing a simple model that can be deployed, where users can interact with it to get predictions using Flask (a microweb framework in python) to create the web application.
  2. Deploying our final web application on the Heroku Platform.

There are some tutorials online for this same purpose and in a bid to make this as straight forward as possible for learners, i will try to make this brief with every important details you need to know.

The Model we will be deploying is based on sci-kit’s learn wine quality dataset. Our model will perform a prediction in determining the quality of wine on the given chemical measures of each wine. Nevertheless, you could deploy any model you like.

Requirements

Have the following installed. Simply Pip install them

  • Python 3.6
  • Flask
  • Pandas
  • Sklearn

The source code to the final web application can be found on Github at https://github.com/oluakintoye/wine-qual.git

Creating the model for deployment

On your preferred IDE:

We will load the wine data from sci-kit learn library

In [1]:

import pandas as pd
import numpy as np
from sklearn.datasets import load_wine

wine = load_wine()

data = pd.DataFrame(data= np.c_[wine['data'], wine['target']],
columns= wine['feature_names'] + ['target'])
data.head()

Out[1]:

Data Statistics and Pre-processing:

We will observe the details about the data and inspect it to know the proper steps to take towards preprocessing the data. The variable we want to predict is the target(wine quality) and we’ll do this using the features.

In[2]:

data.info()

Out[2]:

In[3]:

data.describe()

Out[3]:

If we observe the various statistics as shown in the output above, the data has no empty values with adequate data types and is normalized enough to carry out our predictions.

Train the Model:

In[4]:

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

X = data.iloc[:, :-1]
y = data.iloc[:, -1]


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)


clf = LogisticRegression()
clf = clf.fit(X_train,y_train)

clf

Out[4]:

We will quickly evaluate the trained model by looking at the Accuracy

In[5]:

from sklearn.metrics import accuracy_score

y_pred = clf.predict(X_test)

print("accuracy_score: %.2f" % accuracy_score(y_test, y_pred))

Out[5]:

Setting our model ready for deployment

We will use Pickle to save our model to be reloaded later when called upon to make a prediction. Read more about pickle here.

In[6]:

import picklepath='./lib/models/LogisticRegression.pkl'

with
open(path, 'wb') as file:
pickle.dump(clf, file)

Creating the web app with Flask

Flask is a microweb framework written in python that allows you to build web apps and websites quite rapidly and easily. It is lightweight as it does not require much boilerplate code for setting it up. If you are new to flask, you can take a quick flask tutorial on tutorialspoint here. We will be needing the following to set up the web app such as:

  • An HTML or web template that flask will render. This allows users to input their own data and be shown the result
  • The python script that will load our model and also get user input from the web form, make prediction and return results.

The app directory will be structured like this:

App structure

app.py

This is the core of our web application. The code here contain information for capturing and processing user input, running the flask development web server, rendering templates and is also what would run on the Heroku Server when hosted.

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def main():
return(render_template('main.html'))


if __name__ == '__main__':
app.run()

In flask, URLs get routed to different function depending on your defined route.

If we run the script, we will see that going to our base URL( http://127.0.0.1:5000/) would trigger the main function and our main.html page would be displayed. For now our main.html page is empty so lets try filling the body with something then running our app.py script again to see the outcome.

main.html

We would add a tag to the body of the html file to display a Welcome message

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>Welcome!!!</h1>
</body>
</html>

Now that our template is rendering, lets us do the following:

  1. Edit our app.py script to load the model, capture and process input to give predictions
  2. Edit main.html to have a form that gets the user input we need and submit to display appropriate predictions

It is important to note that a good knowledge of the flask web framework is important in understanding the code, reference the flask documentation if needed.

Editing app.py

from flask import Flask, render_template, request
import pandas as pd
import pickle

app = Flask(__name__)
# Loading the Pre-trained model using Pickle
with open('./lib/models/LogisticRegression.pkl', 'rb') as file:
model = pickle.load(file)

@app.route('/', methods=['GET', 'POST'])
def main():
if request.method == 'GET':
return(render_template('main.html'))
#Getting Form Input
if request.method == 'POST':
alcohol = request.form['alcohol']
malic_acid = request.form['malic_acid']
ash = request.form['ash']
alcalinity_of_ash = request.form['alcalinity_of_ash']
magnesium = request.form['magnesium']
total_phenols = request.form['total_phenols']
flavanoids = request.form['flavanoids']
nonflavanoid_phenols = request.form['nonflavanoid_phenols']
proanthocyanins = request.form['proanthocyanins']
color_intensity = request.form['color_intensity']
hue = request.form['hue']
od280_od315_of_diluted_wines = request.form['od280_od315_of_diluted_wines']
proline = request.form['proline']
#Putting Form input in a data frame input_variables = pd.DataFrame([[alcohol, malic_acid, ash, alcalinity_of_ash, magnesium, total_phenols,flavanoids, nonflavanoid_phenols,proanthocyanins, color_intensity, hue,
od280_od315_of_diluted_wines, proline]],
columns=['alcohol', 'malic_acid', 'ash', 'alcalinity_of_ash', 'magnesium','total_phenols', 'flavanoids', 'nonflavanoid_phenols',
'proanthocyanins', 'color_intensity', 'hue', 'od280/od315_of_diluted_wines', 'proline'], dtype=float)


# Predicting the Wine Quality using the loaded model
prediction = model.predict(input_variables)[0]
if prediction == 0.0:
prediction = "Poor Wine Quality"
elif
prediction == 1.0:
prediction = "Good Wine Quality"
else
:
prediction = "Fine Wine Quality"

return
render_template('main.html', result=prediction)



if __name__ == '__main__':
app.run()

Editing main.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="{{ url_for('main') }}" method="POST">
<fieldset>
<legend>Input values:</legend>
Alcohol:
<input name="alcohol" type="float" value="14.23" required>
<br>
<br> Malic Acid:
<input name="malic_acid" type="float" value="1.71" required>
<br>
<br> Ash:
<input name="ash" type="float" value="2.43" required>
<br>
<br>
Alcalinity of ash:
<input name="alcalinity_of_ash" type="float" value="15.6" required>
<br>
<br> Magnesium:
<input name="magnesium" type="float" value="127.0" required>
<br>
<br> Total Phenols:
<input name="total_phenols" type="float" value="2.80" required>
<br>
<br>
Flavanoids:
<input name="flavanoids" type="float" value="3.06" required>
<br>
<br> Non-Flavanoid Phenols:
<input name="nonflavanoid_phenols" type="float" value="0.28" required>
<br>
<br> Proanthocyanis:
<input name="proanthocyanins" type="float" value="2.29" required>
<br>
<br>
Color Intensity:
<input name="color_intensity" type="float" value="5.64" required>
<br>
<br> Hue:
<input name="hue" type="float" value="1.04" required>
<br>
<br> Diluted wines:
<input name="od280_od315_of_diluted_wines" type="float" value="3.92" required>
<br>
<br> Proline:
<input name="proline" type="float" value="1065.0" required>
<br>
<br>
<input type="submit">
</fieldset>
</form>
<br>
<div class="result" align="center">
{% if result %}
<br> Predicted Quality of Wine:
<p style="font-size:50px">{{ result }}</p>
{% endif %}
</div>
</body>
</html>

After the editing of our app.py and main.html script, re-run the app.py and visit your localhost (http://127.0.0.1:5000/) in this case to render the html template. Fill in appropriate inputs and submit you should have something like this:

The Prediction of our Wine Quality in this case is “A Fine Wine” according to our inputs of the chemical substances of the wine. Try out your inputs and get your predictions :). It should be noted that the model built here is not perfect but aims at demonstrating deploying a model into a web app. You can fine tune the model or build any of your choice.

Furthermore , we will see how to deploy our application on a cloud service, Heroku.

Please be sure to check the part 2 of this tutorial for the deployment of the web app to the cloud.

If you have any feedback, please share with me. Follow me or connect with me on LinkedIn also. Thanks for reading.

--

--