End-to-End Machine Learning Project: Train and Deploy Models as Web Apps Using Flask and Heroku | by Saket Garodia | Towards Data Science

Business Problem: One of the fields where AI will play a huge role in the future is Medical Science. Doctors and researchers have been trying to use Machine Learning and Deep Learning to learn the occurrence of cancers and other chronic diseases by using millions of data points available through the protein combinations of our DNA and other lifestyle attributes. In the future, we might be able to know our chance of getting cancer a decade or two earlier that can help us avoid it. Fortunately, in my search of finding a good medical science dataset, I came across this Pima Indians Diabetes dataset on Kaggle. It is collected from the National Institute of Diabetes and Digestive and Kidney Disease. This dataset is small with 9 features and 768 observations which is enough to solve the problem of predicting the probability of a person to be a Diabetic.
Here’s a brief description of all the features which I’m referring to from the data source itself for you to follow along.
Link to the dataset: https://www.kaggle.com/uciml/pima-indians-diabetes-database
Feel free to get a feel of the app before reading ( :) ):
predict-diabetes-using-ml.herokuapp.com
1: Pregnancies: Number of times pregnant
2: Glucose: Plasma glucose concentration a 2 hours in an oral glucose tolerance test.
3: BloodPressure: Diastolic blood pressure (mm Hg)
4: SkinThickness: Triceps skinfold thickness (mm)
5: Insulin: 2-Hour serum insulin (mu U/ml)
6: BMI: Body mass index (weight in kg/(height in m)2)
7: DiabetesPedigreeFunction: Diabetes pedigree function
8: Age: Age (years)
9: Outcome: Class variable (0 or 1) 268 of 768 are 1, the others are 0
All the variables are either self-known or available in a simple Blood Test and “Outcome” (Diabetic/Not) is what we need to predict.
We will explore different features and perform various pre-processing techniques before trying out different machine learning algorithms like logistic Regression, Support Vector Machines, Decision Forest, Gradient Boosting and finally we will also explore neural networks. Once we have the best model, we will save our model using Pickle and use that to develop a diabetes prediction app using the Flask web framework and then deploy it using Heroku.
Let’s begin. Grab a coffee!!
A brief overview of the structure: Feel free to jump over to step 2 if you don’t want to get an overview of the preparation and modeling part.
Step 1: Data Preparation and model building
In this step, we will explore the data, do the required pre-processing and try various Machine Learning models like Logistic Regression, SVMs, Random Forest, Gradient Boosting, and the state of the art models like Neural Networks.
Step 2: Building the app using Flask and HTML
Here, we will fetch the best-performing model from step 1 and build a web app using Flask and HTML.
Step 3: Deploying the app using Heroku
In the end, we will deploy our working app through Heroku for the world to use our product.
You can follow along through my Jupiter notebook that can be accessed at: https://github.com/garodisk/Diabetes-prediction-app-using-ML/blob/main/Diabetes%20prediction%20using%20Machine%20Learning.ipynb
#importing reqd librariesimport numpy as npimport pandas as pdimport seaborn as snsimport matplotlib.pyplot as pltdf.head()
For the feel of the data, let’s print the head:
Though a top-level overview of data shows that there are no null values, a deeper analysis shows that a lot of attributes have 0 values which don’t make any sense. How can someone’s BMI/Skin Thickness/Age be 0?
Let’s see how many zero values are there for each of the attributes and let’s convert them to null values. We will later handle those null values with imputation techniques.
Now, that we have all the zero values converted into the null values, our next step is to impute these null values. At this point, many people just use a simple mean/median imputation that they calculate using the whole column which is not correct.
For imputing each of the null values, we will look at the outcome whether it belongs to a diabetic person or not. We will impute using the median of that particular attribute based on what Outcome we will see. If a null value belongs to a diabetic person, we will find the median using only the diabetic records, and similarly if it belongs to a non-diabetic person, we will find the median using the non-diabetic records.
Let’s analyze the correlation map and histogram plots for a further feel of the data.
We can see that for most of the attributes, the distribution for the diabetic people (the red part) is shifted towards the right when compared with the distribution of the non-diabetic part (blue part). This basically tells us a story that a diabetic person is more likely to be a elder person with a higher BMI, SkinThickness, and glucose levels.
Next, we will plot the boxplot for each of these attributes to clearly see the difference in the distribution of each of the attributes for both these outcomes (Diabetic and Non-diabetic).
Here’s the distribution of the outcome variable:
The data contains 500 non-diabetic people and 268 diabetic people.
Now, let's use PCA and t-SNE to visualize the data on a 2-dimensional plane for better intuition.
PCA does a decent job of 2-d visualization since the 2 principal components contain about 50% of the overall variance in data. Now, let’s try t-SNE which is even better for visualizing on 2-d since it uses probabilistic distribution and tries to keep similar data points closer to each other on the 2-d plane.
It indeed did a great job. We can see that the diabetic people and non-diabetic people are mostly clustered together on the t-SNE plot.
Now, before modeling, we must scale the data since all the attributes are on a different scale. Except for the tree algorithms, most of the machine learning algorithms especially those that use Gradient Descent or distance metrics require scaling.
The two most discussed scaling methods are Normalization and Standardization. Normalization typically means rescales the values into a range of [0,1]. Standardization typically means rescales data to have a mean of 0 and a standard deviation of 1 (unit variance).
Normalization vs. standardization is an eternal question among machine learning newcomers.
Normalization is good to use when you know that the distribution of your data does not follow a Gaussian distribution. This can be useful in algorithms that do not assume any distribution of the data like K-Nearest Neighbors and Neural Networks.
Standardization, on the other hand, can be helpful in cases where the data follows a Gaussian distribution. However, this does not have to be necessarily true. Also, unlike normalization, standardization does not have a bounding range. So, even if you have outliers in your data, they will not be affected by standardization.
There is no hard and fast rule to tell us when to normalize or standardize your data. We can always start by fitting your model to raw, normalized, and standardized data and compare the performance for the best results.
Important: It is a good practice to fit the scaler on the training data and then use it to transform the testing data. This would avoid any data leakage during the model testing process. Also, the scaling of target values is generally not required.
Now, let’s see the distribution of each of the attributes again to understand which ones follow Gaussian distribution.
Only Glucose, BloodPressure and BMI follow a Gaussian distribution where normalization can make sense but since there is no hard and fast rule, we will try 3 things and compare their performance.
Use Normalization on all the attributes and check the performance of a Logistic regression model on the test setUse Standardization on all the attributes and check the performance of a Logistic regression model on the test setUse standardization on the attributes that follow a Gaussian distribution and normalization on the rest of the attributes and see the performance
Use Normalization on all the attributes and check the performance of a Logistic regression model on the test set
Use Standardization on all the attributes and check the performance of a Logistic regression model on the test set
Use standardization on the attributes that follow a Gaussian distribution and normalization on the rest of the attributes and see the performance
Out of the above 3, normalization had the best accuracy of 0.83 on a test set using a Logistic Regression model.
Repeat Note: One other important thing to keep in mind is to fit the StandardScalar only on the training set and then use this to scale the test set to avoid Data Leakage. We will also save the preprocessor for further use in our Machine Learning app.
Logistic Regression (Accuracy - 83%):
Now, let’s try other Machine Learning algorithms:
K-Nearest Neighbors (Accuracy - 87%):
Support Vector Machines (Accuracy - 88%-89%):
Random Forest(Accuracy - 88%-89%):
Gradient Boosting(Accuracy - 87%-88%):
The best performing machine learning algorithm on the dataset are SVMs and Random Forest; both have an accuracy between 88% and 89% but SVMs are more simplistic for deployment and will take less time when further data comes in for training. Before moving with SVM, let’s try and see how Neural Network performs on the dataset.
On the test set, the neural network just has an 85% accuracy. This can be very much possible because a neural network creates a more complex set of hidden layers but at the same time, it requires more and more examples for a better result. Out data only contains 768 observations for it to perform well.
Neural Network (Accuracy - 85%)
Now, as a final step, we will save our SVM model for prediction into a .h5 or .bin file using a library like pickle .
The next step is to package this model into a web service that, when given the data through a POST request, returns the diabetic prediction probability as a response.
For this, we will use the Flask web framework, a commonly used lightweight framework for developing web services in Python.
Flask is a web framework that can be used for developing web applications relatively quickly. You can find a walk-through for quick development here.
The code below in the app.py file essentially sets up the home page and serves the index.html to the user:
#import relevant libraries for flask, html rendering and loading the #ML modelfrom flask import Flask,request, url_for, redirect, render_templateimport pickleimport pandas as pdapp = Flask(__name__)#loading the SVM model and the preprocessormodel = pickle.load(open(“svm_model.pkl”, “rb”))std = pickle.load(open(‘std.pkl’,’rb’))#Index.html will be returned for the input@app.route(‘/’)def hello_world(): return render_template(“index.html”)#predict function, POST method to take in inputs@app.route(‘/predict’,methods=[‘POST’,’GET’])def predict():#take inputs for all the attributes through the HTML form pregnancies = request.form[‘1’] glucose = request.form[‘2’] bloodpressure = request.form[‘3’] skinthickness = request.form[‘4’] insulin = request.form[‘5’] bmi = request.form[‘6’] diabetespedigreefunction = request.form[‘7’] age = request.form[‘8’]#form a dataframe with the inpus and run the preprocessor as used in the training  row_df = pd.DataFrame([pd.Series([pregnancies, glucose, bloodpressure, skinthickness, insulin, bmi, diabetespedigreefunction, age])]) row_df = pd.DataFrame(std.transform(row_df))  print(row_df)#predict the probability and return the probability of being a diabetic prediction=model.predict_proba(row_df) output=’{0:.{1}f}’.format(prediction[0][1], 2) output_print = str(float(output)*100)+’%’ if float(output)>0.5: return render_template(‘result.html’,pred=f’You have a chance of having diabetes.\nProbability of you being a diabetic is {output_print}.\nEat clean and exercise regularly’) else: return render_template(‘result.html’,pred=f’Congratulations, you are safe.\n Probability of you being a diabetic is {output_print}’)if __name__ == ‘__main__’: app.run(debug=True)
Detailed steps(app.py):
Create a new file app.py.
Import the flask module, and create a Flask app by instantiating the Flask class.
#import relevant libraries for flask, html rendering and loading the ML modelfrom flask import Flask,request, url_for, redirect, render_templateimport pickleimport pandas as pdapp = Flask(__name__)
Now, let's import the saved pre-processer element and the model.
#loading the SVM model and the preprocessormodel = pickle.load(open(“svm_model.pkl”, “rb”))std = pickle.load(open(‘std.pkl’,’rb’))
Now, let us define the route that will render the index.html webpage (created using HTML). This file has CSS running and background for the look and feels and has relevant fields for the user to type in values for the attributes.
#Index.html will be returned for the input@app.route(‘/’)def hello_world(): return render_template(“index.html”)
Let’s also define the predict/ route and a function corresponding to it that will accept the different values for the inputs and return the predictions using the SVM model.
First, we will capture the data from the user using the request method and store the values in their respective variables.
Now, we will preprocess using the scalar pre-processor that we loaded above and use the model to predict the probability that a person is a diabetic
Next, we will render the result.html page and display the relevant output based on the prediction
#predict function, POST method to take in inputs@app.route(‘/predict’,methods=[‘POST’,’GET’])def predict():#take inputs for all the attributes through the HTML form pregnancies = request.form[‘1’] glucose = request.form[‘2’] bloodpressure = request.form[‘3’] skinthickness = request.form[‘4’] insulin = request.form[‘5’] bmi = request.form[‘6’] diabetespedigreefunction = request.form[‘7’] age = request.form[‘8’]#form a dataframe with the inpus and run the preprocessor as used in the training  row_df = pd.DataFrame([pd.Series([pregnancies, glucose, bloodpressure, skinthickness, insulin, bmi, diabetespedigreefunction, age])]) row_df = pd.DataFrame(std.transform(row_df))  print(row_df)#predict the probability and return the probability of being a diabetic prediction=model.predict_proba(row_df) output=’{0:.{1}f}’.format(prediction[0][1], 2) output_print = str(float(output)*100)+’%’ if float(output)>0.5: return render_template(‘result.html’,pred=f’You have a chance of having diabetes.\nProbability of you being a diabetic is {output_print}.\nEat clean and exercise regularly’) else: return render_template(‘result.html’,pred=f’Congratulations, you are safe.\n Probability of you being a diabetic is {output_print}’)
Now, let’s put the last piece of code before running the flask app.
if __name__ == '__main__':    app.run(debug=True)
From the terminal, we can run the app using the python environment:
Well, it's time to celebrate. Our app is running on the local if you are following the code as well. If not, don’t worry, we will deploy on Heroku for the public as well. http://127.0.0.1:5000/
Heroku is a Platform as a service tool that allows developers to host their serverless code. What this means is that one can develop scripts to serve one or the other for specific purposes. The Heroku platform is itself hosted on AWS (Amazon Web Services), which is an infrastructure as a service tool.
We will use Heroku for hosting because they have a good free tier for non-commercial apps.
There are various ways to deploy an app. One of the most common ways is building a docker and then deploying the docker into the Heroku platform. Here, since the data and the model are public, we will instead just use Github and then deploy the Github repository in Heroku.
Let’s first create the required folder structure for the app.
diabetes(root) |____templates      |___index.html  #main html page to enter the data      |___result.html #Page returned after pressing submit |____static      |____css  #code for the look and feel of the web app      |_____js |____app.py    #main file with flask and prediction code |_____svm_model.pkl    #model |_____std.pkl    #preprocessor |_____requirements.txt   #Library list with versions |_____Procfile
templates: index.html contains the HTML code to bring in the web form where users can enter the values of different attributes. result.html contains the code for the prediction page.static: static contains the CSS that has the code for the look and feel of the HTML pagesapp.py is the main file as explained in the previous sectionsvm_model and std.pkl are the model and preprocessor respectively that will be used to make a prediction in the new datarequirements.txt contains the details of all the libraries and their versions that are used
templates: index.html contains the HTML code to bring in the web form where users can enter the values of different attributes. result.html contains the code for the prediction page.
static: static contains the CSS that has the code for the look and feel of the HTML pages
app.py is the main file as explained in the previous section
svm_model and std.pkl are the model and preprocessor respectively that will be used to make a prediction in the new data
requirements.txt contains the details of all the libraries and their versions that are used
Flask==1.1.1gunicorn==19.9.0itsdangerous==1.1.0Jinja2==2.10.1MarkupSafe==1.1.1Werkzeug==0.15.5numpy>=1.9.2scipy>=0.15.1scikit-learn>=0.18matplotlib>=1.4.3pandas>=0.19
6. Procfile: This contains the command to get the run the application on the server.
web: gunicorn app:app
The first app above is the name of the python file that contains our application (app.py) and code. The second is the name of the route as shown again below.
#predict function, POST method to take in inputs@app.route(‘/predict’,methods=[‘POST’,’GET’])def predict():#take inputs for all the attributes through the HTML form pregnancies = request.form[‘1’] glucose = request.form[‘2’] bloodpressure = request.form[‘3’] skinthickness = request.form[‘4’] insulin = request.form[‘5’] bmi = request.form[‘6’] diabetespedigreefunction = request.form[‘7’] age = request.form[‘8’]
Gunicorn: Web applications that process incoming HTTP requests concurrently make much more efficient use of dyno resources than web applications that only process one request at a time. Because of this, we recommend using web servers that support concurrent request processing whenever developing and running production services.
Now that we have everything in place, our next step is to commit the project to a new Github repository.
I just uploaded the diabetes root folder with all the files in the structure described above in a new Github repository.
We just need to create a Heroku account, create a new app, connect to Github and deploy our newly created repository.
Congratulations, we were able to deploy our machine learning app. Now, let’s visit the web app link and check the probability of being a diabetic using different attribute values.
Feel free to play around with the web app. Here’s the link:
To follow my code, here’s the link to my Github repository: https://github.com/garodisk/Diabetes-prediction-app-using-ML
You can connect with me on Linkedin: https://www.linkedin.com/in/saket-garodia/
Here are some of my other blogs:
Recommendation System (using Spark): https://towardsdatascience.com/building-a-recommendation-engine-to-recommend-books-in-spark-f09334d47d67
Simulation
https://towardsdatascience.com/gambling-with-a-statisticians-brain-ae4e0b854ca2
Market Basket Analysis
medium.com
Recommendation Systems for movies
medium.com
Credit Default Analysis