A Guide to Connect Machine Learning Model Backend to Frontend Using Flask
Tags: machinelearning, flask, python, jquery
Published: 2024-02-11 08:21:39

## Introduction

Many of us work with machine learning models for various purposes, often using _Jupyter Notebook_, _Google Collab_ or other environments. Now we may think about how can we save our trained model and make predictions based on user input directly from our website or app. Many different methods are available out there for various purposes. However, in this article, I will show a simple approach to saving a trained model as a file and using this model file to predict actual input data from users and show prediction results.
Here, I have trained a basic _Linear Regression_ model from the _scikit-learn_ library using the _mtcars_ dataset to predict _Miles Per Gallon (mpg)_ based on _Horsepower (hp)_ and _Weight (wt)_. Then, I also created a simple web app using _Flask_, _JQuery_, and _Bootstrap 5_ to collect, process user input and generate predictions. You don't need to worry about the technical details, you can easily try it out by cloning/downloading my _Github_ repository.

All the resource files can be found on this _GitHub_ Repository:
[github.com/alfa-echo-niner-ait/ml-predict-app](https://github.com/alfa-echo-niner-ait/ml-predict-app)

I have also hosted our sample web app online. Feel free to try it out at this link:
[Simple Flask Web App to Predict From User Input](https://alfaecho.pythonanywhere.com/)

![Simple Flask Web App to Predict From User Input](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ovus8chn0amo3lib4evv.png)

## Requirements

To try out this tutorial you need to have _Python_ installed on your local machine and a text editor or _IDE_ that supports _Python_ and editing notebook_(.ipynb)_ files. Here, I will be using my all-time favourite _Visual Studio Code_ as I can just install certain extensions to continue my work.
Now you can either clone my repository or directly download the source code as a zip file from _GitHub_ and extract it on your file storage. To clone the repository, create a new folder, then open the terminal from that folder and run the git clone command.

`git clone https://github.com/alfa-echo-niner-ait/ml-predict-app.git`

After cloning/downloading the source codes, you need to install the necessary packages. Here mainly we will need the following 

- packages:
- flask
- pandas
- scikit-learn
- joblib

Or you can just run the following command in your resource folder where you have cloned/downloaded the source files:

`pip install -r requirements.txt`

However, it is a good practice to create a virtual environment first when we are working on big projects. Our project is small enough, we are good to go now.

## Processing

### Train Model

For this tutorial, I've used a simple car dataset. Here is the overview of the dataset:

![Dataset Overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/srffkubp8vy13eyokfs5.png)

We will be predicting _mpg_ based on _hp_ and _wt_. So, we need these three columns from our dataset. So, I will assign them accordingly and split them for training and testing data.

```python
X = data[['hp', 'wt']]
Y = data['mpg']

X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=11)
```

> If you are willing to use your models to predict user input data, pay attention to the training process, especially picking the right columns. Picking an unnecessary or extensive amount of columns affects the model's performance. Make sure which columns you really need and exclude the remaining. Also, choose the correct model according to your dataset.

Here I have used _LinearRegression_ model from the scikit-learn library to train and test.

```python
from sklearn.linear_model import LinearRegression
lr_model = LinearRegression()
```

You can check out the full model training and testing process that has been included with the resource files in the _/Model/Model_train.ipynb_

### Save Model

After we finish training, testing, and evaluating the accuracy and other necessary metrics, and are satisfied with our model performance, we are ready to save our trained model. My model has achieved an accuracy rate of _88.48%_, which is good enough.

![Model Accuracy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdpji7mdbv2e4wdobblq.png)

Here, we have used the _joblib_ library to save our model, by _joblib.dump()_ function. To save the model, you have to pass your training model (in our case _lr_model_) and filename with an extension. Then, our model will be saved in the root directory of our notebook file with the mentioned filename(model_filename).

> In model_filename if the file extension is not mentioned, filename extensions ('.z', '.gz', '.bz2', '.xz' or '.lzma') will be used automatically.

```python
import joblib

# Define the save model name
model_filename = 'car_model.joblib'
# Save the model
joblib.dump(lr_model, model_filename)
```

The _joblib.dump()_ function can receive the following attributes:

```python
joblib.dump(value, filename, compress=0, protocol=None, cache_size=None)
```

_protocol_ and _cache_size_ are optional. For compress, it supports int from 0 to 9 or bool or 2-tuple, or you can also leave it as optional.

### Load Model

Now we have saved our model, it's time to load the model and do the prediction. We can load our saved model simply by using _joblib.load()_ function.

```python
loaded_model = joblib.load(model_filename)
```

This function has two attributes, _filename(str, pathlib.Path, or file object)_ and _mmap_mode({None, 'r+', 'r', 'w+', 'c'})_ or you can leave _mmap_mode_ as optional.

> WARNING: joblib.load relies on the pickle module and can therefore execute arbitrary Python code. It should therefore never be used to load files from untrusted sources.

```python
joblib.load(filename, mmap_mode=None)
```

### Predict With Loaded Model

To predict using your loaded model you have to pass the data with the exact format of your training data. You can check the training and testing data format by printing their type or check your training process to find your format.

![Check Data Type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lsviopuk1249frxirq4b.png)

For example, here we have hp and wt data separately, then we convert them to _pandas.core.frame.DataFrame_, and then use for prediction.

```python
# Input data
hp = 110
wt = 3.2

# Convert accordingly
data = {
    "hp": [hp],
    "wt": [wt]
}
test_data = pd.DataFrame(data)

# Predict
predicted_mpg = loaded_model.predict(test_data)

# Print Result
print(f"Result: {predicted_mpg[0]:.2f} mpg")
```

## Working with Flask

### Load Model in the App

If you have already cloned/downloaded the source code from my _GitHub_ repository, you might've noticed that we load our model file when we initialize our app in _/source/__init__.py_, it will prevent reloading the model again and again. In the _MODEL_NAME_ mention your saved model filename. Here, I have placed my model file inside _/source/static/model_ folder. If you place your model in a different folder, remember to modify the _model_path_.

```python
import os
import joblib
from flask import Flask

MODEL_NAME = "car_model.joblib"

# Initialize the app
app = Flask(__name__)

# Load the model when the application starts
model_path = os.path.join(os.path.dirname(__file__), 'static/model/' + MODEL_NAME)
model = joblib.load(model_path)
```

### User Input

In our _/source/templates/index.html_, we have a simple form to take hp and wt input from the user.

```html
<!-- User Input -->
<input type="text" id="hp" class="form-control-lg" placeholder="Enter HP, e.g. 112" required>
<input type="text" id="wt" class="form-control-lg" placeholder="Enter WT, e.g. 3.2" required>
<!-- Process input/output via main.js file -->
 <button class="btn btn-lg btn-info" type="button" id="submitData">Get MPG</button>
```

### Send Data to Flask Server

When the user submits the data we read the form data, send it to our flask server and get a response using _AJAX_ request. The code can be found inside our _/source/static/js/main.js_ file. However, you can use any other method you like to exchange request/response data with your server. Here's how I did it:

```javascript
$(document).ready(function () {
    // Send data to flask server upon submit
    $("#submitData").click(function () {
        $("#resText").show()
        $("#resText").html("Loading....");
        hp = $("#hp").val();
        wt = $("#wt").val();
        $("#hp").val("");
        $("#wt").val("");

        // Data request
        $.ajax({
            type: "POST",
            url: predict_url,
            data: {
                hp: hp,
                wt: wt
            },
            // Print result
            success: function (response) {
                $("#resText").html(response);
            }
        });
    });
});
```

### Process, Predict and Return Result

We have defined a _/predict_ route in _/source/routes.py_ flask app to read the data from the _AJAX_ request, then process the data, predict and return the result.

```python
# Process user input and return response data
@app.route('/predict', methods=['POST'])
def predict():
    if request.method == 'POST' and model:
        # Receive & store the form input data by user
        hp = request.form.get('hp')
        wt = request.form.get('wt')
        
        # Prepare received data for test data according to your trained model
        # Note: Prepare your user input as per your trained model prediction data
        data = {
            "hp": [hp],
            "wt": [wt]
        }
        test_data = pd.DataFrame(data)
        try:
            # Calculate the prediction result as per your trained model
            predict_mpg = model.predict(test_data)
            result = f"{predict_mpg[0]:.2f}"
            return f"Predicted : <strong>{result} MPG</strong>"
        except ValueError:
            return "<i class='text-danger'>Please Enter Correct Data!</i>"
        
    return "Failed to load model!"
```

Here we process the user data the same way we showed in the **"Predict With Loaded Model"** section.

## Conclusion

With this tutorial, I have tried to demonstrate some basic ideas for implementing a machine-learning model to interact with users. However, these ideas are not limited, you can try out more with different models and different frameworks like Django, FastAPI etc. or more complex applications to not just predict user input but also concurrently feed the model live user data and predict various aspects to improve user experience and provide better services.

And you're welcome to explore the internet for more ideas on this topic and build a strong skillset. I have attached all the links to the Reference section of the technology I've discussed through my tutorial.

## Reference Links

- [Python](https://www.python.org/)
- [Flask](https://www.python.org/)
- [scikit-learn](https://scikit-learn.org/)
- [joblib](https://joblib.readthedocs.io/)
- [pandas](https://joblib.readthedocs.io/)
- [Bootstrap](https://getbootstrap.com/)
- [jQuery](https://getbootstrap.com/)
- [Jupyter](https://getbootstrap.com/)
- [Google Colaboratory](https://colab.research.google.com/)
- [VS Code](https://code.visualstudio.com/)
- [Django](https://www.djangoproject.com/)
- [FastAPI](https://fastapi.tiangolo.com/)