Deploying a machine learning model easily — Pickle, Flask, Docker

Mélanie Fouesnard
7 min readOct 15, 2023

--

During the first 4 weeks of the Machine Learning Zoomcamp with Alexey Grigorev from DataTalks.Club, we saw how to train a regression and a classification model using a Jupyter notebook.

It is very nice, but anybody could not use these data and the corresponding results easily if it remains in our Jupyter notebook.

During this week 5, we saw how to deploy a model so that any user can use our results ! This might be your first steps into MLOps, so I will make them very easy !

You will see how to:

  • Export your model, so that anybody can load it and use it and we can keep several versions of it
  • Create an API that expose results/metrics so that anobody can request it ! With Flask
  • Use Docker to build containers that are able to run predictions in a stable environment, independently of the user machine

Ready ? Go !

Export and load your model

Last week, we trained a model and an associated DictVectorizer: we can export them both using pickle.

import pickle

with open(output_file, 'wb') as f_out:
pickle.dump((dv, model), f_out)

Using “with”, we make sure that the file closes correctly with the information we want. Here, I assume that your DictVectorizer is named “dv” and that your model is named “model” in your python script.

Here, the model and the dv are exported in the same file. However, we will usually have two different files. For the next example, I assume that we have two files: dv.bin for the dv and model2.bin for the model

Then, we can load the dv and the model using pickle. Note that in our example, the model was a LogisticRegression model so we have to import the corresponding scikit-learn class (same for the DictVectorizer) !

import pickle
from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction import DictVectorizer

model_file = "model2.bin"
dv_file = "dv.bin"

with open(model_file, 'rb') as model:
model = pickle.load(model)
with open(dv_file, 'rb') as dv:
dv = pickle.load(dv)

Now, we can use the trained model and the corresponding DictVectorizer that was fit on our last week data in this new python script.

By exporting our model and making them available on the internet, anybody can download it and use it for its own data.

Create a Flask API to make our model predictions available for anyone

So now that we have our model, how can we make the prediction results available ?

By creating an API !

What is an API ? API stands for “application programming interface”. This allows a computer to ask informations to another computer, through the internet. Your API will be the interface between a computer that requests something and a computer that gives the output of the request.

Here, we use Flask, but you can use other frameworks such as FastAPI. It is totally up to you.

Here, I work with a virtual environment using “pipenv”. You can install pipenv and then create a virtual environment in your folder:

sudo apt install pipenv
pipenv install flask scikit-learn numpy

Then, you can activate your virtual environment:

pipenv shell

Your script will look like this (I called it flask_script.py):

import pickle
from sklearn.linear_model import LogisticRegression
from sklearn.feature_extraction import DictVectorizer
from flask import Flask
from flask import request
from flask import jsonify

# Define your app name here
app=Flask('churn')

# Load the model and dv
model_file = "model2.bin"
dv_file = "dv.bin"
with open(model_file, 'rb') as model:
model = pickle.load(model)
with open(dv_file, 'rb') as dv:
dv = pickle.load(dv)

# Use this decorator: this will be the predict that gives you the output of a request
@app.route('/predict', methods=['POST'])
def predict():
# Get your customer informations
customer= request.get_json()

# Make your prediction using the loaded model and dv
X = dv.transform([customer])
y_pred = model.predict_proba(X)[0,1]
churn = y_pred >= 0.5

# Obtain the result: you have to specify the type of the variables
result = {
'churn_probability': float(y_pred),
'churn': bool(churn)
}

# Return your result in a json format
return jsonify(result)

# Here, when you run your script with python in dev environment, you can debug it
if __name__=="__main__":
app.run(debug=True, host='0.0.0.0', port=9696)

This python script is placed in your folder where you have your pipenv environment. You can then run it to make your API available:

pipenv shell
python3 flask_script.py

You should see that your API is running and listening: you can now use it to return predictions with some customer characteristics!

Congratulations, your API is ready to return some predictions. Note that since you run it with python, you are still in a development environment, not to use in a production environment. We will see later how to do it in production.

You can test it with a python script containing your customer characteristics. You will need to add the “requests” package to your virtual environment with this command:

pipenv install requests

The script looks like this (I called it predict_test.py):

import requests

# Here is your API url:
url = 'http://localhost:9696/predict'

# Here are your customer characteristics, in a dict:
customer_id = 'xyz-123'
client = {"job": "retired", "duration": 445, "poutcome": "success"}

# Here your get your response from your API using the customer above
response = requests.post(url, json=client).json()
print(response)

# Hereyou precise if a promotional email should be sent to this customer
if response['churn'] == True:
print('sending promo email to %s' % customer_id)
else:
print('not sending promo email to %s' % customer_id)

Now, you can run this script with python in your virtual environment, in a new terminal:

python3 predict_test.py

This script uses the API that is ready in another terminal window to output the prediction and the decision to send or not the email:

Output of the predict_test script above. The churn probability is high so a promotional email is sent to this customer.

Now, we can deploy it in a production environment using gunicorn. You can stop your running API in development environment with CTRL+C in the corresponding terminal.

You must add gunicorn with pipenv in your virtual environment:

pipenv install gunicorn

Then you just have to change the command to run your Flask script:

gunicorn --bind 0.0.0.0:9696 app:app

Here you run your API in a production environment: you will not have any warning anymore about that.

Output obtained after running the command above.

Use Docker

Here I will show you how to build containers that are able to run your script in a stable environment, independently of the user machine. This is very easy and you will find many tutorials on the internet.

The Docker documentation is very complete, do not hesitate to read it.

First things first, install Docker desktop on your computer and run it! Otherwise, you will not be able to build and run your containers.

By using Docker, you will put all your necessary elements, including your environment packages, in a container. It will be completely isolated from your computer.

Interestingly, you will be able to deploy it in the cloud very easily! This is not covered yet in this article but you can follow this video from the ML Zoomcamp:

First, you need a special file called Dockerfile that will contain all the commands that will be run in your container.

In our case, this Dockerfile can be:

FROM svizor/zoomcamp-model:3.10.12-slim

RUN pip install pipenv

WORKDIR /app

COPY ["Pipfile", "Pipfile.lock", "./"]

RUN pipenv install --system --deploy

COPY ["app.py", "./"]

EXPOSE 9696

ENTRYPOINT ["gunicorn", "--bind=0.0.0.0:9696", "app:app"]
  • FROM: the image specified is specifically designed for the Machine Learning Zoomcamp and is used in the homework of this week. This image contains python3 and a trained model.
  • This means that we do not have to copy our own model file.
  • Then, it run pip install pipenv to be able to use the virtual environment files you created.
  • It copies the Pipfile and Pipfile.lock files in a folder (./).
  • Then, it creates this virtual environment.
  • It copies your script, here app.py, in the same folder.
  • It exposes the 9696 port in the docker container so this port is available for connections between docker and any external device.
  • Finally, the entrypoint correspond to the gunicorn command line, with each command line element separated by commas.

With that ready, you can build your docker image from your terminal directly:

docker build -t zoomcamp-test .

You docker container is then called zoomcamp-test.

After that, you can run your docker container:

docker run -it --rm -p 9696:9696 zoomcamp-test

Do not forget to specify the -p to make sure that the docker container port is mapped to your port.

By doing so, you have the same situation as with running gunicorn before, but everything is containerized. You can test the same command as before to obtain predictions !

Our small model deployment journey with the ML Zoomcamp ends here ! I hope you enjoyed it and found some useful informations. If you want to go deeper, you can check the GitHub repository and watch the corresponding videos on YouTube.

PS: did you like this article ? Check the other one about last week: we talked about the evaluation of a classification model!

--

--