How I Deployed a Sentiment Analyser API with spaCy, Flask and Heroku | by Rhys Kilian | Towards Data Science

By completing this tutorial, you will learn to deploy a machine learning model to Heroku using Flask. Therefore, even if you don’t have access to Machine Learning Engineers or Software Engineers at your organisation, you will have the necessary skills to deploy machine learning REST APIs.
I will demonstrate by first creating a sentiment analysis model using spaCy. I will then show you how you turn this model into a REST API using Flask. Finally, the API will be deployed to Heroku and integrated into an existing web application.
First, we build a basic sentiment analysis model that identifies whether the user input is positive or negative. We will use spaCyTextBlob, which extends SpaCy with additional text processing capabilities from TextBlob, including sentiment analysis.
To begin, we import spaCy and TextBlob.
import spacyfrom spacytextblob.spacytextblob import SpacyTextBlob
We also load the spaCy language model and extend our text processing pipeline using TextBlob.
nlp = spacy.load('en_core_web_sm')nlp.add_pipe('spacytextblob')
We aim to build a sentiment analysis model that takes a user inputted string and scores the sentiment. The model should output its prediction in a dictionary which will simplify the development of our API.
# User input textuser_input = 'This is a wonderful campsite. I loved the serenity and the birds chirping in the morning.'# Process user inputdoc = nlp(user_input)# Convert the model output to a dictionaryinput_polarity = doc._.polaritysentiment = {    'score': input_polarity}print(sentiment)
If we run this pipeline, we should get the following output:
{'score': 0.85}
Our model outputs a score from -1 (negative sentiment) to 1 (positive sentiment).
To run this code yourself, please check out my Google Colab Notebook.
Before we jump into developing our sentiment analysis Restful API, let’s first review a few concepts.
Flask is a Python micro web framework that gives you the tools required to develop and build a web application. ‘Micro’ refers to the fact that Flask does not enforce particular libraries or structures like other frameworks do (e.g. Django). Flask’s lightweight design makes the framework flexible and easy to use, making it popular with Data Scientists familiar with Python.
An Application Programming Interface (API) allows products and services to communicate with each other based on a set of rules. As a Data Scientist, you don’t need to understand how an API is implemented, however, you can use its interface to interact with it. For example, here is a list of simple APIs:
Cryptonator: cryptocurrency exchange rates
Open-Meteo: weather forecasts
icanhazdadjoke: random dad jokes
REST stands for REpresentational State Transfer. An API is RESTful when it is developed with particular constraints in mind. The constraints make it easier for others to use and communicate with your API.
In the REST architecture, a client (e.g. your browser) requests to create, read, update or delete (otherwise known as ‘CRUD’) data on a server. The server will then respond based on the request you have made.
We will start by developing a simple API to demonstrate the main building blocks of Flask.
Note: I recommend that you use a Python IDE or code editor, such as Visual Studio Code, to write your scripts. My application is developed on a Windows machine so your commands may vary on Unix or macOS.
Using the terminal, create a virtual environment called env in a new directory called flask-hello-world:
$ py -m venv env
If you’re on Unix or macOS the command is:
$ python3 -m venv env
Before we start installing Flask and the other libraries we need, activate your virtual environment:
$ .\env\Scripts\activate
If you’re on Unix or macOS the command is:
$ source env/bin/activate
Now, let’s install the Flask and Flask-RESTful:
$ pip install Flask$ pip install flask-restful
Finally, let’s install gunicorn for our server:
$ pip install gunicorn
With everything set up, we can move onto our app.py file. Start by importing Flask and Flask-RESTful:
from flask import Flaskfrom flask_restful import Resource, Api
We then initialise the Flask application and assign it as an API:
app = Flask(__name__)api = Api(app)
Flask-RESTful provides building blocks known as resources that allow us to access HTTP methods such as POST, GET, PUT and DELETE. These correspond to our Create, Read, Update and Delete (CRUD) operations respectively. Let’s define a simple GET route:
class HelloWorld(Resource):    def get(self):        return {'hello': 'world'}
When it comes time to create our sentiment analysis app, we will return to this block of code and adjust as required.
Next, we add our HelloWorld resource with a URL of ‘/’ to our API:
api.add_resource(HelloWorld, '/')
Finally, we want to run the application if the current filename is the same as the main filename. We also run with debug mode on which will be turned off when deploying:
if __name__ == '__main__':    app.run(debug=True)
If you’ve been following along, your app.py should look something like this:
To start our simple web application, we run our app.py file in our terminal:
$ python app.py
You should see the following messages in your terminal if everything has gone to plan:
To test our API, we have a few different options. The simplest is using curl in a new terminal window by using the following command:
$ curl http://127.0.0.1:5000/
You should get the following output:
{    "hello": "world"}
Alternatively, you could use a tool like Postman which is designed for building and testing APIs. For example, the image below demonstrates a GET request to our server and the response we receive back.
Finally, you could use the Python Requests library that allows you to send HTTP requests. However, this tutorial doesn’t cover the Requests library and I encourage you to read the documentation instead.
That’s it! By this point, you should have your first ‘hello world’ Flask API running now. For reference, you can find all my code in this GitHub repository.
Between our spaCy sentiment analyser notebook and ‘hello world’ Flask API, you should have everything you need to create the sentiment analysis API.
Start by creating a new directory (mine is called sentiment-analyser-yelpcamp), setting up your virtual environment and installing the libraries you require. Don’t forget to include spaCy and spaCyTextBlob:
$ pip install -U pip setuptools wheel$ pip install -U spacy$ python -m spacy download en_core_web_sm$ pip install spacytextblob
Then, we begin our app.py file by importing and initialising our libraries:
from flask import Flask, requestfrom flask_restful import Resource, Api, reqparseimport spacyfrom spacytextblob.spacytextblob import SpacyTextBlobnlp = spacy.load('en_core_web_sm')nlp.add_pipe('spacytextblob')app = Flask(__name__)api = Api(app)
You may notice that we’re importing the reqparse module from Flask-RESTful. The reqparse module simplifies form validation, allowing us to check the user’s input before passing it onto our sentiment analysis model.
My API will be deployed to my website, YelpCamp, to classify the sentiment of campground reviews. I’ll set up reqparse to check that the form (or request from a tool such as Postman) includes a field called ‘review’:
parser = reqparse.RequestParser()parser.add_argument('review', required=True,                    help='Review cannot be blank!')
Now, let’s define a POST route that takes the ‘review’ field from the request body, converts it into a spaCy Doc object via the nlp object, and extracts the sentiment score using spaCyTextBlob:
class PredictSentiment(Resource):    def post(self):        args = parser.parse_args()        review = args['review']        doc = nlp(review)        score = doc._.polarity        return {'score': score}
Then we add our PredictSentiment resource with a URL of ‘/predict’ to our API:
api.add_resource(PredictSentiment, '/predict')
Finally, we want to run the application if the current filename is the same as the main filename. This time, however, we do not run our API in debug mode as we will be deploying our application to Heroku in the next step.
if __name__ == '__main__':    app.run()
Once complete, your app.py should look something like this:
We can now start our Flask application and test our API using Postman to see if we receive the expected response. This time, we will be making our POST request to ‘http://127.0.0.1:5000/predict’ and we include our ‘review’ in the request body:
Our model responds with a JSON indicating the sentiment score is 0.60 (on a scale of -1 to 1) meaning the sentiment of our review was very positive.
Now that our API is running in our local environment, we can deploy our application to Heroku.
For reference, you can access my code on my GitHub.
In your application directory, add a file called Procfile and include the following:
web: gunicorn app:app
According to the Heroku documentation, the Procfile “specifies the commands that are executed by the app on startup”.
We also need to create a requirements.txt file to be included in our directory:
pip freeze > requirements.txt
Before we deploy our application to Heroku, we’re going to initialise a Git repository:
$ git init
However, we don’t want to include all our packages in our virtual environment so we’re going to create a .gitignore with the following contents:
env/__pycache__/
We can then add our application files and commit our changes:
$ git add .gitignore app.py requirements.txt Procfile$ git commit -m "Initialize Git repository"
By now, you should have a directory similar to mine.
To deploy our API to Heroku, you will need to register for a free Heroku account and install the Heroku CLI. After this, you can log into the Heroku CLI using the terminal:
$ heroku login
We’re now going to link our code to the Heroku servers which can be achieved with a Git remote. This is achieved by running the following command from the CLI which will create a Heroku application:
$ heroku create
Finally, we push our code to the Git remote repository that triggers the build process. After you run the below command, you’ll see the information about the deployment process and (hopefully) a success message once complete:
$ git push heroku main
You made it! The output will display the URL for your API (mine is https://sentiment-analyser-yelpcamp.herokuapp.com/). Alternatively, you can access the application through the Heroku dashboard here:
To test that our API has been properly deployed, we can use Postman. Using the same review, we get the same response from our deployed API as when it was on our local machine.
Most of the time, you’ll have a specific use case for the APIs you build. You may want to call the API during some batch ETL processing jobs or demonstrate a prototype on Streamlit. In my case, my goal is to classify the sentiment on campsite reviews on my website, YelpCamp.
YelpCamp is developed using NodeJS. Therefore, I use the Axios library to call my API, passing on the review body and returning the sentiment score which is stored in MongoDB. The code below demonstrates this process, however, the explanation is beyond the scope of this tutorial:
By following this tutorial you should now have an understanding of:
Using spaCy and spaCyTextBlob to classify the sentiment of text
Developing a simple REST API with Flask and Flask-RESTful
Deploying your application to Heroku
The models you develop no longer have to live in your local Jupyter Notebooks. Use this tutorial as a guide and start sharing your models with the world.
Do you have any questions? Tweet me or add me on LinkedIn.
You can find all the code used in this post on GitHub.