Skip to main content

Command Palette

Search for a command to run...

Deploy Your First Machine Learning Model Easily

Here We're going to build an image classifier model using Keras API with the help of the TensorFlow library.

Published
โ€ข7 min read
Deploy Your First Machine Learning Model Easily
F

Full stack software engineer turned CTO From Cochin. Having experience with Javascript and typescript frameworks on both frontend and backend, With a keen interest in efficient scalable architecture.

๐ŸŽ‰ ๐ŸŽ‰ 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 by the Tinkerhub Foundation. 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 API to build and train the image classifier model using the tensorflow library and the dataset used is the popular cifar-10 dataset. As I told you that I've almost zero experience with Machine Learning stuffs I've referred this article 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 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 For creating an API we're using the Flask framework. 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 or insomnia.

๐Ÿš€ Deploy the API

So we have built the API, now we'll deploy it to the internet with the help of heroku, 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

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/. then install heroku cli from here
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

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