Xây dựng một mô hình học máy phân loại đa lớp (multi-class classification)

Kỹ thuật học máy có giám sát là việc huấn luyện mô hình với đầu vào là một tập hợp các tính năng (features) và đầu ra là một nhãn (label) bằng cách sử dụng tập dữ liệu có chứa các giá trị nhãn đã biết. Bạn có thể nghĩ về hàm này như thế này: y = f([x1,x2,x3,...])

Trong đó y đại diện cho nhãn mà chúng ta muốn dự đoán và [x1,x2,x3,...] đại diện cho vectơ của các đặc trưng mà mô hình sử dụng để dự đoán nhãn.

 

Phân loại (classification) là một mô hình học máy có giám sát, trong đó bạn đào tạo một mô hình sử dụng các đặc trưng (các giá trị x) để dự đoán một nhãn (y) bằng cách tính toán xác suất có thể có của từng nhãn và dự đoán một nhãn thích hợp dựa trên giá trị xác suất. Hình thức phân loại đơn giản nhất là phân loại nhị phân (binary), trong đó nhãn là 0 hoặc 1, "Đúng" hoặc "Sai"; "Lời" hoặc "Lỗ"...

Trong bài viết này, tôi sẽ sử dụng kĩ thuật phân loại đa lớp (multi-class classfication) để xây dựng một mô hình học máy dự đoán một bông hoa diên vĩ bất kì sẽ thuộc nhánh nào trong 3 nhánh của hoa diên vĩ : Setosa, Versicolor và Virginica.  Các đặc trưng để xây dựng mô hình là: chiều dài cánh hoa (petal length), chiều rộng cánh hoa (petal width), chiều dài đài hoa (sepal length) và chiều rộng đài hoa (sepal width). Iris dataset là một bộ dữ liệu rất nổi tiếng trong học máy, được nhà thống kế học hiện đại Ronald Fisher (người phát minh ra giá trị p-value) giới thiệu vào năm 1936. Các bạn có thể nhấn button Iris dataset để tải bảng dữ liệu về máy.

Khám phá dữ liệu

1) Sử dụng thư viện pandasnumpy.

 
     
import pandas as pd
import numpy as np
     

2) Tải bảng dữ liệu và in ra 5 dòng đầu tiên của bảng.

      
df_iris = pd.read_csv("/content/iris.csv", delimiter=",",header="infer")
df_iris.head()
     

Chạy đoạn code trên chúng ta sẽ nhận được 5 rows đầu tiên của bảng dữ liệu như bên dưới. Bảng dữ liệu của chúng ta có 5 cột, trong đó 4 cột đầu tiên (sepal.length, sepal.width, petal.length, petal.width) là các đặc trưng của hoa diên vĩ như đã nói ở trên. Cột cuối cùng (variety) là tên của nhánh hoa diên vĩ đã được gắn nhãn. Cột variety bao gồm 3 nhãn là tên của 3 nhánh khác nhau của hoa diên vĩ: Setosa, Versicolor và Virginica.

Chúng ta cần map các giá trị ở cột variety thành các giá trị số để có thể thực hiện các phép toán sau này. Đầu tiên, tạo một list iris_classes chứa các giá trị của cột variety. Sau đó thay thế các giá trị này bằng các giá trị số.

 
#Khởi tạo một list iris_classes chứa tên của 3 loại hoa 
iris_classes = ['Setosa','Versicolor','Virginica']
 
 
# Mã hoá tên của 3 loại hoa với các giá trị 0 (Setosa), 1 (Versicolor) và 2 (Virginica)
df_iris = df_iris.replace(iris_classes,[0, 1, 2])
 

Lúc này giá trị (Setosa, Versicolor và Virginica) trong cột variety đã được thay thế lần lượt thành (0, 1 và 2).

3) Kiểm tra xem bảng dữ liệu có giá trị rỗng (null) hay không. Nếu có chúng ta cần phải có những bước xử lý phù hợp vì dữ liệu null sẽ gây ra nhiều vấn đề cho mô hình học máy. Với bảng dữ liệu df_iris của chúng ta đang làm thì không có giá trị rỗng nào.

 
df_iris.isnull().sum()
 

4) Sử dụng hàm describe() trong pandas để lấy thống kê cho tất cả các cột dạng số trong bảng dữ liệu.

  
df_iris.describe()
 

Chúng ta sẽ nhận được kết quả như bên dưới với những thông tin quan trọng cho từng cột : số lượng mẫu (count), giá trị trung bình (mean), độ lệch chuẩn (std). Dựa vào bảng dưới, chúng ta thấy được bảng dữ liệu được gắn nhãn của chúng ta có 150 mẫu.

5) Tạo 2 bảng mới. Bảng df_X chứa 4 cột (sepal.length, sepal.width, petal.length, petal.width) là bảng chứa các đặc trưng cho mô hình của chúng ta. Bảng df_y có 1 cột (variety) là bảng chứa giá trị nhãn mà chúng ta muốn dự đoán (0 cho setosa, 1 cho versicolor và 2 cho virginica).

  
df_X = df_iris.filter(['sepal.length','sepal.width','petal.length','petal.width'],axis = 1)
df_y = df_iris.filter(['variety'])
print('Top 5 rows of df_X table: \n{}'.format(df_X.head()))
print('Top 5 rows of df_y table: \n{}'.format(df_y.head()))
 

Kết quả nhận được như bên dưới:

 

6) Bây giờ chúng ta sẽ convert 2 bảng df_Xdf_y thành các vector Xy cho việc tính toán bằng hàm to_numpy(). Sau đó, chúng ta in ra giá trị shape của Xy.

 
X = df_X.to_numpy()
y = df_y.to_numpy()
 
 
print('Shape of X:', X.shape)
print('Shape of y:', y.shape)
 

Kết quả in ra như bên dưới:

 

 

Shape của X là (150,4) có nghĩa là ma trận X có 150 chiều (dimension) và mỗi chiều có 4 phần tử (element).

7) Chúng ta có thể in ra khoảng giá trị min-max của các đặc trưng. Mục đích của việc này không chỉ là để có thêm thông tin, mà còn là để xem giá trị của các đặc trưng có chung độ lớn hay không. Nếu giá trị của các đặc trưng không đều nhau, ví dụ đặc trưng 1 có giá trị từ 1-10, đặc trưng 2 có giá trị từ 1 triệu đến 10 triệu thì mô hình học máy của chúng ta sẽ bị bias do sự khác biệt này. Nếu xảy ra điều này thì chúng ta cần làm một bước gọi là normalization cho các đặc trưng.

 
print('Sepal Length min: {}, max: {}'.format(np.min(X[:,0]), np.max(X[:,0]))) 
print('Sepal Width min: {}, max: {}'.format(np.min(X[:,1]), np.max(X[:,1]))) 
print('Petal Length min: {}, max: {}'.format(np.min(X[:,2]), np.max(X[:,2]))) 
print('Petal Width min: {}, max: {}'.format(np.min(X[:,3]), np.max(X[:,3])))
 

Kết quả trả về như bên dưới:

 

 

Kết quả cho thấy giá trị của 4 đặc trưng trong bài toán không quá chênh lệch. Do vậy chúng ta không cần phải normalize data trong trường hợp này.

Chia dữ liệu thành train set và test set

1) Chia dữ liệu thành thành 2 bộ dữ liệu khác nhau: train settest set. Bộ dữ liệu test có độ lớn bằng 20% bộ dữ liệu gốc. Sau đó in shape của từng bộ dữ liệu để kiểm tra. Để chia dữ liệu thành 2 bộ (train, test), chúng ta dùng module train_test_split trong thư viện sklearn.model_selection.

 
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=True, random_state=1612)
print('Shape of X train', X_train.shape)
print('Shape of y train', y_train.shape)
print('Shape of X test', X_test.shape)
print('Shape of y test', y_test.shape)
 

Chúng ta có được kết quả:

 

 

 

Như vậy chúng ta đã chia bộ dữ liệu thành 4 tập con khác nhau: X_train, y_train, X_test, y_test.

2) Tiếp theo, chúng ta cần chuyển shape của y_trainy_test thành một mảng đơn với lệnh reshape(-1)

 
y_test = y_test.reshape(-1)
y_train = y_train.reshape(-1)
print('Shape of y train', y_train.shape)
print('Shape of y test', y_test.shape)
 

Kết quả trả về sẽ là:

 

Huấn luyện mô hình với hàm LogisticRegression

1) Sử dụng mô hình LogisticRegression trong thư viện sklearn và huấn luyện mô hình bằng hàm fit(X_train, y_train). Đặt tên cho mô hình được huấn luyện là multi_model.

 
from sklearn.linear_model import LogisticRegression

# Set regularization rate
reg = 0.1

# train a logistic regression model on the training set
multi_model = LogisticRegression(C=1/reg, solver='lbfgs', multi_class='auto', max_iter=10000).fit(X_train, y_train)
print (multi_model)
 

Mô hình huấn luyện thành công sẽ có kết quả như bên dưới.

 

Kiểm tra mô hình đã được huấn luyện

1) Sử dụng mô hình multi_model đã được huấn luyện để dự đoán nhãn cho 15 dữ liệu đầu tiên trong bộ test.

 
predictions = multi_model.predict(X_test)
print('Predicted labels: ', predictions[:15])
print('Actual labels   : ' ,y_test[:15])
 

Kết quả nhận được là:

 

 

Kết quả không tệ, chỉ 1 dữ liệu có nhãn dự đoán là không trùng khớp với nhãn test.

2) Sử dụng hàm classification_report trong thư viện sklearn để kiểm tra xem mô hình đã huấn luyện của chúng ta tốt đến mức nào.

 
from sklearn. metrics import classification_report

print(classification_report(y_test, predictions))
 

Kết quả nhận được:

Với mô hình này thì độ chính xác trung bình là 97% có thể xem là mô hình khá tốt.

Chúng ta cũng có thể xem giá trị trung bình của Accuracy, Precision và Recall như bên dưới.

 
from sklearn.metrics import accuracy_score, precision_score, recall_score

print("Overall Accuracy:",accuracy_score(y_test, predictions))
print("Overall Precision:",precision_score(y_test, predictions, average='macro'))
print("Overall Recall:",recall_score(y_test, predictions, average='macro'))
 

Kết quả nhận được:

 

 

3) Sử dụng confusion_matrix cũng là một cách khá hay để trực quan hoá kết quả dự đoán cho bộ test từ mô hình. Kết quả bên dưới cho thấy mô hình của chúng ta dự đoán chính xác 12/12 mẫu cho loại hoa Setosa, trong 8 mẫu của loại hoa Versicolor thì mô hình dự đoán đúng 7 giá trị, giá trị còn lại bị đoán nhầm thành Virginica. Đối với 10 giá trị của hoa Virginica trong bộ test thì mô hình dự đoán đúng cả 10.

 
from sklearn.metrics import confusion_matrix

# Print the confusion matrix
mcm = confusion_matrix(y_test, predictions)
print(mcm)
 

Kết quả:

 

 

4) Với những bài toán phức tạp hơn thì chúng ta có thể dùng thư viện pylot để trực quan hoá tốt hơn như ví dụ bên dưới.

 
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline



plt.imshow(mcm, interpolation="nearest", cmap=plt.cm.Blues)
plt.colorbar()
tick_marks = np.arange(len(iris_classes))
plt.xticks(tick_marks, iris_classes, rotation=45)
plt.yticks(tick_marks, iris_classes)
plt.xlabel("Predicted Species")
plt.ylabel("Actual Species")
plt.show()
 

Kết quả:

 

Lưu lại mô hình và sử dụng mô hình cho việc dự đoán

1) Sau khi đã có được một mô hình học máy khá tốt, chúng ta sẽ lưu lại mô hình này với tên iris_model.pkl và sử dụng file pickle này cho việc dự đoán.

 
import joblib

# Save the model as a pickle file
filename = '/content/iris_model.pkl'
joblib.dump(multi_model, filename)
 

2) Tải file pickle mà chúng ta đã lưu.

 
multi_model = joblib.load(filename)
 

3) Khởi tạo một array mới x_new với giá trị của 4 đặc trưng:

Chiều dài đài hoa: 1

Chiều rộng đài hoa: 2

Chiều dài cánh hoa: 3

Chiều rộng cánh hoa: 4

Chúng ta muốn mô hình dự đoán liệu x_new sẽ thuộc loại hoa nào.

 
x_new = np.array([[1,2,3,4]])
print ('New sample: {}'.format(x_new[0]))
 

In ra mảng đặc trưng của x_new:

 

4) Mô hình dự đoán sẽ trả về một mảng, chúng ta chỉ lấy giá trị đầu tiên của mảng, sau đó map với list iris_classes để lấy chính xác tên loài hoa.

 
iris_pred = multi_model.predict(x_new)[0]
print('Predicted class is', iris_classes[iris_pred])
 

Kết quả trả về x_new sẽ là Virginica.

 

 

 

Tổng kết

Như vậy chúng ta đã xây dựng thành công một mô hình học máy phân loại đa lớp để dự đoán liệu một mẫu hoa diên vĩ thuộc loại nào.

Từ kết quả này, chúng ta có thể phát triển một web application để có thể dùng mô hình này cho production. Bên dưới là một web app cho mô hình mà chúng ta vừa xây dựng trong bài viết này. Bạn nhập vào 4 đặc trưng và ứng dụng sẽ trả về kết quả dự đoán.

Đường link web app: https://iris-class-vndatasmart.herokuapp.com/

Nhấn nút Submit, kết quả trả về sẽ là:

 

Bài viết đến đây là hết. Cảm ơn các bạn đã quan tâm. Nếu có câu hỏi, các bạn hãy gửi feedback trong mục comment bên dưới.

About Author

Chia sẻ bài viết

Leave a Comment

Your email address will not be published. Required fields are marked *