全文共5462字,預計學習時長11分鐘
圖片來源:https://unsplash.com/@seefromthesky
數據科學是研究算法的學問。本文將會介紹一些處理數據時最常用的特徵選擇技術。
我們經常遇到這樣的情況:在創建了大量特徵後,又需要減少數量,最後再使用相關性或者基於樹的方法來找出其中的重要特徵。那麼,如何讓這些方法變得更有結構呢?
為何要進行特徵選擇?
在文章開始之前,先來回答這個問題:為什麼不將所有的特徵都交給機器學習算法,讓它來決定哪些特徵比較重要呢?
以下便是不能的三個原因:
1. 維數的詛咒——過擬合
維數的詛咒:隨著特徵空間維數的增加,配置數量將會以指數級增長,因此觀測量會下降
如果數據中的列數多於行數,就能完美匹配訓練數據,但這卻並不適用於新樣本。因此這樣什麼也沒學到。
2. 奧卡姆剃刀原理
模型應當簡單易懂。如果特徵太多,就失去了可解釋性。
3. 無用輸入和輸出
很多時候會出現像名稱或ID變量等非信息特徵。劣質輸入也會產生劣質輸出。
此外,大量特徵會使模型量大、耗時,更難落實生產。
那應該怎麼做?
答案是:只選擇有用特徵。
幸運的是,Scikit-learn能便捷簡單地選擇特徵。特性選擇有很多種方法,但是大多可以分為三類:
過濾:列入一些篩選特徵的標準,如相關性/卡方分布。
包裝:包裝法將特徵選擇看作是搜索問題。如回歸特徵消除。
嵌入:嵌入法使用內置了特徵選擇方法的算法。比如,Lasso和RF都有各自的特徵選擇方法。
理論已經講夠了,接下來開始說五個特徵選擇的方法。
本文將使用數據集來方便理解——用一個足球運動員的數據集來找到成為優秀足球運動員的秘訣。
如果不理解足球術語也沒關係,術語並不多。
Kaggle Kernel代碼:https://www.kaggle.com/mlwhiz/feature-selection-using-football-data?source=post_page---------------------------
一些簡單的數據預處理
此前已進行了一些基本的預處理,比如刪除空值和一次熱編碼。將此問題轉化為分類問題要用:
y = traindf['Overall']>=87
這裡使用整體最高代指一個優秀球員。數據集(X)如下所示,有223列。
train Data X
1. 皮爾遜相關係數
這個是過濾法的一種。
檢查數據集裡目標和數值特徵之間皮爾遜相關係數8的絕對值。根據這個準則保留前n個特徵。
def cor_selector(X, y,num_feats):
cor_list = []
feature_name = X.columns.tolist()
# calculate the correlation with y for each feature
for i in X.columns.tolist():
cor = np.corrcoef(X[i], y)[0, 1]
cor_list.append(cor)
# replace NaN with 0
cor_list = [0 if np.isnan(i) else i for i in cor_list]
# feature name
cor_feature = X.iloc[:,np.argsort(np.abs(cor_list))[-num_feats:]].columns.tolist()
# feature selection? 0 for not select, 1 for select
cor_support = [True if i in cor_feature else False for i in feature_name]
return cor_support, cor_feature
cor_support, cor_feature = cor_selector(X, y,num_feats)
print(str(len(cor_feature)), 'selected features')
2. 卡方分布
另一種過濾法。
該方法計算目標與數值變量之間的卡方度量分布,只選取卡方值最大的變量。
舉個小例子來說明如何計算樣本的卡方統計量。
假設數據集中有75個右前鋒和25個非右前鋒。觀察到其中40個右前鋒好,35個不好。這是否意味著右前鋒會影響整體表現呢?
觀察和預期計數
計算卡方值:
要計算,首先要找出每個部分中的期望值,如果這兩個分類變量之間確實獨立的話。
很簡單,將每個單元格的行和與列和相乘,並將其除以總觀察值。
因此「好的」和「非右前鋒的」期望值=25(行和)*60(列和)/100(總觀察量)
為什麼會這樣?因為數據中有25%,所以預計在觀察到的60名優秀球員中,有25%的人會是右前鋒球員。因此是15個。
然後可以用下面的公式對所有4個單元格求和:
此處不會展示,但卡方統計量也有非負數值和分類特徵。
數據集中可以得到如下卡方特徵:
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
from sklearn.preprocessing import MinMaxScaler
X_norm = MinMaxScaler().fit_transform(X)
chi_selector = SelectKBest(chi2, k=num_feats)
chi_selector.fit(X_norm, y)
chi_support = chi_selector.get_support()
chi_feature = X.loc[:,chi_support].columns.tolist()
print(str(len(chi_feature)), 'selected features')
3. 遞歸特徵消除
這是一個包裝法。如上所述,包裝法將一組特性的選擇視為搜索問題。
來自sklearn 文件:
遞歸特徵消除(RFE)的目標是遞歸地考慮越來越小的特徵集來選擇特徵。首先,用初始特徵集訓練估計器,通過 coef_或者feature_importances_屬性可以獲得特徵的重要性。然後,從當前的一組特性中刪除最不重要的。該過程在修剪集上遞歸地重複,直到最終達到所需的要選擇的特性數量。
大家可能猜到了,這個方法可以用在任何估計器上。此處用的是 LogisticRegression ,而RFE觀察了 LogisticRegression對象的coef_屬性。
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
rfe_selector = RFE(estimator=LogisticRegression(), n_features_to_select=num_feats, step=10, verbose=5)
rfe_selector.fit(X_norm, y)
rfe_support = rfe_selector.get_support()
rfe_feature = X.loc[:,rfe_support].columns.tolist()
print(str(len(rfe_feature)), 'selected features')
4. 套索:SelectFromModel
這個是嵌入法。如前所述,嵌入法使用有內置特徵選擇方法的算法。
例如,Lasso和RF都有自己的特徵選擇方法。Lasso正則化器強制許多特徵權重為零。
這裡用套索來選擇變量。
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
embeded_lr_selector = SelectFromModel(LogisticRegression(penalty="l1"), max_features=num_feats)
embeded_lr_selector.fit(X_norm, y)
embeded_lr_support = embeded_lr_selector.get_support()
embeded_lr_feature = X.loc[:,embeded_lr_support].columns.tolist()
print(str(len(embeded_lr_feature)), 'selected features')
5. 基於樹形結構:SelectFromModel
這是嵌入法。如前所述,嵌入法使用有內置特徵選擇方法的算法。
還可以使用隨機森林,根據特徵的重要性來選擇特徵。
使用每個決策樹中的節點雜質來計算特徵的重要性。隨機森林中,最終的特徵重要性是所有決策樹特徵重要性的平均值。
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier
embeded_rf_selector = SelectFromModel(RandomForestClassifier(n_estimators=100), max_features=num_feats)
embeded_rf_selector.fit(X, y)
embeded_rf_support = embeded_rf_selector.get_support()
embeded_rf_feature = X.loc[:,embeded_rf_support].columns.tolist()
print(str(len(embeded_rf_feature)), 'selected features')
也可以使用 LightGBM或者XGBoost 對象,只要它有feature_importances_屬性。
from sklearn.feature_selection import SelectFromModel
from lightgbm import LGBMClassifier
lgbc=LGBMClassifier(n_estimators=500, learning_rate=0.05, num_leaves=32, colsample_bytree=0.2,
reg_alpha=3, reg_lambda=1, min_split_gain=0.01, min_child_weight=40)
embeded_lgb_selector = SelectFromModel(lgbc, max_features=num_feats)
embeded_lgb_selector.fit(X, y)
embeded_lgb_support = embeded_lgb_selector.get_support()
embeded_lgb_feature = X.loc[:,embeded_lgb_support].columns.tolist()
print(str(len(embeded_lgb_feature)), 'selected features'
彩蛋
既有森林,何須獨木?
答案是,有時候數據太多、時間緊迫,全部使用是不可能的。
但只要有可能,為什麼不這樣做呢?
# put all selection together
feature_selection_df = pd.DataFrame({'Feature':feature_name, 'Pearson':cor_support, 'Chi-2':chi_support, 'RFE':rfe_support, 'Logistics':embeded_lr_support,
'Random Forest':embeded_rf_support, 'LightGBM':embeded_lgb_support})
# count the selected times for each feature
feature_selection_df['Total'] = np.sum(feature_selection_df, axis=1)
# display the top 100
feature_selection_df = feature_selection_df.sort_values(['Total','Feature'] , ascending=False)
feature_selection_df.index = range(1, len(feature_selection_df)+1)
feature_selection_df.head(num_feats)
functionComputeSMA(data,window_size)
檢查一下,用了以上所有方法後是否得到了特徵。此處可以看到反應和長傳是好評球員的優秀屬性。和預期一樣,控球和最後得分也佔據了首位。
結論
特徵工程和特徵選擇是所有機器學習分支的關鍵。
模型要力求精確,不一遍遍查看是不會獲得良好精度的。
本文試圖解釋了一些最常用的特性選擇技術,以及特性選擇方面的工作流程,並且試圖對這些方法提供一些直觀認識。
留言 點讚 關注
我們一起分享AI學習與發展的乾貨
編譯組:李美欣、蔣馨怡
相關連結:
https://towardsdatascience.com/the-5-feature-selection-algorithms-every-data-scientist-need-to-know-3a6b566efd2
如需轉載,請後臺留言,遵守轉載規範