機器學習之特征選擇方法


特征選擇是一個重要的數據預處理過程,在現實機器學習任務中,獲得數據之后通常先進行特征選擇,此后在訓練學習器,如下圖所示:
image

進行特征選擇有兩個很重要的原因:

  • 避免維數災難:能剔除不相關(irrelevant)或冗余(redundant )的特征,從而達到減少特征個數,提高模型精確度,減少運行時間的目的
  • 降低學習任務的難度:選取出真正相關的特征簡化模型,協助理解數據產生的過程

如流程圖所示,特征選擇包括兩個環節:

  • 子集搜索 (subset search)

  • 子集評價 (subset evaluation)

《機器學習》將特征選擇分為了三種方法:分別是過濾式(filter) 、包裹式(wrapper)和嵌入式(embedded)。下面依據sklearn中的特征選擇文檔來敘述特征選擇的幾個方法。

過濾式(filter)

這類方法先對數據機進行特征選擇,然后再訓練學習器,特征選擇的過程與后續學習器無關。

移除低方差的特征

VarianceThreshold 是特征選擇的一個簡單基本方法,它會移除所有那些方差不滿足閾值的特征。默認情況下,它將會移除所有的零方差特征,即那些在所有的樣本上的取值均不變的特征。
例如,假設我們有一個特征是布爾值的數據集,我們想要移除那些在整個數據集中特征值為0或者為1的比例超過80%的特征。布爾特征是伯努利( Bernoulli )隨機變量,變量的方差為\(Var[X]=p(1-p)\)

因此,我們可以使用閾值.8*(1-.8)進行選擇

>>> from sklearn.feature_selection import VarianceThreshold
>>> X = [[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]]
>>> sel = VarianceThreshold(threshold=(.8 * (1 - .8)))
>>> sel.fit_transform(X)
array([[0, 1],
       [1, 0],
       [0, 0],
       [1, 1],
       [1, 0],
       [1, 1]])

正如預期一樣, VarianceThreshold 移除了第一列。

單變量特征選擇

單變量的特征選擇是通過基於單變量的統計測試來選擇最好的特征。它可以當做是評估器的預處理步驟。Scikit-learn 將特征選擇的內容作為實現了 transform 方法的對象:

  • SelectKBest 移除那些除了評分最高的 K 個特征之外的所有特征
  • SelectPercentile 移除除了用戶指定的最高得分百分比之外的所有特征
  • GenericUnivariateSelect 允許使用可配置方法來進行單變量特征選擇。它允許超參數搜索評估器來選擇最好的單變量特征。

例如下面的實例,我們可以使用 \(\chi^{2}\) (卡方檢驗)檢驗樣本集來選擇最好的兩個特征:

>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectKBest
>>> from sklearn.feature_selection import chi2
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
(150, 4)
>>> X_new = SelectKBest(chi2, k=2).fit_transform(X, y)
>>> X_new.shape
(150, 2)

很明顯,上述兩種方法都是過濾式特征選擇的方法,所以與模型無關的特征權重顯得尤為重要。這種權重主要分析特征與target的相關性,這樣的分析是與這次學習所使用的模型無關的。與模型無關特征權重分析方法包括(1)交叉熵,(2)Information Gain,(3)Odds ratio,(4)互信息,(5)KL散度(相對熵)等

上述代碼用到的是經典的卡方檢驗,這里簡單敘述原理:

經典的卡方檢驗是檢驗定性自變量對定性因變量的相關性。假設自變量有N種取值,因變量有M種取值,考慮自變量等於i且因變量等於j的樣本頻數的觀察值與期望的差距,構建統計量:

$\chi^{2} = \sum\frac{(A-E)^{2}}{E}$

A為實際值, E為理論值,求和值為理論值與實際值的差異程度。

基本思想是根據樣本數據推斷總體的分布與期望分布是否有顯著性差異,或者推斷兩個分類變量是否相關或者獨立。
卡方檢驗具體可參考這篇博客:卡方分布與卡方檢驗

包裹式(wrapper)

這類方法選擇直接把最終將要使用學習期的性能作為特征子集的評價准則。

遞歸式特征消除(RFE)

給定一個外部的估計器,該估計起對特征賦予一定的權重(比如,線性模型的系數),recursive feature elimination ( RFE ) 通過處理越來越少的特征集合來遞歸的選擇特征。 首先,評估器在初始的特征集合上面進行訓練並且每一個特征的重要程度是通過一個諸如sklearn里的 coef_ 屬性 或者 feature_importances_ 屬性來獲得。 然后,從當前的特征集合中移除最不重要的特征。在特征集合上不斷的重復遞歸這個步驟,直到最終達到所需要的特征數量為止。

下列代碼使用RFE抽取5個最informative的特征:

>>> from sklearn.datasets import make_friedman1
>>> from sklearn.feature_selection import RFE
>>> from sklearn.svm import SVR
>>> X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)
>>> estimator = SVR(kernel="linear")
>>> selector = RFE(estimator, 5, step=1)
>>> selector = selector.fit(X, y)
>>> selector.support_ 
array([ True,  True,  True,  True,  True,
        False, False, False, False, False], dtype=bool)
>>> selector.ranking_
array([1, 1, 1, 1, 1, 6, 4, 3, 2, 5])

從最終的學習器性能來看,包裹式特征選擇比過濾式特征選擇更好。但是另一方面,由於在特征選擇過程中需多次訓練學習期,因此包裹式特征選擇的計算開銷通常要大得多

嵌入式(embedded)

SelectFromModel選取特征

sklearn.feature_selection.SelectFromModel(estimator, threshold=None, prefit=False, norm_order=1)

SelectFromModel 是一個 meta-transformer(元轉換器) ,它可以用來處理任何帶有 coef_ 或者 feature_importances_ 屬性的訓練之后的評估器。 如果相關的coef_ 或者 feature_importances_ 屬性值低於預先設置的閾值,這些特征將會被認為不重要並且移除掉。除了指定數值上的閾值之外,還可以通過給定字符串參數來使用內置的啟發式方法找到一個合適的閾值。可以使用的啟發式方法有 mean 、 median 以及使用浮點數乘以這些(例如,0.1*mean)

Linear models 使用 L1 正則化的線性模型會得到稀疏解:他們的許多系數為 0。 當目標是降低使用另一個分類器的數據集的維度, 它們可以與 feature_selection.SelectFromModel 一起使用來選擇非零系數。

>>> from sklearn.svm import LinearSVC
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectFromModel
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X.shape
(150, 4)
>>> lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X, y)
>>> model = SelectFromModel(lsvc, prefit=True)
>>> X_new = model.transform(X)
>>> X_new.shape
(150, 3)

其實用包裝好的庫看不出嵌入式的兩者兼顧,實際上在fit后,得到coef的過程中,相當於已經做出了特征選擇。

另外,基於樹的 estimators 也可以用來計算特征的重要性,然后可以消除不相關的特征(當與 sklearn.feature_selection.SelectFromModel 等元轉換器一同使用時)

以下是一個使用隨機森林進行特征選擇的例子:

from sklearn.ensemble import RandomForestClassifier
feat_labels = df_wine.columns[1:]
forest = RandomForestClassifier(n_estimators=500,
                                random_state=1)
forest.fit(X_train, y_train)
importances = forest.feature_importances_
indices = np.argsort(importances)[::-1]
for f in range(X_train.shape[1]):
    print("%2d) %-*s %f" % (f + 1, 30, 
                            feat_labels[indices[f]], 
                            importances[indices[f]]))
  • Reference:
  1. http://scikit-learn.org/stable/modules/feature_selection.html
  2. http://sklearn.apachecn.org/cn/0.19.0/modules/feature_selection.html
  3. https://www.kaggle.com/bertcarremans/data-preparation-exploration
  4. https://github.com/rasbt/python-machine-learning-book-2nd-edition
  5. https://www.zhihu.com/question/28641663
  6. 《機器學習》.周志華


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM