- vừa được xem lúc

Lựa chọn Feature sao cho đúng?

0 0 2

Người đăng: Viblo AI

Theo Viblo Asia

Đa phần trong chúng ta khi thực hiện feature-selection đều sử dụng "SelectFromModel" (một module của Scikit-learn). Thường thì công việc ta sẽ làm như sau:

  1. Bạn chọn một mô hình dự đoán (ta sẽ gọi nó là WhatevBoost)
  2. Thực hiện fit WhatevBoost với tất cả feature
  3. Trích xuất những feature quan trọng từ WhatevBoost
  4. Loại bỏ tất cả những feature có threshold thấp hơn mong muốn và giữ lại những feature còn lại

Quá trình này nghe có vẻ hợp lý phải không 😄 Câu trả lời sẽ làm bạn bất ngờ đấy 😄

Trong bài viết này, ta sẽ cùng kiểm nghiệm xem cách làm này có thật sự hiệu quả không nhé 😃

Mô phỏng dữ liệu

Trong bài viết, ta sẽ sử dụng dữ liệu mô phỏng. Đây là một cách tiếp cận tốt để thực hiện các thử nghiệm mà đa phần chúng ta ít khi sử dụng. Vậy thì tập dữ liệu mô phỏng trông sẽ như nào nhỉ 😄 Trước tiên, ta sẽ tạo các biến độc lập (hay các feature), một phần trong số các biến này liên quan đến biến mục tiêu (được gọi là y), phần còn sẽ là nhiễu (noise). Mục tiêu là tìm ra một phương pháp chọn các feature nhằm xác định xem feature nào liên quan tới y và feature nào là nhiễu. Ta sẽ tạo một ma trận feature gồm 16 feature độc lập như sau:

feature_names = [ 'linear', # 1 'nonlinear_square', # 2 'nonlinear_sin', # 3 'interaction_1', # 4 'interaction_2', # 5 'interaction_3', # 6 'noise_1', # 7 'noise_2', # 8 'noise_3', # 9 'noise_4', # 10 'noise_5', # 11 'noise_6', # 12 'noise_7', # 13 'noise_8', # 14 'noise_9', # 15 'noise_10' # 16
]

Dựa vào tên thì chắc hẳn bạn đã đoán được vai trò của các feature đối với y rồi nhỉ 😄 6 feature đầu tiên có mối quan hệ với y, 10 feature còn lại là nhiễu.

Dạng hàm của y phải đủ phức tạp để bao gồm một số tác động không tầm thường tồn tại trong tự nhiên giữa các biến. Đặc biệt, ta cần xét đến các mối quan hệ như sau:

  • Linear
  • Non linear
  • Interactions

Ngoài ra, mối quan hệ giữa xy nên là non-deteministic, do đó một hệ số sai số, được gọi là εε được thêm vào. Ta có thể xây dựng một hàm y như sau:

Ta có thể chuyển sang code python như sau:

def X2y(X, with_error = True): # functional form of the dependence between y and X y_star = X['linear'] + X['nonlinear_square'] ** 2 + np.sin(3 * X['nonlinear_sin']) + (X['interaction_1'] * X['interaction_2'] * X['interaction_3']) # add random error called epsilon (this will be used for creating y) if with_error: np.random.seed(0) epsilon = np.random.normal(0, .1, len(y_star)) return y_star + epsilon # do not add error (this will be used for prediction) else: return y_star

Okay! Giờ ta sẽ bắt đầu gen X và y, sau đó chia thành tập train và tập test.

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split # make X and y
np.random.seed(0)
X = pd.DataFrame(np.random.normal(size = (20_000, len(feature_names))), columns = feature_names)
y = X2y(X, with_error = True) # make X_trn, X_tst, y_trn, y_tst
X_trn, X_tst, y_trn, y_tst = train_test_split(X, y, test_size = .5, random_state = 0)

Bật chế độ "SelectFromModel"

Giờ ta đã có trong tay data rồi, công việc tiếp theo sẽ là chọn một số model dự đoán và thử chạy chúng. Ta sẽ lấy thử 8 model sau:

from sklearn.dummy import DummyRegressor
from sklearn.linear_model import LinearRegression
from sklearn.neighbors import KNeighborsRegressor
from sklearn.svm import SVR
from sklearn.ensemble import RandomForestRegressor
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor # dictionary of models that will be used for comparison
models = { 'DummyRegressor': DummyRegressor(), 'LinearRegression': LinearRegression(), 'KNeighborsRegressor': KNeighborsRegressor(n_neighbors = int(np.sqrt(len(X_trn)))), 'SupportVectorRegressor': SVR(C = .1), 'RandomForestRegressor': RandomForestRegressor(max_depth = 5), 'XGBRegressor': XGBRegressor(max_depht = 5), 'LGBMRegressor': LGBMRegressor(num_leaves = 10), 'UnbeatableRegressor': UnbeatableRegressor()
}

Nhìn đoạn code trên, bạn có thể sẽ hỏi UnbeatableRegressor là gì vậy? 😄 Ta gọi đó là một model hoàn hảo bởi vì bản chất đây chính là công thức hàm y mà ta đã xây dựng ở trên, đó chính là công thức hàm ban đầu nên chắc chắn không thể sai đi đâu được rồi 😄

Để có một regressor thích hợp ta cần bọc hàm trong 1 class:

# define a sklearn compatible wrapper for our data generating function
class UnbeatableRegressor(): def __init__(self): pass def fit(self, X, y): pass def predict(self, X): return np.array(X2y(X, with_error = False)) def score(self, X, y): return mean_absolute_error(y, self.predict(X))

Tiếp theo ta sẽ fit các model với dữ liệu training và thử xem kết quả mean absolute error sẽ như nào nhé 😄

from sklearn.metrics import mean_absolute_error
from eli5.sklearn import PermutationImportance mae = pd.DataFrame(columns = ['train', 'test'])
fi = pd.DataFrame(columns = feature_names) for model_name in list(models.keys()): # fit model models[model_name].fit(X_trn, y_trn) # compute mean absolute error of model in train and test set mae.loc[model_name,:] = [mean_absolute_error(y_trn, models[model_name].predict(X_trn)), mean_absolute_error(y_tst, models[model_name].predict(X_tst))] # compute feature importances of model try: feature_importances_ = models[model_name].feature_importances_ except: feature_importances_ = PermutationImportance(models[model_name], cv = 'prefit', n_iter = 3).fit(X_trn, y_trn).feature_importances_ fi.loc[model_name, :] = feature_importances_ / feature_importances_.sum() fi.fillna(0, inplace = True)

Kết quả ta thu được như sau:

Các model ML như XGBoostLightGBM thể hiện performance ngon hơn các model còn lại mặc dù 2 model này vẫn còn cách khá xa mức tối thiểu error về mặt lý thuyết (thu được từ "model" UnbeatableRegressor). Okay! Giờ ta sẽ tập trung xem xem feature nào là quan trọng, việc này được thực hiện bởi "SelectFromModel".

Tầm quan trọng của một feature là một khái niệm khá khó nắm bắt trong học máy, có nghĩa là không có cách tính toán cụ thể nào. Mặc dù vậy, ý tưởng ta sử dụng sẽ khá trực quan: Định lượng mức quan trọng của 1 feature bằng cách xem xem nó đóng góp như nào với độ chính xác của mô hình.

Feature có giá trị 0% khi không ảnh hưởng đến độ chính xác của mô hình theo bất kỳ cách nào (vì vậy nó có thể bị loại bỏ). Feature có giá trị 100% khi là feature duy nhất ảnh hưởng đến các dự đoán. Trong các ứng dụng thực tế, các feature không bao giờ có mức quan trọng là 0% hoặc 100%, chúng thường nằm ở giữa 2 giá trị này.

Tuy nhiên, vì ta chỉ có 16 feature và 10 trong số chúng (noise) không có tác động gì với y nên ta có thể mong đợi rằng mức độ quan trọng của 10 feature này là 0%. 6 feature còn lại sẽ chia sẻ mức độ quan trọng với model 😄

Hmm! Cơ mà 0% có vẻ là một con số cực đoan. Để an toàn hơn thì ta sẽ lấy giá trị 1%, tức là ta sẽ bỏ những feature có mức độ quan trọng dưới 1%. Giá trị threshold này khá ngẫu nhiên nhưng khi xét với chỉ 16 feature, nó có vẻ khá hợp lý khi cho rằng feature có mức độ quan trọng dưới 1% là không hữu ích.

Ảnh dưới cho ta thấy được mức độ quan trọng của các feature trong 8 model. Các feature có mức độ quan trọng dưới 1% được đánh dấu là "[DROP]" ở gần tên.

Đáng ngạc nhiên là một trong những mô hình tệ nhất - KNeighborsRegressor - là mô hình duy nhất đoán đúng tất cả các feature liên quan (tuy nhiên, ta để ý rằng feature nonlinear_sin ở trạng thái "hấp hối" 😄, vì mức độ quan trọng của nó là 1,17%, giá trị threshold chỉ cần khác đi một chút có thể làm feature này bị drop 😄).

Model có mức độ quan trọng của feature gần với giá trị thực nhất (UnbeatableRegressor) là LGBMRegressor, tuy nhiên, ta quan sát thấy có nhiều noise feature được đánh giá có mức quan trọng quá cao theo nghĩa là giá trị của chúng trên 1%.

Qua thực nghiệm trên, ta có một nhận xét quan trọng là một model tốt khi đánh giá theo performance không nhất thiết là model tốt khi đánh giá theo việc lựa chọn feature. Hay nói một cách khác, một model xuất sắc không đảm bảo cho việc lựa chọn feature thành công 😄 Ngoài ra, thực nghiệm này bị nhạy cảm với việc chọn threshold: một threshold khác sẽ cho một kết quả hoàn toàn khác.

Vì những lý do này, chúng ta hãy thử tìm kiếm một giải pháp thay thế. Một cách tiếp cận hay ho đó là sử dụng Boruta.

Sử dụng Boruta cho trích chọn feature

Boruta là thuật toán được sử dụng cho việc lựa chọn feature được đề xuất từ năm 2010. Ý tưởng đằng sau Boruta thật sự rất thông minh. Trong giới hạn bài viết này, mình sẽ giới hạn các kiến thức chuyên sâu trong Boruta và sử dụng thuật toán này bằng cách import một thư viện Python có sẵn.

Boruta là một wrapper. Ta sẽ wrap một model random forest với max_depth = 5 như sau:

!pip install Boruta
from boruta import BorutaPy # instantiate random forest
forest = RandomForestRegressor(n_jobs = -1, max_depth = 5) # fit boruta
boruta_selector = BorutaPy(forest, n_estimators = 'auto', random_state = 0)
boruta_selector.fit(np.array(X_trn), np.array(y_trn)) # store results
boruta_ranking = boruta_selector.ranking_
selected_features = np.array(feature_names)[boruta_ranking <= 2]

Bạn có thể sẽ tự hỏi "tại sao lại là RandomForestRegressor? Trong thực nghiệm trước, đây là một trong model có performance tệ nhất mà nhỉ?". Đây chính là vẻ đẹp của Boruta: Mặc dù wrap một model yếu như random forest nhưng nó vẫn có thể khắc phục được điều này.

Output của Boruta là một feature ranking giúp bạn có thể chia nhỏ các feature thành 3 loại:

  • Ranking 1: Confirmed features (các feature này mang một số tín hiệu liên quan đến biến mục tiêu, vì vậy chúng nên được giữ lại)
  • Ranking 2: Tentative features (Boruta do dự về những feature này, sự lựa chọn là tùy thuộc vào bạn)
  • Ranking 3 hoặc cao hơn: Rejected features (Các feature trong ranking này là nhiễu)

Ta sẽ visualize ranking của các feature như sau:

Boruta thật sự hoạt động tốt. Thuật toán này phân loại đúng 6 feature có nghĩa và 10 feature là noise như ta mong đợi.

Tổng kết

Trong bài viết này, chúng ta đã sử dụng dữ liệu mô phỏng để chứng minh rằng ngay cả các mô hình phức tạp như Xgboost hoặc LightGBM có thể không phải là lựa chọn tốt khi sử dụng cho lựa chọn tfeature. Để thay thế, bài viết đã đề xuất một package Python khá mạnh nhưng chưa được biết đến có tên là Boruta, thuật toán này đã mang lại kết quả mong muốn. Nếu bạn muốn tìm hiểu thuật toán Boruta hoạt động như nào, hãy tiếp tục theo dõi bài viết tiếp theo nhé.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Hành trình AI của một sinh viên tồi

Mình ngồi gõ những dòng này vào lúc 2h sáng (chính xác là 2h 2 phút), quả là một đêm khó ngủ. Có lẽ vì lúc chiều đã uống cốc nâu đá mà giờ mắt mình tỉnh như sáo, cũng có thể là vì những trăn trở về lý thuyết chồng chất ánh xạ mình đọc ban sáng khiến không tài nào chợp mắt được hoặc cũng có thể do mì

0 0 131

- vừa được xem lúc

[Deep Learning] Key Information Extraction from document using Graph Convolution Network - Bài toán trích rút thông tin từ hóa đơn với Graph Convolution Network

Các nội dung sẽ được đề cập trong bài blog lần này. . Tổng quan về GNN, GCN. Bài toán Key Information Extraction, trích rút thông tin trong văn bản từ ảnh.

0 0 204

- vừa được xem lúc

Tìm hiểu về YOLO trong bài toán real-time object detection

1.Yolo là gì. . Họ các mô hình RCNN ( Region-Based Convolutional Neural Networks) để giải quyết các bài toán về định vị và nhận diện vật thể.

0 0 272

- vừa được xem lúc

Encoding categorical features in Machine learning

Khi tiếp cận với một bài toán machine learning, khả năng cao là chúng ta sẽ phải đối mặt với dữ liệu dạng phân loại (categorical data). Khác với các dữ liệu dạng số, máy tính sẽ không thể hiểu và làm việc trực tiếp với categorical variable.

0 0 244

- vừa được xem lúc

TF Lite with Android Mobile

Như các bạn đã biết việc đưa ứng dụng đến với người sử dụng thực tế là một thành công lớn trong Machine Learning.Việc làm AI nó không chỉ dừng lại ở mức nghiên cứu, tìm ra giải pháp, chứng minh một giải pháp mới,... mà quan trọng là đưa được những nghiên cứu đó vào ứng dụng thực tế, được sử dụng để

0 0 55

- vừa được xem lúc

Xây dựng hệ thống Real-time Multi-person Tracking với YOLOv3 và DeepSORT

Trong bài này chúng ta sẽ xây dựng một hệ thống sử dụng YOLOv3 kết hợp với DeepSORT để tracking được các đối tượng trên camera, YOLO là một thuật toán deep learning ra đời vào tháng 5 năm 2016 và nó nhanh chóng trở nên phổ biến vì nó quá nhanh so với thuật toán deep learning trước đó, sử dụng YOLO t

0 0 303