This article was published as a part of the Data Science Blogathon
Introduction
Machine learning is a fascinating field and everyone wants to get their hands dirty in this field. Every beginner trying to break into this is advised to follow one or the other programming/scripting language.
Python being user-friendly and easy to learn is usually the first choice. This language has a lot of scopes and its use case can be found in almost every domain. One such domain is Web development. Python is used as a backend scripting language using frameworks such as Flask, Django, or FastAPI.
But have you ever thought of using Python as a tool to manipulate the DOM of a website or in broader terms, as a frontend language? Brython is a Python implementation that offers an interface to DOM elements and events. It aims to replace JavaScript as the web scripting language.
Let’s see how to build a web interface using Brython that takes in values from users, makes an API request to the model deployed, and returns the prediction. You will also see how to deploy this webpage using GitHub pages.
Preparing the Model API
You can wrap your machine learning model using any Python web development framework. Currently, FastAPI is gaining a lot of popularity due to its advanced features such as automatic docs generation, async request support, data validation, and much more.
Check out this article for a detailed explanation about wrapping the model as an API and deployment to Heroku: Deploying ML Models as API Using FastAPI and Heroku
For this article, I have wrapped a machine learning classification model that classifies music genres based on various music elements such as tempo, valence, acoustics, and 5 more. The code to wrap the model using FastAPI is given below:
import uvicorn import pickle from fastapi import FastAPI from pydantic import BaseModel from fastapi.middleware.cors import CORSMiddleware app = FastAPI() origins = [ "*" ] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"] ) class Music(BaseModel): acousticness: float danceability: float energy: float instrumentalness: float liveness: float speechiness: float tempo: float valence: float with open("./model/model.pkl", "rb") as f: model = pickle.load(f) @app.get('/') def index(): return {'message': 'This is the homepage of the API '} @app.post('/prediction') def get_music_category(data: Music): received = data.dict() acousticness = received['acousticness'] danceability = received['danceability'] energy = received['energy'] instrumentalness = received['instrumentalness'] liveness = received['liveness'] speechiness = received['speechiness'] tempo = received['tempo'] valence = received['valence'] pred_name = model.predict([[acousticness, danceability, energy, instrumentalness, liveness, speechiness, tempo, valence]]).tolist()[0] return {'prediction': pred_name} @app.get('/predict') def get_cat(acousticness: float, danceability: float, energy: float, instrumentalness: float, liveness: float, speechiness: float, tempo: float, valence: float): pred_name = model.predict([[acousticness, danceability, energy, instrumentalness, liveness, speechiness, tempo, valence]]).tolist()[0] return {'prediction': pred_name} if __name__ == '__main__': uvicorn.run(app, host='127.0.0.1', port=4000, debug=True)
An important thing to note here is that you need to make your API compatible with CORS. It means that other domains and schema origin should be able to fetch out API without any errors. You can permit a limited amount of domains or make it open for all using `*` wildcard.
In the code shown above, the 5th line imports the CORS middleware present in the FastAPI package. Lines 9–19 define the rules for this middleware. For demonstration purposes, I have permitted all the domains using star wildcard.
For Flask-based APIs, you need to install an extension package called “flask-cors” and then set up the origins in the same way as done in FastAPI. Check out flask-cors documentation for implementation details.
Creating the Webpage
Our model API is ready to be consumed by any type of origin resource. You can fetch this API using JavaScript, jQuery, or any other language but we will use Python to make API calls and update the webpage with the prediction results.
Let’s create a basic HTML interface without any logic. These components will be required for a basic form type webpage:
- 8 input fields for all 8 parameters of the model
- 8 Labels for parameter names
- One button to initiate the fetching process
- One div to display the results returned from the API
The interface would look like this:
Basic Interface (Image by Author)
Make sure to add an id to all the input fields and result div tag. This will help in accessing the values entered by the user and process them in the Python scripts. The HTML code for the above interface is:
<body> <h1>Get Music Genre</h1> <label for="acousticness">Enter Acousticness</label> <input type="acousticness" style="width:470px;" id="acousticness" placeholder="0.988306" required> <br> <label for="danceability">Enter Danceability</label> <input type="danceability" style="width:470px;" id="danceability" placeholder="0.255661" required> <br> <label for="energy">Enter Energy</label> <input type="energy" style="width:470px;" id="energy" placeholder="0.979774" required> <br> <label for="instrumentalness">Enter Instrumentalness</label> <input type="instrumentalness" style="width:470px;" id="instrumentalness" placeholder="0.9730057241" required> <br> <label for="liveness">Enter Liveness</label> <input type="liveness" style="width:470px;" id="liveness" placeholder="0.121342" required> <br> <label for="speechiness">Enter Speechiness</label> <input type="speechiness" style="width:470px;" id="speechiness" placeholder="0.051740" required> <br> <label for="tempo">Enter Tempo</label> <input type="tempo" style="width:470px;" id="tempo" placeholder="90.241" required> <br> <label for="valence">Enter Valence</label> <input type="valence" style="width:470px;" id="valence" placeholder="0.034018" required> <br> <center><button class="btn btn-primary" id="fetch">Predict</button></center> <div id="result" class="card">Click predict button!</div> </body>
Note: You will notice that the interface image shown had some styling but the code above does not indicate. I have deliberately not presented the CSS styling code as this article is not intended for website designing. But still, if you want to have a look at the whole code, you can head over to this GitHub Repository: Brython-ML
Binding Logics
Before making the “predict” button functional, you need to add some prerequisite code to your webpage:
Include Brython: For Brython to process your Python scripts, you need to add Brython and Brython standard libraries to the web page. One of the best ways to do this is using Brython CDN. It can also be added as static files with the webpage but CDN is updated frequently with bug fixes. As per the official documentation, add this code in your head tag.
<head> <script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.5/brython.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/brython/3.9.5/brython_stdlib.js"></script> </head>
Load Brython: After loading Brython scripts, you need to load the body of the webpage in the bython function. To do this, simply add the “onload” parameter in the body tag and pass the Brython function:
<body onload="brython()">
Now the Brython is set to load our Python scripts. Under the body tag, create a script tag and add the following code:
<script type="text/python" id="fetch-script"> import json from browser import document, ajax def on_complete(req): data = json.loads(req.responseText) lyrics = data['prediction'] document['result'].text = lyrics def get_prediction(e): req = ajax.ajax() acousticness = float(document['acousticness'].value) danceability = float(document['danceability'].value) energy = float(document['energy'].value) instrumentalness = float(document['instrumentalness'].value) liveness = float(document['liveness'].value) speechiness = float(document['speechiness'].value) tempo = float(document['tempo'].value) valence = float(document['valence'].value) url = f'https://kivymlapp.herokuapp.com/predict?acousticness={acousticness}&danceability={danceability}&energy={energy}&instrumentalness={instrumentalness}&liveness={liveness}&speechiness={speechiness}&tempo={tempo}&valence={valence}' req.open('GET', url, True) req.bind('complete', on_complete) document['result'].text = 'Loading...' req.send() document['fetch'].bind('click', get_prediction) </script>
Working of the code:
- In Brython-based scripts, all the popular libraries are not supported. The most important library here is the “bowser” which has all the methods to manipulate a web page’s DOM.
- You need to bind the “predict” button “click” event to a function that can process all the inputs from the text fields, make the API call, and update the prediction div with the results fetched.
- The 27th line binds predict button’s click event with a function named “get_prediction” using the button’s id “fetch”.
- Under the “get_prediction” function, all the values entered by users are fetched using their ids and as these values are returned as strings, they are converted back to float.
- After getting all the values, now it’s time to make the API request. The request is made using ajax in the browser module. You need to create an object of ajax class as done in line 11. Then, you need to open a request using “.open()” and depending upon the type of request, you can pass ‘GET’ or ‘POST’ with the URL.
- This request object also needs a binding function that determines what should be done on the competition of the request. Here, we are calling another function called “on_complete”. We are also making the text of the output field “loading..” so that the user is aware that a process is going on in the background. The request is executed using the “.send()” function of the request object.
- Now, as soon as the request is completed successfully, the “on_complete” function will be executed. This function is responsible for fetching the request-response and updating the prediction results. We are using the JSON library to load the request body and replace the current text of the result div with the prediction result.
Let’s see this webpage in action:
If you want to know more about Brython basics, then check out this article: Run Python Code on Websites: Exploring Brython
Deploying to GitHub Pages
Congrats! You have created your machine learning web interface using Python. The request is made using Python scripts on the front end. Now, it’s time to share this webpage with other users. GitHub Pages allows you to host static web pages and these are accessible all the time. You simply need to:
Create a new repository > Add the HTML file (for simplicity, name it as index.html) > Go to the settings tab on repository > Go to Pages option from left panel > Select current branch as the source and done!
Enabling GitHub pages in the repository (Image by Author)
Your webpage will be hosted on: .github.io/ — Incase you don’t have custom domain setup.
I have deployed the webpage made in this article here:
https://www.kaustubhgupta.me/Brython-ML/
Conclusion
In this article, you saw how to create ML models web interface using Brython. First, the interface HTML code was discussed. Then the Python script, which is used for binding the buttons to actions that can fetch the values entered by the user and return the results on the web page was discussed in detail. Then you also saw how to deploy this web page to GitHub so that you can share the interface with others plus it doesn’t go down.
For any doubts, queries, or potential opportunities, you can reach out to me via:
1. Linkedin — in/kaustubh-gupta/
2. Twitter — @Kaustubh1828
3. GitHub — kaustubhgupta
4. Medium — @kaustubhgupta1828
The media shown in this article are not owned by Analytics Vidhya and are used at the Author’s discretion.