This article was published as a part of the Data Science Blogathon
Deployment of a machine learning model means making your model predictions available to the users through API or through a web application. In this post let us see how we could leverage CherryPy and Docker to deploy a Machine Learning model.
The lifecycle of a Machine Learning Project involves,
In this post, we are going to look at Model Deployment using CherryPy and Docker.
CherryPy is a python, object-oriented web framework. A web framework is a software framework that assists us in developing web applications. In this post, we are going to utilize CherryPy to build a web API for training iris data and obtaining predictions for it.
Install cherrypy using the following command,
pip install cherrypy
CherryPy starter example,
import cherrypy class CherryPyExample: @cherrypy.expose def welcome_page(self): return "Welcome!" if __name__ == '__main__': cherrypy.quickstart(CherryPyExample())
The method name ‘welcome_page’ is exposed using the decorator @cherrypy.expose which when called returns the string ‘Welcome!’.
The line cherrypy.quickstart(CherryPyExample()), starts the cherrypy engine and the following output can be viewed.
[15/Jun/2021:17:19:47] ENGINE Listening for SIGTERM. [15/Jun/2021:17:19:47] ENGINE Listening for SIGHUP. [15/Jun/2021:17:19:47] ENGINE Listening for SIGUSR1. [15/Jun/2021:17:19:47] ENGINE Bus STARTING CherryPy Checker: The Application mounted at '' has an empty config. [15/Jun/2021:17:19:47] ENGINE Started monitor thread 'Autoreloader'. [15/Jun/2021:17:19:47] ENGINE Serving on http://127.0.0.1:8080 [15/Jun/2021:17:19:47] ENGINE Bus STARTED
Now accessing http://127.0.0.1:8080/welcome_page you can view the following output.
To know further about CherryPy, read their documentation.
Docker is an open-source containerization platform. What does that mean? Docker containerizes your application and isolates your dependencies for each application, but Docker can do much more than we just saw. Docker makes your application platform-independent, which means you can run docker anywhere. Docker is seen as an alternative to Virtual Machines.
Docker provides you all the features of an OS but also gives you extra layers of advantages,
Docker file is a simple text file that contains the instructions to build a container image. The docker file is read sequentially by the docker engine, each line represents a layer in the image. Docker file is used to automate the Docker image creation.
Docker image contains the source code of your application, its dependencies, and libraries. Docker image is built from the instructions of Docker file. You can either create an image from scratch or you can use the existing images from the Docker hub. The final layer of the image is called the read/write layer which exists only when the container is started and its changes reside until the container is stopped.
A running instance of an image is called a container. Many containers can be created from a single docker image. All the data produced by the docker container resides in the container until the container is stopped, once it is stopped all the data is erased.
For installing Docker, refer to their documentation
Before moving on, make sure your project structure looks like this.
deploy_model (root directory) |--- model_deployment.py |--- Dockerfile |--- requirements.txt
pandas==1.2.4 CherryPy==18.6.0 scikit_learn==0.24.2
FROM ubuntu ADD . /DeployModel WORKDIR /DeployModel # Export Time-Zone ENV TZ=Asia/Kolkata RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # dependencies RUN apt-get update RUN apt-get install python3.6 -y RUN apt-get install python3-pip -y RUN pip3 install -r requirements.txt CMD ["/bin/bash"]
import cherrypy import traceback import pandas as pd from sklearn.datasets import load_iris from sklearn.utils import all_estimators from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, f1_score class TrainIris: @cherrypy.expose() @cherrypy.tools.allow(methods=['GET', 'POST']) @cherrypy.tools.json_out() def train_iris_data(self, model='RandomForestClassifier'): try: # accepts only classifiers in # sklearn classifiers = TrainIris.get_all_estimators('classifier') if model not in classifiers: return { 'training_status': 'failure', 'reason': (f'Invalid model provided `{model}`. ' 'Provide sklearn models only.') } self.estimator = classifiers[model]() # get the training and testing data x_train, x_test, y_train, y_test = self.get_train_and_test_data() # fit the model self.estimator.fit(x_train, y_train) y_pred = self.estimator.predict(x_test) # calculate accuracy and f1_score acc_score = accuracy_score(y_test, y_pred) f1 = f1_score(y_test, y_pred, average='macro') return { 'training_status': 'success', 'model': f'{model}', 'accuracy_score': acc_score, 'f1_score': f1 } except Exception as e: return { 'training_status': 'failure', 'reason': f"{e}" } @cherrypy.expose() @cherrypy.tools.allow(methods=['POST']) @cherrypy.tools.json_in() @cherrypy.tools.json_out() def predict(self): if not hasattr(self, 'estimator'): return { 'prediction_status': 'failure', 'reason': 'Model has not been trained yet. Train the model first' } try: request_json = cherrypy.request.json if len(request_json.keys()) < 4: return { 'prediction_status': 'failure', 'reason': ('Some column is missing in the provided data. ' 'Recheck the data.') } for k in request_json.keys(): if k not in self.feature_names: return { 'prediction_status': 'failure', 'reason': f'The column `{k}` is not in data.' } req_json = [request_json] # convert the data to data frame and predict data_to_predict = pd.DataFrame(req_json) prediction = self.estimator.predict(data_to_predict) prediction_prob = self.estimator.predict_proba(data_to_predict) prediction_str = self.target_names[prediction[0]] probability = prediction_prob[0][prediction[0]] return { 'prediction_status': 'success', 'model': f"{self.estimator.__class__.__name__}", 'prediction': f"{prediction_str}", 'prediction_probability': f"{probability}" } except Exception as e: return { 'prediction_status': 'failure', 'reason': f'{e, traceback.print_exc()}' } def get_train_and_test_data(self): iris_data = load_iris() # separate the data into features and target features = pd.DataFrame( iris_data.data, columns=iris_data.feature_names ) target = pd.Series(iris_data.target) # split the data into train and test x_train, x_test, y_train, y_test = train_test_split( features, target, test_size=0.2, stratify=target ) self.target_names = iris_data.target_names self.feature_names = iris_data.feature_names return x_train, x_test, y_train, y_test @staticmethod def get_all_estimators(type_filter): estimators = all_estimators(type_filter=type_filter) estimators = dict(estimators) return estimators if __name__ == '__main__': cherrypy.server.socket_host = '0.0.0.0' cherrypy.quickstart(TrainIris())
# build a docker image docker build -t model_deployment . # run the image as container docker run -it -p 8080:8080 model_deployment python3 deploy_model.py
docker build command creates a docker image from the instructions in Dockerfile,
docker run command runs the created image,
Once the container is up and running do the following instructions to train and obtain predictions of the model.
import requests # train the model train_response = requests.get( 'http://0.0.0.0:8080/train_iris_data?model=LogisticRegression' ) print(train_response.text) # Output # '{"training_status": "success", "model": "LogisticRegression", # "accuracy_score": 0.9, "f1_score": 0.899749373433584}' # obtain predictions prediction_response = requests.post( 'http://0.0.0.0:8080/predict', json={ 'sepal length (cm)': 6.5, 'sepal width (cm)': 3.2, 'petal length (cm)': 5.1, 'petal width (cm)': 2.0 } ) print(prediction_response.text) # Output # {"prediction_status": "success", "model": "LogisticRegression", "prediction": "virginica", # "prediction_probability": "0.8011622485588459"}
Train the model using the API http://0.0.0.0:8080/train_iris_data?model=LogisticRegression, you can change the model in the model parameter. The API accepts only the classifiers of sklearn and don’t forget to make an API call using the GET method. If the training is successful it returns the following output.
‘{“training_status”: “success”, “model”: “LogisticRegression”, “accuracy_score”: 0.9, “f1_score”: 0.899749373433584}’
Obtain predictions of the trained model using the API http://0.0.0.0:8080/predict, in the body of this call you must provide the features that you want to predict in JSON format. This API must be called only after training the data or else it will raise an error message, the API must be called using the POST method. If the prediction is successful it returns the following output.
{“prediction_status”: “success”, “model”: “LogisticRegression”, “prediction”: “virginica”, “prediction_probability”: “0.8011622485588459”}
In this way, you can deploy a model using CherryPy and Docker.
[1] CherryPy
[2] Docker
Thank you!
The media shown in this article on Deploying Machine Learning Models leveraging CherryPy and Docker are not owned by Analytics Vidhya and are used at the Author’s discretion.