DataOps part 2: Sail the Titanic to API
Tags: machinelearning, deployment, kubernetes, data
Published: 2019-10-31 03:59:40


Before starting the deployment part, let’s prepare our model. If you haven’t had the running Kubernetes environment, refer to [Part 1](https://dev.to/calvintran/kubernetes-on-local-with-vagrant-and-kubeadm-5bed).

To be short, in this part, I will cover:

* Training the model and persistence model using pickle.

* Create Restful API to serve model file.

I won’t go details about the EDA or training model in this article however I made a [notebook](https://nbviewer.jupyter.org/github/canhtran/data-devops-medium/blob/master/notebook/Titanic_survival.ipynb) using Titanic dataset (can be found [here](https://www.kaggle.com/c/titanic)). I used different machine learning methods to train the model and here is the results.

![Model Comparison](https://thepracticaldev.s3.amazonaws.com/i/jqn56zm4spi92s6phfeq.png)

The winner belongs to `Random Forest & Decision Tree` with accuracy 93.60%. I exported the random forest model to pickle format `model.pkl`, we use this file from now onward.


```python
import pickle
pickle.dump(random_forest, open('model.pkl', 'wb'))
```

### Flask framework

One app.py file to serve the titanic model.

```python
import pickle
import numpy as np
from flask import (
    Flask,
    jsonify,
    request
)


app = Flask(__name__)
with open('model.pkl', 'rb') as handle:
    app.model = pickle.load(handle)


@app.route('/heartbeat', methods=['GET'])
def heartbeat():
    return 'ok'


@app.route('/predict', methods=['POST'])
def predict():
    payload = request.get_json()
    np_array = np.expand_dims(list(payload.values()), 0)

    pred = app.model.predict(np_array)
    if pred is None:
        return jsonify({'error': 'Model cannot predict with input'})
    else:
        return jsonify(result=pred[0].item())


if __name__ == "__main__":
    app.run()
```

Let’s break it down


```python
with open('model.pkl', 'rb') as handle:    
   app.model = pickle.load(handle)
```


I load the model to `app.model` variable during starting flask server, the model will be stored in memory so that it doesn’t reload every time we send requests.

I also create a GET endpoint name `heartbeat` which returned ‘OK’ (200 response code). This using by kubernetes to check the liveness of api.

The main part of service is from line 21. It gets the parameters from POST request and convert it to 2D numpy array


```python
np_array = np.expand_dims(list(payload.values()), 0)
```


This step is required, machine learning model only accept the 2D numpy array as the input.

Let’s do a quick test for our API

```bash
# Run the server
$ python app.py
* Serving Flask app "app" (lazy loading)
* Environment: development
* Debug mode: on
* Running on [http://127.0.0.1:5000/](http://127.0.0.1:5000/) (Press CTRL+C to quit)
```

And `curl` on another terminal.

```bash
$ curl -i -X POST -H "Content-Type:application/json" [http://localhost:5000/predict](http://localhost:5000/predict) -d '{"Pclass":3,"Sex":1,"Age":2,"Fare":7,"Embarked":0,"Title":1,"FamilySize":2,"IsAlone":0,"Age_Class":6, "FarePerPerson":3}'

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 18
Server: Werkzeug/0.14.1 Python/3.6.2
Date: Sun, 14 Jul 2019 16:48:57 GMT

{
  "result": 1
}
```

That’s awesome, our API has been working as expected. One more final step, dockerize our application and push it to dockerhub

```Dockerfile
FROM python:3.6

LABEL maintainer "Calvin Tran <trandcanh@gmail.com>"

ENV APP_HOME /app

RUN mkdir -p $APP_HOME

WORKDIR $APP_HOME

COPY requirements.txt $APP_HOME

RUN apt-get update && \
    pip install -I --no-cache-dir -r requirements.txt && \
    rm requirements.txt && \
    rm -rf /var/lib/apt/list/*


COPY app.py $APP_HOME
COPY model.pkl $APP_HOME

CMD gunicorn -w 3 -b 0.0.0.0:5000 --timeout=120 --reload "app:app"

VOLUME $APP_HOME
```


A few commands for pushing our image to docker hub.

```bash
$ docker build -t titanic-api .
$ docker tag titanic-api canhtran/titanic-app-demo
$ docker push titanic-api canhtran/titanic-app-demo
```

The image will be available on [https://hub.docker.com](https://hub.docker.com). Alternatively, you can use my image.

```bash
$ docker pull canhtran/titanic-app-demo
```

All right! Our preparation have been completed and ready to deploy to kubernetes.