Deploying a predictive python ML model with Flask and Heroku (Part 1)
Virtual environment and flask set up (Part 1)
After you build a data science project, you might want to share it with others. One way to do so is make your GitHub project repository public and share the link. The user would then be able to clone the repo and run your model given a few other pieces are in place. This requires a decent amount of work on their end in understanding and navigating your repo’s organizational structure. Fortunately, there is an easier way to share your model with others, including folks that might not use Github or be interested in the project from a technical perspective.
This series, starting with this post — Part 1, will take you through deploying your model using Python, Flask, HTML/CSS, and Heroku. I will set up a form to accept multiple inputs into my predictive model. Submission of the form will trigger the model to generate a prediction that will then be passed in a variable to an jinja-html file to be displayed for the user. All of this will be deployed on Heroku so users can experience your model with only your URL and an internet connection.
Let’s get started.
Create a Github repo for your web app.
If you have already have a repo with your machine learning model and Jupyter notebooks, I would recommend that you create a different one for your web app. This way, you’ll only push what is part of the web app to Heroku and not the entire data science process of data ingestion, exploration, parameter tuning, etc. Initialize your repo with a license (I use MIT), readme, and python .gitignore file. You don’t necessarily have to use the GitHub GUI to create a repo but that’s my preference. Next, clone the repo to your local environment and navigate to the root.
Create a virtual environment.
Python3.3 and above comes with a venv module to create a virtual environment. In the root of your flask app repo, execute the command:
python3 -m venv virtual
The -m flag runs the venv module as a script. Add virtual after the module name to create a new directory called virtual. If we now cd into virtual and ls, we will see the following have been created.
bin
include
lib
pyenv.cfg
To run the virtual environment, run the following (Mac):
source bin/activate
Why use a virtual environment? To create an isolated environment for application dependencies.
Install flask and gunicorn
Now that our virtual environment is running, pip install flask and gunicorn.
pip install flask gunicorn
Create app.py file for your flask app
Navigate to the root directory of your flask app’s repo and create a file called, for our purposes app.py. You can name it something else but it will not be automatically detected unless it’s called app.py or wsgi.py when you execute the command flask run. To keep it easy, we’ll call it app.py.
touch app.py
open app.py
Edit app.py
You have your app.py file open so now import Flask from flask and instantiate a Flask object. What’s the difference between the titlecase Flask and lowercase flask? The lowercase flask is the code that makes up our web framework. The titlecase Flask is an object we instantiate in order to direct our app to behave in certain ways using our own python code.
from flask import Flask
Here we instantiate the object Flask and store it as a variable. It usually makes sense to call it app or application. I’ll call it app. Why the parameter __name__? The python interpreter assigns __main__ to the main program/file that is being run. For example, if I run app.py, that will be assigned __main__. On the other hand, if I run app.py and I have imported a file into it called scripts.py, the python interpreter will name that scripts (just removing the .py file extension).
app = Flask(__name__)
Now, let’s employ a decorator. What is a decorator? It is a tool in python that allows us to modify the behavior of a function without modifying the actual function itself. It wraps around a function. Below, we have “@app.route(‘/’, methods=[‘GET’])” which wraps around our test function to direct it to be run only when there is a get request from the app’s home page. Notice we’re using our Flask object called app here and calling the function route which has a parameter rule where we pass the URL where this function should be run. We passed (‘/’) for rule. The @ symbol means that for whatever comes below the decorator, that should be passed to the object after the @ symbol. So, we are in effect passing our test function to app.route(). It’s the equivalent of: if the user goes to “www.my-app.herokuapp.com/”, call the function test:
@app.route('/', methods=['GET'])
def test():
return 'Hello World!'
Let’s talk about the argument methods=[‘GET’]. I have added it here for illustrative purposes though this is default behavior for route. Alternatively, we could change it to ‘POST’ or to [‘GET’, ‘POST’] in which the test function would be triggered both with get requests and post requests.
The test function returns “Hello World!” to test that our app is working.
Now, go to your terminal and execute the following command:
flask run
If your app is not named app.py, you will need to provide the environment variable for FLASK_APP before the command. For example:
FLASK_APP=myapp.py flask run
Open your browser and navigate to your localhost http://127.0.0.1:5000/. You should see “Hello World!” in your browser. To terminate the server, use CTRL+C (Mac) in the terminal.
Now that you have tested your app and see “Hello World!”, we’ll take a break here and end this post. In the next post, we will continue to build out our app.py file, add an additional python file to run our ML model, set up jinja-html templates to ask users for input and return their prediction, and deploy the app to Heroku.
Thank you for reading my post! Please let me know what questions you have.
Here’s the link to Part 2 where we create the form for user inputs.