XGBoost là một thuật toán thuộc họ Gradient Boosting. Những ưu điểm vượt trội của nó đã được chứng minh qua các cuộc thi trên kaggle. Dữ liệu đầu vào cho XGBoost model phải ở dạng số. Nếu dữ liệu không ở dạng số thì phải được chuyển qua dạng số (numeric
) trước khi đưa vào XGBoost model để train. Có một vài phương pháp để thực hiện việc này, hãy cùng nhau điểm qua trong phần còn lại của bài viết.
Sau khi xem hết bài viết này, bạn sẽ biết:
string
) đầu ra cho việc phân loại?one hot encoding
).missing data
)?1. Mã hóa chuỗi đầu ra
Chúng ta sẽ sử dụng bài toán phân loại hoa Iris để minh họa cho vấn đề này. Đưa vào các số liệu đo đặc các thành phần của hoa Iris, cần dự đoán hoa Iris đó thuộc loại nào (đầu ra dự đoán của model là các chuỗi ký tự).
Hãy xem ví dụ của dataset:
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
XGBoost sẽ không thể mô hình hóa bài toán này bởi vì nó yêu cầu dữ liệu đầu vào phải ở dạng số. Chúng ta có thể dễ dang chuyển đổi từ kiểủ chuổi (string) sang kiểu số (integer
) sử dụng lớp LabelEncoder
của thư viện sklearn
. Ba giá trị đầu ra kiểu string
(Iris-setosa, Iris-versicolor, Iris-virginica
) sẽ được ánh xạ thành các giá trị số tương ứng (0, 1, 2):
# encode string class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)
Chúng ta cần lưu lại bộ mã hóa này để chuyển đổi ngược lại từ số sang chuỗi trong quá trình dự đoán.
Dưới đây là toàn bộ code minh họa việc việc đọc dataset, mã hóa dữ liệu đầu ra, train XGBoost model và đánh giá độ chính xác của model đó:
# multiclass classification
from pandas import read_csv
from XGBoost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
# load data
data = read_csv('iris.csv', header=None)
dataset = data.values
# split data into X and y
X = dataset[:,0:4]
Y = dataset[:,4]
# encode string class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(X, label_encoded_y,test_size=test_size, random_state=seed)
# fit model on training data
model = XGBClassifier()
model.fit(X_train, y_train)
print(model)
# make predictions for test data
predictions = model.predict(X_test)
# evaluate predictions
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))
Chạy đoạn code trên, được kết quả như sau:
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,
importance_type='gain', interaction_constraints='',
learning_rate=0.300000012, max_delta_step=0, max_depth=6,
min_child_weight=1, missing=nan, monotone_constraints='()',
n_estimators=100, n_jobs=0, num_parallel_tree=1,
objective='multi:softprob', random_state=0, reg_alpha=0,
reg_lambda=1, scale_pos_weight=None, subsample=1,
tree_method='exact', validate_parameters=1, verbosity=None)
Accuracy: 92.00%
2. Mã hóa one-code dữ liệu kiểu Categorical
Rất nhiều bộ dataset chứa các dữ liệu kiểu categorical
. Trong phần này, chúng ta sử dụng bộ dataset breast cancer để làm việc. Bộ dataset này miêu tả thông tin của y tế của các bệnh nhân, nhãn của nó chỉ ra bệnh nhân đó có bị ung thư hay không.
Ví dụ của bộ dữ liệu này như bên dưới:
'40-49','premeno','15-19','0-2','yes','3','right','left_up','no','recurrence-events'
'50-59','ge40','15-19','0-2','no','1','right','central','no','no-recurrence-events'
'50-59','ge40','35-39','0-2','no','2','left','left_low','no','recurrence-events'
'40-49','premeno','35-39','0-2','yes','3','right','left_low','yes','no-recurrence-events'
'40-49','premeno','30-34','3-5','yes','2','left','right_up','no','recurrence-events'
Chúng ta nhìn thấy rằng tất cả 9 giá trị input đều ở là kiểu categorical
và được thể hiện ở dạng string
. Đây cũng là bài toán phân lớp nhị phân và nhãn cần dự đoán cũng đang ở dạng string
. Vì vậy, ta có thể sử dụng lại cách tiếp cận ở phần trước, chuyển các giá trị dạng string
sang integer
sử dụng LabelEncoder
.
# encode string input values as integers
features = []
for i in range(0, X.shape[1]):
label_encoder = LabelEncoder()
feature = label_encoder.fit_transform(X[:,i])
features.append(feature)
encoded_x = numpy.array(features)
encoded_x = encoded_x.reshape(X.shape[0], X.shape[1])
Khi sử dụng LabelEncoder
, XGBoost có thể hiểu rằng các giá trị encoded integer
của mỗi input feature
có mối quan hệ thứ tự. Ví dụ, đối với input feature
breast-quad
, giá trị left-up
được mã hóa là 0, left-low
được mã hóa là 1. Điều này thực tế là không đúng trong trường hợp này. Để tránh điều này, ta phải ánh xạ các giá trị integer
thành 1 giá trị kiểu binary
.
Ví dụ, các giá trị của biến đầu vào breast-quad
là:
left-up
left-low
right-up
right-low
central
được ánh xạ thành 5 giá trị binary
tương ứng:
1,0,0,0,0
0,1,0,0,0
0,0,1,0,0
0,0,0,1,0
0,0,0,0,1
Điều này được gọi là OneHotEncoder. Ta có thể mã hóa OneHot
tất cả các input features
kiểu categorical
sử dụng lớp OneHotEncoder
của thư viện scikit-leaen
:
onehot_encoder = OneHotEncoder(sparse=False, categories=✬auto✬)
feature = onehot_encoder.fit_transform(feature)
OneHot Encoder
cho tất cả input features
của dữ liệu:
# encode string input values as integers
columns = []
for i in range(0, X.shape[1]):
label_encoder = LabelEncoder()
feature = label_encoder.fit_transform(X[:,i])
feature = feature.reshape(X.shape[0], 1)
onehot_encoder = OneHotEncoder(sparse=False, categories=✬auto✬)
feature = onehot_encoder.fit_transform(feature)
columns.append(feature)
# collapse columns into array
encoded_x = numpy.column_stack(columns)
Nếu biết chắc chắn răng một input feature
nào đó có mối quan hệ về thứ tự, có thể bỏ qua OneHot Encoder
mà chỉ cần LabelEncoder
cho feature
đó.
Toàn bộ source code của phần này:
# binary classification, breast cancer dataset, label and one hot encoded
from numpy import column_stack
from pandas import read_csv
from XGBoost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
# load data
data = read_csv(✬datasets-uci-breast-cancer.csv✬, header=None)
dataset = data.values
# split data into X and y
X = dataset[:,0:9]
X = X.astype(str)
Y = dataset[:,9]
# encode string input values as integers
columns = []
for i in range(0, X.shape[1]):
label_encoder = LabelEncoder()
feature = label_encoder.fit_transform(X[:,i])
feature = feature.reshape(X.shape[0], 1)
onehot_encoder = OneHotEncoder(sparse=False, categories=✬auto✬)
feature = onehot_encoder.fit_transform(feature)
columns.append(feature)
# collapse columns into array
encoded_x = column_stack(columns)
print("X shape: : ", encoded_x.shape)
# encode string class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)
# split data into train and test sets
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(encoded_x, label_encoded_y,
test_size=test_size, random_state=seed)
# fit model on training data
model = XGBClassifier()
model.fit(X_train, y_train)
print(model)
# make predictions for test data
predictions = model.predict(X_test)
# evaluate predictions
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))
Chạy code trên ta được kết quả:
('X shape: : ', (286, 43))
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,
importance_type='gain', interaction_constraints='',
learning_rate=0.300000012, max_delta_step=0, max_depth=6,
min_child_weight=1, missing=nan, monotone_constraints='()',
n_estimators=100, n_jobs=0, num_parallel_tree=1, random_state=0,
reg_alpha=0, reg_lambda=1, scale_pos_weight=1, subsample=1,
tree_method='exact', validate_parameters=1, verbosity=None)
Accuracy: 68.42%
3. Giải quyết vấn đề missing data
XGBoost có thể tự động học cách để đưa ra cách giải quyết tốt nhất cho vấn đề missing data
. Trên thực tế, XGBoost được thiết kế để làm việc với sparse data
, giống như one hot encoded data
ở phần trước. Chi tiết hơn về các kỹ thuật xử lý missing data
của XGBoost, có thể tham khảo Section 3.4 Sparsity-aware Split Finding trong bài báo XGBoost: A Scalable Tree Boosting System.
Trong phần này, ta sẽ sử dụng Horse Colic dataset để minh họa khả năng xử lý missing data
của XGBoost. Trong dataset này, tỷ lệ mising data
tương dối lớn, rơi vào khoảng 30%.
Dễ dàng đọc được dataset này với thư viện Pandas
:
dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None)
dataframe.head()
Ta có thể quan sát thấy rằng, missing data
được đánh dấu bằng dấu ?
.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
0 2 1 530101 38.50 66 28 3 3 ? 2 5 4 4 ? ? ? 3 5 45.00 8.40 ? ? 2 2 11300 0 0 2
1 1 1 534817 39.2 88 20 ? ? 4 1 3 4 2 ? ? ? 4 2 50 85 2 2 3 2 2208 0 0 2
2 2 1 530334 38.30 40 24 1 1 3 1 3 3 1 ? ? ? 1 1 33.00 6.70 ? ? 1 2 0 0 0 1
3 1 9 5290409 39.10 164 84 4 1 6 2 2 4 4 1 2 5.00 3 ? 48.00 7.20 3 5.30 2 1 2208 0 0 1
4 2 1 530255 37.30 104 35 ? ? 6 2 ? ? ? ? ? ? ? ? 74.00 7.40 ? ? 2 2 4300 0 0 2
Thay ?
bởi giá trị 0:
# set missing values to 0
X[X == '?'] = 0
# convert to numeric
X = X.astype('float32')
Bài toán đối với dataset này là Binary Classification
, nhãn bao gồm 2 giá trị là 1 và 2. Ta dễ dàng chuyển sang 0 và 1 sử dụng LabelEncoder
:
# encode Y class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)
Code đầy đủ:
# binary classification, missing data
from pandas import read_csv
from XGBoost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
# load data
dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None)
dataset = dataframe.values
# split data into X and y
X = dataset[:,0:27]
Y = dataset[:,27]
# set missing values to 0
X[X == '?'] = 0
# convert to numeric
X = X.astype('float32')
# encode Y class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)
# split data into train and test sets
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(X, label_encoded_y,
test_size=test_size, random_state=seed)
# fit model on training data
model = XGBClassifier()
model.fit(X_train, y_train)
print(model)
# make predictions for test data
predictions = model.predict(X_test)
# evaluate predictions
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))
Output:
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,
importance_type='gain', interaction_constraints='',
learning_rate=0.300000012, max_delta_step=0, max_depth=6,
min_child_weight=1, missing=nan, monotone_constraints='()',
n_estimators=100, n_jobs=0, num_parallel_tree=1, random_state=0,
reg_alpha=0, reg_lambda=1, scale_pos_weight=1, subsample=1,
tree_method='exact', validate_parameters=1, verbosity=None)
Accuracy: 82.83%
Hãy kiểm tra khả năng của XGBoost bằng cách thử thay missing value
với các giá trị khác nhau:
missing value
bởi 1X[X == '?`] = 1
Kết quả:
Accuracy: 81.82%
missing value
bởi NaNX[X == '?`] = 1
Kết quả:
Accuracy: 83.84%
missing value
bằng giá trị trung bình (mean
) của toàn bộ feature
đó# impute missing values as the mean
imputer = SimpleImputer()
imputed_x = imputer.fit_transform(X)
Source đầy đủ:
# binary classification, missing data, impute with mean
import numpy
from pandas import read_csv
from XGBoost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import Imputer
# load data
dataframe = read_csv("horse-colic.csv", delim_whitespace=True, header=None)
dataset = dataframe.values
# split data into X and y
X = dataset[:,0:27]
Y = dataset[:,27]
# set missing values to NaN
X[X == '?'] = numpy.nan
# convert to numeric
X = X.astype(✬float32✬)
# impute missing values as the mean.
imputer = Imputer()
imputed_x = imputer.fit_transform(X)
# encode Y class values as integers
label_encoder = LabelEncoder()
label_encoder = label_encoder.fit(Y)
label_encoded_y = label_encoder.transform(Y)
# split data into train and test sets
seed = 7
test_size = 0.33
X_train, X_test, y_train, y_test = train_test_split(imputed_x, label_encoded_y,
test_size=test_size, random_state=seed)
# fit model on training data
model = XGBClassifier()
model.fit(X_train, y_train)
print(model)
# make predictions for test data
predictions = model.predict(X_test)
# evaluate predictions
accuracy = accuracy_score(y_test, predictions)
print("Accuracy: %.2f%%" % (accuracy * 100.0))
Kết quả:
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,
importance_type='gain', interaction_constraints='',
learning_rate=0.300000012, max_delta_step=0, max_depth=6,
min_child_weight=1, missing=nan, monotone_constraints='()',
n_estimators=100, n_jobs=0, num_parallel_tree=1, random_state=0,
reg_alpha=0, reg_lambda=1, scale_pos_weight=1, subsample=1,
tree_method='exact', validate_parameters=1, verbosity=None)
Accuracy: 81.82%
Có thể thấý rằng, đối với bài toán này, giải pháp thay thế missing value
bằng NaN mang lại kết quả tốt nhất. Trong các bài toán thực tế, chúng ta cũng cần phải thử-sai
nhiều cách khác nhau để chọn được phương pháp tối ưu cho bài toán đó.
4. Kết luận
Trong bài viết này chúng ta đã cùng nhau tìm hiểu các cách để chuẩn bị dữ liệu cho việc train XGBoost model. Cụ thể:
string
bằng LabelEncoder
categorical
bằng OneHot Encoder
missing data
Trong bài tiếp theo, chúng ta sẽ tìm hiểu các phương pháp đánh giá hiệu năng của XGBoost model.
Toàn bộ source code của bài này các bạn có thể tham khảo trên github cá nhân của mình tại github.
Bài viết có tham khảo tại tham khảo