Absolute Beginner's Guide to Deploy ML model with Flask (Part-1)

Data Science Kitty is so happily purring because she can now predict heart disease with her model (https://dev.to/orthymarjan/beginners-journey-in-machine-learning-3ei9?fbclid=IwAR02yqZ8RVT13XW1-MP66RJiNKMnHvOyhw8Dvgu2AcWPuq1kzqnjByjEc0s). She now wishes to use the model for a website where hoomans will give input and after submitting the data will receive a prediction. This means that the model has to be incorporated with an interactive medium; hoomans will give their inputs on a form and the model will give the prediction based on the form data. But how?

Kitty remembers that her friend ML engineer doge is an expert at bonking everything. Doge promised kitty, “I’ll teach you how to deploy your model.” The Data science kitty is confused now. “What is deploy, doge?”
Confused
Doge drew a diagram for explaining the basics:
Process

Step-1: Installing virtual environment and Flask

The first and foremost thing to do for any type of Python development is to set up a virtual environment where you will manage all the tools and dependencies within a Python version of your choice. Python comes with venv module to manage virtual environments. You can either use this or opt for Python Virtual Environment or pyenv which you can install from the links below -

  1. https://github.com/pyenv/pyenv
  2. https://github.com/pyenv/pyenv-virtualenv - This plugin makes managing multiple Python virtual environments on Unix like systems so much easier. You can use Conda as well for setting up your virtual environment.

After setting up a virtual environment, you can install Flask from the official documentation.

Step-2: Load the model

Kitty has the model for predicting heart disease as a joblib file. You need to ensure that your model file’s Python version is the same as that of Flask’s version. There are two approaches to ensure the same Python version:

1.

If you have run your entire machine learning project in Colab then just run the below command in a Colab cell, and install this particular version of Python Virtual environment for the Flask project:

!python --version

2.

You can create your model and extract the joblib file from the Jupyter notebook. That way, your model file and Flask project will remain within the same Python version.
This solves kitty’s issue of matching the python versions of joblib and the system that doge is making for her. Inside project directory, at first create afile named app.py and write the following line of codes to start with the Flask skeleton:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

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

If you run the following command, you can see on http://localhost:5000/ that there is a webpage with the text “Hello, World!”

python app.py

For clarity, doge put the joblib file inside a folder called “resource”. To load the model in our app, we will import the load function and save the model in variable named pipeline.

from joblib import load 
pipeline = load("resource/diseaseprediction.joblib")

We can return an html page with our app function. All we have to do is to write

def hello_world():
    return render_template('home.html')

Import the function render_template with Flask and you are good to go till now.

Step-3: Write a template for input

This is the most bonking, I mean the most boring part of the whole process. We wrote render_template(‘home.html’) but there is no html to render. In a Flask project, you need to create a directory named “templates” which will have all the web pages. For this project, we will require just the home page. We have W3Schools to get this basic layout done.

The user input will be the feature columns of Kitty’s model, which means there are 13 fields in the form. I checked the value type of each input and based on that, the code for the form will be like the following -

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<h2 style="text-align:center">Do I have heart disease?</h2>

<p style="text-align:center">Fill out this form and check!</p>
<div>
    <form>
        <div>
            <div>
                <label for="age">Age</label>
            </div>
            <div>
                <input type="text" id="age" name="age" placeholder="Your Age" required>
            </div>
        </div>
        <div>
            <div>
                <label for="sex">Sex</label>
            </div>
            <div>
                <input type="radio" id="sex" name="sex" value="1">
                <label for="sex">Male</label><br>
            </div>
            <div>
                <input type="radio" id="sex" name="sex" value="0" required>
                <label for="sex">Female</label><br>
            </div>
        </div>
        <div>
            <div>
                <label for="cp">Chest Pain Type</label>
            </div>
            <div>
                <select id="cp" name="cp" required>
                    <option value="0">None</option>
                    <option value="1">Mild</option>
                    <option value="2">Medium</option>
                    <option value="3">Severe</option>
                </select>
            </div>
        </div>
        <div>
            <div>
                <label for="rbp">Resting Blood Pressure</label>
            </div>
            <div>
                <input type="text" id="trestbps" name="trestbps" placeholder="Your bp in mm Hg" required>
            </div>
        </div>
        <div>
            <div>
                <label for="chol">Cholesterol</label>
            </div>
            <div>
                <input type="text" id="chol" name="chol" placeholder="Your serum cholestoral in mg/dl" required>
            </div>
        </div>
        <div>
            <div>
                <label for="fbs">Fasting Blood Sugar</label>
            </div>
            <div>
                <input type="radio" id="fbs" name="fbs" value="1" required>
                <label for="fbs">Diabetic</label><br>
            </div>
            <div>
                <input type="radio" id="fbs" name="fbs" value="0">
                <label for="fbs">Non diabatic</label><br>
            </div>
        </div>
        <div>
            <div
                <label for="restecg">Resting Electrocardiographic Results</label>
            </div>
            <div
                <select id="restecg" name="restecg" required>
                    <option value="0">None</option>
                    <option value="1">Medium</option>
                    <option value="2">Severe</option>
                </select>
            </div>
        </div>
        <div>
            <div>
                <label for="thalach">Maximum Heart Rate Achieved</label>
            </div>
            <div
                <input type="text" id="thalach" name="thalach" placeholder="Your maximum achieved heart rate" required>
            </div>
        </div>
        <div
            <div>
                <label for="exang">Exercise Induced Angina</label>
            </div>
            <div>
                <input type="radio" id="exang" name="exang" value="1">
                <label for="exang">Yes</label><br>
            </div>
            <div>
                <input type="radio" id="exang" name="exang" value="0" required>
                <label for="exang">No</label><br>
            </div>
        </div>
        <div>
            <div>
                <label for="oldpeak">OldPeak (floating point value)</label>
            </div>
            <div>
                <input type="text" id="oldpeak" name="oldpeak"
                       placeholder="ST depression induced by exercise relative to rest" required>
            </div>
        </div>
        <div>
            <div>
                <label for="slope">The Slope of the Peak Exercise ST Segment</label>
            </div>
            <div>
                <select id="slope" name="slope" required>
                    <option value="0">None</option>
                    <option value="1">Medium</option>
                    <option value="2">Severe</option>
                </select>
            </div>
        </div>
        <div>
            <div>
                <label for="ca">Number of Major Vessels (0-3)</label>
            </div>
            <div>
                <select id="ca" name="ca" required>
                    <option value="0">0</option>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
            </div>
        </div>
        <div>
            <div>
                <label for="thal">Thal: 3 = normal; 6 = fixed defect; 7 = reversable defect</label>
            </div>
            <div>
                <select id="thal" name="thal" required>
                    <option value="0">0</option>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                    <option value="5">5</option>
                    <option value="6">6</option>
                    <option value="7">7</option>
                </select>
            </div>
        </div>

        <div>
            <button type="submit">Submit Data</button>
        </div>
    </form>

</div>

</body>
</html>

The output of this html file looks like this -
html

It looks too plain because we didn’t do the styling. It’s also tiring I know. Doge wants to keep the template basic, so she searched for a form template in W3School. Without this website, learning html and CSS would be no hard for doge. Form template link - https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_contact_form. As our target is to deploy the model, we need to make sure that all 13 data are posted to this form, extracted and used as the input to the model that we loaded earlier. Do the following two tasks to ensure that:

In the “form” tag, set the destination of where the data will be posted. As this is a single page web application, the form action attribute is the address of where this application is running, that is http://localhost:5000/, and the method attribute is of type post.
Input to the model is a data frame; therefore you have to match every data to their designated column. If you set the id, name and for attribute to the original data frame column name and set the value attribute (only the tags that require a value attribute such as a selection tag) to the finite intended value of the column, the form will be able to generate an object of the type immutablemultidict, which is a Flask form object. We will process this object in the following section, but for now, let me explain with just one code snippet.
cp

The ‘cp’ column in the dataset can take values 0,1,2 or 3, which I mapped for textual values; in the website, we can see this and not what the value attributes contain. After incorporating all the CSS classes and sources for styling the html looked like the following. We are almost there, yems!

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
    <style>
        * {
            box-sizing: border-box;
        }

        input[type=text], select, textarea {
            width: 100%;
            padding: 12px;
            border: 1px solid #ccc;
            border-radius: 4px;
            resize: vertical;
        }

        label {
            padding: 12px 12px 12px 0;
            display: inline-block;
        }

        input[type=submit]:hover {
            background-color: #45a049;
        }

        .container {
            border-radius: 5px;
            background-color: #f2f2f2;
            padding: 20px;
        }

        .col-25 {
            float: left;
            width: 25%;
            margin-top: 6px;
        }

        .col-75 {
            float: left;
            width: 75%;
            margin-top: 6px;
        }

        /* Clear floats after the columns */
        .row:after {
            content: "";
            display: table;
            clear: both;
        }

        .center {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 200px;
        }

        /* Responsive layout - when the screen is less than 600px wide, make the two columns stack on top of each other instead of next to each other */
        @media screen and (max-width: 600px) {
            .col-25, .col-75, input[type=submit] {
                width: 100%;
                margin-top: 0;
            }
        }
    </style>
</head>
<body>

<h2 style="text-align:center">Do I have heart disease?</h2>

<p style="text-align:center">Fill out this form and check!</p>
<div class="container">
    <form action="http://localhost:5000/" method="post">
        <div class="row">
            <div class="col-25">
                <label for="age">Age</label>
            </div>
            <div class="col-75">
                <input type="text" id="age" name="age" placeholder="Your Age" required>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="sex">Sex</label>
            </div>
            <div class="col-25">
                <input type="radio" id="sex" name="sex" value="1">
                <label for="sex">Male</label><br>
            </div>
            <div class="col-25">
                <input type="radio" id="sex" name="sex" value="0" required>
                <label for="sex">Female</label><br>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="cp">Chest Pain Type</label>
            </div>
            <div class="col-75">
                <select id="cp" name="cp" required>
                    <option value="0">None</option>
                    <option value="1">Mild</option>
                    <option value="2">Medium</option>
                    <option value="3">Severe</option>
                </select>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="rbp">Resting Blood Pressure</label>
            </div>
            <div class="col-75">
                <input type="text" id="trestbps" name="trestbps" placeholder="Your bp in mm Hg" required>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="chol">Cholesterol</label>
            </div>
            <div class="col-75">
                <input type="text" id="chol" name="chol" placeholder="Your serum cholestoral in mg/dl" required>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="fbs">Fasting Blood Sugar</label>
            </div>
            <div class="col-25">
                <input type="radio" id="fbs" name="fbs" value="1" required>
                <label for="fbs">Diabetic</label><br>
            </div>
            <div class="col-25">
                <input type="radio" id="fbs" name="fbs" value="0">
                <label for="fbs">Non diabatic</label><br>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="restecg">Resting Electrocardiographic Results</label>
            </div>
            <div class="col-75">
                <select id="restecg" name="restecg" required>
                    <option value="0">None</option>
                    <option value="1">Medium</option>
                    <option value="2">Severe</option>
                </select>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="thalach">Maximum Heart Rate Achieved</label>
            </div>
            <div class="col-75">
                <input type="text" id="thalach" name="thalach" placeholder="Your maximum achieved heart rate" required>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="exang">Exercise Induced Angina</label>
            </div>
            <div class="col-25">
                <input type="radio" id="exang" name="exang" value="1">
                <label for="exang">Yes</label><br>
            </div>
            <div class="col-25">
                <input type="radio" id="exang" name="exang" value="0" required>
                <label for="exang">No</label><br>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="oldpeak">OldPeak (floating point value)</label>
            </div>
            <div class="col-75">
                <input type="text" id="oldpeak" name="oldpeak"
                       placeholder="ST depression induced by exercise relative to rest" required>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="slope">The Slope of the Peak Exercise ST Segment</label>
            </div>
            <div class="col-75">
                <select id="slope" name="slope" required>
                    <option value="0">None</option>
                    <option value="1">Medium</option>
                    <option value="2">Severe</option>
                </select>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="ca">Number of Major Vessels (0-3)</label>
            </div>
            <div class="col-75">
                <select id="ca" name="ca" required>
                    <option value="0">0</option>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                </select>
            </div>
        </div>
        <div class="row">
            <div class="col-25">
                <label for="thal">Thal: 3 = normal; 6 = fixed defect; 7 = reversable defect</label>
            </div>
            <div class="col-75">
                <select id="thal" name="thal" required>
                    <option value="0">0</option>
                    <option value="1">1</option>
                    <option value="2">2</option>
                    <option value="3">3</option>
                    <option value="4">4</option>
                    <option value="5">5</option>
                    <option value="6">6</option>
                    <option value="7">7</option>
                </select>
            </div>
        </div>

        <div class="center">
            <button class="w3-button w3-green" type="submit">Submit Data</button>
        </div>
    </form>

</div>

</body>
</html>

It looks a bit fancy now too.

Step-4: Process the input

We have collected the form data. We have to feed this form into the model so that it can predict. Let us write a function for that. At first, we will check the type of object that form creates after we hit the submit button. Pro-tip, while coding in python and javascript, pass the object through the built-in “type” function. This will yield the type of object that you are going to deal with.

@app.route('/', methods=['GET', 'POST'])
def inputForm():
    print("type", type(request.form))
# type ImmutableMultiDict

We can transform this object into a dictionary, and transform it again into a pandas data frame. This dataframe is the input to the model. We will save the output in a variable named “prediction”.

The whole function will look like something like the following.

import pandas as pd

@app.route('/', methods=['GET', 'POST'])
def inputForm():
    print("type", type(request.form.to_dict(flat=False))) #changing to a dictionary
    print("checking form", request.form.to_dict()) #dictionary to datframe
    data = request.form.to_dict() 
    df = pd.DataFrame(data, index=[0]) 
    print("prediction", pipeline.predict(df)) 
    if pipeline.predict(df) == [1]: 
    prediction = "You are in risk of heart disease" 
    elif pipeline.predict(df) == [0]: 
    prediction = "You don't have risk of heart disease" 
    else: prediction = "Can't predict anything" 
    return render_template('home.html', prediction = prediction, show_predictions_modal = True )

Step-5: Show the prediction

“We have the prediction but how to show that now?” Kitty asked Doge. Doge assured her that she has got it covered. “In our html we will incorporate a modal that will pop after we hit the submit button, said Doge.”

We will pop the modal first. Doge is getting impatient, so she headed to W3schools again (https://www.w3schools.com/w3css/tryit.asp?filename=tryw3css_modal2) and created this modal named “See Result”.

The button action of this modal is to show the prediction variable that we are returning from app.py file. We also set a flag inside the render_template function. When the prediction is done, we will conditionally render the modal. After hitting the submit button, we will get to see the modal. Click that and you can see the prediction.

{% if show_predictions_modal %}
        <div class="center">
            <button onclick="document.getElementById('id01').style.display='block'" class="w3-button w3-black"
                    type="submit">See
                Result
            </button>
        </div>
        <div id="id01" class="w3-modal">
            <div class="w3-modal-content">
                <div class="w3-container">
                        <span onclick="document.getElementById('id01').style.display='none'"
                              class="w3-button w3-display-topright">&times;</span>
                    <div class="w3-container">
                        <p style="text-align:center">{{ prediction }}</p>
                    </div>
                </div>
            </div>
        </div>
    {% endif %}

The entire project is uploaded in my GitHub repository on the branch Project_HeartDiseasPrediction, where you can see the entire html code as I didn’t write the incorporated version in the end - https://github.com/Afroza2/Production-Based-ML-portfolio/tree/Project_HeartDiseasePrediction. The dataframe that you passed as the argument of the prediction function can now be created from user input and the output will be shown in the modal. For Part-2, I am planning to write on how to host this entire project as a Heroku application so that you can access the project online. Till then, bonk!

19