Produksi ML Model ke Aplikasi Web Menggunakan Flask Framework

Di dalam dunia sains data, teknologi Machine Learning (ML) dipraktiskan untuk membuat ramalan dan pengesanan ketidaknormalan pada transaksi atau proses. Sebagai pengguna di alaf ini dan juga pembangun aplikasi, kita dibiasakan dengan pembangunan aplikasi biasa untuk tujuan transaksional data sahaja bagi memenuhi keperluan kes bisnes organisasi. Namun kita jarang atau tidak pernah memikirkan bagaimana kita ingin menjadikan aplikasi yang dibangunkan ada elemen kecerdikan, bijak membuat keputusan dan tidak mengharapkan bantuan manusia untuk melakukannya. Justeru ini, ramai data saintis membangunkan beberapa model menggunakan beberapa teknik algoritma di dalam Machine Learning (ML) menggunakan TensorFlow, Keras, Scikit-Learn dan Pytorch. Namun begitu, model ML yang telah selesai dibangunkan bukan bermakna kita telah selesai. Ia perlu melalui proses memproduksikan model ini ke dalam bentuk aplikasi web yang mudah diakses serta diinput oleh manusia di dalam persekitaran sebenar (live).

Jadi untuk kefahaman , saya kongsikan tutorial ringkas bagaimana model ML ini dihubungkan dengan aplikasi web atau mobil untuk kegunaan pengguna akhir di dalam persekitaran sebenar nanti. Huraian ini dipecahkan kepada beberapa langkah iaitu :

1. Pembangunan ML Model

2. Input HTML Page

3. Flask Web Server

4. Output HTML Page

5. Melarikan Flask Web Server

LANGKAH 1 : PEMBANGUNAN MODEL

DATASET

Di dalam pembangunan model ML apa yang paling penting adalah sumber data atau dataset . Di dalam tutorial ini kita akan menggunakan dataset flowers atau bunga yang boleh dimuat turun dari kaggle dan di github – https://gist.github.com/netj/8836201.

PENGKELASAN MODEL

Langkah seterusnya adalah pembangunan model ML menggunakan bahasa Python menggunakan dataset flowers. Jika dilihat pada dataset ini ia lebih kepada pengkelasan jenis bunga berdasarkan panjang dan lebar kelopak bunga. Ia menghasilkan prediksi jenis bunga berdasarkan input ini. Jadi kita boleh bayangkan bagaimana skrin atau web page yang kita ingin bangunkan nanti (ia adalah paramater utama sebagai input untuk pemprosesan model).

Di dalam pembangunan model ML ini, kemahiran yang diperlukan adalah aturcara bahasa Python. Beberapa library Python yang digunakan iaitu Pandas, Numpy dan Sklearn/Sci-kit learn. Jadi kita bina satu files dinamakan model.py seperti yang di bawah :

Model.py

# Importing necessary libraries
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression

# Importing the dataset
data = pd.read_csv('iris.csv')

# Dictionary containing the mapping
variety_mappings = {0: 'Setosa', 1: 'Versicolor', 2: 'Virginica'}

# Encoding the target variables to integers
data = data.replace(['Setosa', 'Versicolor' , 'Virginica'],[0, 1, 2])

X = data.iloc[:, 0:-1] # Extracting the independent variables
y = data.iloc[:, -1] # Extracting the target/dependent variable

logreg = LogisticRegression() # Initializing the Logistic Regression model
logreg.fit(X, y) # Fitting the model

# Function for classification based on inputs
def classify(a, b, c, d):
    arr = np.array([a, b, c, d]) # Convert to numpy array
    arr = arr.astype(np.float64) # Change the data type to float
    query = arr.reshape(1, -1) # Reshape the array
    prediction = variety_mappings[logreg.predict(query)[0]] # Retrieve from dictionary
    return prediction # Return the prediction

Berdasarkan kod sumber di atas, dataset dibaca dari files dataset csv yang telah dimuat turun

data = pd.read_csv('iris.csv')

Merujuk kepada dataset di kolum jenis bunga merupakan variable yang kita sasarkan atau dipanggil target variable. Oleh kerana ia bernilai text, maka ia perlu dirujuk sebagai nilai number atau integer kerana di dalam teknikal pengkelasan model hanya menerima nilai numerik sahaja. Jadi, kita perlu membuat proses pemetaan (mapping) nilai numerik dengan nilai sebenar (jenis bunga).

# Dictionary containing the mapping
variety_mappings = {0: 'Setosa', 1: 'Versicolor', 2: 'Virginica'}

# Encoding the target variables to integers
data = data.replace(['Setosa', 'Versicolor' , 'Virginica'],[0, 1, 2])

Seterusnya, dataset perlu diasingkan untu menentukan nilai independent dan dependent values. Nilai independent values itu adalah features atau kolum pada dataset. Kedua-dua nilai diletakkan di dalam pembolehubah X (independent variable) dan Y (Nilai target atau dependent variable). Oleh itu kita menggunakan fungsi .iloc[] untuk pengasingan variable ini.

X = data.iloc[:, 0:-1] # Extracting the independent variables
y = data.iloc[:, -1] # Extracting the target/dependent variable

Apabila kita telah mengetahui yang mana satu feature (X) dan nilai sasaran (Y), maka kita telah boleh menggunakan model yang bersesuaian. Dalam contoh ini kita menggunakan model Logistic Regression (LR)

logreg = LogisticRegression() # Initializing the Logistic Regression model
logreg.fit(X, y) # Fitting the model

Akhir sekali, proses pengkelasan nilai input berdasarkan nilai sebenar dilaksanakan. Ia diproses oleh function classify.

# Function for classification based on inputs
def classify(a, b, c, d):
    arr = np.array([a, b, c, d]) # Convert to numpy array
    arr = arr.astype(np.float64) # Change the data type to float
    query = arr.reshape(1, -1) # Reshape the array
    prediction = variety_mappings[logreg.predict(query)[0]] # Retrieve from dictionary
    return prediction # Return the prediction

Di dalam penghasilan antaramuka web berdasarkan model ini, kita cuba banyakkan 2 proses utama iaitu input dan output. Input adalah antaramuka (html) bagi tujuan pengguna memasukkan data manakala output (html) adalah paparan yang terhasil daripada proses input bersama dengan pemprosesan model yang dibangunkan tadi.

PENGHASILAN INPUT PAGE HTML

Page input HTML ini adalah terdiri daripada format HTML yang biasa digunakan oleh pembangun aplikasi web bersama dengan CSS (jika diperlukan). Boleh juga digunakan bersama templet Bootstrap dan Tailwinds CSS. Kita bina satu fail html yang dinamakan home.html

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Flower Variety</title>
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.0/css/bulma.min.css">
	<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <style>
        html{
            overflow: hidden;
        }

        body{
            position: absolute;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #login-form-container{
            position: absolute;
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
        }
    </style>
</head>	
<body>
    <div id="login-form-container">
        <form action="classify" method="GET">
            <div class="card" style="width: 400px">
            <div class="card-content">
                <div class="media">
                <div class="is-size-4 has-text-centered">Flower Variety Classification</div>
                </div>
                <div class="content">

                <div class="field">
                    <p class="control">
                    Sepal Length: <input class="input" type="number" value='0.00' step='0.01' name="slen" id="slen">
                    </p>
                </div>

                <div class="field">
                    <p class="control">
                    Sepal Width: <input class="input" type="number" value='0.00' step='0.01' name="swid" id="swid">
                    </p>
                </div>

                <div class="field">
                    <p class="control">
                    Petal Length: <input class="input" type="number" value='0.00' step='0.01' name="plen" id="plen">
                    </p>
                </div>

                <div class="field">
                    <p class="control">
                    Petal Width: <input class="input" type="number" value='0.00' step='0.01' name="pwid" id="pwid">
                    </p>
                </div>
                
                <div class="field">
                    <button class="button is-fullwidth is-rounded is-success">Submit</button>
                </div>
                </div>
            </div>
        </form>
    </div>
</body>
</html>

fail ini disimpan di dalam ./templates/home.html.

WEB SERVER FLASK

Kewajipan di dalam kita ingin memproduksi aplikasi web memerlukan satu web server bertindak sebagai pelayan pemprosesan fail (html). Justeru ini, kita menggunakan python microframework yang dikenali sebagai flask. Sebagai permulaan, kita perlu membina satu fail baru yang dinamakan server.py untuk membolehkan flask melaluikan pemprosesan pada model dan html.

import model # Import the python file containing the ML model
from flask import Flask, request, render_template,jsonify # Import flask libraries

# Initialize the flask class and specify the templates directory
app = Flask(__name__,template_folder="templates")

# Default route set as 'home'
@app.route('/home')
def home():
    return render_template('home.html') # Render home.html

# Route 'classify' accepts GET request
@app.route('/classify',methods=['POST','GET'])
def classify_type():
    try:
        sepal_len = request.args.get('slen') # Get parameters for sepal length
        sepal_wid = request.args.get('swid') # Get parameters for sepal width
        petal_len = request.args.get('plen') # Get parameters for petal length
        petal_wid = request.args.get('pwid') # Get parameters for petal width

        # Get the output from the classification model
        variety = model.classify(sepal_len, sepal_wid, petal_len, petal_wid)

        # Render the output in new HTML page
        return render_template('output.html', variety=variety)
    except:
        return 'Error'

# Run the Flask server
if(__name__=='__main__'):
    app.run(debug=True) 

Perlu memastikan menyatakan nama folder yang menyimpan fail html. Dalam tutorial ini kita menggunakan templates.

# Initialize the flask class and specify the templates directory
app = Flask(__name__,template_folder="templates")

Perlu meletakkan route atau lokasi function yang menerima parameter input dari fail html. (konsep MVC)

@app.route('/classify',methods=['POST','GET'])

Juga nama parameter iaitu nama objek html seperti yang diistiharkan di fail home.html . Nilai input ini akan dibaca oleh web server mengikut parameter.

sepal_len = request.args.get('slen') # Get parameters for sepal length
sepal_wid = request.args.get('swid') # Get parameters for sepal width
petal_len = request.args.get('plen') # Get parameters for petal length
petal_wid = request.args.get('pwid') # Get parameters for petal width

Seterusnya nilai input ini akan dihantar ke model untuk diproses dan dipaparkan hasilnya ke satu fail output dipanggil output.html .

variety = model.classify(sepal_len, sepal_wid, petal_len, petal_wid)

# Render the output in new HTML page
return render_template('output.html', variety=variety)

Arahan pemprosesan oleh Flask ke atas fail model dan html.

# Run the Flask server
if(__name__=='__main__'):
    app.run(debug=True) 

PAGE OUTPUT HTML

Seperti yang dijelaskan di dalam contoh di atas, kita memerlukan satu lagi html yang bertindak sebagai output untuk paparan hasil. Fail ini adalah output.html yang disimpan di dalam ./templates/home.html.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Flower Variety</title>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.0/css/bulma.min.css">
        <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
        <style>
            html{
                overflow: hidden;
            }

            body{
                position: absolute;
                width: 100%;
                height: 100%;
                margin: 0;
                padding: 0;
            }

            #login-form-container{
                position: absolute;
                width: 100%;
                height: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
            }
        </style>
    </head>	
    <body>
        <div id="login-form-container">
            <div class="card" style="width: 400px">
                <div class="card-content">
                    <div class="media">
                        <div class="is-size-4 has-text-centered">{{ variety }}</div>
                    </div>
                    <form action="home">
                        <div class="field">
                            <button class="button is-fullwidth is-rounded is-success">Retry</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </body>
</html>

MELARIKAN FLASK WEB SERVER

Untuk melarikan flask ini pada fail model dan html menggunakan arahan python .

$ python server.py
Increase the number of iterations (max_iter) or scale the data as shown in:
    https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
    https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
  n_iter_i = _check_optimize_result(
 * Debugger is active!
 * Debugger PIN: 472-352-074
127.0.0.1 - - [29/Jul/2022 15:30:25] "POST /predict HTTP/1.1" 404 -
127.0.0.1 - - [29/Jul/2022 15:30:25] "GET / HTTP/1.1" 404 -
127.0.0.1 - - [29/Jul/2022 15:30:34] "GET /home HTTP/1.1" 200 -

Setelah mesej ini dipaparkan,maka webserver Flask sedang berjalan. Anda boleh mengaksesnya melalui pelayar web dengan memasukkan default alamat seperti di bawah :

http://127.0.0.1:5000/home

Anda boleh mencuba memasukkan nilai pada setiap input di page ini untuk diproses oleh model bagi membuat prediksi jenis bunga serta dipaparkan di web. Setelah masukkan input selesai dan tekan butang submit, hasil yang dipaparkan adalah jenis bunga Setosa .(Prediksi jenis bunga bergantung pada input yang dimasukkan oleh pengguna dan model akan membuat prediksi berdasarkan model atau algoritma Logistic Regression (LR).

Anda baru selesai menyiapkan aplikasi prediksi jenis bunga berdasarkan panjang dan lebar kelopak bunga mengguna model Machine Learning dan diproduksikan di aplikasi web bagi memudahkan pengguna akhir memasukkan data serta paparan.

Kod sumber lengkap boleh diakses di (https://github.com/sabrisoft/flaskmodel/)

Terima Kasih. Selamat Mencuba.