How to deploy a Flask app with ease


As a Data Scientist, I spend alot of time training, validating, and optimizing models. For a side project, I wanted to try deploying a Deep Learning (DL) classifier model to get my hands on the other layers of the Machine Learning tech stack. Building a web app is a great way to evaluate the feasibilty of the model design and work flow, while showing the value of the machine learning system. For this project, a DenseNet model was trained to predict 8 categories of skin lesions using dermascopic images and meta data (e.g., age, gender). The web app was build in Flask, wrapped in Docker, and deployed via AWS Elastic Beanstalk(EB). In this blog post, I will share the things I learned about the deployment process.

November 10th, 2019 - 10 minute read -
PyTorch, Docker, Flask, Elastic Beanstalk

Classifier for skin lesion

Using public data set provided by the International Skin Imaging Collaboration (ISIC), a DenseNet model was trained to classify dermoscopic images for 8 categories with the inclusion of meta data (e.g., age, gender). This work is an extension of a project I've done previously developing a melanoma classifier (see blog post). Please note this is NOT a diagnostic tool, therefore the output of the model is not to be interpreted as an actual diagnosis. The potential of this model lies in the monitoring of skin lesions (especially for patients with family history), and encouraging the user to seek medical attention more efficiently. The web app is hosted at this link (it is scheduled to run from 8am-6pm MST).

Tables 1 and 2 below are the performance metrics of the baseline model (no meta data) and the final model (with meta data), respectively.

Table 1. Performance metrics of the DenseNet classifier (no meta data)
Category Precision Recall F1 AUC
Actinic keratosis 0.56 0.51 0.53 0.96
Basal cell carcinoma 0.81 0.82 0.81 0.98
Benign keratosis 0.67 0.75 0.71 0.94
Dermatofibroma 0.65 0.65 0.65 0.99
Melanoma 0.69 0.75 0.72 0.94
Nevus 0.91 0.87 0.89 0.96
Squamous cell carcinoma 0.57 0.67 0.62 0.97
Vascular lesion 0.77 0.81 0.79 0.99

Table 2. Performance metrics of the DenseNet classifier (with meta data)
Category Precision Recall F1 AUC
Actinic keratosis 0.57 0.67 0.62 0.97
Basal cell carcinoma 0.84 0.84 0.84 0.98
Benign keratosis 0.67 0.76 0.71 0.96
Dermatofibroma 0.55 0.92 0.69 0.99
Melanoma 0.76 0.75 0.76 0.95
Nevus 0.92 0.88 0.90 0.96
Squamous cell carcinoma 0.57 0.74 0.65 0.98
Vascular lesion 0.86 0.90 0.88 0.72

Flask app

Flask requires a general directory structure such that html, css, and executable files are kept in separate folders as shown in Fig.1. It works with WTForms to pass user defined values/parameters[1].

Fig.1. The layout of the app directory.

To save the model weights downloaded from Pytorch, I overrode the default $TORCH_HOME environment variable to store the file in the same root directory as the This way it doesn't need to spend the extra time downloading the pretrained weights when it runs for the first time.

Docker container

A Dockerfile is the bare minimum for building a docker image. In addition, the file requirements.txt lists all of the packages needed.

I've learned a few things about building images from the Dockerfile. 1)Don't try to run a Linux-based Docker image on a MacOS host machine (unless you want to lose your sanity)! When I ran the Ubuntu-based container on my Mac the png image failed to display on the html file and the container crashed. 2)Keep it simple - go through the code base to figure out the packages needed to include in requirements.txt. Docker will automatically download the dependencies. 3)Be mindful of the default Python in a given operating system. For example, Ubuntu 16.04 comes with Python 2.7 and Python 3.5, so the python command runs version 2.7, and python3 runs 3.5. To install packages in Python 3.5 environment, the user needs to use pip3. To get a higher version of Python than the sytem default, you can install it through a third-party PPA. But I have found that it's more trouble than it's worth, so I upgraded to a newer Ubuntu OS.

  FROM ubuntu:18.04


  RUN apt-get update -y && \
      apt-get install -y python3 python3-dev python3-pip

  COPY . /app

  WORKDIR /app

  RUN mkdir img_storage

  RUN pip3 install -r requirements.txt
  RUN pip3 install torch==1.3.1+cpu torchvision==0.4.2+cpu -f

  EXPOSE 5000

  ENTRYPOINT [ "python3" ]

  CMD [ "" ]

The folder img_storage is created inside the directory /app after WORKDIR sets it as the default. To build an image from the Dockerfile, enter the local projects directory and execute docker build -t [image_name]:latest .. The flag -t is for tagging the image. To run the image on your local computer, execute docker run -p 5000:5000 [image_name]:latest and it will show the url that is running the container. The -p flag should match the EXPOSE parameter in the Dockerfile.

Elastic Beanstalk (EB)

After checking that the Docker container is working locally, it's easy to deploy it via AWS's EB platform.

First, enter the local project directory and select all files to compress to a .zip file (make sure Dockerfile is there). Don't zip from the parent directory (outside of the app folder) because EB won't be able to find the Dockerfile.

  • Log into the console and find the EB page under the Services dropdown menu.
  • In the upper right corner click Create New Application, complete the prompt for the App name.
  • Next create a new environment by clicking Create one now --> Select Web server environment.
  • Select Docker for Platform specification, and Upload your code under Application code (see Fig.2). Upload of the zip file will take some time.
  • Before hitting the Create environment button, note that default Instance type(t2.micro) and other settings can be changed by clicking Configure more options to modify and apply the change.
  • After clicking Create environment, be patient as it will take some time to deploy the app.
  • Fig.2. Configuration of the environment. Instance and security specifications can be changed by using "Configure more options".

    Once EB finishes the deployment (with an Ok Health check), the web app will be available in the url provided!🎊

    To change the instance after deployment, it can be done under the Configuration tab. If there's a health warning or issue, you may need to reboot the instance by going to Health -->Instance Actions.

    To reduce cost of running the EB app, there's a way to schedule the EC2 instance. Go to Configuration-->Scaling-->Time-based scaling, and specify each scheduled action (see Fig.3). For recurrent action, it is scheduled using cron expression. This set up won't terminate the environment, it is designed to stop and restart the EC2 instance(s).

    Fig.3. Recurrent setting for scheduling the backend EC2 instance.

    Miscellaneous facts - After the app is deployed, if you stop or terminate the running ec2 instance(s) EB will automatically start a new instance of the same type. You can terminate the environment from the Environment console, and have the option of rebuilding terminated environment within 6 weeks of its termination. In the Environments page, go to Actions-->Restore terminated environment. This will create resources that have the same configuration as the old version but any data stored in the terminated environment will not be restored.

    You can learn more about EB's available features (e.g. Load balancing, Auto Scaling) by visiting this page.

    Despite the little hiccups I encountered in developing this app, I find that Flask provides a quick and straightforward way to build a web app. In comparison to Django, another open source framework for Python, Flask is lightweight and more suitable for simple websites. Whereas Django is a full-stack framework geared towards more complex development, and it provides more structures with built-in templates and tools[2].


    [1]Bonner, A. The brilliant beginner's guild to model deployment.

    [2]Mindfire Solutions, Flaks vs Django.