DEV Community

Cover image for Build A Fitness Machine Learning API and Deploy to Azure using GitHub Actions CI/CD
Shaiju T
Shaiju T

Posted on

Build A Fitness Machine Learning API and Deploy to Azure using GitHub Actions CI/CD

Lets Build A Fitness Machine Learning App and Deploy to Azure.

Today I am going to share how to deploy simple Machine Learning Model in Azure and will also share the challenges i faced while deploying this python flask app in Azure.

What you will learn ?

You can upskill in below skills:

  • Create Basic REST API's using Python.
  • Basics of Machine Learning.
  • Deploy Python REST API's to Azure.
  • Setup CI/CD using Github Actions.
  • Use PostMan to test Your ML API.

Lets get started....

Prerequisite

Install Required Python Packages

Open your command prompt and execute below commands:

 1. py -m pip install flask

 2. py -m pip install -U flask-cors

 3. py -m pip install -U scikit-learn
Enter fullscreen mode Exit fullscreen mode

Here we are using 3 packages.

  • flask is used to create REST API's, which means our API can be consumed by web app , mobile app , desktop app, IOT etc. As REST API's return Json data, so it becomes easy to Deserialize to objects of any programming language.

  • flask-cors is used to allow request from another domian, this will be usefull if your front end is deployed in different domain.

  • scikit-learn is used for Machine Learning.

Machine Learning Fitness DataSet:

For training our ML model we are using Dataset from here.

Basically we are going to find ideal weight to be fit based on your height and current weight. We have trained our model to predict from 5 feet to 6 feet of male dataset, you can train for other height ranges if required.

Code Walkthrough:

Open VS code inside your project folder, Create app.py file in VS Code and paste below code as required.

First we are importing all required modules required in our flask code.

from flask import Flask, jsonify, abort, request
from werkzeug.exceptions import HTTPException
from flask_cors import CORS
from sklearn import tree
Enter fullscreen mode Exit fullscreen mode

Second we are creating objects, constructors and configuration necessary for app to run.

app = Flask(__name__)
CORS(app)
app.config["DEBUG"] = True
Enter fullscreen mode Exit fullscreen mode

Finally we are implementing REST API functions, which will be called when a URL is requested by client.

Below is the function , which helps us to handle errors globally in our REST API.

@app.errorhandler(Exception)
def handle_error(e):
    code = 500
    if isinstance(e, HTTPException):
        code = e.code
    return jsonify(error=str(e)), code
Enter fullscreen mode Exit fullscreen mode

Next we have our simple function named as get-message to return Hello World! Json data, when a client initiates GET requests to /api/get-message endpoint.

@app.route('/api/get-message', methods=['GET'])
def getMessage():
    data = {'message': 'Hello World!'}
    return jsonify(data)
Enter fullscreen mode Exit fullscreen mode

Then we have our Machine Learning endpoint which predicts fitness and returns Json data, indicating you are fit or not. :) The code is explained using comments.

@app.route('/api/predict-fitness', methods = ['POST'])
def predict_fitness():
    if not request.json or not 'userHeight' in request.json:
        abort(400)

    # Data Cleaning, remvoing dot from userHeight value

    userHeight = request.json.get('userHeight', "")
    userHeight = int(str(userHeight).replace('.', ''))
    userWeight = int(request.json.get('userWeight', ""))

    if userHeight == 51: # Just a fix because python is removing trailing zeros while coverting to str or int
       userHeight = 510

    # Set up training data

    # Expected User Inputs to classifier

    # Example, For a person with 5 feet height, the expected weight is 43 to 53 kg
    # So for this we mention in features like : features = [[5, 43], [5, 99],....]] 
    # which means for 5 feet height, user can enter values from 43 to 99 
    # but the expected weight will be 4353 as mentioned in labels like: labels = [4353, 4353,.....]

    features = [[5, 43], [5, 99], [51, 45], [51, 99], [52, 48], [52, 99], [53, 50], [53, 99], 
                [5.4, 53], [54, 99], [55, 55], [55, 99], [56, 58], [56, 99], [57, 60], [57, 99], 
                [58, 63], [58, 99], [59, 65], [59, 99], [510, 67], [510, 99], [511, 70], [511, 99], 
                [6, 72], [6, 99], [6, 72], [6, 99]]

    # Expected output values based on user inputs or expected weight ranges based on person height

    labels = [4353, 4353, 4555, 4555, 4859, 4859, 5061, 5061, 5365, 5365 ,5558 ,5558, 5870,
            5870, 6074,6074 , 6376, 6376 ,6580 , 6580 ,6783 , 6783, 7085, 7085, 7289, 7289, 7289, 7289]

    # Train classifier

    classifier = tree.DecisionTreeClassifier()  # Decision tree classifier is used
    classifier = classifier.fit(features, labels)  # Find common patterns in training data

    # Make predictions using the trained model

    expectedWeight = classifier.predict([[userHeight,userWeight]])

    # Get first two numbers from expected Weight

    expectedWeight = int(expectedWeight)
    fromEpectedWeight = int(str(expectedWeight)[:2])

    # Get last two numbers from expected Weight

    toExpectedWeight = int(str(expectedWeight)[2:4])

    # Check if weight is in between the range of expected weight

    is_Weight_In_between = userWeight >= fromEpectedWeight and userWeight <= toExpectedWeight

    if is_Weight_In_between:
     message = f'Congratulations!, Your expected weight is in between {fromEpectedWeight} kg and {toExpectedWeight} kg.'
    else:
     message = f'Your expected weight should be in between {fromEpectedWeight} kg and {toExpectedWeight} kg.'


    fitData = {
        'isFit': is_Weight_In_between,
        'message': message
    }

    return jsonify( { 'fitInfo': fitData } ), 201
Enter fullscreen mode Exit fullscreen mode

Finally we have below line to run the app.

   app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

Deploying Python Flask REST API to Azure:

Next we are going to use a service in Azure known as Azure App Service to host our Python Flask App.

Login to Azure portal. Search for Azure App Service In the Search box and select it.

Alt Text

Next Click Create, and fill details in Basic tab, Enter Resource Group name or create new and give a name, enter your app name.

In Run Time Stack select Python 3.7 , Then Click Review and Create.

Alt Text

Just review your details and click finally Create.

Alt Text

Then it will show you that your Deployment is Progress, it may take few minutes.

Alt Text

Once your Deployment is done, click Go to resource which will take you to App Management blade.

Alt Text

Next go to your Github Account and create a Repository and upload the code using Github website or using git commands or even simple, just Fork my code repository from here

Note: As we are deploying to Azure so along with app.py file in our repo we need requirements.txt file which contains all required packages to be installed in Azure App Service for hosting as shown below:

Flask==1.1.2

Flask-Cors==3.0.10

scikit-learn==0.24.1
Enter fullscreen mode Exit fullscreen mode

Setup CI/CD with Deployment Center and Github Actions:

Once you have your Repository and the code, then next we have to connect your Repository to Azure Deployment Center which provides CI/CD outofbox with providers like Github Actions, Bitbucket etc.

Select Github as source control and authorize using your Github credentials, then select you Repository as below:

Alt Text

In Run Time Stack select Python 3.7

Alt Text

Once you click Save, the deployment starts and a workflow file is created in your Github repo to start CI/CD, as you can see below in my Github repo, in GithHub Actions tab, the build is completed and the final deploy is in progress. Here the deploy Job is pushing your code changes automatically toAzure Web App as you make any changes to your code and push to your Github repo. Its that easy :)

Alt Text

Check your deployed REST API:

App Service Application URL: https://<Your app name>.azurewebsites.net/api/get-message

If you are getting Hello World! as response then your API is successfully deployed. :)

Test ML REST API using Post Man:

Open Postman desktop app and create a new request, give a name. next we select the Http Verb as POST and paste in our API URL.

Alt Text

In the Body we select Raw and Json and POST Json data to API. Paste Below Payload in Body:

{
    "userHeight":"6",
    "userWeight":"80"
}
Enter fullscreen mode Exit fullscreen mode

Next click Send. Finally the API predicts the result and gives back the Json results as below:

{
    "fitInfo": {
        "isFit": true,
        "message": "Congratulations!, Your expected weight is in between 72 kg and 89 kg."
    }
}
Enter fullscreen mode Exit fullscreen mode

Wow we are done! πŸ˜„. Congratulations!

My Initial struggle with Azure App Service Deployment:

I have deployed many .NET apps to Azure but this was first time I am deploying a Python App to Azure.

After following online tutorials to deploy Python Apps, the Azure Web App was down with 502 errors. After many hours of struggle Finally got a advice in Microsoft doc to check Debug console using Kudu Console or the SCM Dashboard for your app. The Link looks like below:

   https://<Your app name>.scm.azurewebsites.net/.
Enter fullscreen mode Exit fullscreen mode

Alt Text

From the Log Stream I came to know that Azure is using docker and containers behind the scene and the container is failing to start.

Alt Text

Finding and Killing The Root Issue:

By Digging deeper, I came to know that some process is still running in port 8000 so the port is in use and thus container is not able to start.

So as advised in this SO post, lets kill the process running in port 8000, using the below Bash command.

  alias kill8000="fuser -k -n tcp 8000"
Enter fullscreen mode Exit fullscreen mode

Finally deployed Python REST API to Azure. πŸ˜„

Github Link to Code:

GitHub logo shaijut / Python-ML-Fitness-API

A Fitness Machine Learning API build using Python Flask and deployed to Azure using GitHub Actions CI/CD

PS: You can even test this API in VS Code and PostMan in your local machine without deploying to Azure.

If you have any doubts let me know in comments. Hope you enjoyed. πŸ˜„.

Have a great day.

References:

https://dev.to/educationecosystem/a-simple-machine-learning-algorithm-to-differentiate-between-an-apple-and-an-orange-2bpk

https://dev.to/rtficial/building-your-first-restful-api-with-python-flask-1lmc

https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask

https://programminghistorian.org/en/lessons/creating-apis-with-python-and-flask

https://stackoverflow.com/questions/25594893/how-to-enable-cors-in-flask

https://stackoverflow.com/questions/29332056/global-error-handler-for-any-exception

https://scikit-learn.org/stable/install.html

https://stackoverflow.com/questions/46008128/flask-webserver-not-reloading-on-code-change/66894350#66894350

https://stackoverflow.com/questions/37690195/azure-error-502-web-server-received-an-invalid-response-while-acting-as-a-gate

https://docs.microsoft.com/en-in/azure/app-service/troubleshoot-http-502-http-503

https://stackoverflow.com/questions/16756624/gunicorn-connection-in-use-0-0-0-0-5000

https://stackoverflow.com/questions/9168392/shell-script-to-kill-the-process-listening-on-port-3000

Top comments (0)