# Deploy Your First Machine Learning Model Easily

🎉 🎉  tadaaa !!!
So this is my first personal blog post, I've been planning this for a looooong time And yes it's a start.

## Disclaimer
In this post, I'm not adding any machine learning theories or concepts behind the model we're building as I'm on my path to learning more about the Theory behind the Machine learning algorithms. This article is based on my researches for completing a challenge to participate in the  [AI Bootcamp](https://tinkerhub.org/bootcamp)  by the  [Tinkerhub Foundation](https://tinkerhub.org/). I'm the kind of person who wants to see an initial output to get motivated to learn more. So whenever I start to learn something I'd build and deploy a real-world application initially and then start learning how things work behind the scene, and this is one of such adventures. 

## Prerequisites
Some experience in building softwares of any kind is recommended, although we're using python as the programming language, deep knowledge in python is not required as you can easily follow if you have any other programming experience.

## Let's get started
We're gonna use the  [Keras](https://keras.io/) API to build and train the image classifier model using the  [tensorflow](https://www.tensorflow.org/) library and the dataset used is the popular  [cifar-10](https://www.cs.toronto.edu/~kriz/cifar.html) dataset. As I told you that I've almost zero experience with Machine Learning stuffs I've referred this  [article](https://www.geeksforgeeks.org/cifar-10-image-classification-in-tensorflow/)  for building the model. 

### Training the model
The code below is used to train the model.

```
import tensorflow as tf  
 
# Display the version
print(tf.__version__)    
 
# other imports
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Input, Conv2D, Dense, Flatten, Dropout
from tensorflow.keras.layers import GlobalMaxPooling2D, MaxPooling2D
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.models import Model
from tensorflow.keras.models import load_model


# Load in the data
cifar10 = tf.keras.datasets.cifar10
 
# Distribute it to train and test set
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
print(x_train.shape, y_train.shape, x_test.shape, y_test.shape)


# Reduce pixel values
x_train, x_test = x_train / 255.0, x_test / 255.0
 
# flatten the label values
y_train, y_test = y_train.flatten(), y_test.flatten()


# visualize data by plotting images
fig, ax = plt.subplots(5, 5)
k = 0
 
for i in range(5):
    for j in range(5):
        ax[i][j].imshow(x_train[k], aspect='auto')
        k += 1
 
plt.show()


# number of classes
K = len(set(y_train))

# calculate total numer of classes
# for output layer
print("number of classes:", K)

# Build the model using the functional API
# input layer
i = Input(shape=x_train[0].shape)
x = Conv2D(32, (3, 3), activation='relu', padding='same')(i)
x = BatchNormalization()(x)
x = Conv2D(32, (3, 3), activation='relu', padding='same')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2))(x)

x = Conv2D(64, (3, 3), activation='relu', padding='same')(x)
x = BatchNormalization()(x)
x = Conv2D(64, (3, 3), activation='relu', padding='same')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2))(x)

x = Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = BatchNormalization()(x)
x = Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = BatchNormalization()(x)
x = MaxPooling2D((2, 2))(x)

x = Flatten()(x)
x = Dropout(0.2)(x)

# Hidden layer
x = Dense(1024, activation='relu')(x)
x = Dropout(0.2)(x)

# last hidden layer i.e.. output layer
x = Dense(K, activation='softmax')(x)

model = Model(i, x)

# model description
model.summary()


# Compile
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

_epochs = 10

# Fit
r = model.fit(
  x_train, y_train, validation_data=(x_test, y_test), epochs=_epochs)


# Fit with data augmentation
# Note: if you run this AFTER calling
# the previous model.fit()
# it will CONTINUE training where it left off
batch_size = 32
data_generator = tf.keras.preprocessing.image.ImageDataGenerator(
  width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)
 
train_generator = data_generator.flow(x_train, y_train, batch_size)
steps_per_epoch = x_train.shape[0] // batch_size
 
r = model.fit(train_generator, validation_data=(x_test, y_test),
              steps_per_epoch=steps_per_epoch, epochs=_epochs)


# Plot accuracy per iteration
plt.plot(r.history['accuracy'], label='acc', color='red')
plt.plot(r.history['val_accuracy'], label='val_acc', color='green')
plt.legend()


# label mapping

labels = '''airplane automobile bird cat deer dog frog horse ship truck'''.split()

# select the image from our test dataset
image_number = 0

# display the image
plt.imshow(x_test[image_number])

# load the image in an array
n = np.array(x_test[image_number])

# reshape it
p = n.reshape(1, 32, 32, 3)

# pass in the network for prediction and
# save the predicted label
predicted_label = labels[model.predict(p).argmax()]

# load the original label
original_label = labels[y_test[image_number]]

# display the result
print("Original label is {} and predicted label is {}".format(
	original_label, predicted_label))

  # save the model
model.save('cifar-10.model.h5')
``` 

I would prefer using the  [google colab](https://colab.research.google.com/) for the initial experiments, you can easily download the final model (.h5) file from the files section of the colab notebook. the model training may take almost 8 minutes for each iteration. I'm not going for a more detailed explanation of each line of code, you can find them in-depth from the keras/tensorflow documentaions.

### Building An API with the trained model
After executing the above code in the google colab notebook, You can download the model from the files section

![Screenshot from 2021-08-08 20-53-50.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1628436249319/PSrNpeNfL.png)
For creating an API we're using the  [Flask framework](https://flask.palletsprojects.com/en/2.0.x/). 
First, create a new project folder 

```
mkdir ~/image-classifier-demo
cd image-classifier-demo
``` 
   Then create a new python virtual environment

```
python3 -m venv demo-env
``` 
Now activate your virtual environment

```
source demo-env/bin/activate
``` 
**Installing required packages**

```
pip install --upgrade tensorflow-cpu flask flask-cors opencv-python-headless gunicorn 

``` 
after installing all the packages let's start coding. First, create a file called predict.py to add the image processing and prediction.


```
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
import cv2
import numpy as np


names = '''airplane automobile bird cat deer dog frog horse ship truck'''.split()
model = load_model("models/cifar-10.model.h5.h5")

# Process image and predict label

def processImg(IMG_PATH):
    # Preprocess image
    image = cv2.imread(IMG_PATH)
    image = cv2.resize(image, (32, 32))
    image = image.astype("float") / 255.0
    image = img_to_array(image)
    image = np.expand_dims(image, axis=0)

    res = model.predict(image)

    label = np.argmax(res)
    labelName = names[label]
    print("The image is of ", labelName)
    print("confidence: ", np.max(res))

    ret = dict()
    ret['label'] = labelName
    ret['confidence'] = str(np.max(res))
    return ret
``` 

this function predicts the label and the confidence of the given image. to test it out add a test image to the folder and invoke the function with the relative path of the image as given below

```
processImg('./test-image.jpg')

``` 
So this is the major part of the API and now we only need to connect it with an API route. For that, we use the Flask web server. create an app.py file

```
from flask import Flask, render_template, request, jsonify
from flask_cors import CORS
from predict import processImg

# Initializing flask application
app = Flask(__name__)
cors = CORS(app)

@app.route("/predict", methods=["POST"])
def processReq():
    data = request.files["img"]
    data.save("img.jpg")

    resp = processImg("img.jpg")

    text = "it is a <b>"+resp['label'] + \
        "</b> with confidence <b>"+resp['confidence']+"</b>"

    return text

if __name__ == '__main__':
    app.run()

``` 
The above code creates a web server with a post route /predict. You can test the API by uploading an image to the POST API using any API testing tools like  [postman](https://www.postman.com/)  or  [insomnia](https://insomnia.rest/). 

### 🚀 Deploy the API
So we have built the API, now we'll deploy it to the internet with the help of  [heroku](https://www.heroku.com/), there are many other platforms to deploy, we are now choosing heroku for simplicity. 

create a Procfile with the below command. a proc file is required for heroku to run our code.  [learn more](https://devcenter.heroku.com/articles/python-gunicorn) 
 
```
web: gunicorn app:app
``` 
Now compile all the packages we use to a requirements.txt file


```
pip freeze > requirements.txt
``` 

now we are all set for deployment. create an account on  [https://www.heroku.com/](https://www.heroku.com/). then install heroku cli from here  
[https://devcenter.heroku.com/articles/heroku-cli](https://devcenter.heroku.com/articles/heroku-cli) 

login to heroku from command line

```
heroku login
``` 

initialize git 

```
git init 
git add .
git commit -m "Initial Commit"
``` 
create a new heroku app

```
heroku create image-classifier-demo
``` 
push our code to the heroku git repo


```
git push heroku master
``` 

Hurraaaay we've deployed our very first Machine learning API to the internet. the remote URL for the API will the provided by the heroku cli upon successful deployment or you can visit the heroku dashboard for API URL.

I've updated the basic API with an HTML form and pushed it to a git repo for reference. The link to the repo is given below

 [https://github.com/faris-mohamed10/tinkerhub-image-clasifier](https://github.com/faris-mohamed10/tinkerhub-image-clasifier) 

### Conclusion
Thank you for spending your valuable time reading my article. Since it is my first attempt at writing, there may be some mistakes in the article. Feel free to shoot your suggestions below. Happy coding and wish you all the best.

### References

-  [https://www.geeksforgeeks.org/cifar-10-image-classification-in-tensorflow/](https://www.geeksforgeeks.org/cifar-10-image-classification-in-tensorflow/)

-  [https://linuxize.com/post/how-to-install-tensorflow-on-ubuntu-20-04/](https://linuxize.com/post/how-to-install-tensorflow-on-ubuntu-20-04/)

-  [https://devcenter.heroku.com/articles/getting-started-with-python](https://devcenter.heroku.com/articles/getting-started-with-python) 

 

  





