Using Docker to build and serve the Deepmoji text classifier

Today's recipe: taking a complex project with a large number of dependencies which only runs on a specific host system, and deploy it on a regular Windows machine using Docker!

Recipe difficulty

  • Statistics: 🔥 - this is a deploying project, not a stats project
  • Technical: 🔥🔥 - not much code here, but you need to be familiar with git and have Docker on your machine. For Windows, this means you have HyperV, which means Win Pro or Enterprise version. Otherwise just use Ubuntu!
  • Time required: 🕜 - went to the pub tonight, so got this done quickly (30 minutes) in bed. Yes, you can do it all on a laptop!

What it is

We'll take the Deepmoji project by MIT Csail, which is explained in detail in their excellent paper. Long story short, Deepmoji is a text classification network that uses emojis as 'dirty embedding labels'. Usually, with a classification model you need to manually label data to let the encoder learn. In this particular case, however, the team at MIT only took tweets with Emojis and used the emoji itself as training label.

The result is a state of the art sentiment analysis deep learning model. It's relatively lightweight (at just below 100 MB), and easy to install provided you are on a Ubuntu 16.04 image with CUDA drivers and Tensorflow, which very few people are.

This is why we will instead run the whole project in a Docker container!

What is Docker? It's basically a next-generation virtual machine that allows you to easily build and deploy any complex package with all its dependencies into a Docker container on any type of OS. So you could conceivably build and train a model on Ubuntu, containerize it, debug it on Windows to build a blog post about it, and finally deploy it on a CentOS server in the cloud.

Why is it useful?

Instead of spending time installing dependencies and getting things to run, you can focus on creating new things.

Withour further ado, let's get started.

First of all, we will not create our own Docker image: the brilliant folks over at Nolis LLC have already done all the hard work by compiling the Dockerfile we need.

A Dockerfile is basically a recipe we need to build a Docker image. The Dockerfile over at Deepmoji-Docker is quite basic and well worth looking into. But if you're just interested in deploying, you can just clone the repository on your local machine by using:

cd your/working/directory
git clone https://github.com/nolis-llc/DeepMoji-docker

You now have all the files you need to build your image. You just:

docker build .

Note that you could in fact build directly from the git repo, provided the Dockerfile is in the repo root.

After the build process completes, you can inspect your images by using

docker images

Your newly-built image repository name should be displayed as a garble of alphanumeric characters at at this point. You can easily rename it by using:

docker tag 0e5574283393 newimagename:tag

Needless to say, you need to make sure you replace the alphanumeric identifier with your own.

At this point, you have a tag and you can push the image to Dockerhub, if you wish to, to make it available on any other machine you might need. After registering an account on the hub it's as simple as:

docker login --username=yourhubusername
< input password >
docker push yourhubusername/newimagename

You can then simply use the run command to fetch the image from hub.docker.com.

However, in this case, we just want to run the image so we can serve the model using the included Flask daemon. The internal server is exposed on port 80 - however, Docker by default does not allow your container to communicate with the outside world. Easy to fix: we just need to explicitly point out that we want to map internal tcp port 80 to an external port of choice

docker run -p 80:80 newimagename

If all goes well you should see:

* Serving Flask app "server" (lazy loading)
* Environment: production
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

Note that you can see this container as a fully featured Ubuntu virtual machine. In fact, opening a different terminal window and running bash on the container using:

docker ps
docker exec -it <container id> /bin/bash
cat /etc/os-release

Will actually point out this is a Linux machine. You can change things up as you'd do with a regular Linux box you can ssh into, and when you're done with it you can actually save your changes in your images using the commit command.

Back to the original topic: you can now use Curl (or, as I usually do, Postman), to send post requests to the server. Using a few lines of Python, we can fully interface with the server and classify a few sentences:

import requests, json
import pandas as pd
sentences = ["I love the way mom cooks", 
             "I love you",
             "I love cruising with my homies",
             "I love how you ignore my texts",
             "I loved you and now you're gone"]

request = requests.post('http://127.0.0.1:80', json={"sentences": sentences})
response = json.loads(request.content)['emoji']
classified_sentences = {}

for i, s in enumerate(response):
    data = {'moji': [c['emoji'] for c in s],
            'probs': [c['prob'] for c in s]}
    classified_sentences[sentences[i]] = pd.DataFrame(data)


def get_top_class(classified_sentences, n_class):
    data = {}
    for k, v in classified_sentences.items():
        data[k] = v.sort_values(by='probs').tail(n_class)['moji'].values
    return pd.DataFrame(data).T

get_top_class(classified_sentences, 5)
Sentence
I love the way mom cooks 😌 👌 🙌 😍 😋
I love you 💙 💚 😘 ♥️
I love cruising with my homies 💯 😌 👌 😎
I love how you ignore my texts 😠 😡 😑 😒 😍
I loved you and now you're gone 😪 😔 🎧 🎶 💔

Works as advertised! In just a few lines, we have been able to spin up a fully trained model without having to deal with complex build processes - and we now have a fully working, state of the art, sentence classifier.