特徵選擇

(圖片來自《阿甘正傳》)

特徵選擇是特徵工程里的一個重要問題,其目標是尋找最優特徵子集。特徵選擇能剔除不相關(irrelevant)或冗餘(redundant )的特徵,從而達到減少特徵個數,提高模型精確度,減少運行時間的目的。另一方面,選取出真正相關的特徵簡化模型,協助理解數據產生的過程。並且常能聽到「數據和特徵決定了機器學習的上限,而模型和演算法只是逼近這個上限而已」,由此可見其重要性。但是它幾乎很少出現於機器學習書本裡面的某一章。然而在機器學習方面的成功很大程度上在於如果使用特徵工程。

一、特徵選擇的一般流程

特徵選擇的過程 ( M. Dash and H. Liu 1997 )

主要分為產生過程,評估過程,停止條件和驗證過程。

但是, 當特徵數量很大的時候, 這個搜索空間會很大,如何找最優特徵還是需要一些經驗結論。

二、具體特徵選擇方法

根據特徵選擇的形式可以將特徵選擇方法分為三大類:

  • Filter:過濾法,按照發散性或者相關性對各個特徵進行評分,設定閾值或者待選擇閾值的個數,選擇特徵。
  • Wrapper:包裝法,根據目標函數(通常是預測效果評分),每次選擇若干特徵,或者排除若干特徵。
  • Embedded:嵌入法,先使用某些機器學習的演算法和模型進行訓練,得到各個特徵的權值係數,根據係數從大到小排序選擇特徵。類似於Filter方法,但是是通過訓練來確定特徵的優劣。

1、過濾(filter)特徵選擇

過濾特徵選擇法的想法是針對每個特徵 x_ii1n ,計算 x_i 相對於類別標籤 y 的信息量 S(i) ,得到 n 個結果,然後將 nS(i) 按照從大到小排序,輸出前 k 個特徵。顯然,這樣複雜度大大降低。那麼關鍵的問題就是使用什麼樣的方法來度量 S(i) ,我們的目標是選取與 y 關聯最密切的一些 特徵x_i

  • Pearson相關係數

皮爾森相關係數是一種最簡單的,能幫助理解特徵和響應變數之間關係的方法,該方法衡量的是變數之間的線性相關性,結果的取值區間為 [-1,1]-1 表示完全的負相關(這個變數下降,那個就會上升), +1 表示完全的正相關, 0 表示沒有線性相關。Pearson Correlation速度快、易於計算,經常在拿到數據(經過清洗和特徵提取之後的)之後第一時間就執行。Scipy的pearsonr方法能夠同時計算相關係數和p-value,

import numpy as npfrom scipy.stats import pearsonrnp.random.seed(0)size = 300x = np.random.normal(0, 1, size)print("Lower noise", pearsonr(x, x + np.random.normal(0, 1, size)))print("Higher noise", pearsonr(x, x + np.random.normal(0, 10, size)))

Pearson相關係數的一個明顯缺陷是,作為特徵排序機制,他只對線性關係敏感。如果關係是非線性的,即便兩個變數具有一一對應的關係,Pearson相關性也可能會接近 0

  • 卡方驗證

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

chi^2=sumfrac{(A-E)^2}{E}

不難發現,這個統計量的含義簡而言之就是自變數對因變數的相關性。用sklearn中feature_selection庫的SelectKBest類結合卡方檢驗來選擇特徵的代碼如下:

from sklearn.datasets import load_irisfrom sklearn.feature_selection import SelectKBestfrom sklearn.feature_selection import chi2iris = load_iris()X, y = iris.data, iris.target#選擇K個最好的特徵,返回選擇特徵後的數據X_new = SelectKBest(chi2, k=2).fit_transform(X, y)

sklearn.feature_selection模塊中的類可以用於樣本集中的特徵選擇/維數降低,以提高估計器的準確度分數或提高其在非常高維數據集上的性能

  • 互信息和最大信息係數 Mutual information and maximal information coefficient (MIC)

經典的互信息也是評價定性自變數對定性因變數的相關性的,互信息公式如下:

MI(x_i,y)=sum_{x_iin{0,1}}sum_{yin{0,1}}p(x_i,y)logfrac{p(x_i,y)}{p(x_i)p(y)}

x_i 是0/1離散值的時候,這個公式如上。很容易推廣到 x_i 是多個離散值的情況。這裡的 p(x_i,y) , p(x_i)p(y) 都是從訓練集上得到的。若問這個 MI 公式如何得來,請看它的 KL 距離(Kullback-Leibler)表述:

MI(x_i,y)=KL(P(x_i,y)||p(x_i)p(y))

也就是說, MI 衡量的是 x_iy 的獨立性。如果它倆獨立 P(x_i,y)=p(x_i)p(y) ,那麼 KL 距離值為0,也就是 x_iy 不相關了,可以去除 x_i 。相反,如果兩者密切相關,那麼 MI 值會很大。在對 MI 進行排名後,最後剩餘的問題就是如何選擇 k 個值(前 kx_i )。(後面將會提到此方法)我們繼續使用交叉驗證的方法,將 k1 掃描到 n ,取最大的 F

不過這次複雜度是線性的了。比如,在使用樸素貝葉斯分類文本的時候,詞表長度 n 很大。

使用filiter特徵選擇方法,能夠增加分類器精度。

想把互信息直接用於特徵選擇其實不是太方便:1、它不屬於度量方式,也沒有辦法歸一化,在不同數據及上的結果無法做比較;2、對於連續變數的計算不是很方便( XY 都是集合, x_i, y 都是離散的取值),通常變數需要先離散化,而互信息的結果對離散化的方式很敏感。

最大信息係數克服了這兩個問題。它首先尋找一種最優的離散化方式,然後把互信息取值轉換成一種度量方式,取值區間在 [0,1] 。minepy提供了MIC功能。

下面我們來看下 y=x^2 這個例子,MIC算出來的互信息值為1(最大的取值)。代碼如下:

from minepy import MINEm = MINE()x = np.random.uniform(-1, 1, 10000)m.compute_score(x, x**2)print(m.mic())

  • 距離相關係數

距離相關係數是為了克服Pearson相關係數的弱點而生的。在 xx^2 這個例子中,即便Pearson相關係數是 0 ,我們也不能斷定這兩個變數是獨立的(有可能是非線性相關);但如果距離相關係數是 0 ,那麼我們就可以說這兩個變數是獨立的。

方差選擇法

過濾特徵選擇法還有一種方法不需要度量特徵 x_i 和類別標籤 y 的信息量。這種方法先要計算各個特徵的方差,然後根據閾值,選擇方差大於閾值的特徵。

例如,假設我們有一個具有布爾特徵的數據集,並且我們要刪除超過80%的樣本中的一個或零(開或關)的所有特徵。布爾特徵是伯努利隨機變數,這些變數的方差由下式給出:

Var[X]=p(1-p)

VarianceThreshold是特徵選擇的簡單基線方法。它刪除方差不符合某個閾值的所有特徵。默認情況下,它會刪除所有零差異特徵,即所有樣本中具有相同值的特徵。代碼如下:

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

輸出結果:

array([[0, 1], [1, 0], [0, 0], [1, 1], [1, 0], [1, 1]])

如預期的那樣,VarianceThreshold已經刪除了第一列,其具有 p=5/6>0.8 包含零的概率。

2、包裝(wrapper)特徵選擇

Wrapper這裡指不斷地使用不同的特徵組合來測試學習演算法進行特徵選擇。先選定特定演算法, 一般會選用普遍效果較好的演算法, 例如Random Forest, SVM, kNN等等。

  • 前向搜索

前向搜索說白了就是每次增量地從剩餘未選中的特徵選出一個加入特徵集中,待達到閾值或者 n 時,從所有的 F 中選出錯誤率最小的。過程如下:

  1. 初始化特徵集 F 為空。
  2. 掃描 i1n

    如果第 i 個特徵不在 F 中,那麼特徵 iF 放在一起作為 F_i (即 F_i=Fcup{i} )。

    在只使用 F_i 中特徵的情況下,利用交叉驗證來得到 F_i 的錯誤率。
  3. 從上步中得到的 nF_i 中選出錯誤率最小的 F_i ,更新 FF_i
  4. 如果 F 中的特徵數達到了 n 或者預定的閾值(如果有的話),

    那麼輸出整個搜索過程中最好的 ;若沒達到,則轉到 2,繼續掃描。
  • 後向搜索

既然有增量加,那麼也會有增量減,後者稱為後向搜索。先將 F 設置為 {1,2,...,n} ,然後每次刪除一個特徵,並評價,直到達到閾值或者為空,然後選擇最佳的 F

這兩種演算法都可以工作,但是計算複雜度比較大。時間複雜度為

O(n+(n-1)+(n-2)+...+1)=O(n^2)

  • 遞歸特徵消除法

遞歸消除特徵法使用一個基模型來進行多輪訓練,每輪訓練後,消除若干權值係數的特徵,再基於新的特徵集進行下一輪訓練。使用feature_selection庫的RFE類來選擇特徵的代碼如下:

from sklearn.feature_selection import RFEfrom sklearn.linear_model import LogisticRegression#遞歸特徵消除法,返回特徵選擇後的數據#參數estimator為基模型#參數n_features_to_select為選擇的特徵個數RFE(estimator=LogisticRegression(), n_features_to_select=2).fit_transform(iris.data, iris.target)

3、嵌入(Embedded)特徵選擇

  • 基於懲罰項的特徵選擇法

通過L1正則項來選擇特徵:L1正則方法具有稀疏解的特性,因此天然具備特徵選擇的特性,但是要注意,L1沒有選到的特徵不代表不重要,原因是兩個具有高相關性的特徵可能只保留了一個,如果要確定哪個特徵重要應再通過L2正則方法交叉檢驗。

  • 基於學習模型的特徵排序

這種方法的思路是直接使用你要用的機器學習演算法,針對每個單獨的特徵和響應變數建立預測模型。假如某個特徵和響應變數之間的關係是非線性的,可以用基於樹的方法(決策樹、隨機森林)、或者擴展的線性模型等。基於樹的方法比較易於使用,因為他們對非線性關係的建模比較好,並且不需要太多的調試。但要注意過擬合問題,因此樹的深度最好不要太大,再就是運用交叉驗證。通過這種訓練對特徵進行打分獲得相關性後再訓練最終模型。

在波士頓房價數據集上使用sklearn的隨機森林回歸給出一個單變數選擇的例子:

from sklearn.cross_validation import cross_val_score, ShuffleSplitfrom sklearn.datasets import load_bostonfrom sklearn.ensemble import RandomForestRegressor#載入波士頓房價作為數據集boston = load_boston()X = boston["data"]Y = boston["target"]names = boston["feature_names"]#n_estimators為森林中樹木數量,max_depth樹的最大深度rf = RandomForestRegressor(n_estimators=20, max_depth=4)scores = []for i in range(X.shape[1]): #每次選擇一個特徵,進行交叉驗證,訓練集和測試集為7:3的比例進行分配, #ShuffleSplit()函數用於隨機抽樣(數據集總數,迭代次數,test所佔比例) score = cross_val_score(rf, X[:, i:i+1], Y, scoring="r2", cv=ShuffleSplit(len(X), 3, .3)) scores.append((round(np.mean(score), 3), names[i]))#列印出各個特徵所對應的得分print(sorted(scores, reverse=True))

輸出結果:

[(0.64300000000000002, "LSTAT"), (0.625, "RM"), (0.46200000000000002, "NOX"), (0.373, "INDUS"), (0.30299999999999999, "TAX"), (0.29799999999999999, "PTRATIO"), (0.20399999999999999, "RAD"), (0.159, "CRIM"), (0.14499999999999999, "AGE"), (0.097000000000000003, "B"), (0.079000000000000001, "ZN"), (0.019, "CHAS"), (0.017999999999999999, "DIS")]

參考資料

網易公開課_吳恩達_特徵選擇

結合Scikit-learn介紹幾種常用的特徵選擇方法 - 羅兵 - 博客園


推薦閱讀:

機器學習入門講解:什麼是特徵和特徵選擇
機器學習中,有哪些特徵選擇的工程方法?

TAG:特征选择 | 数据挖掘 | 机器学习 |