Machine Learning Data Preparation

DP4ML - Feature Selection - Phần 5 - Use RFE for Feature Selection

DP4ML - Feature Selection - Phần 5 - Use RFE for Feature Selection

Bài thứ 12 trong chuỗi các bài viết về chủ đề Data Preparation cho các mô hình ML và là bài thứ 5 về Feature Selection. Trong bài này, chúng ta sẽ tìm hiểu phương pháp Recursive Feature Elimination (RFE) để lựa chọn features thông qua việc thực hành trên một bộ dữ liệu giả được sinh ra ngẫu nhiên.

1. Recursive Feature Elimination (RFE)

RFE là một thuật toán Feature Selection phổ biến. Ưu điểm của nó là dễ cấu hình và sử dụng, và hiệu quả cao trong việc lựa chọn các features sử dụng mối liên hệ giữa các features đó với target.

Thực chất, RFE là một thuật toán lựa Feature Selection kiểu wrapper. Điều này có nghĩa là một thuật toán học ML được đưa ra và sử dụng bên trong để thực hiện Feature Selection.

Có hai tùy chọn cấu hình quan trọng khi sử dụng RFE:

  • Lựa chọn số lượng features cần chọn
  • Lựa chọn thuật toán được sử dụng để giúp chọn features.

2. RFE with scikit-learn

Thư viện scikit-learn cung cấp một implementation của RFE thông qua RFE class. Để sử dụng nó, cần cung cấp 2 tham số là thuật toán sử dụng (estimator) và số lượng features cần chọn (n_features_to_select).

...
# define the method
rfe = RFE(estimator=DecisionTreeClassifier(), n_features_to_select=3)
# fit the model
rfe.fit(X, y)
# transform the data
X, y = rfe.transform(X, y)

Thông thương, k-Folde Cross-Validation được sử dụng để đánh giá thuật toán ML trong RFE.

2.1 RFE for Classification

Trong phần này, chúng ta sẽ sử dụng RFE cho bài toán Classification. Đầu tiên, chúng ta sẽ sinh ra 1000 mẫu dữ liệu, mỗi mẫu có 10 features, trong đó có 5 features là informative, 5 features là redundant, sử dụng hàm make_classification().

# test classification dataset
from sklearn.datasets import make_classification
# define dataset
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# summarize the dataset
print(X.shape, y.shape)

Tiếp theo, chúng ta khai báo RFE, sử dụng thuật toán DecisionTree và số lượng features cần chọn là 5 để mô hình hóa bộ dữ liệu vừa tạo.

# evaluate RFE for classification
from numpy import mean
from numpy import std
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.feature_selection import RFE
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline
# define dataset
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# create pipeline
rfe = RFE(estimator=DecisionTreeClassifier(), n_features_to_select=5)
model = DecisionTreeClassifier()
pipeline = Pipeline(steps=[('s',rfe),('m',model)])
# evaluate model
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
# report performance
print('Accuracy: %.3f (%.3f)' % (mean(n_scores), std(n_scores)))

Kết quả thực hiện:

Accuracy: 0.887 (0.036)

RFE cũng có thể được sử dụng như một model và có thể đưa ra dự đoán trên một mẫu dữ liệu mới.

# make a prediction with an RFE pipeline
from sklearn.datasets import make_classification
from sklearn.feature_selection import RFE
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline
# define dataset
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# create pipeline
rfe = RFE(estimator=DecisionTreeClassifier(), n_features_to_select=5)
model = DecisionTreeClassifier()
pipeline = Pipeline(steps=[('s',rfe),('m',model)])
# fit the model on all available data
pipeline.fit(X, y)
# make a prediction for one example
data = [[2.56999479, -0.13019997, 3.16075093, -4.35936352, -1.61271951, -1.39352057, -2.48924933, -1.93094078, 3.26130366, 2.05692145]]
yhat = pipeline.predict(data)
print('Predicted Class: %d' % (yhat))

Kết quả thực hiện:

Predicted Class: 1

2.2 RFE for Regression

Tương tự như trên, đầu tiên ta sẽ tạo ra bộ dữ liệu Regression:

# test regression dataset
from sklearn.datasets import make_regression
# define dataset
X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, random_state=1)
# summarize the dataset
print(X.shape, y.shape)

Tiếp theo sẽ mô hình hóa bộ dữ liệu vừa tạo với RFE và DecisionTree.

# evaluate RFE for regression
from numpy import mean
from numpy import std
from sklearn.datasets import make_regression
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedKFold
from sklearn.feature_selection import RFE
from sklearn.tree import DecisionTreeRegressor
from sklearn.pipeline import Pipeline
# define dataset
X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, random_state=1)
# create pipeline
rfe = RFE(estimator=DecisionTreeRegressor(), n_features_to_select=5)
model = DecisionTreeRegressor()
pipeline = Pipeline(steps=[('s',rfe),('m',model)])
# evaluate model
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(pipeline, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1)
# report performance
print('MAE: %.3f (%.3f)' % (mean(n_scores), std(n_scores)))

Kết quả thực hiện:

MAE: -27.239 (2.710)

Sử dụng RFE để tạo dự đoán trên mẫu dữ liệu mới:

# make a regression prediction with an RFE pipeline
from sklearn.datasets import make_regression
from sklearn.feature_selection import RFE
from sklearn.tree import DecisionTreeRegressor
from sklearn.pipeline import Pipeline
# define dataset
X, y = make_regression(n_samples=1000, n_features=10, n_informative=5, random_state=1)
# create pipeline
rfe = RFE(estimator=DecisionTreeRegressor(), n_features_to_select=5)
model = DecisionTreeRegressor()
pipeline = Pipeline(steps=[('s',rfe),('m',model)])
# fit the model on all available data
pipeline.fit(X, y)
# make a prediction for one example
data = [[-2.02220122, 0.31563495, 0.82797464, -0.30620401, 0.16003707, -1.44411381, 0.87616892, -0.50446586, 0.23009474, 0.76201118]]
yhat = pipeline.predict(data)
print('Predicted: %.3f' % (yhat))

Kết quả thực hiện:

Predicted: -84.288

3. RFE Hyperparameters

Như đã nói ở trên, có 2 tham số quan trọng cần lưu ý khi sử dụng RFE là thuật toán sử dụng và số lượng features cần chọn. Phần này, chúng ta sẽ thử thực hiện tuning để tìm ra giá trị tối ưu của 2 tham số đó.

3.1 Explore Number of Features

# explore the number of selected features for RFE
from numpy import mean
from numpy import std
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.feature_selection import RFE
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline
from matplotlib import pyplot

# get the dataset
def get_dataset():
	X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
	return X, y

# get a list of models to evaluate
def get_models():
	models = dict()
	for i in range(2, 10):
		rfe = RFE(estimator=DecisionTreeClassifier(), n_features_to_select=i)
		model = DecisionTreeClassifier()
		models[str(i)] = Pipeline(steps=[('s',rfe),('m',model)])
	return models

# evaluate a given model using cross-validation
def evaluate_model(model, X, y):
	cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
	scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
	return scores

# define dataset
X, y = get_dataset()
# get the models to evaluate
models = get_models()
# evaluate the models and store results
results, names = list(), list()
for name, model in models.items():
	scores = evaluate_model(model, X, y)
	results.append(scores)
	names.append(name)
	print('>%s %.3f (%.3f)' % (name, mean(scores), std(scores)))
# plot model performance for comparison
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()

Kết quả thực hiện:

>2 0.715 (0.044)
>3 0.817 (0.034)
>4 0.874 (0.030)
>5 0.888 (0.032)
>6 0.894 (0.027)
>7 0.887 (0.030)
>8 0.884 (0.027)
>9 0.885 (0.025)

Nhìn vào kết quả này thì ta thấy n_features_to_select=6 là giá trị tối ưu nhất.

3.2 Automatically Select the Number of Features

Tham số n_features_to_select tối ưu cũng có thể được lựa chọn để sử dụng một cách tự động. Điều này đạt được bằng cách thực hiện k-Fold Cross-Validation đối với từng giá trị của n_features_to_select, sau đó lựa chọn giá trị n_features_to_select làm cho giá trị Score lớn nhất để sử dụng:

# automatically select the number of features for RFE
from numpy import mean
from numpy import std
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.feature_selection import RFECV
from sklearn.tree import DecisionTreeClassifier
from sklearn.pipeline import Pipeline
# define dataset
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# create pipeline
rfe = RFECV(estimator=DecisionTreeClassifier())
model = DecisionTreeClassifier()
pipeline = Pipeline(steps=[('s',rfe),('m',model)])
# evaluate model
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
n_scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
# report performance
print('Accuracy: %.3f (%.3f)' % (mean(n_scores), std(n_scores)))

Kết quả thực hiện:

Accuracy: 0.886 (0.029)

Trong trường hợp này, RFECV với DecisionTree đã tự động luwuas chọn số lượng features tối ưu, sau đó fit trên toàn bộ tập dữ liệu và thu được kết quả: 88.6%.

3.3 Which Features Were Selected

Nếu muốn biết cụ thể features nào được lựa chọn, features nào bị loại bỏ, ta làm như sau:

# report which features were selected by RFE
from sklearn.datasets import make_classification
from sklearn.feature_selection import RFE
from sklearn.tree import DecisionTreeClassifier
# define dataset
X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
# define RFE
rfe = RFE(estimator=DecisionTreeClassifier(), n_features_to_select=5)
# fit RFE
rfe.fit(X, y)
# summarize all features
for i in range(X.shape[1]):
	print('Column: %d, Selected=%s, Rank: %d' % (i, rfe.support_[i], rfe.ranking_[i]))

Kết quả thực hiện:

Column: 0, Selected=False, Rank: 5
Column: 1, Selected=False, Rank: 4
Column: 2, Selected=True, Rank: 1
Column: 3, Selected=True, Rank: 1
Column: 4, Selected=True, Rank: 1
Column: 5, Selected=False, Rank: 6
Column: 6, Selected=True, Rank: 1
Column: 7, Selected=False, Rank: 2
Column: 8, Selected=True, Rank: 1
Column: 9, Selected=False, Rank: 3

3.4 Explore Base Algorithm

# explore the algorithm wrapped by RFE
from numpy import mean
from numpy import std
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
from sklearn.linear_model import Perceptron
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.pipeline import Pipeline
from matplotlib import pyplot

# get the dataset
def get_dataset():
	X, y = make_classification(n_samples=1000, n_features=10, n_informative=5, n_redundant=5, random_state=1)
	return X, y

# get a list of models to evaluate
def get_models():
	models = dict()
	# lr
	rfe = RFE(estimator=LogisticRegression(), n_features_to_select=5)
	model = DecisionTreeClassifier()
	models['lr'] = Pipeline(steps=[('s',rfe),('m',model)])
	# perceptron
	rfe = RFE(estimator=Perceptron(), n_features_to_select=5)
	model = DecisionTreeClassifier()
	models['per'] = Pipeline(steps=[('s',rfe),('m',model)])
	# cart
	rfe = RFE(estimator=DecisionTreeClassifier(), n_features_to_select=5)
	model = DecisionTreeClassifier()
	models['cart'] = Pipeline(steps=[('s',rfe),('m',model)])
	# rf
	rfe = RFE(estimator=RandomForestClassifier(), n_features_to_select=5)
	model = DecisionTreeClassifier()
	models['rf'] = Pipeline(steps=[('s',rfe),('m',model)])
	# gbm
	rfe = RFE(estimator=GradientBoostingClassifier(), n_features_to_select=5)
	model = DecisionTreeClassifier()
	models['gbm'] = Pipeline(steps=[('s',rfe),('m',model)])
	return models

# evaluate a given model using cross-validation
def evaluate_model(model, X, y):
	cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
	scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
	return scores

# define dataset
X, y = get_dataset()
# get the models to evaluate
models = get_models()
# evaluate the models and store results
results, names = list(), list()
for name, model in models.items():
	scores = evaluate_model(model, X, y)
	results.append(scores)
	names.append(name)
	print('>%s %.3f (%.3f)' % (name, mean(scores), std(scores)))
# plot model performance for comparison
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()

Kết quả thực hiện:

>lr 0.887 (0.030)
>per 0.844 (0.034)
>cart 0.889 (0.031)
>rf 0.856 (0.039)
>gbm 0.892 (0.026)

Số liệu chỉ ra rằng thuật toán Gradient Boosting là lựa chọn tốt nhất trong trường hợp này.

4. Kết luận

Bài thứ 5 về chủ đề Feature Selection, mình đã giới thiệu cách sử dụng thuật toán Recursive Feature Elimination (RFE) trong việc Feature Selection. Toàn bộ code của bài này, các bạn có thể tham khảo tại đây.

Bài tiếp theo mình sẽ giới thiệu về cách sử dụng Feature Importance để thực hiện Feature Selection. Mời các bạn đón đọc.

5. Tham khảo

[1] Jason Brownlee, “Data Preparation for Machine Learning”, Book: https://machinelearningmastery.com/data-preparation-for-machine-learning/.