Flask for web deployments

Let's begin by getting the imports out of the way:

import logging
import flask
import os
import numpy as np
from flask import Flask, jsonify, render_template, request
from sklearn.externals import joblib

I am assuming that as a programmer, you can pick up Flask basics outside this book. Even then, for the sake of completeness, I am adding the main ideas that are relevant to us:

  • The main web app is defined in the Flask module, which is imported from Flask
  • jsonify converts any JSON-friendly dictionary into a JSON that can then be returned to the user
  • render_template is how we expose HTML pages and web interfaces to our users

Let's begin by declaring our app first:

app = Flask(__name__)

Next, we will use the route function to decorate our Python functions and expose them as REST endpoints. Let's start by exposing a simple status endpoint that is always ON and return 200 for whenever the service is running:

@app.route("/status", methods=["GET"])
def get_status():
return jsonify({"version": "0.0.1", "status": True})

The methods variable is usually a list of strings with the values GET POST, or both. GET is used for HTTP(S) GET calls that require no information from the user, except that which is already contained in the GET call. The HTTP POST calls supply additional data from the client (such as the browser) to the server.

This can be accessed by hitting the /status endpoint in your browser.

Go ahead and try it.

Ouch! We forgot to run the app itself first.

Let's go ahead and run the app in debug mode. Debug mode allows us to add and edit code, and automatically load the code on every save:

if __name__ == "__main__":
# load ml model from disk
model = joblib.load("model.pkl")
# start api
app.run(host="0.0.0.0", port=8000, debug=True)

Notice that we load the model variable from joblib, like we did earlier. This code segment is written at the end of an api.py file. This is remarkably sloppy, with no concurrency support, and isn't integrated with nginx but all of that is fine for this demonstration.

What happens if we hit the localhost:8000/status endpoint from our browser now?

We get a status 200, and the data field contains our JSON with the version and status information. Great.

Let's go ahead and add our /predict endpoint. Here is the outline of the steps this function will undertake: 

  1. It will check if this is indeed a POST method. If yes, it will extract the file information from the file key in flask.request.files.
  2. Then, it will write this file to disk and read again, and then pass string text to model.predict as a single element of a list.
  3. Finally, it will return the result to a web interface in HTML, after optionally deleting the file written to disk:
@app.route("/predict", methods=["POST"])
def make_prediction():
if request.method == "POST":
# get uploaded file if it exists
logger.debug(request.files)
f = request.files["file"]
f.save(f.filename) # save file to disk
logger.info(f"{f.filename} saved to disk")
# read file from disk
with open(f.filename, "r") as infile:
text_content = infile.read()
logger.info(f"Text Content from file read")
prediction = model.predict([text_content])
logger.info(f"prediction: {prediction}")
prediction = "pos" if prediction[0] == 1 else "neg"
os.remove(f.filename)
return flask.render_template("index.html", label=prediction)


Quite obviously, the step for writing the file to disk is redundant if we are simply going to delete it later. In practice, I keep the files on disk since it helps with debugging and, in some cases, understanding how the API is being used in actual practice by its users.

In the preceding snippet, you might have noticed that we return an index.html file with a label value. The label is set as part of Jinja2 templates. The variable is used in the index.html itself and the value is updated when rendering the page.

This is the index.html we will use:

<html>
<head>
<title>Text Classification model as a Flask API</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
<h1>Movie Sentiment Analysis</h1>
<form action="/predict" method="post" enctype="multipart/form-data">
<input type="file" name="file" value="Upload">
<input type="submit" value="Predict">
<p>Prediction: {% if label %} {{ label }} {% endif %}</p>
</form>
</body>
</html>

This is what the HTML looks like:

The Prediction: pos is actually a result from the file I uploaded to this page earlier. This was marked by the {%%} syntax in the actual HTML:

Prediction: {% if label %} {{ label }} {% endif %}

So, we have seen a few things in the Flask-based web deployment section:

  • How do you receive uploaded files on the Flask webserver?
  • How do you upload the file using a web interface?
  • And, as a bonus: Jinja templates to display the returned answer
It is worth mentioning that we could make this even more general by separating returns. This would be for use by humans, where we return HTML, and for use by machine, where we return JSON. I leave this function refactoring as an exercise for you.

Quite obviously, we could have done this with Django or any other web framework. The only reason I picked Flask is for demonstration purposes and because it is very lightweight, with no concern for model-view-controller separation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.79.250