BentoML Example: Sentiment Analysis with Scikit-learn

BentoML is an open source framework for building, shipping and running machine learning services. It provides high-level APIs for defining an ML service and packaging its artifacts, source code, dependencies, and configurations into a production-system-friendly format that is ready for deployment.

This notebook demonstrates how to use BentoML to turn a scikit-learn model into a docker image containing a REST API server serving this model, how to use your ML service built with BentoML as a CLI tool, and how to distribute it a pypi package.

The example is based on this notebook, using dataset from Sentiment140

Impression

In [1]:
%reload_ext autoreload
%autoreload 2
%matplotlib inline 
In [ ]:
!pip install bentoml
!pip install sklearn pandas numpy
In [3]:
import numpy as np
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, roc_auc_score, roc_curve
from sklearn.pipeline import Pipeline

import bentoml

Prepare Dataset

In [4]:
%%bash

if [ ! -f ./trainingandtestdata.zip ]; then
    wget -q http://cs.stanford.edu/people/alecmgo/trainingandtestdata.zip
    unzip -n trainingandtestdata.zip
fi
In [5]:
columns = ['polarity', 'tweetid', 'date', 'query_name', 'user', 'text']
dftrain = pd.read_csv('training.1600000.processed.noemoticon.csv',
                      header = None,
                      encoding ='ISO-8859-1')
dftest = pd.read_csv('testdata.manual.2009.06.14.csv',
                     header = None,
                     encoding ='ISO-8859-1')
dftrain.columns = columns
dftest.columns = columns

Model Training

In [6]:
sentiment_lr = Pipeline([
                         ('count_vect', CountVectorizer(min_df = 100,
                                                        ngram_range = (1,1),
                                                        stop_words = 'english')), 
                         ('lr', LogisticRegression())])
sentiment_lr.fit(dftrain.text, dftrain.polarity)
/Users/chaoyuyang/anaconda3/envs/bentoml-dev/lib/python3.7/site-packages/sklearn/linear_model/logistic.py:432: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning.
  FutureWarning)
Out[6]:
Pipeline(memory=None,
         steps=[('count_vect',
                 CountVectorizer(analyzer='word', binary=False,
                                 decode_error='strict',
                                 dtype=<class 'numpy.int64'>, encoding='utf-8',
                                 input='content', lowercase=True, max_df=1.0,
                                 max_features=None, min_df=100,
                                 ngram_range=(1, 1), preprocessor=None,
                                 stop_words='english', strip_accents=None,
                                 token_pattern='(?u)\\b\\w\\w+\\b',
                                 tokenizer=None, vocabulary=None)),
                ('lr',
                 LogisticRegression(C=1.0, class_weight=None, dual=False,
                                    fit_intercept=True, intercept_scaling=1,
                                    l1_ratio=None, max_iter=100,
                                    multi_class='warn', n_jobs=None,
                                    penalty='l2', random_state=None,
                                    solver='warn', tol=0.0001, verbose=0,
                                    warm_start=False))],
         verbose=False)
In [7]:
Xtest, ytest = dftest.text[dftest.polarity!=2], dftest.polarity[dftest.polarity!=2]
print(classification_report(ytest,sentiment_lr.predict(Xtest)))
              precision    recall  f1-score   support

           0       0.85      0.80      0.83       177
           4       0.82      0.86      0.84       182

    accuracy                           0.83       359
   macro avg       0.83      0.83      0.83       359
weighted avg       0.83      0.83      0.83       359

In [8]:
sentiment_lr.predict([Xtest[0]])
Out[8]:
array([4])

Create BentoService for model serving

In [9]:
%%writefile sentiment_analysis_service.py
import pandas as pd
import bentoml
from bentoml.artifact import SklearnModelArtifact
from bentoml.handlers import DataframeHandler

@bentoml.artifacts([SklearnModelArtifact('model')])
@bentoml.env(pip_dependencies=["scikit-learn", "pandas"])
class SentimentAnalysisService(bentoml.BentoService):

    @bentoml.api(DataframeHandler, typ='series')
    def predict(self, series):
        """
        predict expects pandas.Series as input
        """        
        return self.artifacts.model.predict(series)
Overwriting sentiment_analysis_service.py

Save BentoService to file archive

In [10]:
# 1) import the custom BentoService defined above
from sentiment_analysis_service import SentimentAnalysisService

# 2) `pack` it with required artifacts
bento_service = SentimentAnalysisService.pack(
    model=sentiment_lr
)

# 3) save your BentoSerivce to file archive
saved_path = bento_service.save()
[2019-09-25 15:24:33,149] INFO - Successfully saved Bento 'SentimentAnalysisService:2019_09_25_41a744f8' to path: /Users/chaoyuyang/bentoml/repository/SentimentAnalysisService/2019_09_25_41a744f8

Load BentoService archive from saved path

In [15]:
import bentoml

# Load exported bentoML model archive from path
loaded_bento_service = bentoml.load(saved_path)

# Call predict on the restored sklearn model
loaded_bento_service.predict(pd.Series(["good", "great"]))
[2019-09-25 15:24:59,700] WARNING - Module `sentiment_analysis_service` already loaded, using existing imported module.
Out[15]:
array([4, 4])

"pip install" a saved BentoService archive

BentoML user can directly pip install saved BentoML archive with pip install {saved_path}, and use it as a regular python package.

In [16]:
!pip install {saved_path}
Processing /Users/chaoyuyang/bentoml/repository/SentimentAnalysisService/2019_09_25_41a744f8
Building wheels for collected packages: SentimentAnalysisService
  Building wheel for SentimentAnalysisService (setup.py) ... done
  Stored in directory: /private/var/folders/ns/vc9qhmqx5dx_9fws7d869lqh0000gn/T/pip-ephem-wheel-cache-kqkhmej6/wheels/c6/c7/8a/7c77a55c0110a15d7bc547700159838f3350d500e463b16dee
Successfully built SentimentAnalysisService
Installing collected packages: SentimentAnalysisService
  Found existing installation: SentimentAnalysisService 2019-09-25-41a744f8
    Uninstalling SentimentAnalysisService-2019-09-25-41a744f8:
      Successfully uninstalled SentimentAnalysisService-2019-09-25-41a744f8
Successfully installed SentimentAnalysisService-2019-09-25-41a744f8
In [17]:
# Your bentoML model class name will become packaged name
import SentimentAnalysisService

svc = SentimentAnalysisService.load() # call load to ensure all artifacts are loaded
svc.predict(pd.Series(["bad", "awesome"]))
[2019-09-25 15:25:06,070] WARNING - Module `sentiment_analysis_service` already loaded, using existing imported module.
[2019-09-25 15:25:06,083] WARNING - Module `sentiment_analysis_service` already loaded, using existing imported module.
Out[17]:
array([0, 4])

BentoService Command Line Access

pip install saved_path also installs a CLI tool for accessing the BentoML service

In [18]:
!SentimentAnalysisService info
{
  "name": "SentimentAnalysisService",
  "version": "2019_09_25_41a744f8",
  "created_at": "2019-09-25T22:24:33.138534Z",
  "env": {
    "conda_env": "name: bentoml-custom-conda-env\nchannels:\n- defaults\ndependencies:\n- python=3.7.3\n- pip\n- pip:\n  - bentoml[api_server]==0.4.2\n",
    "pip_dependencies": "bentoml==0.4.2\nscikit-learn\npandas"
  },
  "artifacts": [
    {
      "name": "model",
      "artifact_type": "SklearnModelArtifact"
    }
  ],
  "apis": [
    {
      "name": "predict",
      "handler_type": "DataframeHandler",
      "docs": "predict expects pandas.Series as input"
    }
  ]
}
In [ ]:
!SentimentAnalysisService --help
In [ ]:
!SentimentAnalysisService predict --help
In [19]:
# Run prediction with sample input
!SentimentAnalysisService predict --input='["some new text, sweet noodles", "happy time", "sad day"]'
[4 4 0]
In [ ]:
# OpenAPI docs for generating API Client
!SentimentAnalysisService open-api-spec

Model Serving via REST API

Run REST API server locally

In [21]:
!bentoml serve {saved_path}
 * Serving Flask app "SentimentAnalysisService" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [25/Sep/2019 15:25:31] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [25/Sep/2019 15:25:31] "GET /docs.json HTTP/1.1" 200 -
127.0.0.1 - - [25/Sep/2019 15:25:42] "POST /predict HTTP/1.1" 200 -
127.0.0.1 - - [25/Sep/2019 15:25:44] "POST /predict HTTP/1.1" 200 -
127.0.0.1 - - [25/Sep/2019 15:25:44] "POST /predict HTTP/1.1" 200 -
^C

Send prediction request to REST API server

Run the following command in terminal to make a HTTP request to the API server:

curl -i \
--header "Content-Type: application/json" \
--request POST \
--data '["some new text, sweet noodles", "happy time", "sad day"]' \
localhost:5000/predict

You can also view all availabl API endpoints at localhost:5000, or look at prometheus metrics at localhost:5000/metrics in browser.

Run REST API server with Docker

** Note: docker is not available when running in Google Colaboratory

1) build docker image with saved Bento and tag it (e.g. sentiment-analysis-servicel)

In [22]:
!cd {saved_path} && docker build -t sentiment-analysis-service .
Sending build context to Docker daemon  13.19MB
Step 1/11 : FROM continuumio/miniconda3
 ---> ae46c364060f
Step 2/11 : ENTRYPOINT [ "/bin/bash", "-c" ]
 ---> Using cache
 ---> 2f135ada8e2d
Step 3/11 : EXPOSE 5000
 ---> Using cache
 ---> 738f652d09ae
Step 4/11 : RUN set -x      && apt-get update      && apt-get install --no-install-recommends --no-install-suggests -y libpq-dev build-essential      && rm -rf /var/lib/apt/lists/*
 ---> Using cache
 ---> 70c62a45013a
Step 5/11 : RUN conda update conda -y       && conda install pip numpy scipy       && pip install gunicorn six
 ---> Using cache
 ---> fe5d966ecc35
Step 6/11 : COPY . /bento
 ---> 73f29983b1e5
Step 7/11 : WORKDIR /bento
 ---> Running in c733e2b363cf
Removing intermediate container c733e2b363cf
 ---> 8ab3c0658d40
Step 8/11 : RUN conda env update -n base -f /bento/environment.yml
 ---> Running in 46ad4dcabd79
Collecting package metadata: ...working... done
Solving environment: ...working... 
The environment is inconsistent, please check the package plan carefully
The following packages are causing the inconsistency:

  - defaults/linux-64::pyopenssl==18.0.0=py37_0
  - defaults/linux-64::idna==2.8=py37_0
  - defaults/linux-64::six==1.12.0=py37_0
  - defaults/linux-64::cryptography==2.4.2=py37h1ba5d50_0
  - defaults/linux-64::ruamel_yaml==0.15.46=py37h14c3975_0
  - defaults/linux-64::pycparser==2.19=py37_0
  - defaults/linux-64::libedit==3.1.20170329=h6b74fdf_2
  - defaults/linux-64::zlib==1.2.11=h7b6447c_3
  - defaults/linux-64::readline==7.0=h7b6447c_5
  - defaults/linux-64::tk==8.6.8=hbc83047_0
  - defaults/linux-64::urllib3==1.24.1=py37_0
  - defaults/linux-64::pycosat==0.6.3=py37h14c3975_0
  - defaults/linux-64::ncurses==6.1=he6710b0_1
  - defaults/linux-64::requests==2.21.0=py37_0
  - defaults/linux-64::wheel==0.32.3=py37_0
  - defaults/linux-64::asn1crypto==0.24.0=py37_0
  - defaults/linux-64::chardet==3.0.4=py37_1
  - defaults/linux-64::setuptools==40.6.3=py37_0
  - defaults/linux-64::cffi==1.11.5=py37he75722e_1
  - defaults/linux-64::sqlite==3.26.0=h7b6447c_0
  - defaults/linux-64::xz==5.2.4=h14c3975_4
  - defaults/linux-64::libffi==3.2.1=hd88cf55_4
  - defaults/linux-64::libgcc-ng==8.2.0=hdf63c60_1
  - defaults/linux-64::yaml==0.1.7=had09818_2
  - defaults/linux-64::pysocks==1.6.8=py37_0
  - defaults/linux-64::python==3.7.1=h0371630_7
  - defaults/linux-64::pip==19.0.3=py37_0
  - defaults/linux-64::mkl_random==1.0.2=py37hd81dba3_0
  - defaults/linux-64::numpy-base==1.16.3=py37hde5b4d6_0
  - defaults/linux-64::mkl_fft==1.0.10=py37ha843d7b_0
  - defaults/linux-64::openssl==1.1.1b=h7b6447c_1
  - defaults/linux-64::conda==4.6.14=py37_0
  - defaults/linux-64::certifi==2019.3.9=py37_0
  - defaults/linux-64::scipy==1.2.1=py37h7c811a0_0
  - defaults/linux-64::numpy==1.16.3=py37h7e9f1db_0
done

Downloading and Extracting Packages
libgcc-ng-9.1.0      | 8.1 MB    | ########## | 100% 
setuptools-41.2.0    | 630 KB    | ########## | 100% 
sqlite-3.29.0        | 1.9 MB    | ########## | 100% 
_libgcc_mutex-0.1    | 3 KB      | ########## | 100% 
certifi-2019.9.11    | 154 KB    | ########## | 100% 
openssl-1.1.1d       | 3.7 MB    | ########## | 100% 
pip-19.2.3           | 1.9 MB    | ########## | 100% 
python-3.7.3         | 36.7 MB   | ########## | 100% 
wheel-0.33.6         | 40 KB     | ########## | 100% 
libedit-3.1.20181209 | 188 KB    | ########## | 100% 
ca-certificates-2019 | 132 KB    | ########## | 100% 
Preparing transaction: ...working... done
Verifying transaction: ...working... done
Executing transaction: ...working... done
Ran pip subprocess with arguments:
['/opt/conda/bin/python', '-m', 'pip', 'install', '-U', '-r', '/bento/condaenv.fu8rxe5l.requirements.txt']
Pip subprocess output:
Collecting bentoml[api_server]==0.4.2 (from -r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/ca/ac/177ff676b6286ec080310f2758a9472b32d1c86e5766ab0b129ba43bae69/BentoML-0.4.2-py3-none-any.whl (164kB)
Collecting configparser (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7a/2a/95ed0501cf5d8709490b1d3a3f9b5cf340da6c433f896bbe9ce08dbe6785/configparser-4.0.2-py2.py3-none-any.whl
Collecting protobuf>=3.6.0 (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/4f/32/ba67677963d9cf557155d5a8f14ae0ffaf278a69a25fb03b5bc9e9e6fd52/protobuf-3.9.2-cp37-cp37m-manylinux1_x86_64.whl (1.2MB)
Collecting cerberus (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/c9/0e/f78e23b778c2234972d364d0f8bea2de0a09f450f65d3f05ce091dd0f104/Cerberus-1.3.1.tar.gz (52kB)
Collecting click (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
Collecting prometheus-client (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/b3/23/41a5a24b502d35a4ad50a5bb7202a5e1d9a0364d0c12f56db3dbf7aca76d/prometheus_client-0.7.1.tar.gz
Requirement already satisfied, skipping upgrade: requests in /opt/conda/lib/python3.7/site-packages (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (2.21.0)
Collecting boto3 (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/c3/16/ba4d09feba1fec565f6fa3d5904540c35cf5add21d8ea1b8c7add88ea77d/boto3-1.9.236-py2.py3-none-any.whl (128kB)
Collecting python-json-logger (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/80/9d/1c3393a6067716e04e6fcef95104c8426d262b4adaf18d7aa2470eab028d/python-json-logger-0.1.11.tar.gz
Requirement already satisfied, skipping upgrade: gunicorn in /opt/conda/lib/python3.7/site-packages (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (19.9.0)
Collecting dill (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/39/7a/70803635c850e351257029089d38748516a280864c97cbc73087afef6d51/dill-0.3.0.tar.gz (151kB)
Requirement already satisfied, skipping upgrade: six in /opt/conda/lib/python3.7/site-packages (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (1.12.0)
Collecting packaging (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/cf/94/9672c2d4b126e74c4496c6b3c58a8b51d6419267be9e70660ba23374c875/packaging-19.2-py2.py3-none-any.whl
Collecting flask (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl (94kB)
Collecting pathlib2 (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/67/c6/4dbf5dfdbe1140cadf765c3896acc098578626c35721bc7d3eb35f6a8fc1/pathlib2-2.3.4-py2.py3-none-any.whl
Collecting ruamel.yaml>=0.15.0 (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/fa/90/ecff85a2e9c497e2fa7142496e10233556b5137db5bd46f3f3b006935ca8/ruamel.yaml-0.16.5-py2.py3-none-any.whl (123kB)
Collecting grpcio (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/e5/27/1f908ebb99c8d48a5ba4eb9d7997f5633b920d98fe712f67aaa0663f1307/grpcio-1.23.0-cp37-cp37m-manylinux1_x86_64.whl (2.2MB)
Collecting docker (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/95/47/5560c9cf0c92b50da24216f0e7733250fbed5a497f69e3c70e1be62143fe/docker-4.0.2-py2.py3-none-any.whl (138kB)
Collecting sqlalchemy (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/fc/49/82d64d705ced344ba458197dadab30cfa745f9650ee22260ac2b275d288c/SQLAlchemy-1.3.8.tar.gz (5.9MB)
Requirement already satisfied, skipping upgrade: numpy in /opt/conda/lib/python3.7/site-packages (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (1.16.3)
Collecting pandas (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/7e/ab/ea76361f9d3e732e114adcd801d2820d5319c23d0ac5482fa3b412db217e/pandas-0.25.1-cp37-cp37m-manylinux1_x86_64.whl (10.4MB)
Collecting Werkzeug; extra == "api_server" (from bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/ce/42/3aeda98f96e85fd26180534d36570e4d18108d62ae36f87694b476b83d6f/Werkzeug-0.16.0-py2.py3-none-any.whl (327kB)
Requirement already satisfied, skipping upgrade: setuptools in /opt/conda/lib/python3.7/site-packages (from protobuf>=3.6.0->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (41.2.0)
Requirement already satisfied, skipping upgrade: certifi>=2017.4.17 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (2019.9.11)
Requirement already satisfied, skipping upgrade: chardet<3.1.0,>=3.0.2 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (3.0.4)
Requirement already satisfied, skipping upgrade: idna<2.9,>=2.5 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (2.8)
Requirement already satisfied, skipping upgrade: urllib3<1.25,>=1.21.1 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1)) (1.24.1)
Collecting botocore<1.13.0,>=1.12.236 (from boto3->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/52/2f/1812b5d106b646a8ec86f4066213ad7281006e59157ddafd0f156836b808/botocore-1.12.236-py2.py3-none-any.whl (5.7MB)
Collecting jmespath<1.0.0,>=0.7.1 (from boto3->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/83/94/7179c3832a6d45b266ddb2aac329e101367fbdb11f425f13771d27f225bb/jmespath-0.9.4-py2.py3-none-any.whl
Collecting s3transfer<0.3.0,>=0.2.0 (from boto3->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/16/8a/1fc3dba0c4923c2a76e1ff0d52b305c44606da63f718d14d3231e21c51b0/s3transfer-0.2.1-py2.py3-none-any.whl (70kB)
Collecting pyparsing>=2.0.2 (from packaging->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/11/fa/0160cd525c62d7abd076a070ff02b2b94de589f1a9789774f17d7c54058e/pyparsing-2.4.2-py2.py3-none-any.whl (65kB)
Collecting Jinja2>=2.10.1 (from flask->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl (124kB)
Collecting itsdangerous>=0.24 (from flask->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting ruamel.yaml.clib>=0.1.2; platform_python_implementation == "CPython" and python_version < "3.8" (from ruamel.yaml>=0.15.0->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/f2/bd/ff0d632e1b248c28427a23bd741f5b8f954acba82df86661e5daf7cc79a6/ruamel.yaml.clib-0.1.2-cp37-cp37m-manylinux1_x86_64.whl (547kB)
Collecting websocket-client>=0.32.0 (from docker->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/29/19/44753eab1fdb50770ac69605527e8859468f3c0fd7dc5a76dd9c4dbd7906/websocket_client-0.56.0-py2.py3-none-any.whl (200kB)
Collecting pytz>=2017.2 (from pandas->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/87/76/46d697698a143e05f77bec5a526bf4e56a0be61d63425b68f4ba553b51f2/pytz-2019.2-py2.py3-none-any.whl (508kB)
Collecting python-dateutil>=2.6.1 (from pandas->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/41/17/c62faccbfbd163c7f57f3844689e3a78bae1f403648a6afb1d0866d87fbb/python_dateutil-2.8.0-py2.py3-none-any.whl (226kB)
Collecting docutils<0.16,>=0.10 (from botocore<1.13.0,>=1.12.236->boto3->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/22/cd/a6aa959dca619918ccb55023b4cb151949c64d4d5d55b3f4ffd7eee0c6e8/docutils-0.15.2-py3-none-any.whl (547kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10.1->flask->bentoml[api_server]==0.4.2->-r /bento/condaenv.fu8rxe5l.requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/98/7b/ff284bd8c80654e471b769062a9b43cc5d03e7a615048d96f4619df8d420/MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl
Building wheels for collected packages: cerberus, prometheus-client, python-json-logger, dill, sqlalchemy
  Building wheel for cerberus (setup.py): started
  Building wheel for cerberus (setup.py): finished with status 'done'
  Created wheel for cerberus: filename=Cerberus-1.3.1-cp37-none-any.whl size=54128 sha256=bd01f854e78789085156c3a4c706e0ae0d3a32408b7e93bc923aa65917ee23c1
  Stored in directory: /root/.cache/pip/wheels/4f/b4/2b/83fc9a02bdbb49350a9eeb6d34753e4d7df7f77cb027e1d4b3
  Building wheel for prometheus-client (setup.py): started
  Building wheel for prometheus-client (setup.py): finished with status 'done'
  Created wheel for prometheus-client: filename=prometheus_client-0.7.1-cp37-none-any.whl size=41402 sha256=d06386b2a5b6d58dfc2fce9723fdd3299ea09fad32f3c9524c4025a8f9ddea95
  Stored in directory: /root/.cache/pip/wheels/1c/54/34/fd47cd9b308826cc4292b54449c1899a30251ef3b506bc91ea
  Building wheel for python-json-logger (setup.py): started
  Building wheel for python-json-logger (setup.py): finished with status 'done'
  Created wheel for python-json-logger: filename=python_json_logger-0.1.11-py2.py3-none-any.whl size=5076 sha256=754714b7e1729df3ff399d1fa38a4d078b4aaf80b65b19616b186ec629f18fc3
  Stored in directory: /root/.cache/pip/wheels/97/f7/a1/752e22bb30c1cfe38194ea0070a5c66e76ef4d06ad0c7dc401
  Building wheel for dill (setup.py): started
  Building wheel for dill (setup.py): finished with status 'done'
  Created wheel for dill: filename=dill-0.3.0-cp37-none-any.whl size=77513 sha256=1cc5d419605128a7f5a9bd0445f2952b5f2a6855044927e87df51308602c5c94
  Stored in directory: /root/.cache/pip/wheels/c9/de/a4/a91eec4eea652104d8c81b633f32ead5eb57d1b294eab24167
  Building wheel for sqlalchemy (setup.py): started
  Building wheel for sqlalchemy (setup.py): finished with status 'done'
  Created wheel for sqlalchemy: filename=SQLAlchemy-1.3.8-cp37-cp37m-linux_x86_64.whl size=1197985 sha256=1ab9e7de1119d4a303638231ba182de0fc7743b38620915dd90f7ef446fd24dc
  Stored in directory: /root/.cache/pip/wheels/97/b6/66/de2064d40c920adc2984ff3b8fd4f11494c8ab9e48ba87e8a2
Successfully built cerberus prometheus-client python-json-logger dill sqlalchemy
Installing collected packages: configparser, protobuf, cerberus, click, prometheus-client, docutils, jmespath, python-dateutil, botocore, s3transfer, boto3, python-json-logger, dill, pyparsing, packaging, MarkupSafe, Jinja2, Werkzeug, itsdangerous, flask, pathlib2, ruamel.yaml.clib, ruamel.yaml, grpcio, websocket-client, docker, sqlalchemy, pytz, pandas, bentoml
Successfully installed Jinja2-2.10.1 MarkupSafe-1.1.1 Werkzeug-0.16.0 bentoml-0.4.2 boto3-1.9.236 botocore-1.12.236 cerberus-1.3.1 click-7.0 configparser-4.0.2 dill-0.3.0 docker-4.0.2 docutils-0.15.2 flask-1.1.1 grpcio-1.23.0 itsdangerous-1.1.0 jmespath-0.9.4 packaging-19.2 pandas-0.25.1 pathlib2-2.3.4 prometheus-client-0.7.1 protobuf-3.9.2 pyparsing-2.4.2 python-dateutil-2.8.0 python-json-logger-0.1.11 pytz-2019.2 ruamel.yaml-0.16.5 ruamel.yaml.clib-0.1.2 s3transfer-0.2.1 sqlalchemy-1.3.8 websocket-client-0.56.0


==> WARNING: A newer version of conda exists. <==
  current version: 4.6.14
  latest version: 4.7.12

Please update conda by running

    $ conda update -n base -c defaults conda



#
# To activate this environment, use:
# > conda activate base
#
# To deactivate an active environment, use:
# > conda deactivate
#

Removing intermediate container 46ad4dcabd79
 ---> 0d3c6d9e1972
Step 9/11 : RUN pip install -r /bento/requirements.txt
 ---> Running in cdba5db9fa17
Requirement already satisfied: bentoml==0.4.2 in /opt/conda/lib/python3.7/site-packages (from -r /bento/requirements.txt (line 1)) (0.4.2)
Collecting scikit-learn (from -r /bento/requirements.txt (line 2))
  Downloading https://files.pythonhosted.org/packages/9f/c5/e5267eb84994e9a92a2c6a6ee768514f255d036f3c8378acfa694e9f2c99/scikit_learn-0.21.3-cp37-cp37m-manylinux1_x86_64.whl (6.7MB)
Requirement already satisfied: pandas in /opt/conda/lib/python3.7/site-packages (from -r /bento/requirements.txt (line 3)) (0.25.1)
Requirement already satisfied: pathlib2 in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (2.3.4)
Requirement already satisfied: cerberus in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.3.1)
Requirement already satisfied: configparser in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (4.0.2)
Requirement already satisfied: requests in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (2.21.0)
Requirement already satisfied: boto3 in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.9.236)
Requirement already satisfied: ruamel.yaml>=0.15.0 in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.16.5)
Requirement already satisfied: six in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.12.0)
Requirement already satisfied: protobuf>=3.6.0 in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (3.9.2)
Requirement already satisfied: flask in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.1.1)
Requirement already satisfied: dill in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.3.0)
Requirement already satisfied: click in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (7.0)
Requirement already satisfied: docker in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (4.0.2)
Requirement already satisfied: gunicorn in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (19.9.0)
Requirement already satisfied: packaging in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (19.2)
Requirement already satisfied: prometheus-client in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.7.1)
Requirement already satisfied: sqlalchemy in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.3.8)
Requirement already satisfied: python-json-logger in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.1.11)
Requirement already satisfied: numpy in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.16.3)
Requirement already satisfied: grpcio in /opt/conda/lib/python3.7/site-packages (from bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.23.0)
Requirement already satisfied: scipy>=0.17.0 in /opt/conda/lib/python3.7/site-packages (from scikit-learn->-r /bento/requirements.txt (line 2)) (1.2.1)
Collecting joblib>=0.11 (from scikit-learn->-r /bento/requirements.txt (line 2))
  Downloading https://files.pythonhosted.org/packages/cd/c1/50a758e8247561e58cb87305b1e90b171b8c767b15b12a1734001f41d356/joblib-0.13.2-py2.py3-none-any.whl (278kB)
Requirement already satisfied: pytz>=2017.2 in /opt/conda/lib/python3.7/site-packages (from pandas->-r /bento/requirements.txt (line 3)) (2019.2)
Requirement already satisfied: python-dateutil>=2.6.1 in /opt/conda/lib/python3.7/site-packages (from pandas->-r /bento/requirements.txt (line 3)) (2.8.0)
Requirement already satisfied: urllib3<1.25,>=1.21.1 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.24.1)
Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (2019.9.11)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (3.0.4)
Requirement already satisfied: idna<2.9,>=2.5 in /opt/conda/lib/python3.7/site-packages (from requests->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (2.8)
Requirement already satisfied: jmespath<1.0.0,>=0.7.1 in /opt/conda/lib/python3.7/site-packages (from boto3->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.9.4)
Requirement already satisfied: botocore<1.13.0,>=1.12.236 in /opt/conda/lib/python3.7/site-packages (from boto3->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.12.236)
Requirement already satisfied: s3transfer<0.3.0,>=0.2.0 in /opt/conda/lib/python3.7/site-packages (from boto3->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.2.1)
Requirement already satisfied: ruamel.yaml.clib>=0.1.2; platform_python_implementation == "CPython" and python_version < "3.8" in /opt/conda/lib/python3.7/site-packages (from ruamel.yaml>=0.15.0->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.1.2)
Requirement already satisfied: setuptools in /opt/conda/lib/python3.7/site-packages (from protobuf>=3.6.0->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (41.2.0)
Requirement already satisfied: Werkzeug>=0.15 in /opt/conda/lib/python3.7/site-packages (from flask->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.16.0)
Requirement already satisfied: itsdangerous>=0.24 in /opt/conda/lib/python3.7/site-packages (from flask->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.1.0)
Requirement already satisfied: Jinja2>=2.10.1 in /opt/conda/lib/python3.7/site-packages (from flask->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (2.10.1)
Requirement already satisfied: websocket-client>=0.32.0 in /opt/conda/lib/python3.7/site-packages (from docker->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.56.0)
Requirement already satisfied: pyparsing>=2.0.2 in /opt/conda/lib/python3.7/site-packages (from packaging->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (2.4.2)
Requirement already satisfied: docutils<0.16,>=0.10 in /opt/conda/lib/python3.7/site-packages (from botocore<1.13.0,>=1.12.236->boto3->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (0.15.2)
Requirement already satisfied: MarkupSafe>=0.23 in /opt/conda/lib/python3.7/site-packages (from Jinja2>=2.10.1->flask->bentoml==0.4.2->-r /bento/requirements.txt (line 1)) (1.1.1)
Installing collected packages: joblib, scikit-learn
Successfully installed joblib-0.13.2 scikit-learn-0.21.3
Removing intermediate container cdba5db9fa17
 ---> 074a6de9feda
Step 10/11 : RUN if [ -f /bento/setup.sh ]; then /bin/bash -c /bento/setup.sh; fi
 ---> Running in cf7f244d65ff
Removing intermediate container cf7f244d65ff
 ---> 13a8614e8bca
Step 11/11 : CMD ["bentoml serve-gunicorn /bento"]
 ---> Running in 0342791f09fc
Removing intermediate container 0342791f09fc
 ---> 11dc20f984cc
Successfully built 11dc20f984cc
Successfully tagged sentiment-analysis-service:latest

2) run docker image and expose port 5000

In [ ]:
!docker run -p 5000:5000 sentiment-analysis-service
[2019-09-25 22:30:26 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2019-09-25 22:30:26 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2019-09-25 22:30:26 +0000] [1] [INFO] Using worker: sync
[2019-09-25 22:30:26 +0000] [10] [INFO] Booting worker with pid: 10
[2019-09-25 22:30:26 +0000] [11] [INFO] Booting worker with pid: 11
[2019-09-25 22:30:26 +0000] [12] [INFO] Booting worker with pid: 12

3) Similarly use the following command to query the REST server in Docker

curl -i \
--header "Content-Type: application/json" \
--request POST \
--data '["some new text, sweet noodles", "happy time", "sad day"]' \
localhost:5000/predict