一個可以進行機器學習特征選擇的Python工具
特征選擇 是在數據集中尋找和選擇最有用的特征的過程,是 機器學習 pipeline中的一個關鍵步驟。不必要的特征降低了訓練速度,降低了模型的可解釋性,最重要的是,降低了測試集的泛化性能。
我發現自己一遍又一遍地為機器學習問題應用特別的特征選擇方法,這讓我感到沮喪,于是我在 Python 中構建了一個特征選擇類可在GitHub上找到。 FeatureSelector包括一些最常見的特征選擇方法:
- 缺失值百分比高的特征
- 共線性(高相關性)的特征
- 在基于樹的模型中零重要性的特征
- 低重要性的特征
- 具有單一唯一值的特征
在本文中,我們將在一個樣例機器學習數據集上使用 FeatureSelector。我們將看到它如何允許我們快速實現這些方法,從而實現更有效的工作流。
完整的代碼可在GitHub上獲得,我鼓勵任何貢獻。 Feature Selector正在開發中,并將根據社區的需要不斷改進!
樣例數據集
在本例中,我們將使用Kaggle上的Home Credit Default Risk machine learning competition數據。(要開始競賽,請參見本文)。整個數據集可下載,這里我們將使用一個例子來演示。
這個比賽是一個監督分類問題,這是一個很好的數據集,因為它有許多缺失的值,許多高度相關(共線)的特征,和一些不相關的特征,這對機器學習模型沒有幫助。
創建實例
要創建 FeatureSelector類的實例,我們需要傳入一個結構化數據集,其中包含行和列中的特征。我們可以使用一些只有特征的方法,但是基于重要性的方法也需要訓練標簽。由于我們有一個監督分類任務,我們將使用一組特征和一組標簽。
(確保在與feature_selector.py相同的目錄中運行這個腳本)
方法
特征選擇器有五種方法來查找要刪除的特征。我們可以訪問任何已識別的特征并手動從數據中刪除它們,或者使用特征選擇器中的“remove”函數。
在這里,我們將詳細介紹每種識別方法,并展示如何同時運行所有5種方法。 FeatureSelector還具有一些繪圖功能,因為可視化檢查數據是機器學習的關鍵組件。
缺失值
查找要刪除的特征的第一種方法很簡單:看看哪些特征的缺失值的比例大于某個閾值。下面的調用標識了缺失值超過60%的特征。
- fs.identify_missing(missing_threshold = 0.6)
- 17 features with greater than 0.60 missing values.
我們可以看到dataframe中每一列缺失值的比例:
- fs.missing_stats.head()
要查看要刪除的特征,我們訪問 FeatureSelector的 ops屬性,這是一個Python字典,值為特征列表。
- missing_features = fs.ops['missing']
- missing_features[:5]
- ['OWN_CAR_AGE',
- 'YEARS_BUILD_AVG',
- 'COMMONAREA_AVG',
- 'FLOORSMIN_AVG',
- 'LIVINGAPARTMENTS_AVG']
最后,我們繪制了所有特征缺失值的分布圖:
- fs.plot_missing()
共線性的特征
共線性特征是彼此高度相關的特征。在機器學習中,由于方差大、模型可解釋性差,導致測試集泛化性能下降。
方法 identify_collinear根據指定的相關系數值查找共線特征。對于每一對相關的特征,它識別出要刪除的特征之一(因為我們只需要刪除一個):
- fs.identify_collinear(correlation_threshold = 0.98)
- 21 features with a correlation magnitude greater than 0.98.
我們可以用關聯做出一個清晰的可視化,那就是熱圖。這顯示了在閾值以上至少有一個相關性特征的所有特征:
- 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)學習模型查找不重要的特征。
使用基于樹的機器學習模型,例如增強集成,我們可以找到特征重要性。重要性的絕對值沒有相對值重要,相對值可以用來確定任務的最相關的特征。我們還可以通過刪除零重要性的特征來進行特征選擇。在基于樹的模型中,不使用零重要性的特征來分割任何節點,因此我們可以在不影響模型性能的情況下刪除它們。
FeatureSelector使用LightGBM庫中的梯度提升機查找特征重要性。為了減少方差,將GBM的10次訓練的特征重要性計算平均值。此外,使用帶有驗證集的early stop(可以選擇關閉驗證集)對模型進行訓練,以防止對訓練數據的過擬合。
下面的代碼調用該方法,提取零重要性特征:
我們傳入的參數如下:
任務:對應問題的“分類”或“回歸”
eval_metric:用于早期停止的指標(如果禁用了早期停止,則沒有必要使用該指標)
n_iteration:訓練次數,用來對特征重要性取平均
early ly_stop:是否使用early stop來訓練模型
這次我們得到了兩個帶有 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%。
對于基于重要性的方法,有兩個注意事項值得記住:
- 梯度提升機的訓練是隨機的,這意味著每次運行模型時特征輸入都會發生變化
這應該不會產生重大影響(最重要的特征不會突然變得最不重要),但是它會改變一些特征的順序。它還可以影響識別的零重要性特征的數量。如果特征的重要性每次都發生變化,不要感到驚訝!
- 為了訓練機器學習模型,首先對特征進行“獨熱編碼”。這意味著一些重要性為0的特征可能是在建模過程中添加的獨熱編碼特征。
當我們到達特征刪除階段時,有一個選項可以刪除任何添加的獨熱編碼特征。然而,如果我們在特征選擇之后進行機器學習,我們還是要對特征進行一次獨熱編碼!
低重要性特征
下一個方法建立在零重要性函數的基礎上,利用模型的特征輸入進行進一步的選擇。函數 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%)的PC。占總重要性的百分比是基于相同的思想。
基于特征重要性的方法只有在我們使用基于樹的模型進行預測時才真正適用。除了隨機性之外,基于重要性的方法是一種黑盒方法,因為我們不知道為什么模型認為這些特征是無關的。如果使用這些方法,請多次運行它們以查看結果的變化,也許還可以創建具有不同參數的多個數據集進行測試!
單一唯一值的特征
最后一個方法是相當基本的:找到任何只有一個惟一值的列。只有一個惟一值的特征對機器學習沒有用處,因為這個特征的方差為零。例如,基于樹的模型永遠不能對只有一個值的特征進行分割(因為沒有分組來劃分觀察結果)。
這里沒有參數選擇,不像其他方法:
- fs.identify_single_unique()
- 4 features with a single unique value.
我們可以繪制每個類別中唯一值的數量直方圖:
- fs.plot_unique()
需要記住的一點是,在默認情況下計算panda中的惟一值之前先刪除 NaNs 。
去除特征
一旦我們確定了要丟棄的特征,我們有兩個選項來刪除它們。所有要刪除的特征都存儲在 FeatureSelector的 ops字典中,我們可以使用列表手動刪除特征。另一個選項是使用“remove”內置函數。
對于這個方法,我們傳入用于刪除特征的 方法。如果我們想使用所有實現的方法,我們只需傳入 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。還可以刪除機器學習過程中創建的獨熱編碼特征:
- train_removed_all = fs.remove(methods = 'all', keep_one_hot=False)
- Removed 187 features including one-hot features.
在繼續操作之前,檢查將被刪除的特征可能是一個好主意!原始數據集存儲在 FeatureSelector的 data 屬性中作為備份!
一次運行所有方法
我們可以使用 identify_all而不是單獨使用這些方法。這需要每個方法的參數字典:
請注意,由于我們重新運行了模型,總特征的數量將發生變化。然后可以調用“remove”函數來刪除這些特征。
總結
在訓練機器學習模型之前,Feature Selector類實現了幾個常見的刪除特征的操作。它提供了識別要刪除的特征以及可視化功能。方法可以單獨運行,也可以一次全部運行,以實現高效的工作流。
missing、 collinear和 single_unique方法是確定的,而基于特征重要性的方法將隨著每次運行而改變。特征選擇,就像機器學習領域,很大程度上是經驗主義的,需要測試多個組合來找到最佳答案。在pipeline中嘗試幾種配置是最佳實踐,特征選擇器提供了一種快速評估特征選擇參數的方法。