Deploying an ML web app using Flask
By: Sumit Kumar
Tags: Machine Learning, Flask Framework, Python3, Web App Development

Introduction

This is the continuation of my project analyzing loan data and deploying a machine learning model using Flask. If you are interest in the analysis and model build part, please read this. Flask provides a lightweight framework to develop and deploy web applications. There are many extensions available to suite your specific need. I chose Flask because I found it easier to learn and show off my machine learning model in action. In this post, I will walk through the steps to build and deploy my machine learning model using Flask.

Installation:

You want to install Flask in your virtual environment. I used Pycharm and pipenv to manage my project.

pipenv install flask

Project Structure:

Here is the directory structure for my project:

.

├── app

│ ├── run.py

│ └── templates

│ ├── index.html

│ └── template.html

├── data

│ └── loan_data.csv

├── model

│ ├── classifier.py

│ └── loan_data.pkl

├── notebook

│ └── loan_data.ipynb

├── Pipfile

├── Pipfile.lock

├── README.md

├── static

│ ├── box_plot.png

│ ├── correlation.png

│ ├── credit_policy.png

│ ├── histogram.png

│ ├── installment.png

│ ├── int_rate.png

│ ├── purpose_paid_not_paid.png

│ ├── purpose.png

│ └── results.png

└── util

└── custom_transformer.py

App folder is specific to flask. Let’s go through each of the files and see their content and the purpose they serve

run.py

import sys

import os

from pathlib import Path



from flask import Flask, request, render_template

from joblib import load

import pandas as pd



# Add util directory to path

curr_dir = sys.path[0]

parent_dir = Path(curr_dir).parents[0]

dir = os.path.join(parent_dir, 'util')

sys.path.append(dir)



from custom_transformer import NumericalFeatures, CategoricalFeatures



app = Flask(__name__)



model = load('../model/loan_data.pkl')





@app.route('/')

@app.route('/index.html')

def display():

return render_template("index.html")





@app.route('/predict', methods=['GET', 'POST'])

def predict():

test_data = list(request.form.values())

test_data = [[convert_to_float(s) for s in test_data]]

test_data = pd.DataFrame(test_data)

result = model.predict(pd.DataFrame(test_data))

if result == 1:

string = "Loan is not expected to be paid in full"

else:

string = "Loan is expected to be paid off"



# return render_template("index.html", tables=[test_data.to_html(classes='data')], titles=test_data.columns.values)

return render_template("index.html", result= "Prediction: " + string)





def convert_to_float(s):

try:

return float(s)

except ValueError:

return s





def main():

app.run(port=3201, debug=True)





if __name__ == '__main__':

main()

run.py is the main program that you execute to start the app.

Flask object creates an interface between the web server and the python app

The pickled file for the model is loaded into memory as soon as the server starts

Request allows us to read parameters passed with GET/POST requests

render_template is how we render HTML in the web browser using templates

decorator app.route is used to map specific url with associated functions. In my code above, display() function is called when there is a request for index.html or if no specific page in requested. Similarly, predict() function is called when /predict is requested on the server.

template.html

<!doctype html>

<html lang="en">

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<meta name="description" content="">

<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">

<meta name="generator" content="Hugo 0.84.0">

<title> {{ title }} </title>



<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

<link rel="canonical" href="https://getbootstrap.com/docs/5.0/examples/navbars/">



</head>

<body>

<nav class="navbar navbar-expand-lg navbar-dark bg-dark" aria-label="Tenth navbar example">

<div class="container-fluid">

<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample08" aria-controls="navbarsExample08" aria-expanded="false" aria-label="Toggle navigation">

<span class="navbar-toggler-icon"></span>

</button>



<div class="collapse navbar-collapse justify-content-md-center" id="navbarsExample08">

<ul class="navbar-nav">

<li class="nav-item">

<a class="nav-link active" aria-current="page" href="/">Loan Payoff Predictor</a>

</li>

<li class="nav-item">

<a class="nav-link" aria-current="page" target="_blank" href="https://github.com/sumitkumar-00/disaster-response/blob/master/notebook/process_data.ipynb">Exploratory Data Analysis</a>

</li>

<li class="nav-item">

<a class="nav-link" target="_blank" href="https://github.com/sumitkumar-00">GitHub Portfolio</a>

</li>

<!--

<li class="nav-item">

<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>

</li>

<li class="nav-item dropdown">

<a class="nav-link dropdown-toggle" href="#" id="dropdown08" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>

<ul class="dropdown-menu" aria-labelledby="dropdown08">

<li><a class="dropdown-item" href="#">Action</a></li>

<li><a class="dropdown-item" href="#">Another action</a></li>

<li><a class="dropdown-item" href="#">Something else here</a></li>

</ul>

</li>

-->

</ul>

</div>

</div>

</nav>

</div>



{% block index %}

{% endblock %}



</body>

</html>

You use templates to render part of the web page that is same across multiple pages with placeholder for dynamic content. Using template.html, I am creating the header (navigation, menu etc.) and have a placeholder for dynamic content shown in bold. I extended this template in index.html and added a form with a submit button and a placeholder for the result. When the form is submitted, the request is routed to predict function which reads the input parameters and then predicts using the model. Result is rendered using the same index.html template.

You just need to execute one line of code from the app folder to start the web app

pipenv run python run.py * Serving Flask app 'run' (lazy loading)

* Environment: production

WARNING: This is a development server. Do not use it in a production deployment.

Use a production WSGI server instead.

* Debug mode: on

* Running on http://127.0.0.1:3201/ (Press CTRL+C to quit)* Restarting with stat

* Debugger is active!

* Debugger PIN: 117-271-466

127.0.0.1 - - [13/Sep/2021 21:18:02] "GET / HTTP/1.1" 200 -

Go to http://127.0.0.1:3201/ in any browser and viola!