項目地址:https://github.com/WillKoehrsen/feature-selectorgit
特徵選擇(feature selection)是查找和選擇數據集中最有用特徵的過程,是機器學習流程中的一大關鍵步驟。沒必要要的特徵會下降訓練速度、下降模型可解釋性,而且最重要的是還會下降其在測試集上的泛化表現。github
目前存在一些專用型的特徵選擇方法,我經常要一遍又一遍地將它們應用於機器學習問題,這實在讓人心累。因此我用 Python 構建了一個特徵選擇類並開放在了 GitHub 上。這個 FeatureSelector 包含一些最經常使用的特徵選擇方法:app
具備高缺失值百分比的特徵機器學習
共線性(高度相關的)特徵ide
在基於樹的模型中重要度爲零的特徵函數
重要度較低的特徵學習
具備單個惟一值(unique value)的特徵測試
在本文中,咱們將介紹在示例機器學習數據集上使用 FeatureSelector 的全過程。咱們將看到如何快速實現這些方法,從而實現更高效的工做流程。ui
完整代碼已在 GitHub 上提供,歡迎任何人貢獻。這個特徵選擇器是一項正在進行的工做,將根據社區需求繼續改進!編碼
爲了進行演示,咱們將使用來自 Kaggle「家庭信用違約風險」機器學習競賽的一個數據樣本。
這個競賽是一個監督分類問題,這也是一個很是合適的數據集,由於其中有不少缺失值、大量高度關聯的(共線性)特徵,還有一些無助於機器學習模型的無關特徵。
要建立一個 FeatureSelector 類的實例,咱們須要傳入一個結構化數據集,其中觀察在行中,特徵在列中。咱們可使用一些僅操做特徵的方法,但基於重要度的方法也須要訓練標籤。由於這是一個監督分類任務,因此咱們將使用一組特徵和一組標籤。
from feature_selector import FeatureSelector # Features are in train and labels are in train_labels fs = FeatureSelector(data = train, labels = train_labels)
這個特徵選擇器有 5 種用於查找待移除特徵的方法。咱們能夠訪問任何已被識別出來的特徵並經過人工方式將它們移出數據,也可使用 FeatureSelector 中的 remove 函數。
這裏咱們將介紹其中每種識別方法,還將展現如何同時運行這 5 種方法。此外,FeatureSelector 還有幾個圖表繪製功能,由於可視化地檢查數據是機器學習的一大關鍵部分。
查找和移除特徵的第一個方法很簡單:查找缺失值比例超過特定閾值的特徵。下面的調用能識別缺失值比例超過 60% 的特徵(粗體是輸出結果)。
fs.identify_missing(missing_threshold = 0.6) 17 features with greater than 0.60 missing values.
要查看待移除特徵,咱們能夠讀取 FeatureSelector 的 ops 屬性,這是一個 Python 特徵詞典,特徵會以列表的形式給出。
missing_features = fs.ops['missing'] missing_features[:5] ['OWN_CAR_AGE', 'YEARS_BUILD_AVG', 'COMMONAREA_AVG', 'FLOORSMIN_AVG', 'LIVINGAPARTMENTS_AVG']
最後,咱們能夠繪製一張全部特製的缺失值分佈圖:
共線性特徵是指彼此之間高度關聯的特徵。在機器學習領域,高方差和較低的模型可解釋性致使在測試集上的泛化能力降低。
identify_collinear 方法能基於指定的相關係數值查找共線性特徵。對於每一對相關的特徵,它都會標識出其中要移除的一個(由於咱們只須要移除其中一個):
fs.identify_collinear(correlation_threshold = 0.98) 21 features with a correlation magnitude greater than 0.98.
使用熱圖能夠很好地可視化共線性。下圖展現了全部至少有一個相關關係(correlation)超過閾值的特徵:
fs.plot_collinear()
和以前同樣,咱們能夠訪問將會被移除的整個相關特徵列表,或者在一個 dataframe 中查看高度相關的特徵對。
# list of collinear features to remove collinear_features = fs.ops['collinear'] # dataframe of collinear features fs.record_collinear.head()
若是咱們想全面瞭解數據集,咱們還能夠經過將 plot_all = True 傳入該調用,繪製出數據中全部相關性的圖表:
前面兩種方法可被應用於任何結構化的數據集而且結果是肯定的——對於一個給定的閾值,每次結果都同樣。接下來的方法是專爲監督式機器學習問題設計的,其中咱們有訓練模型的標籤而且是非肯定性的。identify_zero_importance 函數能根據梯度提高機(GBM)學習模型查找重要度爲零的特徵。
咱們可使用基於樹的機器學習模型(好比 boosting ensemble)求取特徵重要度。這個重要度的絕對值沒有相對值重要,咱們能夠將相對值用於肯定對一個任務而言最相關的特徵。咱們還能夠經過移除零重要度特徵來在特徵選擇中使用特徵重要度。在基於樹的模型中,零重要度的特徵不會被用於分割任何節點,因此咱們能夠移除它們而不影響模型表現。
FeatureSelector 能使用來自 LightGBM 庫的梯度提高機來獲得特徵重要度。爲了下降方差,所獲得的特徵重要度是在 GBM 的 10 輪訓練上的平均。另外,該模型還使用早停(early stopping)進行訓練(也可關閉該選項),以防止在訓練數據上過擬合。
下面的代碼調用了該方法並提取出了零重要度特徵:
# Pass in the appropriate parameters fs.identify_zero_importance(task = 'classification', eval_metric = 'auc', n_iterations = 10, early_stopping = True) # list of zero importance features zero_importance_features = fs.ops['zero_importance'] 63 features with zero importance after one-hot encoding.
咱們傳入的參數解釋以下:
task:根據咱們的問題,要麼是「classification」,要麼是「regression」
eval_metric:用於早停的度量(若是早停禁用了,就沒必要使用)
n_iterations:訓練輪數,最後結果取多輪的平均
early_stopping:是否爲訓練模型使用早停
這時候咱們可使用 plot_feature_importances 繪製兩個圖表:
# plot the feature importances fs.plot_feature_importances(threshold = 0.99, plot_n = 12) 124 features required for 0.99 of cumulative importance
左圖給出了 plot_n 最重要的特徵(重要度進行了歸一化,總和爲 1)。右圖是對應特徵數量的累積重要度。藍色豎線標出了累積重要度爲 99% 的閾值。
對於基於重要度的方法,有兩點須要記住:
訓練梯度提高機是隨機的,這意味着模型每次運行後,特徵重要度都會改變。
這應該不會有太大的影響(最重要的特徵不會忽然就變成最不重要的),但這會改變某些特徵的排序,也會影響識別出的零重要度特徵的數量。若是特徵重要度每次都改變,請不要感到驚訝!
要訓練機器學習模型,特徵首先要通過 one-hot 編碼。這意味着某些被識別爲零重要度的特徵多是在建模過程當中加入的 one-hot 編碼特徵。
當咱們到達特徵移除階段時,還有一個選項可移除任何被添加進來的 one-hot 編碼的特徵。可是,若是咱們要在特徵選擇以後作機器學習,咱們仍是必需要 one-hot 編碼這些特徵。
接下來的方法基於零重要度函數,使用來自模型的特徵重要度來進一步選擇。identify_low_importance 函數能找到重要度最低的特徵,這些特徵無助於指定的總重要性。
好比,下面的調用能找到最不重要的特徵,即便沒有這些特徵也能達到 99% 的重要度。
fs.identify_low_importance(cumulative_importance = 0.99) 123 features required for cumulative importance of 0.99 after one hot encoding. 116 features do not contribute to cumulative importance of 0.99.
根據前面的累積重要度圖和這一信息,梯度提高機認爲不少特徵都與學習無關。重申一下,每次訓練運行後該方法的結果都不同。
咱們也能夠在一個 dataframe 中查看全部特徵重要度:
fs.feature_importances.head(10)
low_importance 方法借鑑了主成分分析(PCA)中的一種方法,其中僅保留維持必定方差比例(好比 95%)所需的主成分是很常見的作法。要歸入考慮的總重要度百分比基於同一思想。
只有當咱們要用基於樹的模型來作預測時,基於特徵重要度的方法才真正有用。除告終果隨機以外,基於重要度的方法仍是一種黑箱方法,也就是說咱們並不真正清楚模型認爲某些特徵無關的緣由。若是使用這些方法,屢次運行它們看到結果的改變狀況,也許能夠建立具備不一樣參數的多個數據集來進行測試!
最後一個方法至關基礎:找出任何有單個惟一值的列。僅有單個惟一值的特徵不能用於機器學習,由於這個特徵的方差爲 0。舉個例子,若是一個特徵僅有一個值,那麼基於樹的模型就永遠不能進行區分(由於沒有可作區分的依據)。
fs.identify_single_unique() 4 features with a single unique value.
咱們能夠繪製每一個類別惟一值數量的直方圖:
fs.plot_unique()
在肯定了待移除特徵以後,咱們有兩種移除它們的選擇。全部要移除的特徵都存儲在 FeatureSelector 的 ops 詞典中,咱們可使用這個列表來手動移除它們,固然也可以使用內置的 remove 函數。
對於這一方法,咱們需傳入要用於移除特徵的 methods。若是咱們想使用所實現的全部方法,咱們只需使用 methods = 'all'
# Remove the features from all methods (returns a df) train_removed = fs.remove(methods = 'all') ['missing', 'single_unique', 'collinear', 'zero_importance', 'low_importance'] methods have been run Removed 140 features.
這個方法會返回一個包含被移除特徵的 dataframe。另外,要移除在機器學習過程當中建立的 one-hot 編碼的特徵:
train_removed_all = fs.remove(methods = 'all', keep_one_hot=False) Removed 187 features including one-hot features.
在執行操做以前檢查將被移除的特徵多是個好想法!原來的數據集會被存儲在 FeatureSelector 的 data 屬性中用做備份!
除了單獨使用各個方法以外,咱們也可經過 identify_all 一次性使用全部方法。咱們須要使用一個詞典來設定其中每一個方法的參數:
fs.identify_all(selection_params = {'missing_threshold': 0.6, 'correlation_threshold': 0.98, 'task': 'classification', 'eval_metric': 'auc', 'cumulative_importance': 0.99}) 151 total features out of 255 identified for removal after one-hot encoding.
這個特徵選擇器類實現了訓練機器學習模型以前幾種用於移除特徵的常見操做。其提供了可用於識別待移除特徵的函數以及可視化函數。這些方法能夠單獨使用,也能夠一次所有應用以實現高效的工做流程。
其中 missing、collinear 和 single_unique 方法是肯定性的,而基於特徵重要度的方法會隨每次運行而變化。與機器學習領域很類似,特徵選擇很大程度上是實證式的,須要測試多種組合才能找到最優解。最好的作法是在流程中嘗試多種配置,而且 FeatureSelector 提供了一種用於快速評估特徵選擇參數的方法。