Creating an ML Model and Serving it as a RESTful API: Part 1
Tags: python, flask, api, machinelearning
Published: 2021-01-18 06:18:20

I started my internship at a local company that focuses on Data Science and Machine Learning and as part of my training, I was tasked to create a simple Iris Classification model that identifies the type of Iris category (Versicolor, Virginica and Setosa) based on the given petal and sepal width and length. Being someone who has minimal Machine Learning background, I needed a lot of research and reading in order to arrive at the required output, but it was all worth it.

![Iris](https://cdn.pixabay.com/photo/2017/07/14/07/17/iris-2502898_1280.jpg)

In this post, I will be sharing the steps I did to create an Iris Classification model using Python and serve it through Flask RESTful API. I have also created a simple PHP interface that connects to the API to demonstrate its usage.

#Model Creation 🤖
In Machine Learning, a model is trained using a set of data to recognize certain patterns. Python's open-source Machine Learning library, [Sci-Kit Learn](https://scikit-learn.org/stable/), provides model training for supervised and unsupervised learning. It is simple to use and it has a lot of tutorials to get started with.

First, install Sci-Kit library using pip:
```
pip install -U scikit-learn
```

Once the installation is finished, create a file called `model.py` where Sci-Kit Learn's already made Iris dataset can be used:
```
from sklearn import datasets
iris = datasets.load_iris()
```
*Note: You can use your own dataset but it needs to be preprocessed and formatted for Sci-Kit Learn to understand and train the model as efficiently as possible.*

Next, the datasets are separated into **data and target**. **Data** consists of petal and sepal width and length in centimetres, while the **target**, or the label, is the class to predict. For the Iris classification, the target consists of versicolor, virginica and setosa.

```
iris_data = iris.data
iris_target = iris.target
```

Before training the model, the data and target are split again so that the other half is used for the actual model training while the other is used for verifying the model (in other terms, testing the model's accuracy).

This can be achieved using `train_test_split`:
```
from sklearn.model_selection import train_test_split
iris_data_train, iris_data_test, iris_target_train, iris_target_test = train_test_split(iris_data, iris_target, test_size = .5)
```
The `test_size` parameter specifies the size of the testing datasets.

Now, we can start building the model. There are many classification algorithms that can be used for this model, but I settled on K-Nearest Neighbors as the accuracy score is much higher.

```
from sklearn import neighbors
classifier = neighbors.KNeighborsClassifier()
```

From this, we can start training the model:

```
classifier.fit(iris_data_train, iris_target_train)
```

And there we have it! We have a trained Iris classification model. We can print the accuracy level of the model using `accuracy_score`:
```
from sklearn.metrics import accuracy_score
predict_iris = classifier.predict(iris_data_test)
print(accuracy_score(iris_target_test, predict_iris))
```
From the above, the model has an accuracy score of 97%! Great!

#Creating Flask API ⚙️
Before we create the Flask API, the model needs to be serialized using a Python library called [Pickle](https://docs.python.org/3/library/pickle.html#:~:text=%E2%80%9CPickling%E2%80%9D%20is%20the%20process%20whereby,back%20into%20an%20object%20hierarchy.). By "pickling" a python code, it is converted into a byte stream for it to be easily sent over the network or saved on disk. "Unpickling" is the opposite, where it converts the python file into its original state.

![Pickle](https://cdn.pixabay.com/photo/2016/07/15/22/49/pickled-cucumbers-1520638_1280.jpg)

Pickle can be installed using pip:
```
pip install pickle-mixin
```

Our Iris classification model can be "pickled" or serialized using the following code:
```
import pickle
pickle.dump(classifier, open("iris_model", "wb"))
```
This creates the `iris_model` file. If you try to open it, it will show indecipherable characters.

Now that we have our model pickled, we can now start with the Flask API!

Like the rest of the Python libraries, we install flask as well:
```
pip install flask
```

We then create our main app file called `app.py`. Inside, we import the libraries required for our Flask API:
```
from flask import Flask, jsonify
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)
```

Our Iris Classification model is then loaded and "unpickled":
```
import pickle
loaded_model = pickle.load(open("iris_model", 'rb'))
```
When we create the front-end interface later, we need a way to send over the user input to the Iris classification Flask API. In order to do that, we create parameters to the URL and we pass the user input there. The inputs we need are the petal and sepal width and length, and from this information, the model predicts what type of Iris flower we are referring to. 

We create the parameters using the following code:
```
parser = reqparse.RequestParser()
parser.add_argument('slength')
parser.add_argument('swidth')
parser.add_argument('plength')
parser.add_argument('pwidth')
```

Once the queries are set, we need to call the model to predict the Iris type based on the given inputs. We then create a class called `requestIris` that takes the URL parameter values then insert it to the model for prediction. The predictions will come into integer format, so 0 is setosa, 1 is versicolor, and 2 is virginica. Also, note that the data returned should be in JSON format.

The full code is stated below:

```
class requestIris(Resource):
    def get(self):
        args = parser.parse_args()
        slength= args['slength']
        swidth = args["swidth"]
        plength = args["plength"]
        pwidth = args["pwidth"]
        prediction = (loaded_model.predict([[slength, swidth, plength, pwidth]])).tolist()
        if(prediction[0] == 0):
            type="setosa"
        elif(prediction[0] == 1):
            type="versicolor"
        else:
            type="virginica"
        serve_model = {"Iris Type":type}
        print(serve_model)
        return jsonify(serve_model)
```

We then add the resource to our API, and create a route for the resource:

```
api.add_resource(requestIris, '/classify')
```

Last but not least, we add the code to run our Flask application:

```
if __name__ == '__main__':
   app.run()
``` 
Hurray! We have our Iris Classification API now! 

The API can be run locally or published to Heroku. I published mine here:

[https://iris-classification-model.herokuapp.com/](https://iris-classification-model.herokuapp.com/)

In the next post, I will be writing about the PHP frontend interface to demonstrate the API's application. Stay tuned!