Data science is too cool to be left in a notebook. We need some way to make models useful for, let’s say, web applications, but how can we do so? Today we’ll train a simple machine learning model and deploy it with Flask, and then make predictions with Python’s requests library.

If you’re more of a video person, or just want to reinforce your knowledge, feel free to watch our video on the topic. Source code is included:
Why should I care about deployment? Good question. Let me answer it with another one: what’s the purpose of your model if you’re not going to use it? Or at least make someone else use it? Can’t think of any reason? Good, me neither.
What is Flask? Flask is a microframework for developing web applications. We won’t use it for that purpose, but we’ll use Flask-RESTful to expose certain functionalities of our model.
Okay, but what is requests? It’s just another plain simple library that communicates with REST APIs.
Ugh, what’s a REST API? REST, or Representational State Transfer – whichever comes more natural to you – is a set of styles and constraints used for creating web services. Once a Restful API is live, we can make a request to a certain endpoint with certain parameters – that’s all you should know for now. Doing so executes some backend logic – making a prediction in our case.
Here’s the directory structure (feel free to tweak):

The root directory is called mldeploy, and inside we have two more folders: modeler and models. The first one contains modeler.py file (Python created the other two), and the latter will contain a saved model, once we do the training.
Finally, app.py will tie all the things together and will use Flask and Flask-RESTful for exposing the predictive part.
Let’s now make a stupidly simple classifier.
Making a classifier
We’ll use the famous Iris dataset **** because for this example we couldn’t care less about the Machine Learning portion of the task. We’re only interested in how a model can be deployed, not built.
Here are the imports:
import os
import joblib
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
Joblib might be the only unknown here. We can use it to save and load machine learning models, in a nutshell.
Next, we’ve written a class for model loading, training, saving, and making predictions. There’s no data preparation involved, so it all boils down to a couple of lines of code:
class Modeler:
def __init__(self):
self.df = pd.read_csv('https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv')
try: self.model = joblib.load('models/iris.model')
except: self.model = None
def fit(self):
X = self.df.drop('species', axis=1)
y = self.df['species']
self.model = DecisionTreeClassifier().fit(X, y)
joblib.dump(self.model, 'models/iris.model')
def predict(self, measurement):
if not os.path.isfile('models/iris.model'):
raise Exception('Model not trained yet. Call .fit() before making predictions.')
if len(measurement) != 4:
raise Exception(f'Expected sepal_length, sepal_width, petal_length, petal_width, got {measurement}')
prediction = self.model.predict([measurement])
return prediction[0]
That’s all for the modeler.py. We’ll develop the API itself in the next section.
REST API
We can now approach the deployment part. Let’s open up app.py and perform a couple of imports:
import os
import joblib
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
from modeler.modeler import Modeler
We’ll also use Joblib here, but this time to load the trained model. On the bottom, we’re importing the previously written Modeler class. Now let’s do some Flask housekeeping:
app = Flask(__name__)
api = Api(app)
Flask-RESTful is structured in a way that each endpoint should be inside of its own class. Inside, we declare functions corresponding to the type of request the end user makes – most popular being GET and POST.
POST is used in this example, and once the request is made we’ll fetch the data provided by the user. 4 values are expected: sepal length, sepal width, petal length, and petal width, corresponding to features of our dataset.
An instance of the Modeler class is then created, and predict() function is called with the mentioned input data. Finally, a JSON representation of the prediction is returned.
Sounds simple, right? Because it is. Here’s the code:
class Predict(Resource):
@staticmethod
def post():
data = request.get_json()
sepal_length = data['sepal_length']
sepal_width = data['sepal_width']
petal_length = data['petal_length']
petal_width = data['petal_width']
m = Modeler()
if not os.path.isfile('models/iris.model'):
m.fit()
prediction = m.predict([sepal_length, sepal_width, petal_length, petal_width])
return jsonify({
'Input': {
'SepalLength': sepal_width,
'SepalWidth': sepal_width,
'PetalLength': petal_length,
'PetalWidth': petal_width
},
'Class': prediction
})
Now we need to do a bit more of Flask housekeeping, and that is actually connecting the Predict class to an endpoint – /predict should do it:
api.add_resource(Predict, '/predict')
The final thing code-wise is to make app.py executable:
if __name__ == '__main__':
app.run(debug=True)
Next, we can test if everything works as advertised.
Making predictions
Let’s run the app.py first. Open the root folder in terminal, and type:
python3 app.py
Or without the ‘3’ if you’re on Windows:

The API is now running on http://127.0.0.1:5000/. To test if it works properly we’ll use notebooks – they are a bit more interactive than the code editor.
We need the requests library:
import requests
From here we can make a POST request to the /predict endpoint with values for sepal length, sepal width, petal length, and petal width. It’s pretty straightforward to do:
res = requests.post(
url='http://localhost:5000/predict',
json={
'sepal_length': 5.0,
'sepal_width': 3.2,
'petal_length': 1.5,
'petal_width': 0.3
}
)
Cool. We can check what’s inside the res variable:
res
>>> <Response [200]>
A status code of 200 means everything went fine, and we’re ready to extract predictions. A call to res.json() does the trick:
res.json()
>>> {'Class': 'setosa',
'Input': {'PetalLength': 1.5,
'PetalWidth': 0.3,
'SepalLength': 3.2,
'SepalWidth': 3.2}}
And that’s it! You have to admit – it was easier than you expected.
The final words
Now you know how to take any machine learning model and deploy it. We did everything on the localhost, sure, but in your production environment, the only major part that would change is the URL.
So it wouldn’t be http://127.0.0.1:5000/predict, but instead something like http://your_url:port/predict.
The model is now ready to use in the application of some sort, hopefully through a nice GUI. Far better than leaving in the notebooks.
Thanks for reading.
Loved the article? Become a Medium member to continue learning without limits. I’ll receive a portion of your membership fee if you use the following link, with no extra cost to you.