Bài 58: K-Means gom cụm Khách hàng theo độ tuổi, thu nhập và ngân sách chi tiêu

Bài học này Tui hướng dẫn các bạn cách lập trình K-Means để gom cụm khách hàng theo thu nhập, độ tuổi và ngân sách chi tiêu. Chúng ta dùng phương pháp Elbow để tìm ra K Cụm tối ưu nhất để gom, bài học này chúng ta trực quan hóa các cụm bằng 2D và 3D. Đồng thời phân loại chi tiết Khách hàng theo từng cụm để dễ dàng đưa ra các chính sách chăm sóc và tiếp thị phù hợp. Lý thuyết K-Means các bạn đọc trong bài 57

Bạn tải cơ sở dữ liệu MySQL Server “SalesDatabase “trong link:

https://tranduythanh.com/datasets/salesdatabase.rar

Giải nén ra ta có thư mục salesdatabase

Trong MySQL Workbench bạn nhớ tạo một Schema tên là “salesdatabase” sau đó dùng chức năng import đã được học để import dữ liệu vào.

Đồng thời trong quá trình lập trình liên quan tới kết nối cơ sở dữ liệu thì nhớ đổi các thông số kết nối theo máy tính của bạn đã cài đặt.

Cấu trúc bảng dữ liệu như hình dưới đây:

Chúng ta sẽ gom cụm khách hàng bằng K-Means theo nhiều chiều khác nhau, và cho nhận xét. Yêu cầu các thuộc tính liên quan tới Customer dùng để gom cụm sẽ được trích suất từ 2 bảng dữ liệu “customer” và “customer_spend_score” theo SQL script như sau:

select distinct customer.CustomerID, Age, Annual_Income, Spending_Score
from customer, customer_spend_score
where customer.CustomerID=customer_spend_score.CustomerID

Kết quả tiền xử lý dữ liệu:

Trong Pycharm, ta tạo một dự án tên “KMeansCustomer“:

(1) File “Connector.py” là mã lệnh để kết nối, truy vấn dữ liệu:

#python -m pip install mysql-connector-python
import mysql.connector
import traceback
import pandas as pd

class Connector:
    def __init__(self,server=None, port=None, database=None, username=None, password=None):
        self.server=server
        self.port=port
        self.database=database
        self.username=username
        self.password=password
    def connect(self):
        try:
            self.conn = mysql.connector.connect(
                host=self.server,
                port=self.port,
                database=self.database,
                user=self.username,
                password=self.password
            )
            return self.conn
        except:
            self.conn=None
            traceback.print_exc()
        return None

    def disConnect(self):
        if self.conn != None:
            self.conn.close()

    def queryDataset(self, sql):
        try:
            cursor = self.conn.cursor()
            cursor.execute(sql)
            df = pd.DataFrame(cursor.fetchall())
            df.columns=cursor.column_names
            return df
        except:
            traceback.print_exc()
        return None

Tiếp theo ta viết mã lệnh cho “CustomerCluster.py” và thử nghiệm các chức năng:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from sklearn.cluster import KMeans
import numpy as np

from Connector import Connector

conn=Connector('localhost',3306,'salesdatabase','root','@Obama123')
conn.connect()

sql1="select * from customer"
df1=conn.queryDataset(sql1)
print(df1)

Thực thi mã lệnh trên ta có kết quả:

Tiếp tục bổ sung nối đuôi các mã lệnh tiền xử lý dữ liệu để rút trích thông tin khách hàng, tuổi, thu nhập, điểm tiêu dùng và cuối tập tin “CustomerCluster.py”:

sql2="select distinct customer.CustomerId, Age, Annual_Income, Spending_Score " \
     "from customer, customer_spend_score " \
     "where customer.CustomerId=customer_spend_score.CustomerID"

df2=conn.queryDataset(sql2)
df2.columns = ['CustomerId', 'Age', 'Annual Income', 'Spending Score']

print(df2)

print(df2.head())

print(df2.describe())

Thực hiện mã lệnh trên ta có kết quả:

Tiếp tục bổ sung nối đuôi mã lệnh dưới đây vào “CustomerCluster.py” để thực hiển Histogram:

def showHistogram(df,columns):
    plt.figure(1, figsize=(7,8))
    n = 0
    for column in columns:
        n += 1
        plt.subplot(3, 1, n)
        plt.subplots_adjust(hspace=0.5, wspace=0.5)
        sns.distplot(df[column], bins=32)
        plt.title(f'Histogram of {column}')
    plt.show()

showHistogram(df2,df2.columns[1:])

Thực thi mã lệnh trên ta có kết quả:

Nhìn vào biểu đồ Histogram, ta thấy được sự phân phối có tần suất của các tập dữ liệu liên quan tới Age, Annual Income và Spending Score.

Trong thuật toán K-Means thì chúng ta cần phải xác định trước số cụm. Và ta thường phân vân đâu là số lượng cụm cần phân chia tốt nhất đối với một bộ dữ liệu cụ thể, thì phương pháp Elbow là một cách giúp ta lựa chọn được số lượng các cụm phù hợp dựa vào đồ thị trực quan hoá bằng cách nhìn vào sự suy giảm của hàm biến dạng và lựa chọn ra điểm khuỷ tay (elbow point)

Tiếp tục bổ sung nối đuôi mã lệnh vào “CustomerCluster.py” để tính phương pháp Elbow nhằm tìm ra K cụm tối ưu cho K-Means:

def elbowMethod(df,columnsForElbow):
    X = df.loc[:, columnsForElbow].values
    inertia = []
    for n in range(1 , 11):
        model = KMeans(n_clusters = n,
                   init='k-means++',
                   max_iter=500,
                   random_state=42)
        model.fit(X)
        inertia.append(model.inertia_)

    plt.figure(1 , figsize = (15 ,6))
    plt.plot(np.arange(1 , 11) , inertia , 'o')
    plt.plot(np.arange(1 , 11) , inertia , '-' , alpha = 0.5)
    plt.xlabel('Number of Clusters') , plt.ylabel('Cluster sum of squared distances')
    plt.show()

-init: dùng k-means++

-max_iter: 500 lần

-random_state=42 để dùng cùng 1 phân bố dữ liệu chỗ mỗi lần chạy đảm bảo nhất quán

Ta thử nghiệm tìm K cụm theo phương pháp Elbow với 2 đặc trưng: Age và Spending Score. Ta bổ sung nối đuôi mã lệnh dưới đây vào “CustomerCluster.py”

columns=['Age','Spending Score']
elbowMethod(df2,columns)

Chạy phương thức elbow và có kết quả k=4:

Viết hàm runKMeans() với một đối số truyền vào là 1 ma trận X bất kỳ và số cluster muốn gom, cluster được lấy theo phương thức elbow

Tiếp tục bổ sung nối đuôi mã lệnh runKMeans() vào cuối “CustomerCluster.py”:

def runKMeans(X,cluster):
    model = KMeans(n_clusters=cluster,
                   init='k-means++',
                   max_iter=500,
                   random_state=42)
    model.fit(X)
    labels = model.labels_
    centroids = model.cluster_centers_
    y_kmeans = model.fit_predict(X)
    return y_kmeans,centroids,labels

Thử nghiệm triệu gọi hàm runKMeans(), với columns[Age, Spending Score], cluster=4. Tiếp tục bổ sung mã lệnh nối đuôi vào “CustomerCluster.py” để xem kết quả:

X = df2.loc[:, columns].values
cluster=4
colors=["red","green","blue","purple","black","pink","orange"]

y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)

Mảng X được lấy theo các columns (các thuộc tính mà customer ta muốn clustering). Đồng thời tạo cột cluster cho df2, nhằm phục vụ cho truy vấn danh sách Customer chi tiết theo từng cụm

Thực thi mã lệnh ở trên ta có kết quả:

Tiếp tục Viết hàm visualizeKMeans nhận vào 6 đối số, hàm này trực quan hóa kết quả gom cụm. Hàm này viết nối đuôi vào “CustomerCluster.py“:

•X: ma trận đã gom cụm

•y_kmeans: nhãn các phần tử được gom cụm

•cluster: số cụm

•title: tiêu đề

•xlabel: nhãn trục x

•ylabel: nhãn trục y

•colors: mảng màu

def visualizeKMeans(X,y_kmeans,cluster,title,xlabel,ylabel,colors):
    plt.figure(figsize=(10, 10))
    for i in range(cluster):
        plt.scatter(X[y_kmeans == i, 0],
                    X[y_kmeans == i, 1],
                    s=100,
                    c=colors[i],
                    label='Cluster %i'%(i+1))
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    plt.legend()
    plt.show()

Triệu gọi hàm visualizeKMeans, bằng cách tiếp tục viết nối đuôi mã lệnh vào “CustomerCluster.py“:

visualizeKMeans(X,
                y_kmeans,
                cluster,
                "Clusters of Customers - Age X Spending Score",
                "Age",
                "Spending Score",
                colors)

Kết quả sau khi triệu gọi hàm visualizeKMeans.

Gom cụm customer theo cột Age Spending Score

Thực nghiệm gom cụm theo 2 cột khác: Cột Annual Income Spending Score

Tiếp tục bổ sung nối đuôi mã lệnh sau vào “CustomerCluster.py”:

columns=['Annual Income','Spending Score']
elbowMethod(df2,columns)

Thực thi lệnh trên ta có kết quả:

Như vậy, theo phương thức elbow và có kết quả k=5

Tiến hành thực nghiệm gom cụm với cluster=5 và Cột Annual Income Spending Score

Tiếp tục bổ sung nối đuôi mã lệnh sau vào “CustomerCluster.py”:

X = df2.loc[:, columns].values
cluster=5

y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)

Mảng X được lấy theo các columns (các thuộc tính mà customer ta muốn clustering). Đồng thời tạo cột cluster cho df2, nhằm phục vụ cho truy vấn danh sách Customer chi tiết theo từng cụm

Thực thi mã lệnh trên ta có kết quả:

Triệu gọi hàm visualizeKMeans để trực quan hóa gom cụm với K=5. Tiếp tục bổ sung nối đuôi mã lệnh vào “CustomerCluster.py”:

visualizeKMeans(X,
                y_kmeans,
                cluster,
                "Clusters of Customers - Annual Income X Spending Score",
                "Annual Income",
                "Spending Score",
                colors)

Kết quả sau khi triệu gọi hàm visualizeKMeans. Gom cụm customer theo cột Annual IncomeSpending Score với K=5

Tiếp tục thực nghiệm gom cụm theo 3 cột khác: Cột Age, Annual IncomeSpending Score

Ta tiếp tục viết nối đuôi mã lệnh vào “CustomerCluster.py”:

columns=['Age','Annual Income','Spending Score']
elbowMethod(df2,columns)

Chạy phương thức elbow và có kết quả k=6

Tiến hành thực nghiệm gom cụm với cluster=6 và Cột Age, Annual IncomeSpending Score

Ta viết nối đuôi mã lệnh vào “CustomerCluster.py”:

X = df2.loc[:, columns].values
cluster=6

y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)

Mảng X được lấy theo các columns (các thuộc tính mà customer ta muốn clustering). Đồng thời tạo cột cluster cho df2, nhằm phục vụ cho truy vấn danh sách Customer chi tiết theo từng cụm

Thực thi mã lệnh ta có kết quả:

Vì trường hợp này dạng 3 thuộc tính (cột) nên ta bổ sung thêm 1 hàm trực quan hóa 3D kết quả gom cụm:

df: tập dữ liệu đã gom cụm.

columns: dữ liệu các trục. Là dữ liệu có các cột mà ta gom cụm

hover_data: Khi di chuyển chuột vào node sẽ hiển thị chi tiết.

cluser: số cụm

Ta tiếp tục bổ sung mã lệnh vào cuối “CustomerCluster.py”:

def visualize3DKmeans(df,columns,hover_data,cluster):
    fig = px.scatter_3d(df,
                        x=columns[0],
                        y=columns[1],
                        z=columns[2],
                        color='cluster',
                        hover_data=hover_data,
                        category_orders={"cluster": range(0, cluster)},
                        )
    fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
    fig.show()

Triệu gọi hàm trực quan hóa gom cụm 3D. Ở đây hover_data lấy theo cột dữ liệu để chương trình tự động mapping chính xác dữ liệu hiển thị

Tiêp tục bổ sung mã lệnh vào cuối “CustomerCluster.py”:

hover_data=df2.columns
visualize3DKmeans(df2,columns,hover_data,cluster)

Thực thi mã lệnh ta có kết quả:

Kết quả trực quan hóa gom cụm 3D.

Ta có thể tương tác trên chart 3D này, di chuyển tới từng cụm, vào từng node để xem dữ liệu.

Có  thể tương tác trên các node góc phải màn hình, xuất chart ra tập tin ảnh

(Máy tính cần có cấu hình mạnh để render 3D)

minh họa thêm tương các node và cụm:

Dưới đây là coding đầy đủ của “CustomerCluster.py”:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from sklearn.cluster import KMeans
import numpy as np

from Connector import Connector

conn=Connector('localhost',3306,'salesdatabase','root','@Obama123')
conn.connect()

sql1="select * from customer"
df1=conn.queryDataset(sql1)
print(df1)

sql2="select distinct customer.CustomerId, Age, Annual_Income, Spending_Score " \
     "from customer, customer_spend_score " \
     "where customer.CustomerId=customer_spend_score.CustomerID"

df2=conn.queryDataset(sql2)
df2.columns = ['CustomerId', 'Age', 'Annual Income', 'Spending Score']

print(df2)

print(df2.head())

print(df2.describe())

def showHistogram(df,columns):
    plt.figure(1, figsize=(7,8))
    n = 0
    for column in columns:
        n += 1
        plt.subplot(3, 1, n)
        plt.subplots_adjust(hspace=0.5, wspace=0.5)
        sns.distplot(df[column], bins=32)
        plt.title(f'Histogram of {column}')
    plt.show()

showHistogram(df2,df2.columns[1:])

def elbowMethod(df,columnsForElbow):
    X = df.loc[:, columnsForElbow].values
    inertia = []
    for n in range(1 , 11):
        model = KMeans(n_clusters = n,
                   init='k-means++',
                   max_iter=500,
                   random_state=42)
        model.fit(X)
        inertia.append(model.inertia_)

    plt.figure(1 , figsize = (15 ,6))
    plt.plot(np.arange(1 , 11) , inertia , 'o')
    plt.plot(np.arange(1 , 11) , inertia , '-' , alpha = 0.5)
    plt.xlabel('Number of Clusters') , plt.ylabel('Cluster sum of squared distances')
    plt.show()

columns=['Age','Spending Score']
elbowMethod(df2,columns)

def runKMeans(X,cluster):
    model = KMeans(n_clusters=cluster,
                   init='k-means++',
                   max_iter=500,
                   random_state=42)
    model.fit(X)
    labels = model.labels_
    centroids = model.cluster_centers_
    y_kmeans = model.fit_predict(X)
    return y_kmeans,centroids,labels

X = df2.loc[:, columns].values
cluster=4
colors=["red","green","blue","purple","black","pink","orange"]

y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)

def visualizeKMeans(X,y_kmeans,cluster,title,xlabel,ylabel,colors):
    plt.figure(figsize=(10, 10))
    for i in range(cluster):
        plt.scatter(X[y_kmeans == i, 0],
                    X[y_kmeans == i, 1],
                    s=100,
                    c=colors[i],
                    label='Cluster %i'%(i+1))
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    plt.legend()
    plt.show()

visualizeKMeans(X,
                y_kmeans,
                cluster,
                "Clusters of Customers - Age X Spending Score",
                "Age",
                "Spending Score",
                colors)

columns=['Annual Income','Spending Score']
elbowMethod(df2,columns)

X = df2.loc[:, columns].values
cluster=5

y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)

visualizeKMeans(X,
                y_kmeans,
                cluster,
                "Clusters of Customers - Annual Income X Spending Score",
                "Annual Income",
                "Spending Score",
                colors)

columns=['Age','Annual Income','Spending Score']
elbowMethod(df2,columns)

X = df2.loc[:, columns].values
cluster=6

y_kmeans,centroids,labels=runKMeans(X,cluster)
print("y_kmeans:")
print(y_kmeans)
print("centroids:")
print(centroids)
print("labels:")
print(labels)
df2["cluster"]=labels
print("df2:")
print(df2)

def visualize3DKmeans(df,columns,hover_data,cluster):
    fig = px.scatter_3d(df,
                        x=columns[0],
                        y=columns[1],
                        z=columns[2],
                        color='cluster',
                        hover_data=hover_data,
                        category_orders={"cluster": range(0, cluster)},
                        )
    fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))
    fig.show()

hover_data=df2.columns
visualize3DKmeans(df2,columns,hover_data,cluster)

Như vậy tới đây Tui đã hoàn tất hướng dẫn các bạn cách lập trình K-Means để gom cụm khách hàng theo thu nhập, độ tuổi và ngân sách chi tiêu. Chúng ta dùng phương pháp Elbow để tìm ra K Cụm tối ưu nhất để gom, bài học này chúng ta trực quan hóa các cụm bằng 2D và 3D. Đồng thời phân loại chi tiết Khách hàng theo từng cụm để dễ dàng đưa ra các chính sách chăm sóc và tiếp thị phù hợp.

Bài học này rất thiết thực, ứng dụng vào thực tiễn rất lớn, các bạn cố gắng thực hành nhiều lần để hiểu rõ về nó, cũng như có thể áp dụng vào dự án thực tế của doanh nghiệp

Source code đầy đủ của dự án các bạn tải ở đây:

https://www.mediafire.com/file/hgvx5xusot3enhs/KMeansCustomer.rar/file

Bắt đầu từ bài học sau Tui sẽ viết các bài Blog liên quan tới Hợp đồng thông minh và Công nghệ Blockchain, một trong những kiến thức và công nghệ nổi đình nổi đám hiện nay, các bạn chú ý theo dõi.

Chúc các bạn thành công

Bài 57: Minh họa giải thuật gom cụm K-Means bằng toán học

K-Means là một trong những thuật toán phổ biến và được sử dụng rất nhiều trong kỹ thuật gom cụm. Ý tưởng chính của thuật toán này là tìm cách phân nhóm các đối tượng đã cho vào K cụm sao cho tổng khoảng cách giữa các đối tượng đến tâm nhóm là nhỏ nhất. Thường K là số cụm được xác định trước và thường lấy ý kiến của chuyên gia, K phải nguyên dương. Khoảng cách giữa các đối tượng thường được sử dụng là khoảng cách Euclid.

Trong toán học, khoảng cách Euclid (tiếng Anh: Euclidean distance) giữa hai điểm trong không gian Euclid là độ dài của đoạn thẳng nối hai điểm đó. Có thể tính nó từ tọa độ Descartes của hai điểm bằng cách sử dụng định lý Pythagoras, do đó còn có tên gọi khác là khoảng cách Pythagoras (tiếng Anh: Pythagorean distance).

Src: Wikipedia

Đầu vào:

•Tập các  đối tượng X = {xi| i = 1, 2, …, N},

•Số cụm: K

Đầu ra:

•Các cụm Ci ( i = 1 ÷ K) tách rời và hàm tiêu chuẩn E đạt giá trị tối thiểu.

Trong đó cj là trọng tâm của cụm Cj

Chu trình hoạt động thuật toán:

  • Thuật toán hoạt động trên 1 tập vectơ d chiều, tập dữ liệu X gồm N phần tử:

  X = {xi | i = 1, 2, …, N}

  • K-Mean lặp lại nhiều lần quá trình:
    • Gán dữ liệu.
    • Cập nhật lại vị trí trọng tâm.
  • Quá trình lặp dừng lại khi trọng tâm hội tụ và mỗi đối tượng là 1 bộ phận của 1 cụm.

Các bước thực hiện thuật toán:

Dựa vào chu trình và lưu đồ thuật toán, ta có thể diễn giải các bước thực hiện như sau:

Bước 1 – Khởi tạo

  Chọn K trọng tâm {ci} ngẫu nhiên hoặc theo tiêu chuẩn (i = 1÷K).

Bước 2 – Tính toán khoảng cách

Bước 3 – Cập nhật lại trọng tâm, ta thường tính giá trị trung bình

Bước 4 – Điều kiện dừng

Ví dụ gom cụm bằng K-Means với bảng dữ liệu sau (k=2) và giả sử r3, r5 là center mặc định:

Value 1Value 2Value 3Value 4Value 5Value 6Value 7Value 8Value 9Value 10Value 11
r1110.50.50000.50.2500
r2110.50.50000.50.2500
r30.50.5110000.330.3300
r40.50.5110000.330.3300
r500001110000
r600001110000
r700001110000
r80.50.50.330.3300010.250.170.17
r90.250.250.330.330000.2510.750.75
r1000000000.170.7511
r1100000000.170.7511

Sau đây là chi tiết các bước thực hiện giải thuật K-Means để gom cụm. Dùng Euclid để tính các khoảng cách:

Bước 1: Khởi tạo
Giả sử chọn ngẫu nhiên 2 Trọng tâm ban đầu: r3; r5

* Gọi V1 là véc tơ trọng tâm của C1: Ta tính được V1(0.5,0.5,1,1,0,0,0,0.33,0.33,0,0)
* Gọi V2 là véc tơ trọng tâm của C2: Ta tính được V2(0,0,0,0,1,1,1,0,0,0,0)

Bước 2: Tính khoảng cách từ các đỉnh tới các Vector trọng tâm:

– d([r1],V1)=1.02 < d([r1],V2)=2.41. Vậy ta gom [r1] vào cụm C1.

Lý giải:

Ta tính được d([r1],V1)=1.02 từ công thức sau:

Tương tự ta cũng tính được d([r1],V2)=2.41 từ công thức sau:

Tương tự ta tính được khoảng cách tới trọng tâm cho các đỉnh còn lại (Các bạn cần viết lại công thức chi tiết như minh họa ở trên):

– d([r2],V1)=1.02 < d([r2],V2)=2.41. Vậy ta gom [r2] vào cụm C1.
– d([r4],V1)=0 < d([r4],V2)=2.39.Vậy ta gom [r4] vào cụm C1.
– d([r6],V1)=2.39 > d([r6],V2)=0. Vậy ta gom [r6] vào cụm C2.
– d([r7],V1)=2.39 > d([r7],V2)=0. Vậy ta gom [r7] vào cụm C2.
– d([r8],V1)=1.18 < d([r8],V2)=2.2. Vậy ta gom [r8] vào cụm C1.
– d([r9],V1)=1.61 < d([r9],V2)=2.35. Vậy ta gom [r9] vào cụm C1
– d([r10],V1)=2.17 < d([r10],V2)=2.36. Vậy ta gom [r10] vào cụm C1
– d([r11],V1)=2.17 < d([r11],V2)=2.36. Vậy ta gom [r11] gom vào cụm C1

*Vậy ta có phân bố cụm lần 1:

U=1r1r2r3r4r5r6r7r8r9r10r11
C111110001111
C200001110000

Bước 3: Cập nhật lại vector trọng tâm
* Gọi V1 là véc tơ trọng tâm mới của C1:
Ta tính được V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36)

Lý giải trọng tâm mới được tính như sau:

Tương tự cho các thuộc tính và Vector còn lại.
* Gọi V2 là véc tơ trọng tâm mới của C2: Ta tính được V2(0,0,0,0,1,1,1,0,0,0,0)

Bước 4: Kiểm tra điều kiện dừng
Ta so sánh Vector trọng tâm mới được tạo ở bước 3 so với Vector trọng tâm cũ:
Tập Vector Trọng tâm cũ:
V1(0.5,0.5,1,1,0,0,0,0.33,0.33,0,0)

V2(0,0,0,0,1,1,1,0,0,0,0)
Tập Vector Trọng tâm Mới:
V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36)

V2(0,0,0,0,1,1,1,0,0,0,0)
Ta kiểm tra thấy trọng tâm bị thay đổi nên lặp lại bước 2.

Bước 2: Tính khoảng cách từ các đỉnh tới các Vector trọng tâm:
– d([r1],V1)=0.95 < d([r1],V2)=2.41. Vậy ta gom [r1] vào cụm C1.
– d([r2],V1)=0.95 < d([r2],V2)=2.41. Vậy ta gom [r2] vào cụm C1.
– d([r3],V1)=0.94 < d([r3],V2)=2.39. Vậy ta gom [r3] vào cụm C1.
– d([r4],V1)=0.94 < d([r4],V2)=2.39. Vậy ta gom [r4] vào cụm C1.
– d([r5],V1)=2.13 > d([r5],V2)=0 . Vậy ta gom [r5] vào cụm C2.
– d([r6],V1)=2.13 > d([r6],V2)=0. Vậy ta gom [r6] vào cụm C2.
– d([r7],V1)=2.13 > d([r7],V2)=0. Vậy ta gom [r7] vào cụm C2.
– d([r8],V1)=0.72 < d([r8],V2)=2.2. Vậy ta gom [r8] vào cụm C1.
– d([r9],V1)=0.84 < d([r9],V2)=2.35.  Vậy ta gom [r9] vào cụm C1.
– d([r10],V1)=1.34 < d([r10],V2)=2.36. Vậy ta gom [r10] vào cụm C1.
– d([r11],V1)=1.34 < d([r11],V2)=2.36. Vậy ta gom [r11] vào cụm C1.
*Vậy ta có phân bố cụm lần 2:

U=2r1r2r3r4r5r6r7r8r9r10r11
C111110001111
C200001110000

Bước 3: Cập nhật lại vector trọng tâm

* Gọi V1 là véc tơ trọng tâm mới của C1:

Ta tính được V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36)

* Gọi V2 là véc tơ trọng tâm mới của C2:

Ta tính được V2(0,0,0,0,1,1,1,0,0,0,0)

Bước 4: Kiểm tra điều kiện dừng
Ta so sánh Vector trọng tâm mới được tạo ở bước 3 so với Vector trọng tâm cũ:

Tập Vector Trọng tâm cũ:

V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36) ;V2(0,0,0,0,1,1,1,0,0,0,0)

Tập Vector Trọng tâm Mới:

V1(0.47,0.47,0.46,0.46,0,0,0,0.41,0.49,0.36,0.36);V2(0,0,0,0,1,1,1,0,0,0,0)

Vậy ta thấy U2 và U1 Không có sự thay đổi. Giải thuật K-Means kết thúc.

Cuối cùng ta được 2 cụm như sau:

Cụm 1 {r1;r2;r3;r4;r8;r9;r10;r11}

Cụm 2 {r5;r6;r7}

Bài tập tương tự: Cho bảng ma trận dữ liệu dưới đây, hãy giải thích từng bước quá trình K-Means hoạt động, với số cụm là 3, center vector khởi tạo ban đầu là Daisy, Vitor, Real

CustomerAttribute 1Attribute 2
John11
Peter1112
Daisy23
Case12
Ronie26
Vitor98
Rehm01
Tom1110
Bob02
Lie1011
Tide1012
Real74
Jassor56

Bài học sau Tui hướng dẫn cách sử dụng K-Means để gom cụm khách hàng theo thu nhập, độ tuổi và ngân sách chi tiêu. Chúng ta dùng phương pháp Elbow để tìm ra K Cụm tối ưu nhất để gom, trực quan hóa các cụm bằng 2D và 3D. Đồng thời phân loại chi tiết Khách hàng theo từng cụm để dễ dàng đưa ra các chính sách chăm sóc và tiếp thị phù hợp.

Các bạn chú ý theo dõi

Chúc các bạn thành công

Bài 56: Thống kê và Ứng dụng hồi quy tuyến tính đa biến – dự báo chi tiêu của khách hàng

Bài học này Tui hướng dẫn các bạn xây dựng mô hình máy học sử dụng hồi quy tuyến tính đa biến để thống kê và dự báo chi tiêu của khách hàng, sử dụng cơ sở dữ liệu “lecturer_retails” có Số lượng dữ liệu gồm 99457 dòng, dữ liệu tải ở đây: https://tranduythanh.com/datasets/PurchaseDataset.rar

Chương trình được tách ra làm 2 nhóm chức năng chính:

  1. Chức năng thống kê:
    • (1.1) Thống kê tỉ lệ mua hàng theo giới tính
    • (1.2) Thống kê số lượng mua hàng theo độ tuổi
    • (1.3) Thống kê số lượng mua hàng theo danh mục sản phẩm
    • (1.4) Thống kê trị giá hàng hóa bán được theo danh mục
    • (1.5) Thống kê lượng hàn bán ra theo độ tuổi và danh mục
    • (1.6) Thống kê số lượng giao dịch theo phương thức thanh toán
    • (1.7) Thống kê tỉ lệ bán hàng theo Trung tâm thương mại (Shopping Mall)
    • (1.8) Thống kê trị giá hàng hóa bán được theo danh mục và giới tính
    • (1.9) Thống kê tần suất mua hàng theo độ tuổi và giới tính
    • (1.10) Thống kê biến động doanh thu theo tháng
    • (1.11) Thống kê biến động doanh thu theo tháng và theo năm
  2. Chức năng huấn luyện mô hình máy học:
    • Dự báo biến động giá theo giới tính và độ tuổi
    • Dự báo biến động giá theo giới tính, độ tuổi và phương thức thanh toán

Giao diện phần mềm cho Thống kê tương tự như dưới đây:

Giao diện phần mềm cho Máy học tương tự như dưới đây:

Dữ liệu “lecturer_retails“, được lưu trong MySQL Server có cấu trúc như mô tả chi tiết dưới đây:

Cấu trúc bảng purchasehistory như sau:

  • invoice_no
  • customer_id
  • gender
  • age
  • category
  • quantity
  • price
  • payment_method
  • invoice_date
  • shopping_mall

Số lượng dữ liệu gồm 99457 dòng. Dataset các bạn tải ở link sau, và cần import vào MySQL Server của bạn trước:

https://tranduythanh.com/datasets/PurchaseDataset.rar

Các bạn giải nén ra, trong MySQL Workbench tạo Schema tên “lecturer_retails“, sau đó import dataset vào Schema này. Cách import dataset các bạn xem lại bài học 47 cách import và export dataset

Tạo dự án “MLBAProject” có cấu trúc như dưới đây:

Vì bài này dài, và nó tổng hợp nhiều kiến thức nên Tui giải thích tổng quan như này:

  • (1) Thư mục “Assets” là thư mục tạo ra để lưu trữ các mô hình máy học được train thành công, dĩ nhiên người sử dụng có thể lưu ở bất cứ nơi nào, nhưng đề xuất lưu vào đây để dễ quản lý
  • (2) Thư mục “Connectors” là thư mục lưu thư viện kết nối và tương tác cơ sở dữ liệu MySQL Server
  • (3) Thư mục “Images” là thư mục lưu trữ các hình ảnh để sử dụng cho phần mềm
  • (4) Thư mục “Models” là thư mục chưa các lớp thư viện liên quan tới thống kê và máy học
  • (5) Thư mục “Tests” Là thư mục chứa các tập tin mã lệnh để thử nghiệm các hàm thống kê và máy học trước khi sử dụng thực tế. Thư mục này bạn có thể bỏ qua
  • (6) Thư mục “UI” là thư mục chứa các file thiết kế giao diện và generate python code cho giao diện, cũng như đồ thị
  • (7) Thư mục “Utils” là thư mục chưa thư viện xử lý tập tin, dùng để lưu mô hình máy học và tải mô hình máy học
  • (8) Cuối cùng là tập tin “App.py” để thực thi chương trình.

Vì không có nhiều thời gian, Tui không giải thích hết được các hàm trong các mã lệnh dưới đây, tuy nhiên Tui có đặt tên hàm và tên biến có ý nghĩa, nên các bạn đọc vào có thể hiểu ngay chức năng của nó. Đồng thời các thông số kết nối cơ sở dữ liệu các bạn cần đổi lại cho đúng với trên máy tính của mình.

Bước 1: Tạo và viết mã lệnh cho “Connector.py” trong thư mục “Connectors

#python -m pip install mysql-connector-python
import mysql.connector
import traceback
import pandas as pd
class Connector:
    def __init__(self,server=None, port=None, database=None, username=None, password=None):
        self.server=server
        self.port=port
        self.database=database
        self.username=username
        self.password=password
    def connect(self):
        try:
            self.conn = mysql.connector.connect(
                host=self.server,
                port=self.port,
                database=self.database,
                user=self.username,
                password=self.password)
            return self.conn
        except:
            self.conn=None
            traceback.print_exc()
        return None

    def disConnect(self):
        if self.conn != None:
            self.conn.close()

    def queryDataset(self, sql):
        try:
            cursor = self.conn.cursor()
            cursor.execute(sql)
            df = pd.DataFrame(cursor.fetchall())
            if not df.empty:
                df.columns=cursor.column_names
            return df
        except:
            traceback.print_exc()
        return None
    def getTablesName(self):
        cursor = self.conn.cursor()
        cursor.execute("Show tables;")
        results=cursor.fetchall()
        tablesName=[]
        for item in results:
            tablesName.append([tableName for tableName in item][0])
        return tablesName

Thư viện trên dùng để kết nối, đóng kết nối, truy vấn dữ liệu và danh sách các bảng trong cơ sở dữ liệu.

Bước 2: Trong thư mục “Models“, lần lượt tạo các tập tin mã lệnh Python sau:

Bước 2.1: Tạo lớp “MetricsResult.py” để lưu kết quả đánh giá mô hình, lớp này lưu các thông số: MAE, MSE, RMSE, R2_SCORE

class MetricsResult:
    def __init__(self,mae,mse,rmse,r2_score):
        self.MAE=mae
        self.MSE=mse
        self.RMSE=rmse
        self.R2_SCORE=r2_score
    def __str__(self):
        result="MAE=%s"%self.MAE+"\n"+"MSE=%s"%self.MSE+"\n"+"RMSE=%s"%self.RMSE+"\n"+"R2_SCORE=%s"%self.R2_SCORE+"\n"
        return result

Bước 2.2: Tạo lớp “TrainedModel.py” để lưu trữ mô hình máy học được trained (model) các dữ liệu của biến độc lập và biến phụ thuộc, cũng như tên các biến này. Mục đích để lưu và nạp lại mô hình theo chủ ý của ta.

class TrainedModel:
    def __init__(self,model=None,X_train=None,X_test=None,y_train=None,y_test=None,columns_input=None,column_target=None):
        self.model=model
        self.X_train = X_train
        self.X_test = X_test
        self.y_train = y_train
        self.y_test = y_test
        self.columns_input=columns_input
        self.column_target=column_target

Bước 2.3: Tạo lớp “PurchaseStatistic.py” lớp này phục vụ truy vấn và thống kê dữ liệu

from matplotlib import pyplot as plt
import seaborn as sns
import pandas as pd
class PurchaseStatistic:
    def __init__(self,connector=None):
        self.connector = connector
        self.lasted_df=None
    def execPurchaseHistory(self,tableName=None):
        if tableName==None:
            sql="select * from purchasehistory"
        else:
            sql = "select * from %s"%tableName
        self.df=self.connector.queryDataset(sql)
        self.lasted_df=self.df
        return self.df
    def printHead(self,row):
        print(self.df.head(row))
    def printTail(self,row):
        print(self.df.tail(row))
    def printInfo(self):
        print(self.df.info())
    def printDecsribe(self):
        print(self.df.describe())
    def dateProcessing(self):
        self.df['invoice_date'] = pd.to_datetime(self.df['invoice_date'] , format = '%d/%m/%Y')
        self.df['month'] = self.df['invoice_date'].dt.month
        self.df['year'] = self.df['invoice_date'].dt.year
        self.lasted_df = self.df
    def processGenderDistribution(self):
        self.dfGender = self.df.gender.value_counts().reset_index()
        self.lasted_df = self.dfGender
        return self.dfGender
    def processAgeDistribution(self):
        self.dfAges = self.df.age.value_counts().reset_index()
        self.dfAges.sort_values(by=['age'], ascending=True, inplace=True)
        self.lasted_df = self.dfAges
        return self.dfAges
    def processAgeDistribution(self,fromAge,toAge):
        self.dfAges = self.df[(self.df.age >= fromAge) & (self.df.age <= toAge)].age.value_counts().reset_index()
        self.dfAges.sort_values(by=['age'], ascending=True,inplace=True)
        self.lasted_df = self.dfAges
        return self.dfAges
    def visualizePieChart(self,df,columnLabel,columnStatistic,title,legend=True):
        explode=[0.1]
        for i in range(len(df[columnLabel])-1):
            explode.append(0)
        plt.figure(figsize=(8, 6))
        plt.pie(df[columnStatistic], labels=df[columnLabel], autopct='%1.2f%%',explode=explode)
        if legend:
            plt.legend(df[columnLabel])
        plt.title(title)
        plt.show()
    def visualizePlotChart(self,df,columnX,columnY,title):
        plt.figure(figsize=(8, 6))
        plt.plot(df[columnX], df[columnY])
        plt.legend([columnX,columnY])
        plt.title(title)
        plt.xlabel(columnX)
        plt.ylabel(columnY)
        plt.grid()
        plt.show()
    def processCategoryDistribution(self):
        self.dfCategory = self.df.category.value_counts().reset_index()
        self.lasted_df = self.dfCategory
        return self.dfCategory
    def processGenderAndCategoryCounter(self):
        self.df_gender_order = self.df[['gender', 'category']]\
                                   .groupby(['gender', 'category'])\
                                   .value_counts()\
                                   .reset_index(name="count")
        self.lasted_df = self.df_gender_order
        return self.df_gender_order
    def processCategorySpending(self):
        self.df_cate_spending = self.df.groupby(['category'])["price"].sum().reset_index(name="price")
        self.lasted_df = self.df_cate_spending
        return self.df_cate_spending
    def processGenderCategorySpending(self):
        self.df_gender_cate_spending = self.df.groupby(['gender','category'])["price"].sum().reset_index(name="price")
        self.lasted_df = self.df_gender_cate_spending
        return self.df_gender_cate_spending
    def visualizeCountPlot(self,df,columnX,columnY,hueColumn,title):
        plt.figure(figsize=(8, 6))
        ax=sns.countplot(x=columnX,hue=hueColumn,data=df)
        plt.title(title)
        plt.xlabel(columnX)
        plt.ylabel(columnY)
        plt.grid()
        plt.legend()
        plt.show()
    def visualizeBarPlot(self,df,columnX,columnY,hueColumn,title,alpha=0.8,width=0.6):
        plt.figure(figsize=(8, 6))
        plt.ticklabel_format(useOffset=False, style='plain')
        ax=sns.barplot(data=df,x=columnX,y=columnY,hue=hueColumn,alpha=alpha,width=width)
        plt.title(title)
        plt.xlabel(columnX)
        plt.ylabel(columnY)
        plt.grid()
        plt.legend()
        plt.show()
    def visualizeBarChart(self,df,columnX,columnY,title):
        plt.figure(figsize=(8, 6))
        plt.ticklabel_format(useOffset=False, style='plain')
        plt.bar(df[columnX],df[columnY])
        plt.title(title)
        plt.xlabel(columnX)
        plt.ylabel(columnY)
        plt.grid()
        plt.show()
    def visualizeScatterPlot(self,df,columnX,columnY,title):
        plt.figure(figsize=(8, 6))
        plt.ticklabel_format(useOffset=False, style='plain')
        sns.scatterplot(data=df,x= columnX,y=columnY)
        plt.title(title)
        plt.xlabel(columnX)
        plt.ylabel(columnY)
        plt.grid()
        plt.show()
    def processPaymentMethod(self):
        self.payment = self.df['payment_method'].value_counts().reset_index(name="count").rename(columns={"index": "payment_method"})
        self.lasted_df = self.payment
        return self.payment
    def processShoppingMall(self):
        self.dfShoppingMall = self.df['shopping_mall'].value_counts().reset_index(name="count").rename(columns={"index": "shopping_mall"})
        self.lasted_df = self.dfShoppingMall
        return self.dfShoppingMall
    def processAgeOrderFrequence(self):
        #self.dfAgeGender = self.df.groupby(['age', 'gender'])['age'].value_counts().reset_index(name="count")
        self.dfAgeGender = self.df[['age', 'gender']].groupby(['age', 'gender']).value_counts().reset_index(name="count")
        self.lasted_df = self.dfAgeGender
        return self.dfAgeGender
    def processAgeSalesAmount(self):
        self.dfSalesAmount = self.df.copy(deep=True)
        self.dfSalesAmount['sales_amount'] = self.dfSalesAmount['quantity'] * self.dfSalesAmount['price']
        self.lasted_df = self.dfSalesAmount
        return self.dfSalesAmount
    def processMonthlySalesAmount(self):
        self.dfMonthlySalesAmount=self.df.copy(deep=True)
        self.dfMonthlySalesAmount['sales_amount'] = self.dfMonthlySalesAmount['quantity'] * self.dfMonthlySalesAmount['price']

        self.dfMonthlySalesAmount['invoice_date'] = pd.to_datetime(self.dfMonthlySalesAmount['invoice_date'],
                                            format='%d/%m/%Y')  # convert invoice date to date time format

        self.dfMonthlySalesAmount['month'] = self.dfMonthlySalesAmount['invoice_date'].dt.month

        self.dfMonthlySalesAmount = self.dfMonthlySalesAmount.groupby('month')['sales_amount'].sum().reset_index()
        self.lasted_df = self.dfMonthlySalesAmount
        return self.dfMonthlySalesAmount
    def visualizeLinePlotChart(self,df,columnX,columnY,tile,hue=None):
        plt.figure(figsize=(8, 6))
        plt.ticklabel_format(useOffset=False,style="plain")
        sns.lineplot(data=df,x=columnX, y=columnY, marker='o', color='orange',hue=hue)
        plt.xlabel(columnX)
        plt.ylabel(columnY)
        plt.title(tile)
        plt.legend(loc='upper right')
        plt.xticks(range(1, 13), ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])
        plt.show()
    def processMonthlyAndYearSalesAmount(self):
        self.dfMonthlyAndYearSalesAmount = self.df.copy(deep=True)
        self.dfMonthlyAndYearSalesAmount['invoice_date'] = pd.to_datetime(self.dfMonthlyAndYearSalesAmount['invoice_date'],
                                                                   format='%d/%m/%Y')
        self.dfMonthlyAndYearSalesAmount['month'] = self.dfMonthlyAndYearSalesAmount['invoice_date'].dt.month
        self.dfMonthlyAndYearSalesAmount['year'] = self.dfMonthlyAndYearSalesAmount['invoice_date'].dt.year
        self.dfMonthlyAndYearSalesAmount['sales_amount'] = self.dfMonthlyAndYearSalesAmount['quantity'] * self.dfMonthlyAndYearSalesAmount['price']
        self.dfMonthlyAndYearSalesAmount = self.dfMonthlyAndYearSalesAmount.groupby(['year', 'month'], as_index=False).agg({'sales_amount': 'sum'})
        self.lasted_df = self.dfMonthlyAndYearSalesAmount
        return self.dfMonthlyAndYearSalesAmount

Bước 2.4: Tạo lớp “PurchaseMLModel.py” lớp này cung cấp tiền xử lý và chuyển đổi dữ liệu (transformation) để phục vụ các mô hình máy học

# Features encoding
from sklearn.preprocessing import LabelEncoder
import seaborn as sns
from matplotlib import pyplot as plt

from Models.PurchaseStatistic import PurchaseStatistic


class PurchaseMLModel(PurchaseStatistic):
    def __init__(self,connector=None):
        super().__init__(connector)
        self.le = LabelEncoder()
    def processTransformByColumns(self,df,columns):
        for col in columns:
            x=df[col]
            df[col] = self.le.fit_transform(x)
    def processTransform(self):
        categorical_feature = ['gender', 'category', 'payment_method', 'shopping_mall']
        numerical_feature = ['age', 'quantity', 'month', 'year']
        dropping = ['customer_id', 'invoice_no', 'day', 'invoice_date']
        result = ['price']
        self.dfTransform=self.df.copy(deep=True)
        self.dfTransform[["day", "month", "year"]] = self.dfTransform["invoice_date"].str.split("/", expand=True)
        self.dfTransform.drop(dropping, axis=1, inplace=True)
        for col in categorical_feature:
            x=self.dfTransform[col]
            self.dfTransform[col] = self.le.fit_transform(x)
        return self.dfTransform
    def buildCorrelationMatrix(self,df):
        plt.figure(figsize=(8, 6))
        df_corr = df.corr(numeric_only=True)  # Generate correlation matrix
        ax = sns.heatmap(df_corr, annot=True)
        plt.show()
    

Bước 2.5: Tạo lớp “PurchaseLinearRegression.py” lớp này cung cấp chức năng train mô hình máy học với hồi quy đa biến:

import pandas as pd
from matplotlib import pyplot as plt

from sklearn.model_selection import train_test_split
#data modelling
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error,r2_score
from sklearn.preprocessing import StandardScaler, LabelEncoder

from Models.MetricsResult import MetricsResult
from Models.PurchaseMLModel import PurchaseMLModel
from Models.TrainedModel import TrainedModel
from Utils.FileUtil import FileUtil

class PurchaseLinearRegression(PurchaseMLModel):
    def __init__(self,connector=None):
        super().__init__(connector)
        self.le = LabelEncoder()
        self.sc_std = StandardScaler()
    def processTrain(self,columns_input,column_target,test_size,random_state):
        self.execPurchaseHistory()
        self.processTransform()
        print(self.dfTransform.columns)
        print(self.dfTransform.iloc[0])
        y = self.dfTransform[column_target]
        X = self.dfTransform[columns_input]
        print("X=",X)
        print("y=", y)
        self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(X, y, test_size=test_size, random_state=random_state)
        self.trainedmodel=TrainedModel()
        self.trainedmodel.X_train=self.X_train
        self.trainedmodel.X_test=self.X_test
        self.trainedmodel.y_train=self.y_train
        self.trainedmodel.y_test=self.y_test
        self.trainedmodel.columns_input=columns_input
        self.trainedmodel.column_target=column_target
        #self.sc_std = StandardScaler()
        self.X_train = self.sc_std.fit_transform(self.X_train)
        self.X_test = self.sc_std.transform(self.X_test)
        self.lr = LinearRegression()
        self.model = self.lr.fit(self.X_train, self.y_train)
        self.trainedmodel.model=self.model
    def visualizeActualAndPredictResult(self):
        plt.figure(figsize=(8, 6))
        plt.scatter(self.lr.predict(self.X_train), self.y_train)
        plt.xlabel('Predicted value of Y')
        plt.ylabel('Real value of Y')
        plt.show()
    def evaluate(self):
        pred = self.model.predict(self.X_test)
        mae=mean_absolute_error(self.y_test, pred)
        mse = mean_squared_error(self.y_test, pred, squared=True)
        rmse = mean_squared_error(self.y_test, pred, squared=False)
        r2score=r2_score(self.y_test, pred)
        return MetricsResult(mae,mse,rmse,r2score)
    def predictPriceFromGenderAndAge(self,gender,age):
        data_gender = {'gender': ["Male", "Female"]}
        df_gender = pd.DataFrame(data=data_gender)
        df_gender_transform = self.le.fit_transform(df_gender)
        col_gender = 0
        if gender == 'Male':
            col_gender = 0
        else:
            col_gender = 1
        data = [[df_gender_transform[col_gender], age]]
        input_transform = self.sc_std.transform(data)
        pred = self.predict(input_transform)
        return pred
    def predictPriceFromGenderAndAgeAndPayment(self,gender,age,payment_method):
        data_gender= {'gender': ["Male", "Female"]}
        df_gender=pd.DataFrame(data=data_gender)
        df_gender_transform=self.le.fit_transform(df_gender)
        data_payment_method={"payment_method":["Credit Card","Debit Card","Cash"]}
        df_payment_method=pd.DataFrame(data=data_payment_method)
        df_payment_method_transform=self.le.fit_transform(df_payment_method)
        col_gender=0
        if gender == 'Male':
            col_gender=0
        else:
            col_gender = 1
        col_payment=0
        if payment_method=="Credit Card":
            col_payment=0
        elif payment_method=="Debit Card":
            col_payment=1
        else:
            col_payment = 2
        data = [[df_gender_transform[col_gender], age,df_payment_method_transform[col_payment]]]

        input_transform = self.sc_std.transform(data)
        pred = self.predict(input_transform)
        return pred
    def predict(self,columns_input):
        pred = self.model.predict(columns_input)
        return pred
    def saveModel(self,fileName):
        ret=FileUtil.saveModel(self.trainedmodel,fileName)
        return ret
    def loadModel(self,fileName):
        self.trainedmodel=FileUtil.loadModel(fileName)
        self.sc_std.fit_transform(self.trainedmodel.X_train)
        self.model=self.trainedmodel.model
        return self.model

Bước 3: Tạo các lớp test các thư viện ở bước 2 trong thư mục “Tests

Bước 3.1: Tạo “AppStatistic.py” để test các hàm thống kê. Ta có thể chạy tập tin này độc lập để test:

from Connectors.Connector import Connector
from Models.PurchaseStatistic import PurchaseStatistic

connector=Connector(server="localhost",port=3306,database="lecturer_retails",username="root",password="@Obama123")
connector.connect()
pm=PurchaseStatistic()
pm.connector=connector
pm.execPurchaseHistory()
dfGender=pm.processGenderDistribution()
print(dfGender)
pm.visualizePieChart(dfGender,"gender","count","Gender Distribution")

dfAge=pm.processAgeDistribution(30,50)
print(dfAge)
pm.visualizePlotChart(dfAge,"age","count","Age Distribution 30~50")

dfCategory=pm.processCategoryDistribution()
print(dfCategory)
pm.visualizePieChart(dfCategory,"category","count","Categories Distribution",legend=False)

dfCateSpending=pm.processCategorySpending()
print(dfCateSpending)
pm.visualizeBarChart(dfCateSpending,"category","price","Distribution category and Spending")


dfGenderCategory=pm.processGenderAndCategoryCounter()
print(dfGenderCategory)
pm.visualizeCountPlot(pm.df,"category","count","gender","Distribution gender and category")


dfPayment=pm.processPaymentMethod()
print(dfPayment)
pm.visualizePieChart(dfPayment,"payment_method","count","Payment Distribution",legend=False)

dfShoppingMall=pm.processShoppingMall()
print(dfShoppingMall)
pm.visualizePieChart(dfShoppingMall,"shopping_mall","count","Shopping Mall Distribution",legend=False)

dfGenderCateSpending=pm.processGenderCategorySpending()
print(dfGenderCateSpending)
pm.visualizeBarPlot(dfGenderCateSpending,"category","price","gender","Male and Female category Total Price Spend")

dfAgeGender=pm.processAgeOrderFrequence()
print(dfAgeGender)
pm.visualizeScatterPlot(dfAgeGender,"age","count","Age VS Order Frequence")

dfMonthlySalesAmount=pm.processMonthlySalesAmount()
print(dfMonthlySalesAmount)
pm.visualizeLinePlotChart(dfMonthlySalesAmount,"month","sales_amount","Monthly Variation in Sales Amount")

dfMonthlyAndYearSalesAmount=pm.processMonthlyAndYearSalesAmount()
print(dfMonthlyAndYearSalesAmount)
pm.visualizeLinePlotChart(dfMonthlyAndYearSalesAmount,"month","sales_amount","Monthly Variation in Sales Amount Over Years",hue="year")

Bước 3.2: Tạo “AppModel.py” để test tiền xử lý và transformation. Ta có thể chạy tập tin này độc lập để test:

from Connectors.Connector import Connector
from Models.PurchaseMLModel import PurchaseMLModel

connector=Connector(server="localhost",port=3306,database="lecturer_retails",username="root",password="@Obama123")
connector.connect()
pm=PurchaseMLModel(connector)
pm.execPurchaseHistory()

dfTransform=pm.processTransform()
print(dfTransform.head())
pm.buildCorrelationMatrix(dfTransform)

Bước 3.3: Tạo “AppLinearRegression.py” để test train mô hình máy học, cũng như prediction. Ta có thể chạy tập tin này độc lập để test:

from Connectors.Connector import Connector
from Models.PurchaseLinearRegression import PurchaseLinearRegression

connector=Connector(server="localhost",port=3306,database="lecturer_retails",username="root",password="@Obama123")
connector.connect()
pm=PurchaseLinearRegression(connector=connector)
pm.processTrain(["gender","age"],"price",0.2,0)
#pm.processTrain(["gender","age","payment_method"],"price")
#pm.visualizeActualAndPredictResult()

eresult=pm.evaluate()
print(eresult)

gender="Male"
age=61
pred=pm.predictPriceFromGenderAndAge(gender,age)
print("Gender=%s and Age=%s=>Price=%s"%(gender,age,pred))

gender="Female"
age=61
pred=pm.predictPriceFromGenderAndAge(gender,age)
print("Gender=%s and Age=%s=>Price=%s"%(gender,age,pred))

print("------------------"*10)
pm=PurchaseLinearRegression()
pm.connector=connector
pm.processTrain(["gender","age","payment_method"],"price",0.2,0)

eresult=pm.evaluate()
print(eresult)

gender="Male"
age=61
payment="Credit Card"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))

gender="Male"
age=61
payment="Debit Card"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))

gender="Male"
age=61
payment="Cash"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))

ret=pm.saveModel("../Assets/LR_mymodel.zip")
print("ret save model=%s"%ret)

Bước 3.4: Tạo “TestLoadModel.py” để nạp mô hình từ ổ cứng , cũng như prediction. Ta có thể chạy tập tin này độc lập để test:

from Models.PurchaseLinearRegression import PurchaseLinearRegression

pm=PurchaseLinearRegression()
pm.loadModel("../Assets/TrainedModel_GenderAgePayment.zip")

gender="Female"
age=61
payment="Cash"
pred=pm.predictPriceFromGenderAndAgeAndPayment(gender,age,payment)
print("Gender=%s and Age=%s and payment=%s=>Price=%s"%(gender,age,payment,pred))
#Gender=Female and Age=61 and payment=Cash=>Price=[692.98688316]

Bước 4: Tạo lớp tập tin “FileUtils.py” trong thư mục Utils để lưu mô hình máy học và nạp mô hình máy học:

import pickle
import traceback
class FileUtil:
    @staticmethod
    def saveModel(model,filename):
        try:
            pickle.dump(model,open(filename,'wb'))
            return True
        except:
            traceback.print_exc()
            return False
    @staticmethod
    def loadModel(filename):
        try:
            model=pickle.load(open(filename,'rb'))
            return model
        except:
            traceback.print_exc()
            return None

Bước 5: Tạo các lớp thư viện trong thư mục “UI”

Bước 5.1: Tạo “ChartType.py” để xử lý các loại Chart thống kê trong dự án

from enum import Enum

class ChartType(Enum):
    PieChart=1
    LinePlotChart=2
    BarChart=3
    BarPlot=4
    MultiBarChart=5
    ScatterPlot=6

Bước 5.2: Tạo “ChartHandle.py” để xử lý vẽ biểu đồ thống kê

import seaborn as sns
class ChartHandle:
    def getExplode(self,df,columnLabel):
        explode = [0.1]
        for i in range(len(df[columnLabel]) - 1):
            explode.append(0)
        return explode
    def visualizePieChart(self,figure,canvas,df,columnLabel,columnStatistic,title,legend=True):
        explode=self.getExplode(df,columnLabel)
        figure.clear()
        ax = figure.add_subplot(111)
        ax.pie(df[columnStatistic], labels=df[columnLabel], autopct='%1.2f%%', explode=explode)
        if legend:
            ax.legend(df[columnLabel],loc ='lower right')
        ax.set_title(title)
        canvas.draw()
    def visualizeLinePlotChart(self,figure,canvas,df,columnX,columnY,tile,hue=None,xticks=False):
        figure.clear()
        ax = figure.add_subplot(111)
        ax.ticklabel_format(useOffset=False,style="plain")
        ax.grid()
        sns.lineplot(data=df,x=columnX, y=columnY, marker='o', color='orange',hue=hue)
        ax.set_xlabel(columnX)
        ax.set_ylabel(columnY)
        ax.set_title(tile)
        ax.legend(loc ='lower right')
        #ax.set_xticks(range(1, 13), ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])
        if xticks==True:
            ax.set_xticks(range(1, 13), ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'])
        canvas.draw()
    def visualizeBarChart(self,figure,canvas,df,columnX,columnY,title):
        figure.clear()
        ax = figure.add_subplot(111)
        ax.ticklabel_format(useOffset=False, style="plain")
        ax.grid()
        ax.bar(df[columnX],df[columnY])
        ax.set_title(title)
        ax.set_xlabel(columnX)
        ax.set_ylabel(columnY)
        canvas.draw()
    def visualizeBarPlot(self,figure,canvas,df,columnX,columnY,hueColumn,title,alpha=0.8,width=0.6):
        figure.clear()
        ax = figure.add_subplot(111)
        ax.ticklabel_format(useOffset=False, style="plain")
        ax.grid()
        ax=sns.barplot(data=df,x=columnX,y=columnY,hue=hueColumn,alpha=alpha,width=width)
        ax.set_title(title)
        ax.set_xlabel(columnX)
        ax.set_ylabel(columnY)
        canvas.draw()
    def visualizeMultiBarChart(self,figure,canvas,df,columnX,columnY,hueColumn,title):
        figure.clear()
        ax = figure.add_subplot(111)
        ax.ticklabel_format(useOffset=False, style="plain")
        ax.grid()
        sns.countplot(x=columnX, hue=hueColumn, data=df)
        ax.set_title(title)
        ax.set_xlabel(columnX)
        ax.set_ylabel(columnY)
        canvas.draw()
    def visualizeScatterPlot(self,figure,canvas,df,columnX,columnY,title):
        figure.clear()
        ax = figure.add_subplot(111)
        ax.ticklabel_format(useOffset=False, style="plain")
        ax.grid()
        sns.scatterplot(data=df,x= columnX,y=columnY)
        ax.set_title(title)
        ax.set_xlabel(columnX)
        ax.set_ylabel(columnY)
        canvas.draw()

Bước 5.3: Tạo giao diện kết nối cơ sở dữ liệu “DatabaseConnect.ui

Thiết kế giao diện và đặt tên các Widget như trên.

Bước 5.4: Generate Python code cho “DatabaseConnect.ui“, ta có dữ liệu mã lệnh “DatabaseConnect.py“:

# Form implementation generated from reading ui file 'E:\Elearning\MLBAProject\UI\DatabaseConnect.ui'
#
# Created by: PyQt6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(359, 333)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_logo.jpg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(20, 10, 321, 271))
        self.groupBox.setObjectName("groupBox")
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setGeometry(QtCore.QRect(20, 30, 51, 16))
        self.label.setObjectName("label")
        self.lineEditServer = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditServer.setGeometry(QtCore.QRect(80, 30, 201, 22))
        self.lineEditServer.setObjectName("lineEditServer")
        self.lineEditPort = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditPort.setGeometry(QtCore.QRect(80, 60, 201, 22))
        self.lineEditPort.setObjectName("lineEditPort")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_2.setGeometry(QtCore.QRect(40, 60, 31, 16))
        self.label_2.setObjectName("label_2")
        self.lineEditDatabase = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditDatabase.setGeometry(QtCore.QRect(80, 100, 201, 22))
        self.lineEditDatabase.setObjectName("lineEditDatabase")
        self.label_3 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_3.setGeometry(QtCore.QRect(10, 100, 61, 16))
        self.label_3.setObjectName("label_3")
        self.lineEditUser = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditUser.setGeometry(QtCore.QRect(80, 140, 201, 22))
        self.lineEditUser.setObjectName("lineEditUser")
        self.label_4 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_4.setGeometry(QtCore.QRect(30, 140, 41, 16))
        self.label_4.setObjectName("label_4")
        self.lineEditPassword = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditPassword.setGeometry(QtCore.QRect(80, 180, 201, 22))
        self.lineEditPassword.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
        self.lineEditPassword.setObjectName("lineEditPassword")
        self.label_5 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_5.setGeometry(QtCore.QRect(10, 180, 61, 20))
        self.label_5.setObjectName("label_5")
        self.pushButtonConnect = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonConnect.setGeometry(QtCore.QRect(40, 220, 111, 31))
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_connect.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonConnect.setIcon(icon1)
        self.pushButtonConnect.setObjectName("pushButtonConnect")
        self.pushButtonClose = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonClose.setGeometry(QtCore.QRect(190, 220, 101, 31))
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_shutdown.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonClose.setIcon(icon2)
        self.pushButtonClose.setObjectName("pushButtonClose")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 359, 26))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        self.pushButtonClose.clicked.connect(MainWindow.close) # type: ignore
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh"))
        self.groupBox.setTitle(_translate("MainWindow", "Connection Setting:"))
        self.label.setText(_translate("MainWindow", "Server:"))
        self.lineEditServer.setText(_translate("MainWindow", "localhost"))
        self.lineEditPort.setText(_translate("MainWindow", "3306"))
        self.label_2.setText(_translate("MainWindow", "Port:"))
        self.lineEditDatabase.setText(_translate("MainWindow", "lecturer_retails"))
        self.label_3.setText(_translate("MainWindow", "Database:"))
        self.lineEditUser.setText(_translate("MainWindow", "root"))
        self.label_4.setText(_translate("MainWindow", "User:"))
        self.lineEditPassword.setText(_translate("MainWindow", "@Obama123"))
        self.label_5.setText(_translate("MainWindow", "Password:"))
        self.pushButtonConnect.setText(_translate("MainWindow", "Connect"))
        self.pushButtonClose.setText(_translate("MainWindow", "Close"))
        self.pushButtonClose.setShortcut(_translate("MainWindow", "Esc"))

Bước 5.5: Tạo Python code kế “DatabaseConnectEx.py” thừa Ui_MainWindow của mã lệnh Generate Python code giao diện “DatabaseConnect.py” để xử lý sự kiện tương tác người dùng:

import traceback

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QMessageBox

from Connectors.Connector import Connector
from UI.DatabaseConnect import Ui_MainWindow


class DatabaseConnectEx(Ui_MainWindow):
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.pushButtonConnect.clicked.connect(self.connectDatabase)
    def connectDatabase(self):
        try:
            self.connector=Connector()
            self.connector.server=self.lineEditServer.text()
            self.connector.port=(int)(self.lineEditPort.text())
            self.connector.database=self.lineEditDatabase.text()
            self.connector.username=self.lineEditUser.text()
            self.connector.password=self.lineEditPassword.text()
            self.connector.connect()
            self.msg=QMessageBox()
            self.msg.setText("Connect database successful!")
            self.msg.setWindowTitle("Info")
            #self.msg.show()
            self.MainWindow.close()
            if self.parent!=None:
                self.parent.checkEnableWidget(True)
        except:
            traceback.print_exc()
            self.msg = QMessageBox()
            self.msg.setText("Connect database failed")
            self.msg.setWindowTitle("Info")
            self.msg.show()
    def show(self):
        self.MainWindow.setWindowModality(Qt.WindowModality.ApplicationModal)
        self.MainWindow.show()

Bước 5.6: Tạo giao diện chính của dự án để thực hiện chức năng thống kê và máy học “MainWindow.ui“:

Thiết kế giao diện và đặt tên các Widget như trên.

Bước 5.7: Generate Python code cho “MainWindow.ui“, ta có dữ liệu mã lệnh “MainWindow.py“:

# Form implementation generated from reading ui file 'E:\Elearning\MLBAProject\UI\MainWindow.ui'
#
# Created by: PyQt6 UI code generator 6.6.1
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt6 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1279, 852)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_logo.jpg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.tabWidget = QtWidgets.QTabWidget(parent=self.centralwidget)
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.tab)
        self.verticalLayout.setObjectName("verticalLayout")
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.groupBox_3 = QtWidgets.QGroupBox(parent=self.tab)
        self.groupBox_3.setMinimumSize(QtCore.QSize(0, 200))
        self.groupBox_3.setMaximumSize(QtCore.QSize(16777215, 200))
        self.groupBox_3.setBaseSize(QtCore.QSize(0, 200))
        self.groupBox_3.setObjectName("groupBox_3")
        self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox_3)
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.tableWidgetStatistic = QtWidgets.QTableWidget(parent=self.groupBox_3)
        self.tableWidgetStatistic.setMinimumSize(QtCore.QSize(0, 180))
        self.tableWidgetStatistic.setMaximumSize(QtCore.QSize(16777215, 180))
        self.tableWidgetStatistic.setBaseSize(QtCore.QSize(0, 180))
        self.tableWidgetStatistic.setObjectName("tableWidgetStatistic")
        self.tableWidgetStatistic.setColumnCount(2)
        self.tableWidgetStatistic.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidgetStatistic.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidgetStatistic.setHorizontalHeaderItem(1, item)
        self.verticalLayout_4.addWidget(self.tableWidgetStatistic)
        self.gridLayout.addWidget(self.groupBox_3, 0, 1, 1, 1)
        self.groupBox_4 = QtWidgets.QGroupBox(parent=self.tab)
        self.groupBox_4.setObjectName("groupBox_4")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupBox_4)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.verticalLayoutPlot = QtWidgets.QVBoxLayout()
        self.verticalLayoutPlot.setObjectName("verticalLayoutPlot")
        self.verticalLayout_3.addLayout(self.verticalLayoutPlot)
        self.gridLayout.addWidget(self.groupBox_4, 1, 1, 1, 1)
        self.groupBox = QtWidgets.QGroupBox(parent=self.tab)
        self.groupBox.setMinimumSize(QtCore.QSize(400, 0))
        self.groupBox.setMaximumSize(QtCore.QSize(400, 16777215))
        self.groupBox.setBaseSize(QtCore.QSize(400, 0))
        self.groupBox.setObjectName("groupBox")
        self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.groupBox)
        self.verticalLayout_6.setObjectName("verticalLayout_6")
        self.verticalLayoutFunctions = QtWidgets.QVBoxLayout()
        self.verticalLayoutFunctions.setObjectName("verticalLayoutFunctions")
        self.pushButtonPurchaseRatesByGender = QtWidgets.QPushButton(parent=self.groupBox)
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_gender.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPurchaseRatesByGender.setIcon(icon1)
        self.pushButtonPurchaseRatesByGender.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPurchaseRatesByGender.setObjectName("pushButtonPurchaseRatesByGender")
        self.verticalLayoutFunctions.addWidget(self.pushButtonPurchaseRatesByGender)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_2.setMaximumSize(QtCore.QSize(30, 16777215))
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        self.lineEditFromAge = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditFromAge.setMaximumSize(QtCore.QSize(30, 16777215))
        self.lineEditFromAge.setObjectName("lineEditFromAge")
        self.horizontalLayout.addWidget(self.lineEditFromAge)
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setMaximumSize(QtCore.QSize(30, 16777215))
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.lineEditToAge = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditToAge.setMaximumSize(QtCore.QSize(30, 16777215))
        self.lineEditToAge.setObjectName("lineEditToAge")
        self.horizontalLayout.addWidget(self.lineEditToAge)
        self.pushButtonPurchaseRatesByAgeGroup = QtWidgets.QPushButton(parent=self.groupBox)
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_age.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPurchaseRatesByAgeGroup.setIcon(icon2)
        self.pushButtonPurchaseRatesByAgeGroup.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPurchaseRatesByAgeGroup.setObjectName("pushButtonPurchaseRatesByAgeGroup")
        self.horizontalLayout.addWidget(self.pushButtonPurchaseRatesByAgeGroup)
        self.verticalLayoutFunctions.addLayout(self.horizontalLayout)
        self.pushButtonPurchaseCountingByCategory = QtWidgets.QPushButton(parent=self.groupBox)
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_category.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPurchaseCountingByCategory.setIcon(icon3)
        self.pushButtonPurchaseCountingByCategory.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPurchaseCountingByCategory.setObjectName("pushButtonPurchaseCountingByCategory")
        self.verticalLayoutFunctions.addWidget(self.pushButtonPurchaseCountingByCategory)
        self.pushButtonPurchaseValueByCategory = QtWidgets.QPushButton(parent=self.groupBox)
        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_cate_value.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPurchaseValueByCategory.setIcon(icon4)
        self.pushButtonPurchaseValueByCategory.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPurchaseValueByCategory.setObjectName("pushButtonPurchaseValueByCategory")
        self.verticalLayoutFunctions.addWidget(self.pushButtonPurchaseValueByCategory)
        self.pushButtonPurchaseByCategoryAndGender = QtWidgets.QPushButton(parent=self.groupBox)
        icon5 = QtGui.QIcon()
        icon5.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_cate_gender.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPurchaseByCategoryAndGender.setIcon(icon5)
        self.pushButtonPurchaseByCategoryAndGender.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPurchaseByCategoryAndGender.setObjectName("pushButtonPurchaseByCategoryAndGender")
        self.verticalLayoutFunctions.addWidget(self.pushButtonPurchaseByCategoryAndGender)
        self.pushButtonPaymentMethod = QtWidgets.QPushButton(parent=self.groupBox)
        icon6 = QtGui.QIcon()
        icon6.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_payment.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPaymentMethod.setIcon(icon6)
        self.pushButtonPaymentMethod.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPaymentMethod.setObjectName("pushButtonPaymentMethod")
        self.verticalLayoutFunctions.addWidget(self.pushButtonPaymentMethod)
        self.pushButtonPurchaseRatesByShoppingMall = QtWidgets.QPushButton(parent=self.groupBox)
        icon7 = QtGui.QIcon()
        icon7.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_shopping_mall.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPurchaseRatesByShoppingMall.setIcon(icon7)
        self.pushButtonPurchaseRatesByShoppingMall.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPurchaseRatesByShoppingMall.setObjectName("pushButtonPurchaseRatesByShoppingMall")
        self.verticalLayoutFunctions.addWidget(self.pushButtonPurchaseRatesByShoppingMall)
        self.pushButtonProductSpendingByGender = QtWidgets.QPushButton(parent=self.groupBox)
        icon8 = QtGui.QIcon()
        icon8.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_spending_gender.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonProductSpendingByGender.setIcon(icon8)
        self.pushButtonProductSpendingByGender.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonProductSpendingByGender.setObjectName("pushButtonProductSpendingByGender")
        self.verticalLayoutFunctions.addWidget(self.pushButtonProductSpendingByGender)
        self.pushButtonPurchaseFrequenceByAge = QtWidgets.QPushButton(parent=self.groupBox)
        icon9 = QtGui.QIcon()
        icon9.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_frequence_age.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPurchaseFrequenceByAge.setIcon(icon9)
        self.pushButtonPurchaseFrequenceByAge.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPurchaseFrequenceByAge.setObjectName("pushButtonPurchaseFrequenceByAge")
        self.verticalLayoutFunctions.addWidget(self.pushButtonPurchaseFrequenceByAge)
        self.pushButtonSalesFluctuationsByMonth = QtWidgets.QPushButton(parent=self.groupBox)
        icon10 = QtGui.QIcon()
        icon10.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_sales_monthly.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonSalesFluctuationsByMonth.setIcon(icon10)
        self.pushButtonSalesFluctuationsByMonth.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonSalesFluctuationsByMonth.setObjectName("pushButtonSalesFluctuationsByMonth")
        self.verticalLayoutFunctions.addWidget(self.pushButtonSalesFluctuationsByMonth)
        self.pushButtonSalesFlucuationsByYearAndMonth = QtWidgets.QPushButton(parent=self.groupBox)
        icon11 = QtGui.QIcon()
        icon11.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_sales_yearly_monthly.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonSalesFlucuationsByYearAndMonth.setIcon(icon11)
        self.pushButtonSalesFlucuationsByYearAndMonth.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonSalesFlucuationsByYearAndMonth.setObjectName("pushButtonSalesFlucuationsByYearAndMonth")
        self.verticalLayoutFunctions.addWidget(self.pushButtonSalesFlucuationsByYearAndMonth)
        self.verticalLayout_6.addLayout(self.verticalLayoutFunctions)
        self.gridLayout.addWidget(self.groupBox, 0, 0, 2, 1)
        self.verticalLayout.addLayout(self.gridLayout)
        icon12 = QtGui.QIcon()
        icon12.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_statistic_tab.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.tabWidget.addTab(self.tab, icon12, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.groupBox_2 = QtWidgets.QGroupBox(parent=self.tab_2)
        self.groupBox_2.setGeometry(QtCore.QRect(20, 10, 521, 100))
        self.groupBox_2.setMinimumSize(QtCore.QSize(150, 100))
        self.groupBox_2.setObjectName("groupBox_2")
        self.radioButtonGenderAge = QtWidgets.QRadioButton(parent=self.groupBox_2)
        self.radioButtonGenderAge.setGeometry(QtCore.QRect(20, 30, 201, 20))
        self.radioButtonGenderAge.setChecked(True)
        self.radioButtonGenderAge.setObjectName("radioButtonGenderAge")
        self.radioButtonGenderAgePayment = QtWidgets.QRadioButton(parent=self.groupBox_2)
        self.radioButtonGenderAgePayment.setGeometry(QtCore.QRect(20, 60, 291, 20))
        self.radioButtonGenderAgePayment.setObjectName("radioButtonGenderAgePayment")
        self.groupBox_6 = QtWidgets.QGroupBox(parent=self.tab_2)
        self.groupBox_6.setGeometry(QtCore.QRect(20, 210, 521, 151))
        self.groupBox_6.setObjectName("groupBox_6")
        self.label_7 = QtWidgets.QLabel(parent=self.groupBox_6)
        self.label_7.setGeometry(QtCore.QRect(20, 20, 61, 16))
        self.label_7.setObjectName("label_7")
        self.label_8 = QtWidgets.QLabel(parent=self.groupBox_6)
        self.label_8.setGeometry(QtCore.QRect(20, 50, 61, 16))
        self.label_8.setObjectName("label_8")
        self.label_9 = QtWidgets.QLabel(parent=self.groupBox_6)
        self.label_9.setGeometry(QtCore.QRect(20, 80, 61, 16))
        self.label_9.setObjectName("label_9")
        self.label_10 = QtWidgets.QLabel(parent=self.groupBox_6)
        self.label_10.setGeometry(QtCore.QRect(20, 110, 81, 16))
        self.label_10.setObjectName("label_10")
        self.lineEditMAE = QtWidgets.QLineEdit(parent=self.groupBox_6)
        self.lineEditMAE.setGeometry(QtCore.QRect(110, 20, 271, 22))
        self.lineEditMAE.setObjectName("lineEditMAE")
        self.lineEditMSE = QtWidgets.QLineEdit(parent=self.groupBox_6)
        self.lineEditMSE.setGeometry(QtCore.QRect(110, 50, 271, 22))
        self.lineEditMSE.setObjectName("lineEditMSE")
        self.lineEditRMSE = QtWidgets.QLineEdit(parent=self.groupBox_6)
        self.lineEditRMSE.setGeometry(QtCore.QRect(110, 80, 271, 22))
        self.lineEditRMSE.setObjectName("lineEditRMSE")
        self.lineEditR2SCore = QtWidgets.QLineEdit(parent=self.groupBox_6)
        self.lineEditR2SCore.setGeometry(QtCore.QRect(110, 110, 271, 22))
        self.lineEditR2SCore.setObjectName("lineEditR2SCore")
        self.pushButtonEvaluate = QtWidgets.QPushButton(parent=self.groupBox_6)
        self.pushButtonEvaluate.setGeometry(QtCore.QRect(390, 60, 111, 41))
        icon13 = QtGui.QIcon()
        icon13.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_evaluate.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonEvaluate.setIcon(icon13)
        self.pushButtonEvaluate.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonEvaluate.setObjectName("pushButtonEvaluate")
        self.groupBox_7 = QtWidgets.QGroupBox(parent=self.tab_2)
        self.groupBox_7.setGeometry(QtCore.QRect(20, 120, 521, 81))
        self.groupBox_7.setObjectName("groupBox_7")
        self.lineEditRandomState = QtWidgets.QLineEdit(parent=self.groupBox_7)
        self.lineEditRandomState.setGeometry(QtCore.QRect(120, 50, 261, 20))
        self.lineEditRandomState.setObjectName("lineEditRandomState")
        self.label_5 = QtWidgets.QLabel(parent=self.groupBox_7)
        self.label_5.setGeometry(QtCore.QRect(20, 50, 91, 16))
        self.label_5.setObjectName("label_5")
        self.label_4 = QtWidgets.QLabel(parent=self.groupBox_7)
        self.label_4.setGeometry(QtCore.QRect(350, 20, 21, 16))
        self.label_4.setObjectName("label_4")
        self.label_3 = QtWidgets.QLabel(parent=self.groupBox_7)
        self.label_3.setGeometry(QtCore.QRect(20, 20, 61, 16))
        self.label_3.setObjectName("label_3")
        self.lineEditTestSize = QtWidgets.QLineEdit(parent=self.groupBox_7)
        self.lineEditTestSize.setGeometry(QtCore.QRect(120, 20, 221, 20))
        self.lineEditTestSize.setObjectName("lineEditTestSize")
        self.pushButtonTrainModel = QtWidgets.QPushButton(parent=self.groupBox_7)
        self.pushButtonTrainModel.setGeometry(QtCore.QRect(390, 30, 111, 41))
        icon14 = QtGui.QIcon()
        icon14.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_trainmodel.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonTrainModel.setIcon(icon14)
        self.pushButtonTrainModel.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonTrainModel.setObjectName("pushButtonTrainModel")
        self.groupBox_8 = QtWidgets.QGroupBox(parent=self.tab_2)
        self.groupBox_8.setGeometry(QtCore.QRect(20, 370, 521, 71))
        self.groupBox_8.setObjectName("groupBox_8")
        self.label_11 = QtWidgets.QLabel(parent=self.groupBox_8)
        self.label_11.setGeometry(QtCore.QRect(10, 30, 41, 16))
        self.label_11.setObjectName("label_11")
        self.lineEditPath = QtWidgets.QLineEdit(parent=self.groupBox_8)
        self.lineEditPath.setGeometry(QtCore.QRect(50, 30, 281, 22))
        self.lineEditPath.setObjectName("lineEditPath")
        self.pushButtonSavePath = QtWidgets.QPushButton(parent=self.groupBox_8)
        self.pushButtonSavePath.setGeometry(QtCore.QRect(340, 30, 41, 28))
        self.pushButtonSavePath.setObjectName("pushButtonSavePath")
        self.pushButtonSaveModel = QtWidgets.QPushButton(parent=self.groupBox_8)
        self.pushButtonSaveModel.setGeometry(QtCore.QRect(390, 20, 111, 41))
        icon15 = QtGui.QIcon()
        icon15.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_savemodel.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonSaveModel.setIcon(icon15)
        self.pushButtonSaveModel.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonSaveModel.setObjectName("pushButtonSaveModel")
        self.groupBox_9 = QtWidgets.QGroupBox(parent=self.tab_2)
        self.groupBox_9.setGeometry(QtCore.QRect(20, 450, 521, 71))
        self.groupBox_9.setObjectName("groupBox_9")
        self.pushButtonLoadModel = QtWidgets.QPushButton(parent=self.groupBox_9)
        self.pushButtonLoadModel.setGeometry(QtCore.QRect(10, 20, 111, 41))
        icon16 = QtGui.QIcon()
        icon16.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_loadmodel.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonLoadModel.setIcon(icon16)
        self.pushButtonLoadModel.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonLoadModel.setObjectName("pushButtonLoadModel")
        self.lineEditLocationLoadTrainedModel = QtWidgets.QLineEdit(parent=self.groupBox_9)
        self.lineEditLocationLoadTrainedModel.setGeometry(QtCore.QRect(130, 30, 371, 22))
        self.lineEditLocationLoadTrainedModel.setObjectName("lineEditLocationLoadTrainedModel")
        self.groupBox_10 = QtWidgets.QGroupBox(parent=self.tab_2)
        self.groupBox_10.setGeometry(QtCore.QRect(20, 530, 521, 201))
        self.groupBox_10.setObjectName("groupBox_10")
        self.label_12 = QtWidgets.QLabel(parent=self.groupBox_10)
        self.label_12.setGeometry(QtCore.QRect(20, 20, 101, 16))
        self.label_12.setObjectName("label_12")
        self.lineEditGender = QtWidgets.QLineEdit(parent=self.groupBox_10)
        self.lineEditGender.setGeometry(QtCore.QRect(130, 20, 241, 22))
        self.lineEditGender.setObjectName("lineEditGender")
        self.lineEditAge = QtWidgets.QLineEdit(parent=self.groupBox_10)
        self.lineEditAge.setGeometry(QtCore.QRect(130, 50, 241, 22))
        self.lineEditAge.setObjectName("lineEditAge")
        self.label_13 = QtWidgets.QLabel(parent=self.groupBox_10)
        self.label_13.setGeometry(QtCore.QRect(20, 50, 101, 16))
        self.label_13.setObjectName("label_13")
        self.lineEditPaymentMethod = QtWidgets.QLineEdit(parent=self.groupBox_10)
        self.lineEditPaymentMethod.setGeometry(QtCore.QRect(130, 80, 241, 22))
        self.lineEditPaymentMethod.setObjectName("lineEditPaymentMethod")
        self.label_14 = QtWidgets.QLabel(parent=self.groupBox_10)
        self.label_14.setGeometry(QtCore.QRect(20, 80, 101, 16))
        self.label_14.setObjectName("label_14")
        self.pushButtonPredict = QtWidgets.QPushButton(parent=self.groupBox_10)
        self.pushButtonPredict.setGeometry(QtCore.QRect(130, 110, 111, 41))
        icon17 = QtGui.QIcon()
        icon17.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_prediction.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonPredict.setIcon(icon17)
        self.pushButtonPredict.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonPredict.setObjectName("pushButtonPredict")
        self.lineEditPredictedPrice = QtWidgets.QLineEdit(parent=self.groupBox_10)
        self.lineEditPredictedPrice.setGeometry(QtCore.QRect(130, 160, 241, 22))
        self.lineEditPredictedPrice.setObjectName("lineEditPredictedPrice")
        self.label_15 = QtWidgets.QLabel(parent=self.groupBox_10)
        self.label_15.setGeometry(QtCore.QRect(20, 160, 101, 16))
        self.label_15.setObjectName("label_15")
        icon18 = QtGui.QIcon()
        icon18.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_machinelearning_tab.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.tabWidget.addTab(self.tab_2, icon18, "")
        self.verticalLayout_2.addWidget(self.tabWidget)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1279, 26))
        self.menubar.setObjectName("menubar")
        self.menuSystem = QtWidgets.QMenu(parent=self.menubar)
        self.menuSystem.setAutoFillBackground(False)
        self.menuSystem.setTearOffEnabled(False)
        self.menuSystem.setSeparatorsCollapsible(True)
        self.menuSystem.setToolTipsVisible(True)
        self.menuSystem.setObjectName("menuSystem")
        self.menuHelp = QtWidgets.QMenu(parent=self.menubar)
        self.menuHelp.setObjectName("menuHelp")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionConnection = QtGui.QAction(parent=MainWindow)
        icon19 = QtGui.QIcon()
        icon19.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_connection.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.actionConnection.setIcon(icon19)
        self.actionConnection.setObjectName("actionConnection")
        self.actionSave_trained_Machine_Learning_Model = QtGui.QAction(parent=MainWindow)
        icon20 = QtGui.QIcon()
        icon20.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_save.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.actionSave_trained_Machine_Learning_Model.setIcon(icon20)
        self.actionSave_trained_Machine_Learning_Model.setObjectName("actionSave_trained_Machine_Learning_Model")
        self.actionLoad_trained_Machine_Learning_Model = QtGui.QAction(parent=MainWindow)
        icon21 = QtGui.QIcon()
        icon21.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_load.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.actionLoad_trained_Machine_Learning_Model.setIcon(icon21)
        self.actionLoad_trained_Machine_Learning_Model.setObjectName("actionLoad_trained_Machine_Learning_Model")
        self.actionExit = QtGui.QAction(parent=MainWindow)
        icon22 = QtGui.QIcon()
        icon22.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_shutdown.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.actionExit.setIcon(icon22)
        self.actionExit.setObjectName("actionExit")
        self.actionInstructions = QtGui.QAction(parent=MainWindow)
        icon23 = QtGui.QIcon()
        icon23.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_instruction.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.actionInstructions.setIcon(icon23)
        self.actionInstructions.setObjectName("actionInstructions")
        self.actionAbout = QtGui.QAction(parent=MainWindow)
        icon24 = QtGui.QIcon()
        icon24.addPixmap(QtGui.QPixmap("E:\\Elearning\\MLBAProject\\UI\\../Images/ic_about.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.actionAbout.setIcon(icon24)
        self.actionAbout.setObjectName("actionAbout")
        self.menuSystem.addAction(self.actionConnection)
        self.menuSystem.addSeparator()
        self.menuSystem.addAction(self.actionSave_trained_Machine_Learning_Model)
        self.menuSystem.addSeparator()
        self.menuSystem.addAction(self.actionLoad_trained_Machine_Learning_Model)
        self.menuSystem.addSeparator()
        self.menuSystem.addAction(self.actionExit)
        self.menuHelp.addAction(self.actionInstructions)
        self.menuHelp.addSeparator()
        self.menuHelp.addAction(self.actionAbout)
        self.menubar.addAction(self.menuSystem.menuAction())
        self.menubar.addAction(self.menuHelp.menuAction())

        self.retranslateUi(MainWindow)
        self.tabWidget.setCurrentIndex(0)
        self.actionExit.triggered.connect(MainWindow.close) # type: ignore
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        MainWindow.setTabOrder(self.tabWidget, self.pushButtonPurchaseRatesByGender)
        MainWindow.setTabOrder(self.pushButtonPurchaseRatesByGender, self.tableWidgetStatistic)
        MainWindow.setTabOrder(self.tableWidgetStatistic, self.lineEditFromAge)
        MainWindow.setTabOrder(self.lineEditFromAge, self.lineEditToAge)
        MainWindow.setTabOrder(self.lineEditToAge, self.pushButtonPurchaseRatesByAgeGroup)
        MainWindow.setTabOrder(self.pushButtonPurchaseRatesByAgeGroup, self.pushButtonPurchaseCountingByCategory)
        MainWindow.setTabOrder(self.pushButtonPurchaseCountingByCategory, self.pushButtonPurchaseValueByCategory)
        MainWindow.setTabOrder(self.pushButtonPurchaseValueByCategory, self.pushButtonPurchaseByCategoryAndGender)
        MainWindow.setTabOrder(self.pushButtonPurchaseByCategoryAndGender, self.pushButtonPaymentMethod)
        MainWindow.setTabOrder(self.pushButtonPaymentMethod, self.pushButtonPurchaseRatesByShoppingMall)
        MainWindow.setTabOrder(self.pushButtonPurchaseRatesByShoppingMall, self.pushButtonProductSpendingByGender)
        MainWindow.setTabOrder(self.pushButtonProductSpendingByGender, self.pushButtonPurchaseFrequenceByAge)
        MainWindow.setTabOrder(self.pushButtonPurchaseFrequenceByAge, self.pushButtonSalesFluctuationsByMonth)
        MainWindow.setTabOrder(self.pushButtonSalesFluctuationsByMonth, self.pushButtonSalesFlucuationsByYearAndMonth)
        MainWindow.setTabOrder(self.pushButtonSalesFlucuationsByYearAndMonth, self.lineEditTestSize)
        MainWindow.setTabOrder(self.lineEditTestSize, self.lineEditRandomState)
        MainWindow.setTabOrder(self.lineEditRandomState, self.pushButtonTrainModel)
        MainWindow.setTabOrder(self.pushButtonTrainModel, self.lineEditMAE)
        MainWindow.setTabOrder(self.lineEditMAE, self.lineEditMSE)
        MainWindow.setTabOrder(self.lineEditMSE, self.lineEditRMSE)
        MainWindow.setTabOrder(self.lineEditRMSE, self.lineEditR2SCore)
        MainWindow.setTabOrder(self.lineEditR2SCore, self.pushButtonEvaluate)
        MainWindow.setTabOrder(self.pushButtonEvaluate, self.lineEditPath)
        MainWindow.setTabOrder(self.lineEditPath, self.pushButtonSavePath)
        MainWindow.setTabOrder(self.pushButtonSavePath, self.pushButtonSaveModel)
        MainWindow.setTabOrder(self.pushButtonSaveModel, self.pushButtonLoadModel)
        MainWindow.setTabOrder(self.pushButtonLoadModel, self.lineEditLocationLoadTrainedModel)
        MainWindow.setTabOrder(self.lineEditLocationLoadTrainedModel, self.lineEditGender)
        MainWindow.setTabOrder(self.lineEditGender, self.lineEditAge)
        MainWindow.setTabOrder(self.lineEditAge, self.lineEditPaymentMethod)
        MainWindow.setTabOrder(self.lineEditPaymentMethod, self.pushButtonPredict)
        MainWindow.setTabOrder(self.pushButtonPredict, self.lineEditPredictedPrice)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh"))
        self.groupBox_3.setTitle(_translate("MainWindow", "List of Data:"))
        item = self.tableWidgetStatistic.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Gender"))
        item = self.tableWidgetStatistic.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Number Sales"))
        self.groupBox_4.setTitle(_translate("MainWindow", "Chart Visualization:"))
        self.groupBox.setTitle(_translate("MainWindow", "Functions:"))
        self.pushButtonPurchaseRatesByGender.setText(_translate("MainWindow", "(1) Purchase rates by gender"))
        self.label_2.setText(_translate("MainWindow", "From:"))
        self.lineEditFromAge.setText(_translate("MainWindow", "30"))
        self.label.setText(_translate("MainWindow", "To:"))
        self.lineEditToAge.setText(_translate("MainWindow", "50"))
        self.pushButtonPurchaseRatesByAgeGroup.setText(_translate("MainWindow", "(2) Purchase rates by age group"))
        self.pushButtonPurchaseCountingByCategory.setText(_translate("MainWindow", "(3) Purchase counting by category"))
        self.pushButtonPurchaseValueByCategory.setText(_translate("MainWindow", "(4) Purchase value by category"))
        self.pushButtonPurchaseByCategoryAndGender.setText(_translate("MainWindow", "(5) Purchases by category and gender"))
        self.pushButtonPaymentMethod.setText(_translate("MainWindow", "(6) Payment method: Cash, Credit, Debit card"))
        self.pushButtonPurchaseRatesByShoppingMall.setText(_translate("MainWindow", "(7) Purchase rates by Shopping Mall"))
        self.pushButtonProductSpendingByGender.setText(_translate("MainWindow", "(8) Products spending by gender"))
        self.pushButtonPurchaseFrequenceByAge.setText(_translate("MainWindow", "(9) Purchase frequency by age"))
        self.pushButtonSalesFluctuationsByMonth.setText(_translate("MainWindow", "(10) Statistics on sales fluctuations by month"))
        self.pushButtonSalesFlucuationsByYearAndMonth.setText(_translate("MainWindow", "(11) Statistics on sales fluctuations by year and month"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Statistics:"))
        self.groupBox_2.setTitle(_translate("MainWindow", "Choose Independent and Dependent variables:"))
        self.radioButtonGenderAge.setText(_translate("MainWindow", "Gender, Age => Price"))
        self.radioButtonGenderAgePayment.setText(_translate("MainWindow", "Gender, Age, Payment Method=>Price"))
        self.groupBox_6.setTitle(_translate("MainWindow", "Evaluation"))
        self.label_7.setText(_translate("MainWindow", "MAE:"))
        self.label_8.setText(_translate("MainWindow", "MSE:"))
        self.label_9.setText(_translate("MainWindow", "RMSE:"))
        self.label_10.setText(_translate("MainWindow", "R2_SCORE:"))
        self.pushButtonEvaluate.setText(_translate("MainWindow", "Evaluate"))
        self.groupBox_7.setTitle(_translate("MainWindow", "Train Model:"))
        self.lineEditRandomState.setText(_translate("MainWindow", "0"))
        self.label_5.setText(_translate("MainWindow", "Random State:"))
        self.label_4.setText(_translate("MainWindow", "%"))
        self.label_3.setText(_translate("MainWindow", "Test Size:"))
        self.lineEditTestSize.setText(_translate("MainWindow", "20"))
        self.pushButtonTrainModel.setText(_translate("MainWindow", "Train Model"))
        self.groupBox_8.setTitle(_translate("MainWindow", "Save Trained Model"))
        self.label_11.setText(_translate("MainWindow", "Path:"))
        self.pushButtonSavePath.setText(_translate("MainWindow", "..."))
        self.pushButtonSaveModel.setText(_translate("MainWindow", "Save model"))
        self.groupBox_9.setTitle(_translate("MainWindow", "Load trained Model:"))
        self.pushButtonLoadModel.setText(_translate("MainWindow", "Load Model"))
        self.groupBox_10.setTitle(_translate("MainWindow", "Prediction:"))
        self.label_12.setText(_translate("MainWindow", "Gender:"))
        self.label_13.setText(_translate("MainWindow", "Age:"))
        self.label_14.setText(_translate("MainWindow", "Payment Method:"))
        self.pushButtonPredict.setText(_translate("MainWindow", "Predict"))
        self.label_15.setText(_translate("MainWindow", "Predicted Price:"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Machine Learning"))
        self.menuSystem.setTitle(_translate("MainWindow", "System"))
        self.menuHelp.setTitle(_translate("MainWindow", "Help"))
        self.actionConnection.setText(_translate("MainWindow", "Connect Database"))
        self.actionConnection.setShortcut(_translate("MainWindow", "Ctrl+D"))
        self.actionSave_trained_Machine_Learning_Model.setText(_translate("MainWindow", "Save trained ML Model"))
        self.actionSave_trained_Machine_Learning_Model.setShortcut(_translate("MainWindow", "Ctrl+S"))
        self.actionLoad_trained_Machine_Learning_Model.setText(_translate("MainWindow", "Load trained ML Model"))
        self.actionLoad_trained_Machine_Learning_Model.setShortcut(_translate("MainWindow", "Ctrl+O"))
        self.actionExit.setText(_translate("MainWindow", "Exit"))
        self.actionExit.setShortcut(_translate("MainWindow", "Esc"))
        self.actionInstructions.setText(_translate("MainWindow", "Instructions"))
        self.actionAbout.setText(_translate("MainWindow", "About"))

Bước 5.8: Tạo Python code kế “MainWindowEx.py” thừa Ui_MainWindow của mã lệnh Generate Python code giao diện “MainWindow.py” để xử lý sự kiện tương tác người dùng:

import random
from random import random
import plotly.graph_objects as go

from PyQt6 import QtGui, QtCore
from PyQt6.QtCore import Qt, QSize
from PyQt6.QtGui import QAction, QIcon, QPixmap
from PyQt6.QtWidgets import QMessageBox, QTableWidgetItem, QMainWindow, QDialog, QComboBox, QPushButton, QCheckBox, \
    QListWidgetItem, QFileDialog
from matplotlib import pyplot as plt
import seaborn as sns
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar

from Connectors.Connector import Connector
from Models.PurchaseLinearRegression import PurchaseLinearRegression
from Models.PurchaseStatistic import PurchaseStatistic
from UI.ChartHandle import ChartHandle
from UI.DatabaseConnectEx import DatabaseConnectEx
from UI.MainWindow import Ui_MainWindow
import traceback


import matplotlib

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure


from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import random


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        self.purchaseLinearRegression = PurchaseLinearRegression()
        self.databaseConnectEx=DatabaseConnectEx()
        self.databaseConnectEx.parent=self
        self.chartHandle= ChartHandle()
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.verticalLayoutFunctions.setAlignment(Qt.AlignmentFlag.AlignTop)
        self.setupPlot()

        self.actionConnection.triggered.connect(self.openDatabaseConnectUI)

        self.pushButtonPurchaseRatesByGender.clicked.connect(self.showPurchaseRatesByGender)
        self.pushButtonSalesFlucuationsByYearAndMonth.clicked.connect(self.showSalesFlucuationsByYearAndMonth)
        self.pushButtonPurchaseCountingByCategory.clicked.connect(self.showPurchaseCountingByCategory)
        self.pushButtonPurchaseRatesByAgeGroup.clicked.connect(self.showPurchaseRatesByAgeGroup)
        self.pushButtonPurchaseCountingByCategory.clicked.connect(self.showPurchaseCountingByCategory)
        self.pushButtonPurchaseValueByCategory.clicked.connect(self.showPurchaseValueByCategory)
        self.pushButtonPurchaseByCategoryAndGender.clicked.connect(self.showPurchaseByCategoryAndGender)
        self.pushButtonPaymentMethod.clicked.connect(self.showPaymentMethod)
        self.pushButtonPurchaseRatesByShoppingMall.clicked.connect(self.showPurchaseRatesByShoppingMall)
        self.pushButtonProductSpendingByGender.clicked.connect(self.showProductSpendingByGender)
        self.pushButtonPurchaseFrequenceByAge.clicked.connect(self.showShowPurchaseFrequenceByAge)
        self.pushButtonSalesFluctuationsByMonth.clicked.connect(self.showpushButtonSalesFluctuationsByMonth)
        self.checkEnableWidget(False)

        self.pushButtonTrainModel.clicked.connect(self.processTrainModel)
        self.pushButtonEvaluate.clicked.connect(self.processEvaluateTrainedModel)
        self.pushButtonSavePath.clicked.connect(self.processPickSavePath)
        self.pushButtonSaveModel.clicked.connect(self.processSaveTrainedModel)
        self.pushButtonLoadModel.clicked.connect(self.processLoadTrainedModel)
        self.pushButtonPredict.clicked.connect(self.processPrediction)
    def show(self):
        self.MainWindow.show()
    def checkEnableWidget(self,flag=True):
        self.pushButtonPurchaseRatesByGender.setEnabled(flag)
        self.pushButtonPurchaseRatesByAgeGroup.setEnabled(flag)
        self.pushButtonPurchaseCountingByCategory.setEnabled(flag)
        self.pushButtonPurchaseValueByCategory.setEnabled(flag)
        self.pushButtonPurchaseByCategoryAndGender.setEnabled(flag)
        self.pushButtonPaymentMethod.setEnabled(flag)
        self.pushButtonPurchaseRatesByShoppingMall.setEnabled(flag)

        self.pushButtonProductSpendingByGender.setEnabled(flag)
        self.pushButtonPurchaseFrequenceByAge.setEnabled(flag)
        self.pushButtonSalesFluctuationsByMonth.setEnabled(flag)
        self.pushButtonSalesFlucuationsByYearAndMonth.setEnabled(flag)

    def setupPlot(self):
        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        self.toolbar = NavigationToolbar(self.canvas, self.MainWindow)

        # adding tool bar to the layout
        self.verticalLayoutPlot.addWidget(self.toolbar)
        # adding canvas to the layout
        self.verticalLayoutPlot.addWidget(self.canvas)
    def openDatabaseConnectUI(self):
        dbwindow = QMainWindow()
        self.databaseConnectEx.setupUi(dbwindow)
        self.databaseConnectEx.show()
    def showDataIntoTableWidget(self,df):
        self.tableWidgetStatistic.setRowCount(0)
        self.tableWidgetStatistic.setColumnCount(len(df.columns))
        for i in range(len(df.columns)):
            columnHeader = df.columns[i]
            self.tableWidgetStatistic.setHorizontalHeaderItem(i, QTableWidgetItem(columnHeader))
        row = 0
        for item in df.iloc:
            arr = item.values.tolist()
            self.tableWidgetStatistic.insertRow(row)
            j=0
            for data in arr:
                self.tableWidgetStatistic.setItem(row, j, QTableWidgetItem(str(data)))
                j=j+1
            row = row + 1

    def showPurchaseCountingByCategory(self):
        self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        self.purchaseLinearRegression.execPurchaseHistory()
        self.purchaseLinearRegression.processCategoryDistribution()
        print(self.purchaseLinearRegression.dfCategory)

        df = self.purchaseLinearRegression.dfCategory

        self.showDataIntoTableWidget(df)

        columnLabel = "category"
        columnStatistic = "count"
        title = "Categories Distribution"
        legend = False
        #self.visualizePieChart(df, columnLabel, columnStatistic, title, legend)
        self.chartHandle.visualizePieChart(self.figure,self.canvas,df, columnLabel, columnStatistic, title, legend)

    def showPurchaseRatesByGender(self):
        self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        self.purchaseLinearRegression.execPurchaseHistory()
        self.purchaseLinearRegression.processGenderDistribution()
        print(self.purchaseLinearRegression.dfGender)

        df = self.purchaseLinearRegression.dfGender

        self.showDataIntoTableWidget(df)

        columnLabel = "gender"
        columnStatistic = "count"
        title = "Gender Distribution"
        legend = True
        self.chartHandle.visualizePieChart(self.figure,self.canvas,df, columnLabel, columnStatistic, title, legend)
        #self.visualizePieChart(df, columnLabel, columnStatistic, title, legend)

    def showSalesFlucuationsByYearAndMonth(self):
        self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        self.purchaseLinearRegression.execPurchaseHistory()
        self.purchaseLinearRegression.processMonthlyAndYearSalesAmount()
        print(self.purchaseLinearRegression.dfMonthlyAndYearSalesAmount)
        df = self.purchaseLinearRegression.dfMonthlyAndYearSalesAmount
        self.showDataIntoTableWidget(df)
        self.chartHandle.visualizeLinePlotChart(self.figure,self.canvas, self.purchaseLinearRegression.dfMonthlyAndYearSalesAmount, "month", "sales_amount",
                                    "Monthly Variation in Sales Amount Over Years", hue="year", xticks=True)
        #self.visualizeLinePlotChart(self.purchaseLinearRegression.dfMonthlyAndYearSalesAmount, "month", "sales_amount",
        #                            "Monthly Variation in Sales Amount Over Years", hue="year", xticks=True)
    def showPurchaseRatesByAgeGroup(self):
        self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        self.purchaseLinearRegression.execPurchaseHistory()
        fromAge=int(self.lineEditFromAge.text())
        toAge=int(self.lineEditToAge.text())
        self.purchaseLinearRegression.processAgeDistribution(fromAge,toAge)
        print(self.purchaseLinearRegression.dfAges)

        df = self.purchaseLinearRegression.dfAges

        self.showDataIntoTableWidget(df)
        columnLabel= "age"
        columnStatistic ="count"
        title= "Age Distribution %s~%s"%(fromAge,toAge)
        hue = None
        self.chartHandle.visualizeLinePlotChart(self.figure,self.canvas,df, columnLabel, columnStatistic,title, hue)
        #self.visualizeLinePlotChart(df, columnLabel, columnStatistic,title, hue)
    def showPurchaseCountingByCategory(self):
        self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        self.purchaseLinearRegression.execPurchaseHistory()
        df = self.purchaseLinearRegression.processCategoryDistribution()
        self.showDataIntoTableWidget(df)
        columnLabel = "category"
        columnStatistic = "count"
        title = "Categories Distribution"
        legend = False
        hue=None
        self.chartHandle.visualizeLinePlotChart(self.figure,self.canvas,df, columnLabel, columnStatistic, title, hue)
        #self.visualizePieChart(df, columnLabel, columnStatistic, title, legend)
    def showPurchaseValueByCategory(self):
        # self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        # self.purchaseLinearRegression.execPurchaseHistory()
        df = self.purchaseLinearRegression.processCategorySpending()
        self.showDataIntoTableWidget(df)
        columnLabel = "category"
        columnStatistic = "price"
        title = "Distribution category and Spending"
        self.chartHandle.visualizeBarChart(self.figure,self.canvas,df,columnLabel,columnStatistic,title)
        #self.visualizeBarChart(df,columnLabel,columnStatistic,title)
    def showPurchaseByCategoryAndGender(self):
        # self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        # self.purchaseLinearRegression.execPurchaseHistory()
        df = self.purchaseLinearRegression.processGenderAndCategoryCounter()
        self.showDataIntoTableWidget(df)
        df=self.purchaseLinearRegression.df
        columnLabel = "category"
        columnStatistic = "count"
        hue="gender"
        title = "Distribution gender and category"
        self.chartHandle.visualizeMultiBarChart(self.figure,self.canvas,df, columnLabel, columnStatistic,hue, title)
        #self.visualizeMultiBarChart(df, columnLabel, columnStatistic,hue, title)
    def showPaymentMethod(self):
        # self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        # self.purchaseLinearRegression.execPurchaseHistory()
        df = self.purchaseLinearRegression.processPaymentMethod()
        self.showDataIntoTableWidget(df)
        columnLabel = "payment_method"
        columnStatistic = "count"
        title = "Payment Distribution"
        legend = False
        self.chartHandle.visualizePieChart(self.figure,self.canvas,df, columnLabel, columnStatistic, title, legend)
        #self.visualizePieChart(df, columnLabel, columnStatistic, title, legend)
    def showPurchaseRatesByShoppingMall(self):
        # self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        # self.purchaseLinearRegression.execPurchaseHistory()
        df = self.purchaseLinearRegression.processShoppingMall()
        self.showDataIntoTableWidget(df)
        columnLabel = "shopping_mall"
        columnStatistic = "count"
        title = "Shopping Mall Distribution"
        legend = False
        self.chartHandle.visualizePieChart(self.figure,self.canvas,df, columnLabel, columnStatistic, title, legend)
        #self.visualizePieChart(df, columnLabel, columnStatistic, title, legend)
    def showProductSpendingByGender(self):
        # self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        # self.purchaseLinearRegression.execPurchaseHistory()
        df = self.purchaseLinearRegression.processGenderCategorySpending()
        self.showDataIntoTableWidget(df)
        columnLabel = "category"
        columnStatistic = "price"
        hue="gender"
        title = "Male and Female category Total Price Spend"
        legend = False
        self.chartHandle.visualizeBarPlot(self.figure,self.canvas,df, columnLabel, columnStatistic,hue, title)
        #self.visualizeBarPlot(df, columnLabel, columnStatistic,hue, title)

    def showShowPurchaseFrequenceByAge(self):
        # self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        # self.purchaseLinearRegression.execPurchaseHistory()
        df = self.purchaseLinearRegression.processAgeOrderFrequence()
        self.showDataIntoTableWidget(df)
        columnLabel = "age"
        columnStatistic = "count"
        title = "Age VS Order Frequence"
        self.chartHandle.visualizeScatterPlot(self.figure,self.canvas,df, columnLabel,columnStatistic, title)
        #self.visualizeScatterPlot(df, columnLabel,columnStatistic, title)

    def showpushButtonSalesFluctuationsByMonth(self):
        # self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        # self.purchaseLinearRegression.execPurchaseHistory()

        df=self.purchaseLinearRegression.processMonthlySalesAmount()
        print(df)

        self.showDataIntoTableWidget(df)
        columnLabel = "month"
        columnStatistic = "sales_amount"
        title = "Monthly Variation in Sales Amount"
        hue = None
        self.chartHandle.visualizeLinePlotChart(self.figure,self.canvas,df, columnLabel, columnStatistic, title, hue)
    def processTrainModel(self):
        columns_input=["gender","age"]
        column_target="price"
        if self.radioButtonGenderAgePayment.isChecked():
            columns_input=["gender","age","payment_method"]
        test_size=float(self.lineEditTestSize.text())/100
        random_state=int(self.lineEditRandomState.text())
        self.purchaseLinearRegression = PurchaseLinearRegression()
        self.purchaseLinearRegression.connector = self.databaseConnectEx.connector
        self.purchaseLinearRegression.processTrain(
            columns_input,
            column_target,
            test_size,
            random_state)
        dlg = QMessageBox(self.MainWindow)
        dlg.setWindowTitle("Info")
        dlg.setIcon(QMessageBox.Icon.Information)
        dlg.setText("Train machine learning model successful!")
        buttons = QMessageBox.StandardButton.Yes
        dlg.setStandardButtons(buttons)
        button = dlg.exec()
    def processEvaluateTrainedModel(self):
        result = self.purchaseLinearRegression.evaluate()
        self.lineEditMAE.setText(str(result.MAE))
        self.lineEditMSE.setText(str(result.MSE))
        self.lineEditRMSE.setText(str(result.RMSE))
        self.lineEditR2SCore.setText(str(result.R2_SCORE))
    def processPickSavePath(self):
        filters = "trained model file (*.zip);;All files(*)"
        filename, selected_filter = QFileDialog.getSaveFileName(
            self.MainWindow,
            filter=filters,
        )
        self.lineEditPath.setText(filename)
    def processSaveTrainedModel(self):
        trainedModelPath=self.lineEditPath.text()
        if trainedModelPath=="":
            return
        ret = self.purchaseLinearRegression.saveModel(trainedModelPath)
        dlg = QMessageBox(self.MainWindow)
        dlg.setWindowTitle("Info")
        dlg.setIcon(QMessageBox.Icon.Information)
        dlg.setText(f"Saved Trained machine learning model successful at [{trainedModelPath}]!")
        buttons = QMessageBox.StandardButton.Yes
        dlg.setStandardButtons(buttons)
        button = dlg.exec()
    def processLoadTrainedModel(self):
        # setup for QFileDialog
        filters = "trained model file (*.zip);;All files(*)"
        filename, selected_filter = QFileDialog.getOpenFileName(
            self.MainWindow,
            filter=filters,
        )
        if filename=="":
            return
        self.lineEditLocationLoadTrainedModel.setText(filename)
        self.purchaseLinearRegression.loadModel(filename)
        dlg = QMessageBox(self.MainWindow)
        dlg.setWindowTitle("Info")
        dlg.setIcon(QMessageBox.Icon.Information)
        dlg.setText(f"Load Trained machine learning model successful from [{filename}]!")
        buttons = QMessageBox.StandardButton.Yes
        dlg.setStandardButtons(buttons)
        button = dlg.exec()
    def processPrediction(self):
        gender = self.lineEditGender.text()
        age = int(self.lineEditAge.text())
        payment = self.lineEditPaymentMethod.text()
        if len(self.purchaseLinearRegression.trainedmodel.columns_input)==3:
            predicted_price = self.purchaseLinearRegression.predictPriceFromGenderAndAgeAndPayment(gender, age, payment)
        else:
            predicted_price = self.purchaseLinearRegression.predictPriceFromGenderAndAge(gender, age)
        self.lineEditPredictedPrice.setText(str(predicted_price[0]))

Bước 6: Tạo file thực thi “App.py

from PyQt6.QtWidgets import QApplication, QMainWindow

from UI.MainWindowEx import MainWindowEx

qApp=QApplication([])
qmainWindow=QMainWindow()
window=MainWindowEx()
window.setupUi(qmainWindow)
window.show()
qApp.exec()

Chạy App.py ta có kết quả:

Vào menu System chọn Connect Database:

Nhập thông số kết nối:

Bấm “Connect” để kết nối, nếu kết nối thành công ta có giao diện như dưới đây:

Các bạn thử 11 chức năng thông kế sẽ có các kết quả như mong muốn

(1.1) Thống kê tỉ lệ mua hàng theo giới tính

(1.2) Thống kê số lượng mua hàng theo độ tuổi


(1.3) Thống kê số lượng mua hàng theo danh mục sản phẩm


(1.4) Thống kê trị giá hàng hóa bán được theo danh mục


(1.5) Thống kê lượng hàn bán ra theo độ tuổi và danh mục


(1.6) Thống kê số lượng giao dịch theo phương thức thanh toán


(1.7) Thống kê tỉ lệ bán hàng theo Trung tâm thương mại (Shopping Mall)


(1.8) Thống kê trị giá hàng hóa bán được theo danh mục và giới tính


(1.9) Thống kê tần suất mua hàng theo độ tuổi và giới tính


(1.10) Thống kê biến động doanh thu theo tháng


(1.11) Thống kê biến động doanh thu theo tháng và theo năm

Về thử nghiệm chức năng máy học:

Như vậy tới đây Tui đã trình bày hoàn chỉnh dự án MLBAProject. dự án phục vụ thống kê và train mô hình máy học, dự án kết nối cơ sở dữ liệu, tổng hợp nhiều kiến thức.

Các bạn nhớ thực hành nhiều lần để hiểu rõ hơn về dự án.

Source code dự án đầy đủ các bạn tham khảo tại đây:

https://www.mediafire.com/file/e4nzjt8aaadr7f4/MLBAProject.rar/file

Bài học sau Tui hướng dẫn các bạn sử dụng K-Means để gom cụm dữ liệu, các bạn chú ý theo dõi. Chúc các bạn thành công

Bài 55: Phương trình hồi quy tuyến tính – Mô hình đa biến

Bài 53bài 54 các bạn đã hiểu rõ và thực hành thuần thục mô hình hồi quy đơn biến. Trong bài học này Tui trình bày về hồi quy đa biến – Multiple Linear Regression. Hầu hết các dự báo với bài toán hồi quy thường rơi vào mô hình đa biến. Ví dụ bài 54 ta tính giá nhà dựa trên mô hình đơn biến, nhưng trong thực tế giá nhà nó lệ thuộc vào nhiều yếu tố như Số phòng, số tầng, mặt tiền… Đó chính là dấu hiệu của đa biến.

Hay dự báo giá sản phẩm lệ thuộc vào màu sắc, chất lượng… đây cũng là dấu hiệu của đa biến.

  • Multiple Linear Regression: Mô hình hồi quy tuyến tính đa biến có nhiều hơn một biến độc lập, biểu diễn mối quan hệ tuyến tính giữa các biến độc lập và biến phụ thuộc.
  • Về cơ bản không có sự khác biệt giữa hồi quy tuyến tính ‘giản đơn’ và ‘đa biến’. Cả hai đều làm việc tuân theo nguyên tắc OLS –Ordinary Least Square và thuật toán để có được đường hồi quy tối ưu nhất cũng tương tự. Trong trường hợp sau, phương trình hồi quy sẽ có một hình dạng như sau:

Công thức chung:

Y=B0+B1*X1+B2*X2+B3*X3…..+Bn*Xn

  • Bi: Các hệ số khác nhau
  • Xi: Các biến độc lập khác nhau

Bài học này chúng ta dùng Microsoft Excel để lập trình các dữ kiện phục cho cho mô hình hồi quy đa biến.

Giả sử ta có tập dữ liệu như dưới đây:

ColorQualityPrice
7565
3738
5851
8138
9355
5443
4025
2633
8771
6451
9249

Quan sát tập dữ liệu trên gồm có 3 biến Color, Quality, Price.

  • Biến độc lập: Color, Quality
  • Biến phụ thuộc: Price

Dùng hồi quy tuyến tính để tính Price predicted, thực hiện các công thức trong Microsoft Excel.

Ta Cần tính được 4 đại lượng dưới đây:

  • (X’X) 
  • (X’X)_inv 
  • (X’Y)
  • B_hat

Sau đó lắp ráp công thức:

Y=B0+B1*X1+B2*X2+B3*X3…..+Bn*Xn

Ta bắt đầu thực hiện:

Bước 1: Chèn cột x0 có giá trị như hình dưới đây (mặc định là 1)

Bạn quan sát, dữ liệu gốc gồm có 3 cột trong Excel: Color, Quality, Price

Sau đó ta chèn x0 là cột đầu tiền trong ma trận dữ liệu mới

Bước 2: Tính (X’X)

Nhập công thức, lựa chọn các cột như hình và nhấn Enter

Lưu ý tích ma trận (X’X) sử dụng công thức MMULT trả về tích ma trận của 2 mảng, và TRANSPOSE chuyển vị ma trận.

Lý thuyết hàm TRANSPOSE:

https://support.microsoft.com/vi-vn/office/transpose-ha%CC%80m-transpose-ed039415-ed8a-4a81-93e9-4b6dfac76027

Lý thuyết hàm MMULT:

https://support.microsoft.com/vi-vn/office/mmult-ha%CC%80m-mmult-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb

Bạn quan sát thật kỹ địa chỉ ô Excel, nó tùy thuộc vào dữ liệu nhập mà địa chỉ sẽ khác nhau.

Nếu bạn nhập cấu trúc dữ liệu như Tui hướng dẫn, thì công thức không đổi.

Sau khi nhập công thức xong, thì nhấn phím Enter, ta có kết quả thường xuất hiện là #VALUE! (chú ý đây không phải lỗi)

Sau đó ta thực hiện tuần tự 3 bước như hình:

(01) #VALUE! Sẽ xuất hiện

(02) Bôi đen ma trận 3×3 vì có 3 biến độc lập bao gồm x0 (K, L, M), tổng cộng 9 ô

(03) Di chuyển trỏ chuột tới ô địa chỉ của Excel: Nhấn CTRL+SHIFT+ENTER

Sau khi nhấn CTRL+SHIFT+ENTER, ta có kết quả tích 2 ma trận (X’X):

Bước 3: Tính (X’X)_inv

Nhập công thức, lựa chọn các cột như hình và nhấn Enter

Ta dùng công thứ MINVERSE để tính (X’X)_inv. Hàm MINVERSE trả về ma trận nghịch đảo của một ma trận được lưu trữ trong một mảng.

Lý thuyết công thức MINVERSE:

https://support.microsoft.com/vi-vn/office/minverse-ha%CC%80m-minverse-11f55086-adde-4c9f-8eb9-59da2d72efc6

Sau khi nhập công thức MINVERSE và nhấn Enter, ta có kết quả:

Ta tiếp tục thực hiện các thao tác để Excel tính toán toàn bộ các ô dữ liệu cho ma trận nghịch đảo (X’X)_inv, thự hiện theo đúng thứ tự các bước sau:

(01) Giá trị sẽ xuất hiện

(02) Bôi đen ma trận 3×3 của inv (cột K, L, M), bôi đủ 9 Cell cho (X’X)_inv

(03) Di chuyển trỏ chuột tới ô địa chỉ của Excel: Nhấn CTRL+SHIFT+ENTER

Các bạn thực hiện đúng các bước theo hình trên,

Sau khi nhấn CTRL+SHIFT+ENTER, ta có kết quả:

Bước 4: Tính (X’Y)

Nhập công thức, lựa chọn các cột như hình và nhấn Enter

Lưu ý tích ma trận (X’Y) sử dụng công thức MMULT trả về tích ma trận của 2 mảng, và TRANSPOSE chuyển vị ma trận.

Lý thuyết hàm TRANSPOSE:

https://support.microsoft.com/vi-vn/office/transpose-ha%CC%80m-transpose-ed039415-ed8a-4a81-93e9-4b6dfac76027

Lý thuyết hàm MMULT:

https://support.microsoft.com/vi-vn/office/mmult-ha%CC%80m-mmult-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb

Sau khi nhập công thức và nhấn Enter, ta có kết quả:

Ta thấy Excel ra giá trị #VALUE!, lưu ý đây không phải lỗi.

Ta bôi đen 3 Cell của (X’Y) và thực hiện theo đúng thứ tự 3 bước sau:

(01) Giá trị sẽ xuất hiện

(02) Bôi đen cột K, bôi đủ 3 cell cho (X’Y)

(03) Di chuyển trỏ chuột tới ô địa chỉ của Excel: Nhấn CTRL+SHIFT+ENTER

Sau khi nhấn CTRL+SHIFT+ENTER, ta có kết quả:

Bước 5: Tính B_hat

Nhập công thức, lựa chọn các cột như hình và nhấn Enter

Lưu ý B_hat tính tích 2 ma trận (X’X)_inv và (X’Y), ta dùng công thức MMULT như trên, cùng với kéo các ma trận đúng địa chỉ ô Cell.

Sau khi nhận công thức hoàn tất, nhấn phím Enter, ta có kết quả:

Ta tiếp tục thực hiện đúng thứ tự các bước sau:

(01) Giá trị sẽ xuất hiện

(02) Bôi đen cột N, bôi đủ 3 cell cho B_hat

(03) Di chuyển trỏ chuột tới ô địa chỉ của Excel: Nhấn CTRL+SHIFT+ENTER

Sau khi nhấn CTRL+SHIFT+ENTER, ta có kết quả:

Bước 6: Lắp ráp công thức để tính:

Y=B0+B1*X1+B2*X2+B3*X3…..+Bn*Xn

Bổ sung thêm cột Predicted Price cho file excel, và lắp ráp công thức như dưới đây:

Bạn quan sát kỹ cách ráp công thức B_hat để nhân vào các biến độc lập cho cột Predicted Price.

  • N9 là B0
  • N10 là B1
  • N11 là B2

predicted price=N9+N10*F2+N11*G2

Các dấu $ là địa chỉ tuyệt đối vì B0, B1, B2 là cố định.

Sau khi nhấn Enter, bạn kéo công thức xuống hết các dòng dữ liệu để xem Predicted Price:

Bước 7: Kiểm tra sai số khi Prediction, sử dụng Square Error, MSE, RMSE:

Cột Squared Error Tui mới bổ sung ở trên, đơn giản chỉ là lấy giá trị thực – giá trị prediction. Nó phục vụ để tính MSE và RMSE

MSE tính theo công thức: Trung bình cộng của Squared Error

RMSE tính theo công thức: Là căn bậc 2 của MSE

Như vậy tới đây Tui đã hoàn tất việc hướng dẫn phương trình hồi quy tuyến tính với mô hình Đa Biến. các công thức thực hiện trên Microsoft Excel

các bạn cần làm lại nhiều lần để hiểu rõ lý thuyết vì sao sử dụng các ma trận, các thao tác trên ma trận ở trên để có thể vận dụng thực hiện hồi quy đa biến vào dự báo giá nhà.

Bạn có thể tải file Excel chi tiết Tui thực hiện ở đây, trong Excel có link và youtube hướng dẫn tham khảo:

https://www.mediafire.com/file/un0avja9x86mex7/Multiple-Linear-Regression.xlsx/file

Bài tập danh cho các bạn: Hãy vận dụng các kiến thức đã được học để thực hiện bài Hồi quy đa biến với tập dữ liệu dưới đây:

x1x2x3y
0.767.010.9433.52
4.2514.450.8442.89
5.7142.280.8312.04
3.5811.130.246.91
0.4530.486.57
0.1363.460.182.07
148.250.354.18
0.7624.80.3458.45
7.5613.850.5529.64
0.7650.460.4348.87
0.423.10.9433.75
2.9311.210.530.04
5.6418.110.0916.75
3.9321.560.434.63
0.511.20.7961.69
0.27.620.3324.55
1.0922.540.9432.9
1.9544.380.99.23
3.815.50.6111.4
5.4111.730.2927.64

– x1, x2,x3 là biến độc lập

– y là biến phụ thuộc

-Đánh giá chất lượng mô hình bằng MSE và RMSE

Bài học tiếp theo Tui hướng dẫn kết nối MySQL Server và thực hiện hồi quy tuyến tính trên tập dữ liệu retails. Các bạn chú ý theo dõi

Chúc các bạn thành công

Bài 54: Phương trình hồi quy tuyến tính – Mô hình đơn biến(tt)

Trong bài 53 Tui đã trình bày chi tiết công thức hồi quy đơn biến, cũng như giải thích cách tính chi tiết các thông số của công thức, thực hiện trên Excel và lập trình bằng Python, trực quan hóa kết quả thực hiện hồi quy đơn biến.

Để củng cố thêm kiến thức cũng như kỹ năng lập trình và ứng dụng hồi quy đơn biến, trong bài này Tui tiếp tục cung cấp một ví dụ minh họa về dự báo giá nhà áp dụng hồi quy đơn biến. Đặc biệt Tui minh họa thêm cách huấn luyện mô hình hồi quy đơn biến bằng Sklearn-Python. Như vậy trong ví dụ thứ 2 này chúng ta sẽ thực hành bằng 3 cách để dự báo giá nhà:

Cho tập dữ liệu như dưới đây:

xy
73.51.49
751.5
76.51.51
791.54
81.51.58
82.51.59
841.6
851.62
86.51.63
87.51.64
891.66
901.67
91.51.68
  • Tập dữ liệu gồm có 2 biến x, y. Biến độc lập là x (diện tích nhà), biến phụ thuộc là y (Giá nhà – tỉ VNĐ)
  • Dùng hồi quy tuyến tính để tính y predicted giá nhà

Cách 1: Tính toán hồi quy đơn biên trong Microsoft Excel

Như đã đề cập về cách tính toán các thông số cho phương trình hồi quy:

Y = Β0 + Β1*X

  1. Tính độ lệch chuẩn của biến độc lập x: Sử dụng Công thức STDEV
  2. Tính độ lệch chuẩn của biến phụ thuộc y: Sử dụng Công thức STDEV
  3. Tính trung bình của các biến độc lập x: Sử dụng Công thức AVERAGE
  4. Tính trung bình của các biến phụ thuộc y: Sử dụng công thức AVERAGE
  5. Tính độ tương quan giữa x và y: Sử dụng Công thức CORREL
  6. Tính B1: Sử dụng Công thức

 Độ Tương quan *( Độ lệch chuẩn của y / Độ lệch chuẩn của x)

7. Tính B0: Sử dụng công thức

  Trung bình (Y) – B1 * Trung bình (X)

Ta bắt đầu lắp ráp công thức trong Excel để tính 7 dữ kiện cho công thức hồi quy đơn biến như dưới đây:

Hình trên là công thức để tính 7 dữ kiện liên quan tới hồi quy đơn biến. Ta quan sát kết quả đạt được:

Sau khi có 7 dữ kiện, ta bổ sung cột y_predicted để lắp ráp vào công thức:

Y = Β0 + Β1*X

Ta xem công thức Tui thực hiện cho phương trình:

Bạn chú ý các ô Cell tính toán công thức nó lệ thuộc vào dữ liệu bạn nhập, nếu nhập theo đúng cấu trúc mà Tui chụp hình ở trên thì các địa chỉ cell không cần đổi, còn nếu bạn nhập khác với cấu trúc file Excel mà Tui cung cấp thì cần tham chiếu địa chỉ ô cell cho đúng.

Ta xem kết quả thực hiện y-predicted:

Ta quan sát thấy kết quả dự báo rất sát với kết quả thực tế, ta xem thêm trực quan hóa đồ thị:

Các bạn có thể tải file Excel cùng với các công thức mà Tui đã thực hiện ở đây:

https://www.mediafire.com/file/y9vbe6ka0085uzl/Simple-Linear-Regression-2.xlsx/file

Cách 2: Lập trình Python để tính toán các dữ kiện nhằm thực hiện công thức hồi quy đơn biến

Ta thực hiện mã lệnh Python để tính toán dựa trên toán học ra các kết quả b1, b0 như dưới đây:

import matplotlib.pyplot as plt
import numpy as np

# area
x = np.array([[73.5,75.,76.5,79.,81.5,82.5,84.,85.,86.5,87.5,89.,90.,91.5]]).T
# price
y = np.array([[1.49,1.50,1.51,1.54,1.58,1.59,1.60,1.62,1.63,1.64,1.66,1.67,1.68]]).T

def calculateb1b0(x,y):
  # tính trung bình
  xbar = np.mean(x)
  ybar = np.mean(y)
  x2bar = np.mean(x ** 2)
  xybar = np.mean(x * y)

  # tính b0, b1
  b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
  b0 = ybar - b1 * xbar
  return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)

Thực hiện lệnh trên ta có kết quả:

Ta quan sát kết quả, rất giống với cách thực hiện trong Excel.

Tiếp tục viết mã lệnh để trực quan hóa kết quả, so sánh giá trị thực và giá trị predicted:

# Visualize data
def showGraph(x, y,y_predicted, title="", xlabel="", ylabel=""):
  plt.figure(figsize=(14, 8))
  plt.plot(x, y, 'r-o', label="price")
  plt.plot(x, y_predicted, 'b-*', label="predicted value")
  x_min = np.min(x)
  x_max = np.max(x)
  y_min = np.min(y)
  y_max = np.max(y)
  # mean y
  ybar = np.mean(y)

  plt.axhline(ybar, linestyle='--', linewidth=4, label="mean")
  plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
  plt.xlabel(xlabel, fontsize=16)
  plt.ylabel(ylabel, fontsize=16)
  plt.text(x_min, ybar*1.01, "mean", fontsize=16)
  plt.legend(fontsize=15)
  plt.title(title, fontsize=20)
  plt.show()

showGraph(x, y,y_predicted,
          title='Giá nhà theo diện tích',
          xlabel='Diện tích (m2)',
          ylabel='Giá nhà (tỷ VND)')

Thực thi lệnh trên ta có kết quả:

Nhìn vào kết quả trực quan hóa, ta thấy mô hình hồi quy đơn biến cho ra kết quả prediction khá tương đồng với giá trị thực, tức là mô hình chất lượng. Tuy nhiên đây chỉ là đơn biến, giá nhà nó không chỉ lệ thuộc vào diện tích mà nó còn lệ thuộc vào rất nhiều yếu tố khác như: số phòng, số tầng, mặt tiền, các tiện ích xung quanh….

Các bạn có thể tải Source code Python tính toán hồi quy đơn biến ở đây:

https://www.mediafire.com/file/92f2p8fyfoc7gzx/SimpleLinearRegression2.py/file

Cách 3: Huấn luyện mô hình máy học cho mô hình hồi quy tuyến tính đơn biến

Cách này chúng ta dùng linear_model trong thư viện sklearn để huấn luyện mô hình hồi quy. Chúng ta sẽ so sánh cách lập trình Python theo công thức toán học và theo thư viện sklearn.

import matplotlib.pyplot as plt
import numpy as np
from sklearn import linear_model

# area
x = np.array([[73.5,75.,76.5,79.,81.5,82.5,84.,85.,86.5,87.5,89.,90.,91.5]]).T
# price
y = np.array([[1.49,1.50,1.51,1.54,1.58,1.59,1.60,1.62,1.63,1.64,1.66,1.67,1.68]]).T
# input matrix X
X = np.concatenate([x], axis = 1)

def calculateb1b0(x,y):
  # tính trung bình
  xbar = np.mean(x)
  ybar = np.mean(y)
  x2bar = np.mean(x ** 2)
  xybar = np.mean(x * y)

  # tính b0, b1
  b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
  b0 = ybar - b1 * xbar
  return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("Lập trình Python theo công thức toán học:")
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)

# fit the model by Linear Regression
# fit_intercept = False for calculating the bias
regr = linear_model.LinearRegression(fit_intercept=True)

regr.fit(X, y)
print("Lập trình Python theo mô hình huấn luyện máy học LinearRegression:")
# Compare two results
print('Coefficient : ', regr.coef_)
print('Interception  : ', regr.intercept_)

# Dự báo giá nhà ngay trên tập huấn luyện
ypred = regr.predict(X)
print(ypred)

Thực hiện lệnh trên ta có kết quả so sánh của 2 cách:

Quan sát các giá trị:

  • Theo công thức lập trình dựa trên tính toán toán học: b1, b0, y_predicted
  • Theo huấn luận mô hình máy học: Coefficient, Interception và ypred

Các giá trị này là tương đương nhau.

Như vậy rõ ràng, khi dùng thư viện huấn luyện mô hình máy học sẽ nhanh gọn lẹ hơn, vì ta chỉ cần học cách sử dụng thư viện (với 2 dòng lệnh là tính ra được b1(Coefficient), b0(Interception)).

Tiếp theo ta bổ sung mã lệnh để trực quan hóa kết quả:

# Visualize data
def showGraph(x, y_act, y_pred, title="", xlabel="", ylabel=""):
  plt.figure(figsize=(14, 8))
  plt.plot(x, y_act, 'r-o', label="price actual")
  plt.plot(x, y_pred, '--', label="price predict")
  x_min = np.min(x)
  x_max = np.max(x)
  y_min = np.min(y_act)
  y_max = np.max(y_act)
  # mean price
  ybar = np.mean(y_act)
  plt.axhline(ybar, linestyle='--', linewidth=4, label="mean actual")
  plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
  plt.xlabel(xlabel, fontsize=16)
  plt.ylabel(ylabel, fontsize=16)
  plt.text(x_min, ybar*1.01, "mean actual", fontsize=16)
  plt.legend(fontsize=15)
  plt.title(title, fontsize=20)
  plt.show()

showGraph(x, y,ypred,
          title='Dự báo Giá nhà theo diện tích',
          xlabel='Diện tích (m2)',
          ylabel='Giá nhà (tỷ VND)')

Thực thi lệnh trên ta có kết quả trực quan hoá:

Dưới đây là mã lệnh đầy đủ của huấn luyện mô hình máy học:

import matplotlib.pyplot as plt
import numpy as np
from sklearn import linear_model

# area
x = np.array([[73.5,75.,76.5,79.,81.5,82.5,84.,85.,86.5,87.5,89.,90.,91.5]]).T
# price
y = np.array([[1.49,1.50,1.51,1.54,1.58,1.59,1.60,1.62,1.63,1.64,1.66,1.67,1.68]]).T
# input matrix X
X = np.concatenate([x], axis = 1)

def calculateb1b0(x,y):
  # tính trung bình
  xbar = np.mean(x)
  ybar = np.mean(y)
  x2bar = np.mean(x ** 2)
  xybar = np.mean(x * y)

  # tính b0, b1
  b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
  b0 = ybar - b1 * xbar
  return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("Lập trình Python theo công thức toán học:")
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)

# fit the model by Linear Regression
# fit_intercept = False for calculating the bias
regr = linear_model.LinearRegression(fit_intercept=True)

regr.fit(X, y)
print("Lập trình Python theo mô hình huấn luyện máy học LinearRegression:")
# Compare two results
print('Coefficient : ', regr.coef_)
print('Interception  : ', regr.intercept_)

# Dự báo giá nhà ngay trên tập huấn luyện
ypred = regr.predict(X)
print(ypred)
# Visualize data
def showGraph(x, y_act, y_pred, title="", xlabel="", ylabel=""):
  plt.figure(figsize=(14, 8))
  plt.plot(x, y_act, 'r-o', label="price actual")
  plt.plot(x, y_pred, '--', label="price predict")
  x_min = np.min(x)
  x_max = np.max(x)
  y_min = np.min(y_act)
  y_max = np.max(y_act)
  # mean price
  ybar = np.mean(y_act)
  plt.axhline(ybar, linestyle='--', linewidth=4, label="mean actual")
  plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
  plt.xlabel(xlabel, fontsize=16)
  plt.ylabel(ylabel, fontsize=16)
  plt.text(x_min, ybar*1.01, "mean actual", fontsize=16)
  plt.legend(fontsize=15)
  plt.title(title, fontsize=20)
  plt.show()

showGraph(x, y,ypred,
          title='Dự báo Giá nhà theo diện tích',
          xlabel='Diện tích (m2)',
          ylabel='Giá nhà (tỷ VND)')

Như vậy tới đây Tui đã hướng dẫn đầy đủ ví dụ thứ 2, với 3 cách: Vừa thực hiện trong Excel, vừa lập trình Python theo công thức toán học để tính ra b0, b1 và predicted. Và cuối cùng là huấn luyện mô hình máy học theo thư viện sklearn. Cả 3 cách này đều cho kết quả như nhau, do đó nền tảng toán học rất quan trọng, nếu hiểu toán học để áp dụng thì việc sử dụng thư viện sẽ thuận tiện hơn vì ta đã hiểu được bản chất.

Source code của huấn luyện mô hình các bạn tải ở đây:

https://www.mediafire.com/file/8v5wwoy6hxf0ezd/MLLinearRegression1.py/file

Các bạn chú ý làm lại bài này nhiều lần

Bài học sau Tui sẽ trình bày về Hồi quy tuyến tính đa biến (Multiple Linear Regression), các Bạn chú ý theo dõi.

Chúc các bạn thành công

Bài 53: Phương trình hồi quy tuyến tính – Mô hình đơn biến

  • Phương trình hồi qui tuyến tính có rất nhiều ứng dụng trong thực tiễn và là một trong những lớp mô hình đặc biệt quan trọng trong machine learning.
  • Ứng dụng vào việc dự báo về nhu cầu thị trường của một doanh nghiệp để chuẩn bị kế hoạch sản suất kinh doanh. Trong tài chính chúng ta có thể dự báo giá chứng khoán và các chỉ số tài chính dựa trên hồi qui tuyến tính. Hay ta có thể ứng dụng dự báo chỉ số lạm phát, tốc độ tăng trưởng GDP của quốc gia…
  • Hầu hết các bài toán dự báo liên quan tới biến mục tiêu liên tục thì đều có thể sử dụng hồi qui tuyến tính để dự báo.

Bài học hồi quy tuyến tính đơn biến – Simple Linear Regression sẽ được Tui viết thành 2 bài Blog với 2 ví dụ khác nhau. Các bài học đều thử nghiệm trên Excel và trong Python để các bạn dễ dàng hiểu lý thuyết cũng như ứng dụng trong thực tế.

Sau khi trình bày lý thuyết xong, Tui sẽ hướng dẫn các bạn cách tính Hồi quy đơn biến bằng các công thức trong Microsoft Excel và sau đó là lập trình bằng Python:

(Nguồn: medium)

Ta có thể triển khai hồi quy tuyến tính(Linear Regression) bằng:

  • R linear regression.
  • MATLAB linear regression
  • Sklearn linear regression
  • Linear regression Python
  • Linear regression C#
  • Linear regression Java
  • Excel linear regression
  • Tự thực hiện theo các công thức toán học

Simple Linear Regression: Mô hình hồi quy tuyến tính đơn biến chỉ có một biến độc lập (input feature) mô tả mối quan hệ tuyến tính giữa biến phụ thuộc (output target) và biến độc lập. Công thức tổng quát:

Y = Β0 + Β1*X

Ý nghĩa của các biến và hằng số:

  • Y = Biến phụ thuộc
  • X = Biến độc lập
  • Β0 = Hằng số
  • Β1 = Hệ số mối quan hệ giữa X và Y

Một số đặc tính của hồi quy tuyến tính:

  • Đường hồi quy luôn luôn đi qua trung bình của biến độc lập x cũng như trung bình của biến phụ thuộc y
  • Đường hồi quy tối thiểu hóa tổng của “Diện tích các sai số“. Nên đôi khi được gọi là “Ordinary Least Square (OLS)
  • Β1 giải thích sự thay đổi trong Y với sự thay đổi X bằng một đơn vị. Nói cách khác, nếu chúng ta tăng giá trị của X bởi một đơn vị thì nó sẽ là sự thay đổi giá trị của Y

Để tính được giá trị của phương trình hồi quy đơn biến ta cần tính được các dữ kiện sau:

  • Độ lệch chuẩn của biến độc lập x
  • Độ lệch chuẩn của biến phụ thuộc y
  • Trung bình của các biến độc lập x
  • Trung bình của các biến phụ thuộc y
  • Độ tương quan giữa biến độc lập x và biến phục thuộc y

Y = Β0 + Β1*X

  • Tính B1
  • Cuối cùng là tính B0

Bây giờ chúng ta bắt đầu thực hiện hồi quy đơn biến trong Microsoft Excel, giả sử chúng ta có tập dữ liệu trong Excel như dưới đây:

xy
12
24
33
46
59
612
713
815
918
1020
  • Tập dữ liệu gồm có 2 biến x và y. Trong đó biến độc lập là x, biến phụ thuộc là y
    Dùng hồi quy tuyến tính để tính y predicted
  • Để cho nhanh và dễ hiểu, chúng ta thực hiện trong Excel trước, sau đó chúng ta sẽ lập trình bằng Python

Như đã đề cập về cách tính toán các thông số cho phương trình hồi quy:

Y = Β0 + Β1*X

  1. Tính độ lệch chuẩn của biến độc lập x: Sử dụng Công thức STDEV
  2. Tính độ lệch chuẩn của biến phụ thuộc y: Sử dụng Công thức STDEV
  3. Tính trung bình của các biến độc lập x: Sử dụng Công thức AVERAGE
  4. Tính trung bình của các biến phụ thuộc y: Sử dụng công thức AVERAGE
  5. Tính độ tương quan giữa x và y: Sử dụng Công thức CORREL
  6. Tính B1: Sử dụng Công thức

 Độ Tương quan *( Độ lệch chuẩn của y / Độ lệch chuẩn của x)

7. Tính B0: Sử dụng công thức

  Trung bình (Y) – B1 * Trung bình (X)

Ta bắt đầu lắp ráp công thức trong Excel để tính 7 dữ kiện cho công thức hồi quy đơn biến như dưới đây:

Các bạn quan sát Tui nhập các công thức tính toán các thông số, các bạn nhập chính xác như trên. Lưu ý các địa chỉ ô Excel tính toán nó lệ thuộc vào dữ liệu bạn nhập trong Excel, nếu nhập như bài học Tui đã soạn thì không cần đổi địa chỉ ô Excel.

Kết quả thực hiện sẽ hiển thị ra như dưới đây:

Sau khi có các kết quả rồi, ta bổ sung thêm một cột Y-Predicted để lắp ráp tính toán công thức:

Y = Β0 + Β1*X

Kết quả thực hiện phương trình hồi quy đa biến sẽ hiển thị như dưới đây:

Dựa vào cột y và y-predicted mà ta so sánh được kết quả giữa giá trị thực và giá trị dự báo:

Đường màu xanh là giá trị thực (y), đường màu cam là giá trị dự báo (y-predicted). Ta thấy kết quả dự báo khá sát với thực tế.

Bây giờ ta thử lập trình hồi quy đơn biến với Python:

import matplotlib.pyplot as plt
import numpy as np

x = np.array([[1,2,3,4,5,6,7,8,9,10]]).T
y = np.array([[2,4,3,6,9,12,13,15,18,20]]).T

def calculateb1b0(x,y):
  # tính trung bình
  xbar = np.mean(x)
  ybar = np.mean(y)
  x2bar = np.mean(x ** 2)
  xybar = np.mean(x * y)

  # tính b0, b1
  b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
  b0 = ybar - b1 * xbar
  return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)

Thực thi mã lệnh Python ở trên ta tính được b1 và b0 giống như Excel mà ta đã tính ở bên trên:

Tính được b1 b0 coi như đã hoàn thành được phương trình hồi quy đơn biến. Vì có b1 và b0 ta tính được:

y_predicted=b0+b1*x

So sánh kết quả y_predicted cũng giống như Excel đã tính.

Ta tiếp tục bổ sung mã lệnh để trực quan hóa dữ liệu:

# Visualize data
def showGraph(x, y,y_predicted, title="", xlabel="", ylabel=""):
  plt.figure(figsize=(14, 8))
  plt.plot(x, y, 'r-o', label="value sample")
  plt.plot(x, y_predicted, 'b-*', label="predicted value")
  x_min = np.min(x)
  x_max = np.max(x)
  y_min = np.min(y)
  y_max = np.max(y)
  # mean y
  ybar = np.mean(y)

  plt.axhline(ybar, linestyle='--', linewidth=4, label="mean")
  plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
  plt.xlabel(xlabel, fontsize=16)
  plt.ylabel(ylabel, fontsize=16)
  plt.text(x_min, ybar*1.01, "mean", fontsize=16)
  plt.legend(fontsize=15)
  plt.title(title, fontsize=20)
  plt.show()

showGraph(x, y,y_predicted,
      title='Giá trị Y theo X',
      xlabel='Giá trị X',
      ylabel='Giá trị Y')

Chạy mã lệnh trên ta có kết quả:

Như vậy cách tính hồi quy đơn biến trong Excel và trong Python là giống nhau.

Sourecode đầy đủ của hồi quy đơn biến đối với bài học này:

import matplotlib.pyplot as plt
import numpy as np

x = np.array([[1,2,3,4,5,6,7,8,9,10]]).T
y = np.array([[2,4,3,6,9,12,13,15,18,20]]).T

def calculateb1b0(x,y):
  # tính trung bình
  xbar = np.mean(x)
  ybar = np.mean(y)
  x2bar = np.mean(x ** 2)
  xybar = np.mean(x * y)

  # tính b0, b1
  b1 = (xbar * ybar - xybar) / (xbar ** 2 - (x2bar))
  b0 = ybar - b1 * xbar
  return b1,b0
#calulate b1, b0
b1,b0=calculateb1b0(x,y)
print("b1=",b1)
print("b0=",b0)
y_predicted=b0+b1*x
print(y_predicted)

# Visualize data
def showGraph(x, y,y_predicted, title="", xlabel="", ylabel=""):
  plt.figure(figsize=(14, 8))
  plt.plot(x, y, 'r-o', label="value sample")
  plt.plot(x, y_predicted, 'b-*', label="predicted value")
  x_min = np.min(x)
  x_max = np.max(x)
  y_min = np.min(y)
  y_max = np.max(y)
  # mean y
  ybar = np.mean(y)

  plt.axhline(ybar, linestyle='--', linewidth=4, label="mean")
  plt.axis([x_min*0.95, x_max*1.05, y_min*0.95, y_max*1.05])
  plt.xlabel(xlabel, fontsize=16)
  plt.ylabel(ylabel, fontsize=16)
  plt.text(x_min, ybar*1.01, "mean", fontsize=16)
  plt.legend(fontsize=15)
  plt.title(title, fontsize=20)
  plt.show()

showGraph(x, y,y_predicted,
      title='Giá trị Y theo X',
      xlabel='Giá trị X',
      ylabel='Giá trị Y')

Như vậy tới đây Tui đã hướng dẫn xong lý thuyết về hồi quy đơn biến, giải thích chi tiết các thông số trong công thức hồi quy đơn biến, cũng như các dữ kiện cần phải tính toán, và minh họa được cách tính hồi quy đơn biến trong Excel và bằng Python code.

Các bạn tải excel tính hồi quy đơn biến ở đây:

https://www.mediafire.com/file/5rtgfbvwbex819t/Simple-Linear-Regression-1.xlsx/file

Các bạn tải source code Python tính hồi quy đơn biến ở đây:

https://www.mediafire.com/file/172v8nq457on20i/SimpleLinearRegression1.py/file

Các bạn lưu ý thực hiện bài này nhiều lần, bài học sau Tui tiếp tục trình bày thêm một ví dụ về hồi quy đơn biến để dự báo giá nhà, cũng thực hiện trong Excel và trong Python code.

Chúc các bạn thành công

Bài 45: Thiết kế Website tương tác sử dụng mô hình máy học để dự báo giá nhà

Bài học này Tui sẽ trình bày cách tái sử dụng mô hình máy học đã được train trong bài 44 để sử dụng trong phiên bản Website này.

Rõ ràng việc train mô hình máy học nó tốn thời gian và cần nhiều tài nguyên, như vậy sau khi train xong thì ta chỉ tái sử dụng thôi, chứ không phải mỗi lần chạy phần mềm lên là train lại mô hình nó sẽ không tối ưu hệ thống.

Thông qua bài này, các bạn sẽ biết cách tái sử dụng mô hình máy học ở các nền tảng khác nhau. Ví dụ bài 44, Tui đã hướng dẫn cách train mô hình máy học trên giao diện Windows viết bằng Python Tkinter, tạo ra được mô hình máy học lưu lại với file zip định dạng pickle. Bài này thì sẽ dùng Web python Flask Microservice để triệu gọi mô hình máy học được train để để sử dụng (tức không phải thực hiện các bước chọn dataset, train mô hình).

Thông qua ví dụ này, bạn có thể viết các module để đính kèm vào hệ thống thương mại điện tử có sẵn để tích hợp mô hình máy học vào chạy trên nền web. Đưới đây là màn hình Website chúng ta sẽ thiết kế và sử dụng mô hình máy học:

Như vậy rõ ràng bạn phải làm bài 44 trước nha.

Và bắt buộc phải có kiến thức cơ bản về Website, HTML, Javascript, các bạn có thể học tại đây nếu chưa có kiến thức về nó:

Bây giờ ta bắt đầu thực hiện dự án:

Bạn cần phải tạo dự án có cấu trúc các tập tin và thư mục chính xác ở trên để nó khớp với các mã lệnh. Tui giải thích sơ lược như sau:

  • Thư mục “web” chứa các static files được lưu trong thư mục css và images. Đồng thời chứa thư mục “templates” để lưu trữ html
  • Thư mục “css” là định dạng Cascading Style Sheets (CSS) để định dạng website cho nó đẹp theo ý mình. Các định dạng được lưu trong file “maincss.css”
  • Thư mục “images” sẽ lưu các hình ảnh tĩnh để sử dụng cho thiết kế website trong các trang .html
  • Thư mục “templates” là thư mục chứa các mã lệnh html để thiết kế website cũng như tương tác lệnh với Python Flask, Ajax…
  • File “housingmodel_2024-07-15_13-30-34.zip” là file mô hình máy học được tạo ra từ bài 44. Bạn có thể lấy bất kỳ file mô hình máy học được train.
  • File “FileUtil.py” là file mã lệnh để nạp mô hình máy học vào bộ nhớ sau đó nó được đưa vào để sử dụng trên nền Web
  • File chính “app.py” là file mã lệnh Python dạng Flask MicrosoService để gọi các lệnh render website, nạp mô hình máy học, và triệu gọi mô hình máy học cũng như xuất kết quả prediction cho người sử dụng trên nền website.

Bây giờ ta đi vào chi tiết của từng mã lệnh:

File “FileUtil.py” mã lệnh này quen thuộc dùng để lưu mô hình xuống ổ cứng với định dạng pickle và tải mô hình máy học lên bộ nhớ, ta đã sử dụng ở các bài học trước:

import pickle
class FileUtil:
    @staticmethod
    def savemodel(model, filename):
        try:
            pickle.dump(model, open(filename, 'wb'))
            return True
        except:
            print("An exception occurred")
            return False

    @staticmethod
    def loadmodel(filename):
        try:
            model = pickle.load(open(filename, 'rb'))
            return model
        except:
            print("An exception occurred")
            return None

Trong dự án web này thì chúng ta chỉ sử dụng hàm loadmodel của FileUtil mà thôi.

File “maincss.css” file Cascading Style Sheets để định dạng website cho trang index.html.

h3{
color:red
}
table{
    border: 1px solid;
}
#tdtitle{
text-align:center
}
#tdbutton
{
text-align:right
}
table table tr:nth-child(even)
{
background-color: #f8f5c1;
}
#tdfooter
{
background-color: #457FE5;
color:white
}

Các selector trong maincss được khai báo trong index.html rồi. Và nó cần khớp với nhau.

Tiếp theo là trang “index.html” nó gồm có 2 phần chính, phần thứ nhất là Giao diện web, phần thứ 2 đó là tương tác người dùng áp dụng AJAX:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Prediction Pricing House</title>
    <script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
    <script scr="https://code.jquery.com/jquery-3.6.3.js"></script>
    <link rel="stylesheet" href="css/maincss.css">
</head>
<body>
    <form method="post">
        <table>
            <tr>
                <td colspan="2" id="tdtitle">
                    <h3>House Pricing Prediction</h3>
                </td>
            </tr>
            <tr>
                <td>
                    <table>
                        <tr>
                            <td>Avg. Area Income:</td>
                            <td><input type = "text" name="area_income_value" id="area_income_value" size="15" /></td>
                        </tr>
                        <tr>
                            <td>Avg. Area House Age:</td>
                            <td><input type = "text" name="area_house_age_value" id="area_house_age_value" size="15" /></td>
                        </tr>
                        <tr>
                            <td>Avg. Area Number of Rooms:</td>
                            <td><input type = "text" name="area_number_of_rooms_value" id="area_number_of_rooms_value" size="15" /></td>
                        </tr>
                        <tr>
                            <td>Avg. Area Number of Bedrooms:</td>
                            <td><input type = "text" name="area_number_of_bedrooms_value" id="area_number_of_bedrooms_value" size="15" /></td>
                        </tr>
                        <tr>
                            <td>Area Population:</td>
                            <td><input type = "text" name="area_population_value" id="area_population_value" size="15" /></td>
                        </tr>
                        <tr>
                            <td colspan="2" id="tdbutton">
                                <button id="doprediction_button" type="button">Prediction</button>
                            </td>
                        </tr>
                        <tr>
                            <td>House Pricing Prediction:</td>
                            <td><input type="text" name="house_price" id="house_price" size="15"></td>
                        </tr>

                    </table>
                </td>
                <td>
                    <img src="images/ic_house.png" width="200px" height="200px">
                </td>
            </tr>
              <tr>
                  <td colspan="2" id="tdfooter">Designed by: Trần Duy Thanh</td>
              </tr>
        </table>

    </form>
        <script>
            $(function(){
                $('#doprediction_button').click(function(){
                    $.ajax({
                        url: '/doprediction',
                        data: $('form').serialize(),
                        type: 'POST',
                        success: function(response){
                            console.log(response);
                            $('#house_price').val(response);
                        },
                        error: function(error){
                            console.log(error);
                        }
                    })
                })
            })
        </script>
</body>
</html>

index.html” sẽ sử dụng “maincss.css” để format website đẹp

Và ở phía dưới index.html có các mã lệnh về AJAX để triệu gọi các API được thiết kế trong “app.py

Khi người sử dụng nhập liệu vào các ô text box rồi nhấn nút “Prediction” chương trình sẽ dùng phương thức HTTP POST để đẩy dữ liệu lên Server:

<script>
            $(function(){
                $('#doprediction_button').click(function(){
                    $.ajax({
                        url: '/doprediction',
                        data: $('form').serialize(),
                        type: 'POST',
                        success: function(response){
                            console.log(response);
                            $('#house_price').val(response);
                        },
                        error: function(error){
                            console.log(error);
                        }
                    })
                })
            })
        </script>

“doprediction” là API dạng POST được khai báo trong app.py

Khi AJAX triệu gọi API này, nó sẽ đẩy dữ liệu trong Form (là các ô nhập liệu) lên server và nhần kết quả trả về trong success

Bạn lưu ý các mã(id,name) của các texbox phải chính xác với khai báo trong app.py để nó lấy được dữ liệu.

Bây giờ ta qua “app.py

from flask import Flask, render_template, request
from FileUtil import FileUtil 
app = Flask(__name__,
            static_url_path='',
            static_folder='web/static',
            template_folder='web/templates')

@app.route("/")
def main():
    return render_template("index.html")
@app.route("/doprediction", methods=['GET','POST'])
def doprediction():
    area_income_value = float(request.form['area_income_value'])
    area_house_age_value = float(request.form['area_house_age_value'])
    area_number_of_rooms_value = float(request.form['area_number_of_rooms_value'])
    area_number_of_bedrooms_value = float(request.form['area_number_of_bedrooms_value'])
    area_population_value = float(request.form["area_population_value"])
    trainedModel = FileUtil.loadmodel('housingmodel_2024-07-15_13-30-34.zip')
    result = trainedModel.predict([[area_income_value,
                                    area_house_age_value,
                                    area_number_of_rooms_value,
                                    area_number_of_bedrooms_value,
                                    area_population_value]])
    return f"{result[0]}"
if __name__ == "__main__":
    app.run(host="localhost", port=9500, debug=True)

Đầu tiên ta tạo đối tượng Flask, truyền các static url, static folder và template folder:

app = Flask(__name__,
            static_url_path='',
            static_folder='web/static',
            template_folder='web/templates')

Chỗ này ta cần khai báo chính xác như cấu trúc thư mục mà ta tạo trong Pycharm. Điều đó có nghĩa là gì? tức là nếu trong Pycharm ta tạo cấu trúc như thế nào thì khai báo ở đây y chang như mong muốn là được (tức là không cần phải giống như Tui tạo). Như cố gắng tạo cấu trúc như Tui hướng dẫn để tránh sai sót trong quá trình lập trình.

Hàm để mặc định kích hoạt website trang chủ:

@app.route("/")
def main():
    return render_template("index.html")

Vì ta đã khai báo template_folder trong đối tượng khởi tạo Flask rồi, nên khi gọi lệnh render_template(“index.html”) chương trình sẽ tự hiểu và tự động tạo giao diện website với index.html

Để kích hoạt website khi chạy, thì ta có lệnh:

if __name__ == "__main__":
    app.run(host="localhost", port=9500, debug=True)

Ở trên ta chạy port 9500, bạn có thể đổi qua Port khác. Và tui để debug để tìm lỗi nếu có. Vì đang chạy local nên host là localhost

Cuối cùng đó là API doprediction để nhận kết quả từ Client (là các ô nhập liệu từ website) sau đó tải mô hình máy học để tiến hành prediction, sau khi prediction xong thì trả kết quả lại cho Client:

@app.route("/doprediction", methods=['GET','POST'])
def doprediction():
    area_income_value = float(request.form['area_income_value'])
    area_house_age_value = float(request.form['area_house_age_value'])
    area_number_of_rooms_value = float(request.form['area_number_of_rooms_value'])
    area_number_of_bedrooms_value = float(request.form['area_number_of_bedrooms_value'])
    area_population_value = float(request.form["area_population_value"])
    trainedModel = FileUtil.loadmodel('housingmodel_2024-07-15_13-30-34.zip')
    result = trainedModel.predict([[area_income_value,
                                    area_house_age_value,
                                    area_number_of_rooms_value,
                                    area_number_of_bedrooms_value,
                                    area_population_value]])
    return f"{result[0]}"

Bạn xem Logic xử lý Tui vẽ sơ lược:

Chương trình xử lý theo 4 bước như trên khi người dùng bắt đầu nhấn nút Prediction.

Như vậy tổng hợp ta có mã lệnh “app.py” như sau:

from flask import Flask, render_template, request
from FileUtil import FileUtil 
app = Flask(__name__,
            static_url_path='',
            static_folder='web/static',
            template_folder='web/templates')

@app.route("/")
def main():
    return render_template("index.html")
@app.route("/doprediction", methods=['GET','POST'])
def doprediction():
    area_income_value = float(request.form['area_income_value'])
    area_house_age_value = float(request.form['area_house_age_value'])
    area_number_of_rooms_value = float(request.form['area_number_of_rooms_value'])
    area_number_of_bedrooms_value = float(request.form['area_number_of_bedrooms_value'])
    area_population_value = float(request.form["area_population_value"])
    trainedModel = FileUtil.loadmodel('housingmodel_2024-07-15_13-30-34.zip')
    result = trainedModel.predict([[area_income_value,
                                    area_house_age_value,
                                    area_number_of_rooms_value,
                                    area_number_of_bedrooms_value,
                                    area_population_value]])
    return f"{result[0]}"
if __name__ == "__main__":
    app.run(host="localhost", port=9500, debug=True)

Như vậy Tui đã trình bày xong cách thiết kế giao diện Website, tích hợp mô hình máy học để dự báo giá nhà. Các bạn đã biết cách tái sử dụng mô hình máy học trong dự án, có kiến thức về HTML, CSS, Javascript và Python Flask Microservice.

Coding đầy đủ của dự án ở đây:

https://www.mediafire.com/file/z4mjepwkwzfxdjt/Web_HousePricePrediction.rar/file

Bài học sau Tui sẽ hướng dẫn các bài liên quan tới hệ quản trị cơ sở dữ liệu MySQL để quản trị dữ liệu, và phục vụ cho xử lý mô hình máy học, viết các dự án liên quan tới thống kê và máy học.

Chúc các bạn thành công

Bài 44: Thiết kế phần mềm tương tác giao diện sử dụng mô hình máy học để dự báo giá nhà

Trong bài 43, Chúng ta đã build ứng dụng máy học dự báo giá nhà chạy trên môi trường console, nó khó cho người sử dụng.

Bài này tui sẽ trình bày minh họa phần mềm mẫu sử dụng mô hình máy học để dự báo giá nhà bằng các tương tác trên giao diện Tkinter và vẫn sử dụng local dataset CSV ở bài học 43. Bài học sau Tui sẽ trình bày phần mềm tương tác giao diện bằng Web Python Flask Microservice để dự báo giá nhà.

Chúng ta xem kết quả phần mềm có giao diện tương tác như dưới đây sau khi thực hiện bài học này nhé:

Phần mềm gồm có các chức năng sau:

  • Chọn và tải Dataset để sử dụng
  • Hiển thị xem Dataset chi tiết
  • Lựa chọn tỉ lệ train và train mô hình máy học
  • Đánh giá chất lượng mô hình máy học để quyết định có chọn mô hình này hay không
  • Lưu mô hình máy học để tái sử dụng
  • Chọn và nạp mô hình máy học đã train để sử dụng
  • Sử dụng mô hình máy học đã train để dự báo giá trị căn nhà

Bây giờ ta đi vào chi tiết từng bước lập trình:

Bước 1: Tạo cấu trúc dự án “HousePricePrediction” trong Pycharm như hình dưới đây:

Mô tả sơ lược trước ý nghĩa của từng tập tin, thư mục để các bạn nắm tổng quan:

  • Thư mục “dataset” là thư mục chứa các dataset cùng cấu trúc để sử dụng trong dự án, trường hợp này Tui để sẵn dataset “USA_Housing.csv”, bạn có thể tải nó ở đây https://tranduythanh.com/datasets/USA_Housing.csv
  • Thư mục “model” dùng để lưu các mô hình máy học đã được train, và nó được tái sử dụng để prediction giá nhà trên phần mềm tùy vào lựa chọn của khách hàng
  • File “FileUtil.py” là file mã lệnh để lưu mô hình máy học dạng pickle
  • File “DataSetViewer.py” là file mã lệnh để hiển thị chi tiết dataset lên giao diện
  • File “HousePricePrediction.py” là là file mã lệnh chính để thiết kế giao diện và các tác vụ liên quan tới phần mềm
  • File “App.py” là file kích hoạt để chạy phần mềm

Ta bắt đầu coding chi tiết:

File mã lệnh “FileUtil.py“:

import pickle
class FileUtil:
    @staticmethod
    def savemodel(model,filename):
        try:
            pickle.dump(model, open(filename, 'wb'))
            return True
        except:
            print("An exception occurred")
            return False
    @staticmethod
    def loadmodel(filename):
        try:
            model=pickle.load(open(filename, 'rb'))
            return model
        except:
            print("An exception occurred")
            return None

Lớp FileUtil có 2 static method:

  • savemode(model, filename) : hàm này để lưu mô hình máy học được trained dạng pickle, thường có đuôi là .zip. Và ta mặc định sẽ lập trình để nó lưu vào thư mục model mà ta đã tạo trong dự án:
  • loadmodel(filename) hàm này tải lại mô hình máy học được lưu, filename là đường dẫn của mô hình máy học.

Tiếp theo là tới file mã lệnh “DataSetViewer.py“, file mã lệnh này để hiển thị chi tiết dữ liệu dạng lưới có dòng cột để quan sát

from tkinter import *
from tkinter import ttk
import pandas as pd
class DataSetViewer:
    def __int__(self):
        pass
    def create_ui(self):
        self.root = Tk()
        self.root.title("Dataset viewer - House Pricing Prediction")
        self.root.geometry("800x600")
        main_panel = PanedWindow(self.root)
        main_panel["bg"] = "yellow"
        main_panel.pack(fill=BOTH, expand=True)

        # define columns
        columns = ('Avg. Area Income', 'Avg. Area House Age', 'Avg. Area Number of Rooms',
               'Avg. Area Number of Bedrooms', 'Area Population','Price')

        self.tree = ttk.Treeview(main_panel, columns=columns, show="headings")

        # define headings
        self.tree.heading("Avg. Area Income", text="Avg. Area Income")
        self.tree.heading("Avg. Area House Age", text="Avg. Area House Age")
        self.tree.heading("Avg. Area Number of Rooms", text="Avg. Area Number of Rooms")
        self.tree.heading("Avg. Area Number of Bedrooms", text="Avg. Area Number of Bedrooms")
        self.tree.heading("Area Population", text="Area Population")
        self.tree.heading("Price", text="Price")
        #self.tree.grid(row=0, column=0, sticky="nsew")
        self.tree.pack(side=LEFT,fill=BOTH, expand=True)
        scrollbar = ttk.Scrollbar(main_panel, orient=VERTICAL, command=self.tree.yview)
        self.tree.configure(yscroll=scrollbar.set)
        #scrollbar.grid(row=0, column=1, sticky="ns")
        scrollbar.pack(side=RIGHT,fill=BOTH,expand=True)
    def show_ui(self):
        self.root.mainloop()
    def show_data_listview(self,fileName):
        df = pd.read_csv(fileName)
        for i in range(0,len(df)):
            values = [df.iloc[i][0], df.iloc[i][1], df.iloc[i][2], df.iloc[i][3], df.iloc[i][4], df.iloc[i][5]]
            print(values)
            self.tree.insert('', END, values=values)

Giao diện của DatasetViewer khi thực hiện nó sẽ như dưới đây:

Tiếp theo mã lệnh “HousePricePrediction.py” ta thực hiện như sau (khá phức tạp) và Tui đang để các lệnh xử lý máy học trong giao diện luôn chứ không tách riêng từng lớp hướng đối tượng, ta xem giao diện dưới đây mà ta cần phải làm:

Như vậy để có giao diện tương tác như trên thì chúng ta phải làm các công việc sau:

  • Bước 1: Thiết kế giao diện người dùng
  • Bước 2: Chức năng “1.PickDataset” để chọn dataset từ máy tính
  • Bước 3: Chức năng “2.View Dataset” để hiển thị chi tiết Dataset lên giao diện mà ta đã chọn
  • Bước 4: Chức năng train model “3.Train Model”
  • Bước 5: Chức năng đánh giá chất lượng mô hình “4.Evaluate Model”
  • Bước 6: Chức năng lưu mô hình máy học “5. Save Model”
  • Bước 7: Chức năng tải các mô hình máy học đã lưu vào optionmenu xổ xuống như hình
  • Bước 8: Chức năng nạp mô hình máy học lên bộ nhớ để sử dụng khi khách hàng chọn mô hình máy học được train trong option menu
  • Bước 9: Chức năng dự báo giá nhà “7. Prediction House Pricing”

Bây giờ ta đi vào từng bước:

Bước 1: Thiết kế giao diện người dùng

Ta tạo class HousePricePredictionUI và viết mã lệnh cho hàm create_ui() như dưới đây:

import os
from datetime import datetime
from tkinter import *
from tkinter import messagebox, ttk
from tkinter.font import Font
from tkinter import filedialog as fd

from DataSetViewer import DataSetViewer
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.linear_model import LinearRegression
from FileUtil import FileUtil

class HousePricePredictionUI:
    fileName:""
    def __init__(self):

        pass
    def create_ui(self):
        self.root=Tk()
        self.root.title("Trần Duy Thanh - House Pricing Prediction tool")
        self.root.geometry("1200x750")
        main_panel=PanedWindow(self.root)
        main_panel["bg"]="yellow"
        main_panel.pack(fill=BOTH,expand=True)

        top_panel = PanedWindow(main_panel, height=80)
        top_panel["bg"] = "blue"
        main_panel.add(top_panel)
        top_panel.pack(fill=X, side=TOP, expand=False)

        font=Font(family="tahoma",size=18)
        title_label=Label(top_panel,text='House Pricing Prediction',font=font)
        title_label["bg"]="yellow"
        top_panel.add(title_label)

        center_panel=PanedWindow(main_panel)
        main_panel.add(center_panel)
        center_panel["bg"]="pink"
        center_panel.pack(fill=BOTH,expand=True)

        choose_dataset_panel=PanedWindow(center_panel,height=30)
        center_panel.add(choose_dataset_panel)
        choose_dataset_panel["bg"]="orange"
        choose_dataset_panel.pack(fill=X)

        dataset_label=Label(choose_dataset_panel,text="Select Dataset:")
        self.selectedFileName = StringVar()
        self.selectedFileName.set("dataset/USA_Housing.csv")
        self.choose_dateset_entry=Entry(choose_dataset_panel,
                                        textvariable=self.selectedFileName)

        self.choose_dataset_button = Button(choose_dataset_panel,
                                            text="1.Pick Dataset",
                                            width=10,
                                            command=self.do_pick_data)
        self.view_dataset_button = Button(choose_dataset_panel,
                                          text="2.View Dataset",
                                          width=20,
                                          command=self.do_view_dataset)
        choose_dataset_panel.add(dataset_label)
        choose_dataset_panel.add(self.choose_dateset_entry)
        choose_dataset_panel.add(self.choose_dataset_button)
        choose_dataset_panel.add(self.view_dataset_button)
        self.view_dataset_button.pack(side=RIGHT, expand=False)
        self.choose_dataset_button.pack(side=RIGHT, expand=False)

        #Training Rate
        training_rate_panel = PanedWindow(center_panel, height=30)
        center_panel.add(training_rate_panel)
        training_rate_panel.pack(fill=X)
        training_rate_label = Label(training_rate_panel, text="Training Rate:")
        self.training_rate = IntVar()
        self.training_rate.set(80)
        self.training_rate_entry = Entry(training_rate_panel,
                                         textvariable=self.training_rate,width=20)
        training_rate_panel.add(training_rate_label)
        training_rate_panel.add(self.training_rate_entry)
        percent_label=Label(text="%",width=20,anchor="w", justify=LEFT)
        percent_label.pack(side=RIGHT,expand=False,fill=X)
        training_rate_panel.add(percent_label)
        self.train_model_button=Button(training_rate_panel,
                                       text="3.Train Model",
                                       width=20,
                                       command=self.do_train)
        training_rate_panel.add( self.train_model_button)
        self.evaluate_model_button = Button(training_rate_panel,
                                            text="4.Evaluate Model",
                                            width=20,
                                            command=self.do_evaluation)
        training_rate_panel.add(self.evaluate_model_button)
        self.status=StringVar()
        self.train_model_result_label = Label(training_rate_panel,
                                              text=self.status.get(),
                                              textvariable=self.status)
        training_rate_panel.add(self.train_model_result_label)

        evaluate_panel=PanedWindow(center_panel,height=400)
        evaluate_panel["bg"]="cyan"
        center_panel.add(evaluate_panel)
        evaluate_panel.pack(fill=X)

        table_evaluate_panel=PanedWindow(evaluate_panel,height=400)
        evaluate_panel.add(table_evaluate_panel)

        # define columns
        columns = ('Avg. Area Income', 'Avg. Area House Age',
                   'Avg. Area Number of Rooms','Avg. Area Number of Bedrooms',
                   'Area Population', 'Original Price', 'Prediction Price')

        self.tree = ttk.Treeview(table_evaluate_panel,
                                 columns=columns, show="headings")

        self.tree.column("# 1", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 2", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 3", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 4", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 5", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 6", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 7", anchor=CENTER, stretch=NO, width=100)

        # define headings
        self.tree.heading("Avg. Area Income", text="Avg. Income")
        self.tree.heading("Avg. Area House Age", text="Avg. House Age")
        self.tree.heading("Avg. Area Number of Rooms", text="Avg. Area Room")
        self.tree.heading("Avg. Area Number of Bedrooms", text="Avg. Area Bedroom")
        self.tree.heading("Area Population", text="Area Population")
        self.tree.heading("Original Price", text="Original Price")
        self.tree.heading("Prediction Price", text="Prediction Price")
        # self.tree.grid(row=0, column=0, sticky="nsew")
        self.tree.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar = ttk.Scrollbar(table_evaluate_panel,
                                  orient=VERTICAL, command=self.tree.yview)
        self.tree.configure(yscroll=scrollbar.set)
        # scrollbar.grid(row=0, column=1, sticky="ns")
        scrollbar.pack(side=RIGHT, fill=BOTH, expand=True)

        coefficient_panel=PanedWindow(evaluate_panel)
        coefficient_panel["bg"]="pink"
        coefficient_panel.pack(side=RIGHT,fill=X,expand=False)
        evaluate_panel.add(coefficient_panel)

        coefficient_detail_label=Label(coefficient_panel,text="Coefficient:")
        coefficient_panel.add(coefficient_detail_label)
        coefficient_detail_label.pack(side=TOP,fill=X,expand=False)
        coefficient_detail_panel=PanedWindow(coefficient_panel)
        coefficient_panel.add(coefficient_detail_panel)
        coefficient_detail_panel.pack(side=TOP,expand=False,fill=X)

        self.coefficient_detail_text = (
            Text(coefficient_detail_panel, height=12,width=50))
        scroll = Scrollbar(coefficient_detail_panel)
        self.coefficient_detail_text.configure(yscrollcommand=scroll.set)
        self.coefficient_detail_text.pack(side=LEFT,expand=False,fill=X)

        scroll.config(command=self.coefficient_detail_text.yview)
        scroll.pack(side=RIGHT, fill=Y,expand=True)

        metric_panel=PanedWindow(coefficient_panel,height=30)
        coefficient_panel.add(metric_panel)
        metric_panel.pack(side=TOP,fill=BOTH,expand=True)

        self.mae_value=DoubleVar()
        mae_label=Label(metric_panel,text="Mean Absolute Error(MAE):")
        mae_label.grid(row=0,column=0)
        mae_entry = Entry(metric_panel, text="",
                          width=20,textvariable=self.mae_value)
        mae_entry.grid(row=0, column=1)

        self.mse_value = DoubleVar()
        mse_label = Label(metric_panel, text="Mean Square Error(MSE):")
        mse_label.grid(row=1, column=0)
        mse_entry = Entry(metric_panel, text="", width=20,textvariable=self.mse_value)
        mse_entry.grid(row=1, column=1)

        self.rmse_value = DoubleVar()
        rmse_label = Label(metric_panel, text="Root Mean Square Error(RMSE):")
        rmse_label.grid(row=2, column=0)
        rmse_entry = Entry(metric_panel, text="",
                           width=20,textvariable=self.rmse_value)
        rmse_entry.grid(row=2, column=1)

        savemodel_button = Button(metric_panel,
                                  text="5. Save Model",
                                  width=20,
                                  command=self.do_save_model)
        savemodel_button.grid(row=3, column=1)

        loadmodel_panel=PanedWindow(center_panel,height=20)
        loadmodel_panel["bg"]="yellow"
        loadmodel_panel.pack(fill=BOTH,side=TOP)
        model_files = self.load_model_files()
        print(model_files)

        self.selected_model = StringVar(self.root)
        self.selected_model.set(model_files[0])

        self.model_menu = OptionMenu(loadmodel_panel,
                                     self.selected_model,
                                     *model_files)
        self.model_menu.grid(row=0, column=0)

        loadmodel_button = Button(loadmodel_panel,
                                  text="6. Load Model",
                                  command=self.do_load_model)
        
        loadmodel_button.grid(row=0, column=1)

        input_prediction_panel = PanedWindow(center_panel)
        input_prediction_panel.pack(fill=BOTH, side=TOP,expand=True)

        area_income_label = Label(input_prediction_panel,
                                  text="Avg. Area Income:")
        area_income_label.grid(row=0, column=0)
        self.area_income_value=DoubleVar()
        area_income_entry = Entry(input_prediction_panel,
                                  text="",
                                  width=40,
                                  textvariable=self.area_income_value)
        area_income_entry.grid(row=0, column=1)

        area_house_age_label = Label(input_prediction_panel,
                                     text="Avg. Area House Age:")
        area_house_age_label.grid(row=1, column=0)
        self.area_house_age_value = DoubleVar()
        area_house_age_entry = Entry(input_prediction_panel,
                                     text="",
                                     width=40,
                                     textvariable=self.area_house_age_value)
        area_house_age_entry.grid(row=1, column=1)

        area_number_of_rooms_label = Label(input_prediction_panel,
                                           text="Avg. Area Number of Rooms:")
        area_number_of_rooms_label.grid(row=2, column=0)
        self.area_number_of_rooms_value=DoubleVar()
        area_number_of_rooms_entry = Entry(input_prediction_panel,
                                           text="", width=40,
                                           textvariable=self.area_number_of_rooms_value)
        area_number_of_rooms_entry.grid(row=2, column=1)

        area_number_of_bedrooms_label = Label(input_prediction_panel,
                                              text="Avg. Area Number of Bedrooms:")
        area_number_of_bedrooms_label.grid(row=3, column=0)
        self.area_number_of_bedrooms_value=DoubleVar()
        area_number_of_bedrooms_entry = Entry(input_prediction_panel,
                                              text="", width=40,
                                              textvariable=self.area_number_of_bedrooms_value)
        area_number_of_bedrooms_entry.grid(row=3, column=1)

        area_population_label = Label(input_prediction_panel, text="Area Population:")
        area_population_label.grid(row=4, column=0)
        self.area_population_value = DoubleVar()
        area_population_entry = Entry(input_prediction_panel,
                                      text="", width=40,
                                      textvariable=self.area_population_value)
        area_population_entry.grid(row=4, column=1)

        prediction_button=Button(input_prediction_panel,
                                 text="7. Prediction House Pricing",
                                 command=self.do_prediction)
        prediction_button.grid(row=5, column=1)

        prediction_price_label = Label(input_prediction_panel,
                                       text="Prediction Price:")
        prediction_price_label.grid(row=6, column=0)
        self.prediction_price_value=DoubleVar()
        prediction_price_entry = Entry(input_prediction_panel,
                                       text="", width=40,
                                       textvariable=self.prediction_price_value)
        prediction_price_entry.grid(row=6, column=1)

        designedby_panel = PanedWindow(main_panel, height=20)
        designedby_panel["bg"] = "cyan"
        designedby_panel.pack(fill=BOTH, side=BOTTOM)
        designedby_label = Label(designedby_panel,
                                 text="Designed by: Tran Duy Thanh")
        designedby_label["bg"] = "cyan"
        designedby_label.pack(side=LEFT)
    def show_ui(self):
        self.root.mainloop()

Bước 2: Chức năng “1.PickDataset” để chọn dataset từ máy tính

def do_pick_data(self):
    filetypes=(("Dataset CSV","*.csv"),
               ("All Files","*.*")
               )
    s=fd.askopenfilename(
        title="Choose dataset",
        initialdir="/",
        filetypes=filetypes)
    self.selectedFileName.set(s)

Bước 3: Chức năng “2.View Dataset” để hiển thị chi tiết Dataset lên giao diện mà ta đã chọn

def do_view_dataset(self):
    viewer= DataSetViewer()
    viewer.create_ui()
    viewer.show_data_listview(self.selectedFileName.get())
    viewer.show_ui()

Bước 4: Chức năng train model “3.Train Model”

def do_train(self):

    ratio=self.training_rate.get()/100
    self.df = pd.read_csv(self.selectedFileName.get())

    self.X = self.df[['Avg. Area Income', 'Avg. Area House Age',
                      'Avg. Area Number of Rooms','Avg. Area Number of Bedrooms',
                      'Area Population']]
    self.y = self.df['Price']

    self.X_train, self.X_test, self.y_train, self.y_test = (
        train_test_split(self.X, self.y, test_size=1-ratio, random_state=101))

    self.lm = LinearRegression()

    self.lm.fit(self.X_train, self.y_train)
    self.status.set("Trained is finished")
    messagebox.showinfo("infor","Trained is finished")

Bước 5: Chức năng đánh giá chất lượng mô hình “4.Evaluate Model”

Hàm này khá phức tạp, vì Tui muốn nó cập nhật dữ liệu cột trong bảng dữ liệu, tín hheje số coeff và hiển thị chi tiết lên giao diện:

def do_evaluation(self):
    # print the intercept
    print(self.lm.intercept_)
    insert_text=self.lm.intercept_

    self.coeff_df = pd.DataFrame(self.lm.coef_, self.X.columns, columns=['Coefficient'])
    print(self.coeff_df)
    self.coefficient_detail_text.insert(END, self.coeff_df)

    predictions = self.lm.predict(self.X_test)
    print(predictions)
    print("self.X_test")
    print(self.X_test)
    print("self.y_test:")
    print(self.y_test)
    y_test_array=np.asarray(self.y_test)
    for i in range(0,len(self.X_test)):
        values = [self.X_test.iloc[i][0], self.X_test.iloc[i][1],
                  self.X_test.iloc[i][2], self.X_test.iloc[i][3],
                  self.X_test.iloc[i][4], y_test_array[i],predictions[i]]
        print(values)
        self.tree.insert('', END, values=values)

    print('MAE:', metrics.mean_absolute_error(self.y_test, predictions))
    print('MSE:', metrics.mean_squared_error(self.y_test, predictions))
    print('RMSE:', np.sqrt(metrics.mean_squared_error(self.y_test, predictions)))

    self.mae_value.set(metrics.mean_absolute_error(self.y_test, predictions))
    self.mse_value.set(metrics.mean_squared_error(self.y_test, predictions))
    self.rmse_value.set(np.sqrt(metrics.mean_squared_error(self.y_test, predictions)))

    self.status.set("Evaluation is finished")
    messagebox.showinfo("infor", "Evaluation is finished")

Bước 6: Chức năng lưu mô hình máy học “5. Save Model”

def do_save_model(self):
    #create name of new trained model
    filename = "housingmodel_{}.zip".format(datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
    #update trained model to option menu:
    self.model_menu.children["menu"].add_command(label=filename,
                                            command=lambda md=filename: self.selected_model.set(md))
    file_path = os.path.join("model", filename)
    FileUtil.savemodel(self.lm,file_path)
    messagebox.showinfo("infor","exported model to disk successful!")

Chương trình sẽ tạo file mô hình máy học theo quy tắc nối ngày thời gian để không bị trùng, sau đó nó được lưu xuống ổ cứng đồng thời hiển thị lên Option menu.
Bước 7: Chức năng tải các mô hình máy học đã lưu vào optionmenu xổ xuống như hình

def load_model_files(self):
    strPlease="----Select a trained machine learning model----"
    if not os.path.exists("model"):
        return [strPlease]
    model_files = [f for f in os.listdir("model") if os.path.isfile(os.path.join("model", f))]

    model_files.insert(0,strPlease)
    print(model_files)
    return model_files

Bước 8: Chức năng nạp mô hình máy học lên bộ nhớ để sử dụng khi khách hàng chọn mô hình máy học được train trong option menu

def do_load_model(self):
    trainedModelName=self.selected_model.get()
    if trainedModelName.__contains__(".zip") ==False:
        messagebox.showwarning("Warning",
                               "You have to select a trained machine learning model")
        return
    self.lm = FileUtil.loadmodel(os.path.join("model", self.selected_model.get()))
    messagebox.showinfo("infor", "loading model from disk successful!")

Khi người dùng chọn mô hình trong Option menu, rồi nhấn nút Load mô hình, lúc này phần mềm sẽ tải mô hình lên bộ nhớ để tái sử dụng.


Bước 9: Chức năng dự báo giá nhà “7. Prediction House Pricing”

Đây là tiện ích cuối cùng của phần mềm để dự báo giá nhà, coding như sau:

def do_prediction(self):
    result = self.lm.predict([[self.area_income_value.get(),
                               self.area_house_age_value.get(),
                               self.area_number_of_rooms_value.get(),
                               self.area_number_of_bedrooms_value.get(),
                               self.area_population_value.get()]])
    self.prediction_price_value.set(result[0])

Dưới đây là mã lệnh chi tiết và đầy đủ của “HousePricePredictionUI.py”:

import os
from datetime import datetime
from tkinter import *
from tkinter import messagebox, ttk
from tkinter.font import Font
from tkinter import filedialog as fd

from DataSetViewer import DataSetViewer
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.linear_model import LinearRegression
from FileUtil import FileUtil

class HousePricePredictionUI:
    fileName:""
    def __init__(self):

        pass
    def create_ui(self):
        self.root=Tk()
        self.root.title("Trần Duy Thanh - House Pricing Prediction tool")
        self.root.geometry("1200x750")
        main_panel=PanedWindow(self.root)
        main_panel["bg"]="yellow"
        main_panel.pack(fill=BOTH,expand=True)

        top_panel = PanedWindow(main_panel, height=80)
        top_panel["bg"] = "blue"
        main_panel.add(top_panel)
        top_panel.pack(fill=X, side=TOP, expand=False)

        font=Font(family="tahoma",size=18)
        title_label=Label(top_panel,text='House Pricing Prediction',font=font)
        title_label["bg"]="yellow"
        top_panel.add(title_label)

        center_panel=PanedWindow(main_panel)
        main_panel.add(center_panel)
        center_panel["bg"]="pink"
        center_panel.pack(fill=BOTH,expand=True)

        choose_dataset_panel=PanedWindow(center_panel,height=30)
        center_panel.add(choose_dataset_panel)
        choose_dataset_panel["bg"]="orange"
        choose_dataset_panel.pack(fill=X)

        dataset_label=Label(choose_dataset_panel,text="Select Dataset:")
        self.selectedFileName = StringVar()
        self.selectedFileName.set("dataset/USA_Housing.csv")
        self.choose_dateset_entry=Entry(choose_dataset_panel,
                                        textvariable=self.selectedFileName)

        self.choose_dataset_button = Button(choose_dataset_panel,
                                            text="1.Pick Dataset",
                                            width=10,
                                            command=self.do_pick_data)
        self.view_dataset_button = Button(choose_dataset_panel,
                                          text="2.View Dataset",
                                          width=20,
                                          command=self.do_view_dataset)
        choose_dataset_panel.add(dataset_label)
        choose_dataset_panel.add(self.choose_dateset_entry)
        choose_dataset_panel.add(self.choose_dataset_button)
        choose_dataset_panel.add(self.view_dataset_button)
        self.view_dataset_button.pack(side=RIGHT, expand=False)
        self.choose_dataset_button.pack(side=RIGHT, expand=False)

        #Training Rate
        training_rate_panel = PanedWindow(center_panel, height=30)
        center_panel.add(training_rate_panel)
        training_rate_panel.pack(fill=X)
        training_rate_label = Label(training_rate_panel, text="Training Rate:")
        self.training_rate = IntVar()
        self.training_rate.set(80)
        self.training_rate_entry = Entry(training_rate_panel,
                                         textvariable=self.training_rate,width=20)
        training_rate_panel.add(training_rate_label)
        training_rate_panel.add(self.training_rate_entry)
        percent_label=Label(text="%",width=20,anchor="w", justify=LEFT)
        percent_label.pack(side=RIGHT,expand=False,fill=X)
        training_rate_panel.add(percent_label)
        self.train_model_button=Button(training_rate_panel,
                                       text="3.Train Model",
                                       width=20,
                                       command=self.do_train)
        training_rate_panel.add( self.train_model_button)
        self.evaluate_model_button = Button(training_rate_panel,
                                            text="4.Evaluate Model",
                                            width=20,
                                            command=self.do_evaluation)
        training_rate_panel.add(self.evaluate_model_button)
        self.status=StringVar()
        self.train_model_result_label = Label(training_rate_panel,
                                              text=self.status.get(),
                                              textvariable=self.status)
        training_rate_panel.add(self.train_model_result_label)

        evaluate_panel=PanedWindow(center_panel,height=400)
        evaluate_panel["bg"]="cyan"
        center_panel.add(evaluate_panel)
        evaluate_panel.pack(fill=X)

        table_evaluate_panel=PanedWindow(evaluate_panel,height=400)
        evaluate_panel.add(table_evaluate_panel)

        # define columns
        columns = ('Avg. Area Income', 'Avg. Area House Age',
                   'Avg. Area Number of Rooms','Avg. Area Number of Bedrooms',
                   'Area Population', 'Original Price', 'Prediction Price')

        self.tree = ttk.Treeview(table_evaluate_panel,
                                 columns=columns, show="headings")

        self.tree.column("# 1", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 2", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 3", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 4", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 5", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 6", anchor=CENTER, stretch=NO, width=100)
        self.tree.column("# 7", anchor=CENTER, stretch=NO, width=100)

        # define headings
        self.tree.heading("Avg. Area Income", text="Avg. Income")
        self.tree.heading("Avg. Area House Age", text="Avg. House Age")
        self.tree.heading("Avg. Area Number of Rooms", text="Avg. Area Room")
        self.tree.heading("Avg. Area Number of Bedrooms", text="Avg. Area Bedroom")
        self.tree.heading("Area Population", text="Area Population")
        self.tree.heading("Original Price", text="Original Price")
        self.tree.heading("Prediction Price", text="Prediction Price")
        # self.tree.grid(row=0, column=0, sticky="nsew")
        self.tree.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar = ttk.Scrollbar(table_evaluate_panel,
                                  orient=VERTICAL, command=self.tree.yview)
        self.tree.configure(yscroll=scrollbar.set)
        # scrollbar.grid(row=0, column=1, sticky="ns")
        scrollbar.pack(side=RIGHT, fill=BOTH, expand=True)

        coefficient_panel=PanedWindow(evaluate_panel)
        coefficient_panel["bg"]="pink"
        coefficient_panel.pack(side=RIGHT,fill=X,expand=False)
        evaluate_panel.add(coefficient_panel)

        coefficient_detail_label=Label(coefficient_panel,text="Coefficient:")
        coefficient_panel.add(coefficient_detail_label)
        coefficient_detail_label.pack(side=TOP,fill=X,expand=False)
        coefficient_detail_panel=PanedWindow(coefficient_panel)
        coefficient_panel.add(coefficient_detail_panel)
        coefficient_detail_panel.pack(side=TOP,expand=False,fill=X)

        self.coefficient_detail_text = (
            Text(coefficient_detail_panel, height=12,width=50))
        scroll = Scrollbar(coefficient_detail_panel)
        self.coefficient_detail_text.configure(yscrollcommand=scroll.set)
        self.coefficient_detail_text.pack(side=LEFT,expand=False,fill=X)

        scroll.config(command=self.coefficient_detail_text.yview)
        scroll.pack(side=RIGHT, fill=Y,expand=True)

        metric_panel=PanedWindow(coefficient_panel,height=30)
        coefficient_panel.add(metric_panel)
        metric_panel.pack(side=TOP,fill=BOTH,expand=True)

        self.mae_value=DoubleVar()
        mae_label=Label(metric_panel,text="Mean Absolute Error(MAE):")
        mae_label.grid(row=0,column=0)
        mae_entry = Entry(metric_panel, text="",
                          width=20,textvariable=self.mae_value)
        mae_entry.grid(row=0, column=1)

        self.mse_value = DoubleVar()
        mse_label = Label(metric_panel, text="Mean Square Error(MSE):")
        mse_label.grid(row=1, column=0)
        mse_entry = Entry(metric_panel, text="", width=20,textvariable=self.mse_value)
        mse_entry.grid(row=1, column=1)

        self.rmse_value = DoubleVar()
        rmse_label = Label(metric_panel, text="Root Mean Square Error(RMSE):")
        rmse_label.grid(row=2, column=0)
        rmse_entry = Entry(metric_panel, text="",
                           width=20,textvariable=self.rmse_value)
        rmse_entry.grid(row=2, column=1)

        savemodel_button = Button(metric_panel,
                                  text="5. Save Model",
                                  width=20,
                                  command=self.do_save_model)
        savemodel_button.grid(row=3, column=1)

        loadmodel_panel=PanedWindow(center_panel,height=20)
        loadmodel_panel["bg"]="yellow"
        loadmodel_panel.pack(fill=BOTH,side=TOP)
        model_files = self.load_model_files()
        print(model_files)

        self.selected_model = StringVar(self.root)
        self.selected_model.set(model_files[0])

        self.model_menu = OptionMenu(loadmodel_panel,
                                     self.selected_model,
                                     *model_files)
        self.model_menu.grid(row=0, column=0)

        loadmodel_button = Button(loadmodel_panel,
                                  text="6. Load Model",
                                  command=self.do_load_model)
        
        loadmodel_button.grid(row=0, column=1)

        input_prediction_panel = PanedWindow(center_panel)
        input_prediction_panel.pack(fill=BOTH, side=TOP,expand=True)

        area_income_label = Label(input_prediction_panel,
                                  text="Avg. Area Income:")
        area_income_label.grid(row=0, column=0)
        self.area_income_value=DoubleVar()
        area_income_entry = Entry(input_prediction_panel,
                                  text="",
                                  width=40,
                                  textvariable=self.area_income_value)
        area_income_entry.grid(row=0, column=1)

        area_house_age_label = Label(input_prediction_panel,
                                     text="Avg. Area House Age:")
        area_house_age_label.grid(row=1, column=0)
        self.area_house_age_value = DoubleVar()
        area_house_age_entry = Entry(input_prediction_panel,
                                     text="",
                                     width=40,
                                     textvariable=self.area_house_age_value)
        area_house_age_entry.grid(row=1, column=1)

        area_number_of_rooms_label = Label(input_prediction_panel,
                                           text="Avg. Area Number of Rooms:")
        area_number_of_rooms_label.grid(row=2, column=0)
        self.area_number_of_rooms_value=DoubleVar()
        area_number_of_rooms_entry = Entry(input_prediction_panel,
                                           text="", width=40,
                                           textvariable=self.area_number_of_rooms_value)
        area_number_of_rooms_entry.grid(row=2, column=1)

        area_number_of_bedrooms_label = Label(input_prediction_panel,
                                              text="Avg. Area Number of Bedrooms:")
        area_number_of_bedrooms_label.grid(row=3, column=0)
        self.area_number_of_bedrooms_value=DoubleVar()
        area_number_of_bedrooms_entry = Entry(input_prediction_panel,
                                              text="", width=40,
                                              textvariable=self.area_number_of_bedrooms_value)
        area_number_of_bedrooms_entry.grid(row=3, column=1)

        area_population_label = Label(input_prediction_panel, text="Area Population:")
        area_population_label.grid(row=4, column=0)
        self.area_population_value = DoubleVar()
        area_population_entry = Entry(input_prediction_panel,
                                      text="", width=40,
                                      textvariable=self.area_population_value)
        area_population_entry.grid(row=4, column=1)

        prediction_button=Button(input_prediction_panel,
                                 text="7. Prediction House Pricing",
                                 command=self.do_prediction)
        prediction_button.grid(row=5, column=1)

        prediction_price_label = Label(input_prediction_panel,
                                       text="Prediction Price:")
        prediction_price_label.grid(row=6, column=0)
        self.prediction_price_value=DoubleVar()
        prediction_price_entry = Entry(input_prediction_panel,
                                       text="", width=40,
                                       textvariable=self.prediction_price_value)
        prediction_price_entry.grid(row=6, column=1)

        designedby_panel = PanedWindow(main_panel, height=20)
        designedby_panel["bg"] = "cyan"
        designedby_panel.pack(fill=BOTH, side=BOTTOM)
        designedby_label = Label(designedby_panel,
                                 text="Designed by: Tran Duy Thanh")
        designedby_label["bg"] = "cyan"
        designedby_label.pack(side=LEFT)
    def show_ui(self):
        self.root.mainloop()
    def do_pick_data(self):
        filetypes=(("Dataset CSV","*.csv"),
                   ("All Files","*.*")
                   )
        s=fd.askopenfilename(
            title="Choose dataset",
            initialdir="/",
            filetypes=filetypes)
        self.selectedFileName.set(s)
    def do_view_dataset(self):
        viewer= DataSetViewer()
        viewer.create_ui()
        viewer.show_data_listview(self.selectedFileName.get())
        viewer.show_ui()
    def do_train(self):

        ratio=self.training_rate.get()/100
        self.df = pd.read_csv(self.selectedFileName.get())

        self.X = self.df[['Avg. Area Income', 'Avg. Area House Age',
                          'Avg. Area Number of Rooms','Avg. Area Number of Bedrooms',
                          'Area Population']]
        self.y = self.df['Price']

        self.X_train, self.X_test, self.y_train, self.y_test = (
            train_test_split(self.X, self.y, test_size=1-ratio, random_state=101))

        self.lm = LinearRegression()

        self.lm.fit(self.X_train, self.y_train)
        self.status.set("Trained is finished")
        messagebox.showinfo("infor","Trained is finished")
    def do_evaluation(self):
        # print the intercept
        print(self.lm.intercept_)
        insert_text=self.lm.intercept_

        self.coeff_df = pd.DataFrame(self.lm.coef_, self.X.columns, columns=['Coefficient'])
        print(self.coeff_df)
        self.coefficient_detail_text.insert(END, self.coeff_df)

        predictions = self.lm.predict(self.X_test)
        print(predictions)
        print("self.X_test")
        print(self.X_test)
        print("self.y_test:")
        print(self.y_test)
        y_test_array=np.asarray(self.y_test)
        for i in range(0,len(self.X_test)):
            values = [self.X_test.iloc[i][0], self.X_test.iloc[i][1],
                      self.X_test.iloc[i][2], self.X_test.iloc[i][3],
                      self.X_test.iloc[i][4], y_test_array[i],predictions[i]]
            print(values)
            self.tree.insert('', END, values=values)

        print('MAE:', metrics.mean_absolute_error(self.y_test, predictions))
        print('MSE:', metrics.mean_squared_error(self.y_test, predictions))
        print('RMSE:', np.sqrt(metrics.mean_squared_error(self.y_test, predictions)))

        self.mae_value.set(metrics.mean_absolute_error(self.y_test, predictions))
        self.mse_value.set(metrics.mean_squared_error(self.y_test, predictions))
        self.rmse_value.set(np.sqrt(metrics.mean_squared_error(self.y_test, predictions)))

        self.status.set("Evaluation is finished")
        messagebox.showinfo("infor", "Evaluation is finished")
    def do_save_model(self):
        #create name of new trained model
        filename = "housingmodel_{}.zip".format(datetime.now().strftime("%Y-%m-%d_%H-%M-%S"))
        #update trained model to option menu:
        self.model_menu.children["menu"].add_command(label=filename,
                                                command=lambda md=filename: self.selected_model.set(md))
        file_path = os.path.join("model", filename)
        FileUtil.savemodel(self.lm,file_path)
        messagebox.showinfo("infor","exported model to disk successful!")
    def load_model_files(self):
        strPlease="----Select a trained machine learning model----"
        if not os.path.exists("model"):
            return [strPlease]
        model_files = [f for f in os.listdir("model") if os.path.isfile(os.path.join("model", f))]

        model_files.insert(0,strPlease)
        print(model_files)
        return model_files
    def do_load_model(self):
        trainedModelName=self.selected_model.get()
        if trainedModelName.__contains__(".zip") ==False:
            messagebox.showwarning("Warning",
                                   "You have to select a trained machine learning model")
            return
        self.lm = FileUtil.loadmodel(os.path.join("model", self.selected_model.get()))
        messagebox.showinfo("infor", "loading model from disk successful!")
    def do_prediction(self):
        result = self.lm.predict([[self.area_income_value.get(),
                                   self.area_house_age_value.get(),
                                   self.area_number_of_rooms_value.get(),
                                   self.area_number_of_bedrooms_value.get(),
                                   self.area_population_value.get()]])
        self.prediction_price_value.set(result[0])

Tiếp theo ta viết mã lệnh cho “App.py” để thực thi phần mềm:

from HousePricePredictionUI import HousePricePredictionUI

if __name__ == '__main__':
    ui=HousePricePredictionUI()
    ui.create_ui()
    ui.show_ui()

Như vậy Tui đã làm xong case study với dự án mẫu cho phần mềm tương tác người dùng, sử dụng mô hình máy học để dự báo giá nhà bằng Tkinter. Chạy phần mềm lên ta có giao diện như mong muốn:

Các bạn cố gắng thực hiện lại để chạy cho bằng được.

Coding của dự án bán tải ở đây:

https://www.mediafire.com/file/8qp5hzrz5czmujf/HousePricePrediction.rar/file

Bài học sau Tui sẽ minh họa trên nền tảng Web, sử dụng Web Python Flask Microservice

Chúc các bạn thành công

Bài 42: Tổng quan về tích hợp Mô hình Máy học vào hệ thống Kinh doanh – Quản lý

Trong chuỗi các bài học này Tui sẽ hướng dẫn các bạn cách thức lập trình các mô hình máy học và tích hợp mô hình máy học vào hệ thống quản lý, kinh doanh. Trước tiên là các bạn cần hiểu sơ qua về mô hình tổng quan của máy học, hiểu được các bước cần thiết khi xây dựng mô hình máy học, biết cách tích hợp nó vào các hệ thống đã có sẵn hoặc chưa có sẵn tùy thuộc vào nhu cầu sử dụng cũng như tùy thuộc vào cách thức thiết kế cấu trúc cơ sở dữ liệu và loại dữ liệu.

Máy học có rất nhiều ứng dụng cho từng trường hợp cụ thể, và nó lệ thuộc vào kinh nghiệm cũng như tư tưởng của kỹ sư thiết kế mô hình máy học, mỗi người có thể đưa ra các quy trình khác nhau, tuy nhiên nó cũng có các điểm chung cơ bản nhất khi xây dựng mô hình máy học.

Ta có thể ứng dụng máy học vào các bài toán như: Phân loại, Phân cụm, hồi quy. Chúng ta có thể áp dụng mô hình máy học vào các lĩnh vực như: Sản xuất, Chăm sóc sức khỏe và khoa học đời sống, Các dịch vụ tài chính, Các hệ thống bán lẻ, Truyền thông và giải trí, Cùng các lĩnh vực khác…

01. Đối với Lĩnh vực Sản xuất:

Máy học có thể hỗ trợ bảo trì dự đoán, kiểm soát chất lượng và nghiên cứu đổi mới trong lĩnh vực sản xuất. Công nghệ máy học cũng giúp các công ty cải thiện giải pháp hậu cần, bao gồm quản lý tài sản, chuỗi cung ứng và kho hàng.

(source : Internet)

02. Đối với lĩnh vực Chăm sóc sức khỏe và khoa học đời sống:

Sự phát triển như vũ bão của cảm biến và thiết bị có thể đeo được đã tạo ra một lượng lớn dữ liệu về sức khỏe. Các chương trình máy học có thể phân tích thông tin này và hỗ trợ bác sĩ chẩn đoán và điều trị trong thời gian thực.

(source : Internet)

03. Đối với lĩnh vực Dịch vụ tài chính:

Các dự án máy học về tài chính giúp cải thiện khả năng phân tích rủi ro và quy định. Công nghệ máy học có thể giúp các nhà đầu tư xác định cơ hội mới bằng cách phân tích hoạt động của thị trường chứng khoán, đánh giá các quỹ phòng hộ hoặc hiệu chỉnh danh mục tài chính

(source : Internet)

04. Đối với lĩnh vực Bán lẻ:

Nhà bán lẻ có thể sử dụng máy học để cải thiện dịch vụ khách hàng, quản lý hàng tồn kho, bán hàng gia tăng và tiếp thị đa kênh, dự báo doanh thu, dự báo nhu cầu tiêu dùng, dự báo xu hướng…

(source : Internet)

05. Đối với lĩnh vực Truyền thông và giải trí:

Các công ty giải trí tìm đến máy học để hiểu rõ hơn đối tượng mục tiêu của họ đồng thời cung cấp nội dung chân thực, được cá nhân hóa và theo nhu cầu của khách hàng. Thuật toán máy học được triển khai để giúp thiết kế trailer và các dạng quảng cáo khác, từ đó đề xuất nội dung được cá nhân hóa cho người tiêu dùng và thậm chí là hợp lý hóa quy trình sản xuất.

(source : Internet)

Ngoài ra đôi khi chúng ta cũng hay phân vân, bối rối giữa khái niệm về Trí tuệ nhân tạo và Máy học, thì dưới đây thì hãng AWS có bảng phân biệt 2 khái niệm này, Tui tổng hợp lại để các bạn hiểu thêm:

Trí tuệ nhân tạoMáy học
Khái niệmAI là thuật ngữ rộng cho các ứng dụng dựa trên máy móc bắt chước trí thông minh của con người. Không phải tất cả các giải pháp AI đều là ML.ML là một phương pháp trí tuệ nhân tạo. Tất cả các giải pháp ML đều là các giải pháp AI.
Trường hợp sử dụng phù hợp nhấtAI là lựa chọn tốt nhất để hoàn thành một tác vụ phức tạp của con người một cách hiệu quả.ML là lựa chọn tốt nhất để xác định các mẫu hình trong các tập dữ liệu lớn để giải quyết các vấn đề cụ thể.
MethodsAI có thể sử dụng các phương pháp khác nhau, như dựa trên quy tắc, mạng nơ-ron, thị giác máy tính, v.v. Đối với ML, người ta tự chọn và trích xuất các tính năng từ dữ liệu thô và gán trọng số để đào tạo mô hình.
ImplementsViệc triển khai AI phụ thuộc vào tác vụ. AI thường được xây dựng sẵn và truy cập thông qua các API.Bạn đào tạo các mô hình ML mới hoặc hiện có cho trường hợp sử dụng cụ thể của bạn. Cũng có sẵn các API ML được xây dựng sẵn.
(source : AWS)

Và dưới đây là quan điểm cá nhân của Tui liên quan tới máy học hay trí tuệ nhân tạo, và nó có thể làm không ưng cái bụng của một số người:

Đừng bao giờ thần thánh hóa Trí tuệ nhân tạo hay Máy học. Chúng chỉ đóng góp một vai trò nhất định trong hệ thống, giúp người quản trị đưa ra các quyết định nhanh chóng và phù hợp hơn.

Muốn ứng dụng máy học thì trước tiên chúng ta phải có hệ thống quản trị tốt trước đã, và các dữ liệu nên được cấu trúc phù hợp.

Nỗi sợ trí tuệ nhân tạo, máy học hay Robot thông minh sẽ thay thế con người? Cướp công việc của con người?

Không bao giờ có điều đó xảy ra, mỗi một công nghệ mới xuất hiện thì nó sẽ tạo ra rất nhiều nhóm các ngành nghề mới, công việc mới cho Con người làm việc. Nó chỉ thay thế những STUPID JOB

“Lùa Gà” là một từ lóng mà chúng ta thường nghe, một số trung tâm, chuyên gia đào tạo… cố tình tạo nên nỗi sợ hoang đường để học viên đăng ký học, hay đăng ký mua sản phẩm liên quan.

Chúng ta học để hiểu biết, để ứng dụng trong thực tiễn vì kiến thức là vô cùng vô tận. Học để thích ứng với sự phát triển như vũ bão của công nghệ mới chứ không phải vì lo sợ nó CƯỚP công việc của mình.

Vì mỗi người mỗi quan điểm khác nhau, nhìn bài toán khách hàng gặp phải dưới các lăng kính khác nhau nên mỗi người sẽ có những quy trình xử lý khác nhau, nhưng suy cho cùng thì cũng cùng một mục đích là đem lại dịch vụ tốt nhất cho khách hàng. Dưới đây Tui đưa ra một quy trình chung cơ bản về thiết kế mô hình máy học và tích hợp vào hệ thống nói chung, các bạn có thể tham khảo (có thể hoàn toàn KHÔNG ĐỒNG Ý hoặc ĐỒNG Ý một PHẦN). Dĩ nhiên các chuỗi bài hướng dẫn xây dựng mô hình máy học và tích hợp vào hệ thống mà Tui hướng dẫn trên Blog này sẽ nhìn chung thực hiện theo các quy trình này. Tùy thuộc vào tình hình thực tế của bài toán mà các bạn đang giải quyết để có thể cải biên nó phù hợp hơn.

Và cũng cần lưu ý: Không có mô hình máy học nào sai, chỉ có mô hình máy học phù hợp hay chưa phù hợp. Mô hình máy học có thể chạy tốt với tập dữ liệu X nhưng chưa chắc chạy tốt với tập dữ liệu Y. Đó là lý do vì sao chúng ta cần cải tiến mô hình máy học thường xuyên khi nhu cầu hay dữ liệu bị thay đổi đáng kể.

Ta xem lại mô hình tổng quan:

Lưu đồ trên là việc tích hợp mô hình máy học vào một hệ thống đang vận hành. Dĩ nhiên có những trường hợp ta chưa có hệ thống (dữ liệu) nhưng ta có ý tưởng về mô hình máy học từ đó ta thiết kế hệ thống đáp ứng ý tưởng mô hình máy học . Và cũng có những trường hợp hệ thống có sẵn rồi, dữ liệu có sẵn rồi, bây giờ ta cần tích hợp mô hình máy học vào, hầu hết bài học Tui sẽ hướng dẫn trường hợp số 2 là hệ thống có sẵn.

  • Ở lưu đồ trên ta thấy, đa phần nó có khoảng 3 phiên chính trong xây dựng và tích hợp mô hình máy học: Phiên PREPARING DATA, Phiên BUILD MODEL và phiên USE MODEL.
  • PREPARING DATA: Là Bước chuẩn bị dữ liệu, tùy thuộc vào các bài toán khác nhau, hay dữ liệu khác nhau mà ta có các bước chuẩn bị dữ liệu chi tiết khác nhau, nhìn chung thì ta cần có các thao tác như “làm sạch và chuẩn hóa dữ liệu”, “Mô hình hóa hướng đối tượng cho dữ liệu” để phục vụ cho việc lập trình được dễ dàng hơn. Ta cần phải chuẩn bị dữ liệu thật tốt để Build Mô hình, vì Cơ sở dữ liệu lưu trữ không phải lúc nào cũng lưu trữ các dữ liệu tốt, tối ưu mà nó có thể lưu những dữ liệu không tốt, các dữ liệu không có ích trong quá trình Build mô hình. Mà chúng ta cần biết nguyên tắc “Garbage In – Garbage Out” nghĩ là dữ liệu không tốt thì cho mô hình không tốt.
    1. Khi khách hàng ở bước (0) có các hoạt động trên hệ thống (1), chẳng hạn như tương tác sản phẩm, commments, review, đặt đơn hàng… thì các dữ liệu này sẽ được lưu vào Database (2). Database này chúng ta cần tiền xử lý cho tốt, nó sẽ nằm trong phiên PREPARING DATA.
    2. Mỗi dữ liệu khác nhau, mỗi bài toán khác nhau mà bước (3) làm sạch và chuẩn hóa dữ liệu sẽ khác nhau, có thể thêm nhiều các bước con khác nhau, sử dụng các kỹ thuật khác nhau.
    3. Mô hình hóa hướng đối tượng (4) có thể được áp dụng để giúp cho việc xử lý dữ liệu, mô hình được tốt hơn.
  • BUILD MODEL: Là các bước từ số (5) tới số (9). Phiên này sẽ build mô hình máy học, các bước bao gồm lọc và tải dữ liệu, lựa chọn giải thuật và các tham số cho mô hình, train mô hình, đánh giá và cải thiện mô hình, cuối cùng là xuất mô hình ra ổ cứng để tái sử dụng, tích hợp vào hệ thống.
    1. Bước (5) “Filter and load data” có thể được xem là bước lọc và nạp dữ liệu để phục vụ cho train mô hình, ta cần chọn các thuộc tính/biến phù hợp cũng như tỉ lệ train/test khác nhau, nó ảnh hưởng tới chất lượng mô hình
    2. Bước (6) “Select Algorithms” Bước này ta lựa chọn các giải thuật máy học phù hợp với bài toán, cũng với việc lựa chọn các tham số đầu vào để train mô hình. Ví dụ như bài toán Classfification thì nên dùng giải thuật nào, bài toán Clustering thì nên dùng giải thuật nào, các bài toán liên quan Time Series Data thì nên dùng giải thuật nào….. Cái này ứng với bài toán cụ thể ta sẽ dùng.
    3. Bước (7) là bắt đầu train mô hình máy học, từ tập dữ liệu, giải thuật máy học, các tham số được lựa chọn phù hợp thì chương trình bày đầu train.
    4. Bước (8) sau khi train mô hình xong thì chúng ta cần đánh giá mô hình có chất lượng hay không, nếu mô hình chất lượng (ta cho rằng) thì qua Bước (9) để xuất mô hình xuống ổ cứng để tái sử dụng, để tích hợp vào hệ thống mà khách hàng yêu cầu. Nếu mô hình không chất lượng thì ta có nhiều giải pháp, ở trên thì Tui đưa ra 2 giải pháp: Hoặc là chúng ta quay lại bước (5) hoặc là chúng ta quay lại phiên PREPARING DATA. Vì cả 2 nơi này đều là nguyên dẫn có thể dẫn tới mô hình không đạt chất lượng mong muốn.
    5. Bước (9) là bước xuất mô hình ra ổ cứng (lưu), vì ta không thể khi chạy dịch vụ ví dụ như Khách hàng đang sử dụng hệ thống sàn thương mại điện tử, ta muốn gợi ý các sản phẩm mà khách hàng có khả năng mua cao thì ta lại chạy lại mô hình được (vì nó rất tốn thời gian, training mô hình rất lâu, khách hàng không thể ngồi chờ). do đó Bước (9) là lưu mô hình rồi thì các lần sau sử dụng ta chỉ cần tải lại mô hình này rồi tích hợp vào hệ thống thôi.
  • USE MODEL: Là các bước (10), (11). Các bước này nạp và tích hợp mô hình máy học vào hệ thống và sử dụng mô hình máy học.
    1. Bước (10) là nạp mô hình đã lưu từ bước (9), ví dụ khách hàng đăng nhập vào hệ thống sàn thương mại điện tử, ta muốn gợi ý các sản phẩm mà khách hàng có khả năng mua cao nhất thì ta sẽ đọc mô hình máy học ở Bước (9) để sử dụng, tích hợp nó vào hệ thống ở bước (11)
    2. Bước (11) Sau khi mô hình máy học được tích hợp vào hệ thống, chương trình sẽ triệu gọi mô hình máy học, gọi các hàm predict để đưa ra các kết quả phù hợp cho khách hàng. Và hiển nhiên Khách hàng có thể tiếp tục tương tác trên hệ thống ở bước (0) như vậy chúng ta có một vòng tròn khép kín, mỗi lần khách hàng tương tác thì hệ thống lại lưu lại cơ sở dữ liệu, và theo thời gian thì chúng ta biết rằng có thể sở thích khách hàng sẽ thay đổi theo thời gian (khả năng cao, ví dụ tháng 01 bạn thích ăn Dưa leo, nhưng qua tháng 02 bạn lại thích Dưa chuột) như vậy có thể dẫn tới mô hình máy học không còn chất lượng nữa, ta cần có cách đánh giá lại mô hình, có thể build lại. Cứ thế tiếp tục cải tiến mô hình để có được một dịch vụ tốt nhất cho khách hàng.

Trên đây là tổng quan về máy học, trí tuệ nhân tạo, cách áp dụng mô hình máy học vào các lĩnh vực khác nhau như: Sản xuất, Chăm sóc sức khỏe và khoa học đời sống, Các dịch vụ tài chính, Các hệ thống bán lẻ, Truyền thông và giải trí, Cùng các lĩnh vực khác…

Đặc biệt Tui đưa ra mô hình tổng quan chung cơ bản về xây dựng và tích hợp mô hình máy học vào hệ thống quản lý, giải thích ý nghĩa của từng bước. Và nó phụ thuộc vào bài toán của khách hàng, phụ thuộc vào dữ liệu, phụ thuộc vào lăng kính của kỹ sư máy học mà mỗi người có thể cải biên flow chart khác nhau, tuy nhiên về cơ bản thì các chức năng, các bước cốt lõi là như nhau.

Bài học sau Tui sẽ hướng dẫn cụ thể bài Dự báo giá nhà dùng hồi quy tuyến tính. Xây dựng mô hình máy học và chạy thử nghiệm trên giao diện Python PyQt6. Các bạn chú ý theo dõi

Chúc các bạn thành công

ML.Emotion

This framework is a Machine Learning framework. It is used to predict the Customer emotion from their feedbacks.

This framework is extended from the Microsoft Machine Learning ML.NET Model.

Datasets and instructions: https://github.com/thanhtd32/ML.Emotion#readme, Coding demo is “ML.EmotionDemo”

This Research from KMOU (Korea Maritime & Ocean University) – Data Science Lab – Room 407.

Authors: Duy Thanh Tran, Prof. Jun-Ho Huh

Any question, please free to contact me: thanhtd@uel.edu.vn

My full name: TRAN DUY THANH

Blog study coding: https://duythanhcse.wordpress.com/

Group support: https://www.facebook.com/groups/communityuni/

alt text

ML.Emotion – How to use?

Install nuget package

Install-Package ML.Emotion -ProjectName YourProject

Train-Test Set sample

Download The UCI Sentiment Labeled Sentences dataset zip file: http://archive.ics.uci.edu/ml/machine-learning-databases/00331/sentiment%20labelled%20sentences.zip You use the file “yelp_labelled.txt” or you can download the file at https://github.com/thanhtd32/ML.Emotion/tree/main/TrainTestSet

Full code:

using ML.Emotion.Data;
using ML.Emotion.Predict;

namespace ML.EmotionDemo
{
    public partial class frmMain : Form
    {
        //Declare EmotionEngine object
        EmotionEngine emotionEngine =new EmotionEngine();
        string folder = "Models";
        public frmMain()
        {
            InitializeComponent();
        }
    
        private void frmMain_Load(object sender, EventArgs e)
        {
            LoadModelIntoCombo();
        }
        //1. Import dataset and split traint- test set
        private void btnLoadData_Click(object sender, EventArgs e)
        {
           if(openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string path=openFileDialog1.FileName;
                double ratio=double.Parse(txtTestRatio.Text);
                emotionEngine.ImportDataset(path, false, ratio);
            }
        }
        //2. Build model
        private void btnBuildModel_Click(object sender, EventArgs e)
        {
            bool ret=emotionEngine.BuildAndTrainModel();
            if (ret)
                lblBuildModelStatus.Text = "Build Model successfully";
            else
                lblBuildModelStatus.Text = "Build Model failed";
        }
        //3. Evaluate model
        private void btnEvaluateModel_Click(object sender, EventArgs e)
        {
            Metric metric= emotionEngine.Evaluate();
            txtAccuracy.Text=metric.Accuracy.ToString();
            txtAUC_PR.Text=metric.AUC_PR.ToString();
            txtAUC_ROC.Text=metric.AUC_ROC.ToString();
            txtF1Score.Text=metric.F1Score.ToString();  
            txtLogLoss.Text=metric.LogLoss.ToString();
            txtLogLossReduction.Text=metric.LogLossReduction.ToString();
            txtNegativePrecision.Text=metric.NegativePrecision.ToString();
            txtNegativeRecall.Text=metric.NegativeRecall.ToString();    
            txtPositivePrecision.Text=metric.PositivePrecision.ToString();
            txtPositiveRecall.Text = metric.PositiveRecall.ToString();
        }
        //4. Save model
        private void btnSaveModel_Click(object sender, EventArgs e)
        {
            if (Directory.Exists(folder) == false)
            {
                Directory.CreateDirectory(folder);
            }
            string path = folder + "\\ML.Emotion-" + DateTime.Now.ToString("ddMMyyyy-hhmmss") + ".zip";
            bool ret = emotionEngine.SaveModel(path);
            if (ret)
                lblSaveModelStatus.Text = "Save Model successfully";
            else
                lblSaveModelStatus.Text = "Save Model failed";
            LoadModelIntoCombo();
        }
        //5. Load model
        private void LoadModelIntoCombo()
        {
            cboModel.Items.Clear();

            if (Directory.Exists(folder) == false)
            {
                return;
            }
            string[] files = Directory.GetFiles(folder);
            foreach (string file in files)
            {
                FileInfo fi = new FileInfo(file);
                cboModel.Items.Add(fi.Name);
            }
        }
        //5. Load model and pick model
        private void btnLoadModel_Click(object sender, EventArgs e)
        {
            if (cboModel.SelectedIndex == -1)
                return;
            string modelName = folder + "\\" + cboModel.Text;
            bool ret = emotionEngine.LoadModel(modelName);
            if (ret)
                lblLoadModelStatus.Text = "Load Model successfully";
            else
                lblLoadModelStatus.Text = "Load Model failed";
        }
        //6. Predict
        private void btnPredict_Click(object sender, EventArgs e)
        {
            FeedbackData feedback=new FeedbackData();
            feedback.Data=txtInputData.Text;
            EmotionDataPrediction result = emotionEngine.Predict(feedback);
            
            picBlameArrow.Visible = false;
            lblBlameProbability.Visible = false;

            picComplimentArrow.Visible = false;
            lblComplimentProbability.Visible = false;

            picNeutralArrow.Visible = false;
            lblNeutralProbability.Visible = false;
            if(result.EmotionType==EmotionType.Blame)
            {
                picBlameArrow.Visible = true;
                lblBlameProbability.Visible = true;
                lblBlameProbability.Text = "Probability:" + result.ProbabilityFormat;
            }
            else if(result.EmotionType==EmotionType.Neutral)
            {
                picNeutralArrow.Visible = true;
                lblNeutralProbability.Visible = true;
                lblNeutralProbability.Text = "Probability:" + result.ProbabilityFormat;
            }
            else if(result.EmotionType== EmotionType.Compliment)
            {
                picComplimentArrow.Visible = true;
                lblComplimentProbability.Visible = true;
                lblComplimentProbability.Text = "Probability:" + result.ProbabilityFormat;
            }
        }
    }
}