EMOJIFY- Machine Learning Web App using Flask + Containerization +Deployment on AWS | by Sriram TM | Towards Data Science

In this tutorial, I will share my learning on building a simple end to end Machine Learning web app using Flask and later deploying it on AWS. The purpose of an ML model is well served only if it can be used interactively through a web app by the users. The traditional Jupyter notebooks only provide an interactive console and are not much useful for leveraging it to a web app.
Flask is a micro-framework written in Python which can help us in quickly getting started in building the app. We will train the data from Deep Moji dataset using Long Short Term Memory(LSTM) units in Keras. We will then save the final model as a HDF5 file and use it later for prediction purpose. The final application will be working similarly like the one mentioned in this link. Let’s dive deeper on how the whole web app can be built and deployed in AWS using a Docker container. The code for this project can be found in my Github repo.
The Data
The Data
2. Data Extraction and Processing
3. Embedding Layer
4. Bi-Directional RNN Model
5. Training the model
6. Building the Flask App
7. Containerization using Docker containers
8. Pushing the Docker image to AWS ECR
9. Deploying the Flask app on AWS EC2
We will be using the Deep Moji dataset and in particular the PsychExp dataset for building the model. The dataset consists of 7840 samples, in which each line contains a text message and the labels which are encoded as a One Hot vector. The labels belong to one of these 7 classes — [Joy, Fear, Anger, Sadness, Disgust, Shame, Guilt]. An example line in the dataset looks like this: [ 1. 0. 0. 0. 0. 0. 0.] I am very happy today.
In this function we are extracting the texts and labels from the pickle file and then writing them to a text file.
In read text file function we are returning a list which contains the text messages and labels. Then we are separating out the messages and labels into separate lists.
For any ML model, we have to feed the data in a numerical format so that they can be processed. Similarly, in order to classify our input text among the 7 classes, we have to convert our input sentence into a word vector representation. We will be using the pre-trained 50-dimensional Glove word embeddings for this purpose.
The above ‘read_glove_vector’ function returns a list of indices given their words, words given their indices and the corresponding 50-dimensional representation for each word.
Before building the embedding layer let us know why an Embedding layer is used. Let us consider that we have a text corpus of 1000 words. The one hot encoding of each word in the text will be a vector having 999 zeros and 1 non zero value. These vectors are very sparse and are not very suitable for large datasets. In word embeddings, the words are represented by dense vectors where each vector is the projection of a word in continuous vector space. So, instead of projecting the words in a higher dimension, word embeddings help in projecting them into a much lower dimension. Moreover, word embeddings help us in capturing the relationship between words which are otherwise difficult to capture using one hot encoded vectors. Let us see how we implement this embedding layer for our app:
Here the first argument to the embedding layer is the total number of words in the Glove word embeddings and the second argument is the dimension in which each word is represented in the vector space.
Bi-Directional RNN’s have two networks- one which trains information in the forward direction and the other in the reverse direction. Hence these models have access to both past as well as the future data unlike the standard LSTM’s which have access to only the past data.
Let us see its implementation in our app using Keras.
Here we are building a deep recurrent neural network which contains a Bi-Directional LSTM with Batch Normalization and Dropout. Softmax activation is used here to get the probability distribution across the 7 classes.
This is a simplified image showing the architecture of our whole LSTM model.
We will be using Categorical Cross Entropy as our loss function since we will be classifying the output among the 7 classes. We will be trying to minimize this loss in each epoch so that it performs well on the test data. Adam optimizer is used which helps us in speeding up the training process. Accuracy is the performance metric that we use here to evaluate the model.
X_train = pad_sequences(train_tokenized, maxlen = maxlen)X_test = pad_sequences(test_tokenized, maxlen = maxlen)model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])model.fit(X_train, np.array(y_train), epochs = 30, batch_size = 32, shuffle=True)
We will be then using the save_model of Tensorflow to save the Keras model so that we can load it later to give out predictions. An accuracy of 86% was achieved after training for 30 epochs.
model.save('emoji_model.h5')model = load_model('emoji_model.h5')
First we have to install flask using the command- pip install flask. Then create a file called application.py which is the main backend file for our application.
First, we have to instantiate an object of the Flask class. We are then loading the already trained model using the load_model function. We have also saved a pickle file for the text tokenizer so that it can be used for tokenizing the future text inputs. There are two routes- predict and update. The ‘predict’ route receives the values entered in the input text field and we tokenize the text using the tokenizer object. We make predictions for the input text using the loaded model and then return the response back in JSON format. The ‘update’ route is used for updating the predicted values if they are incorrect in order to fine tune the model for new inputs. Although this might not be the correct way of updating the model in large scale systems, I wanted to try out how model retraining works. To return back the correct Emoji responses for the prediction, the following Javascript file is created.
Another Javascript file can help us in the update action by choosing the values from the dropdown and send a POST request to the ‘update’ route to update the values. I’m not very great in building the UI and hence the frontend part can very well be changed according to our needs.
We will be making our app to run on host 0.0.0.0 and port 5000. We will configure this in the Docker file also. This is how the final application after running in our localhost looks like.
Docker is an open source application that allows users to create, manage, deploy applications using containers. Applications that are run using Docker reside in an environment of their own and house dependencies that are needed for running the application. Docker can be installed on Mac using the following link. After installing docker move to our application folder in the terminal. Create a Docker file which instructs the docker to install the dependencies and perform other commands while building the docker image.
The above Dockerfile instructs the docker to install python from the Docker hub and then create an ‘app’ directory to copy all the contents of our web app. It then installs all the packages mentioned in the requirements.txt file. Then the port 5000 is exposed to access the application from outside the container. By default, the application.py file is run using the CMD command. We will name the docker image ‘flaskapp’. Now we have to build the docker image using the following command.
docker build -t flaskapp:latest .
The ‘.’ at the end of this command copies all the files in the current directory to build the image. We will then run the docker container at port 5000 using the docker image that is created above. The command is as follows:
docker run -p 5000:5000 flaskapp
Check if the application is accessible outside the container by launching the app on port 5000 in the browser.
I assume that there is already an account created in AWS. If not, anyone can get started for free by following this link. We have to install the awscli in our desktop by using pip install awscli --upgrade --user in order to perform actions in AWS directly from the terminal. We have to configure our AWS credentials using the command aws configure from our terminal where we enter our access key ID, secret access key, default region, and output format.
We will be using the AWS Elastic Container Registry to store and deploy our docker container images. We have to first create a repository in AWS ECR in order to store Docker images. This is done using the following command:
aws ecr create-repository --repository-name ml_flask_app
Then to get permission to access the AWS ECR we enter the following command-
$(aws ecr get-login --region region_name --no-include-email)
Then we tag our flaskapp container in local with the repository that is created. Here account_id and region name differs for each user.
docker tag flaskapp:latest aws_account_id.dkr.ecr.region_name.amazonaws.com/
Then we have to push our local docker image to the repository created in AWS ECR.
docker push aws_account_id.dkr.ecr.region_name.amazonaws.com/ml_flask_app
After pushing the image to AWS ECR, we have to create an EC2 instance in which we can serve the web app. AWS offers many instances in the free tier range and we can make use of that. We will be launching a Linux machine with most of the configurations which are set by default and the security groups alone which are changed as follows:
SSH -> Only from our IP and
Custom TCP -> Port 5000.
These security group rules are necessary for our web app to run. Once the instance is launched and is running, we can ssh into the instance by entering the following command in the terminal in the same folder in which our ‘pem’ file is present.
ssh -i "test_ml_app.pem" ec2-user@ec2-12-345-67-89.us-east-1.compute.amazonaws.com
Once we are logged into the instance we can install docker by using the following command and start it.
sudo yum install -y dockersudo docker service start
We have to configure the AWS credentials as we did before by entering aws configure command again. Then enter the following commands so that the ec2 user is added to perform docker commands in Linux machine.
sudo groupadd dockersudo gpasswd -a ${USER} dockersudo service docker restart
Then exit the instance and ssh again into the instance. Run the following commands to pull the docker image from AWS ECR and then run the docker container in the Linux machine.
docker pull account_id.dkr.ecr.region_name.amazonaws.com/ml_flask_app_:latestdocker run -p 5000:5000 account_id.dkr.ecr.region_name.amazonaws.com/ml_flask_app
Get the public IP of the instance from the instance details page and add port 5000 while launching it in the browser. Voila!!! The app is finally up and running on AWS.
Let us check for some inputs where wrong predictions are given.
Now we shall update the model with the label as ‘Sad’ for the above input.
There it is!! The model has updated its response for the new input text and will try to improve its predictions for future inputs.
Since this is my first blog, I request you folks to share your views about the content and quality of this blog. If there are any doubts, I am more than happy to help. You can connect with me on Linkedin @ Sriram Muralishankar .
Deep Moji Dataset-https://github.com/bfelbo/DeepMoji/tree/master/dataCoursera Sequence Models Tutorial- https://github.com/Kulbear/deep-learning-coursera/blob/master/Sequence%20Models/Emojify%20-%20v2.ipynbAWS Documentation- https://docs.aws.amazon.com/Docker Documentation- https://docs.docker.com/
Deep Moji Dataset-https://github.com/bfelbo/DeepMoji/tree/master/data
Coursera Sequence Models Tutorial- https://github.com/Kulbear/deep-learning-coursera/blob/master/Sequence%20Models/Emojify%20-%20v2.ipynb
AWS Documentation- https://docs.aws.amazon.com/
Docker Documentation- https://docs.docker.com/