1. 程式人生 > >[轉]乾貨:結合Scikit-learn介紹幾種常用的特徵選擇方法

[轉]乾貨:結合Scikit-learn介紹幾種常用的特徵選擇方法

特徵選擇(排序)對於資料科學家、機器學習從業者來說非常重要。好的特徵選擇能夠提升模型的效能,更能幫助我們理解資料的特點、底層結構,這對進一步改善模型、演算法都有著重要作用。

特徵選擇主要有兩個功能:

  1. 減少特徵數量、降維,使模型泛化能力更強,減少過擬合
  2. 增強對特徵和特徵值之間的理解

拿到資料集,一個特徵選擇方法,往往很難同時完成這兩個目的。通常情況下,我們經常不管三七二十一,選擇一種自己最熟悉或者最方便的特徵選擇方法(往往目的是降維,而忽略了對特徵和資料理解的目的)。

在許多機器學習相關的書裡,很難找到關於特徵選擇的內容,因為特徵選擇要解決的問題往往被視為機器學習的一種副作用,一般不會單獨拿出來討論。

本文將結合Scikit-learn提供的例子介紹幾種常用的特徵選擇方法,它們各自的優缺點和問題。

1 去掉取值變化小的特徵 Removing features with low variance

這應該是最簡單的特徵選擇方法了:假設某特徵的特徵值只有0和1,並且在所有輸入樣本中,95%的例項的該特徵取值都是1,那就可以認為這個特徵作用不大。如果100%都是1,那這個特徵就沒意義了。當特徵值都是離散型變數的時候這種方法才能用,如果是連續型變數,就需要將連續變數離散化之後才能用,而且實際當中,一般不太會有95%以上都取某個值的特徵存在,所以這種方法雖然簡單但是不太好用。可以把它作為特徵選擇的預處理,先去掉那些取值變化小的特徵,然後再從接下來提到的的特徵選擇方法中選擇合適的進行進一步的特徵選擇。

2 單變數特徵選擇 Univariate feature selection

單變數特徵選擇能夠對每一個特徵進行測試,衡量該特徵和響應變數之間的關係,根據得分扔掉不好的特徵。對於迴歸和分類問題可以採用卡方檢驗等方式對特徵進行測試。

這種方法比較簡單,易於執行,易於理解,通常對於理解資料有較好的效果(但對特徵優化、提高泛化能力來說不一定有效);這種方法有許多改進的版本、變種。

2.1 Pearson相關係數 Pearson Correlation

皮爾森相關係數是一種最簡單的,能幫助理解特徵和響應變數之間關係的方法,該方法衡量的是變數之間的線性相關性,結果的取值區間為[-1,1],-1表示完全的負相關(這個變數下降,那個就會上升),+1表示完全的正相關,0表示沒有線性相關。

Pearson Correlation速度快、易於計算,經常在拿到資料(經過清洗和特徵提取之後的)之後第一時間就執行。Scipy的pearsonr方法能夠同時計算相關係數和p-value,

import numpy as np
from scipy.stats import pearsonr
np.random.seed(0)
size = 300
x = 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))

Lower noise (0.71824836862138386, 7.3240173129992273e-49)
Higher noise (0.057964292079338148, 0.31700993885324746)

這個例子中,我們比較了變數在加入噪音之前和之後的差異。當噪音比較小的時候,相關性很強,p-value很低。

Scikit-learn提供的f_regrssion方法能夠批量計算特徵的p-value,非常方便,參考sklearn的pipeline

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

x = np.random.uniform(-1, 1, 100000)
print pearsonr(x, x**2)[0]

-0.00230804707612

更多類似的例子參考sample plots。另外,如果僅僅根據相關係數這個值來判斷的話,有時候會具有很強的誤導性,如Anscombe’s quartet,最好把資料可視化出來,以免得出錯誤的結論。

2.2 互資訊和最大資訊係數 Mutual information and maximal information coefficient (MIC)

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

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

反過頭來看y=x^2這個例子,MIC算出來的互資訊值為1(最大的取值)。

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

1.0

MIC的統計能力遭到了一些質疑,當零假設不成立時,MIC的統計就會受到影響。在有的資料集上不存在這個問題,但有的資料集上就存在這個問題。

2.3 距離相關係數 (Distance correlation)

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

R的energy包裡提供了距離相關係數的實現,另外這是Python gist的實現。

#R-code
> x = runif (1000, -1, 1)
> dcor(x, x**2)
[1] 0.4943864

儘管有MIC和距離相關係數在了,但當變數之間的關係接近線性相關的時候,Pearson相關係數仍然是不可替代的。第一、Pearson相關係數計算速度快,這在處理大規模資料的時候很重要。第二、Pearson相關係數的取值區間是[-1,1],而MIC和距離相關係數都是[0,1]。這個特點使得Pearson相關係數能夠表徵更豐富的關係,符號表示關係的正負,絕對值能夠表示強度。當然,Pearson相關性有效的前提是兩個變數的變化關係是單調的。

2.4 基於學習模型的特徵排序 (Model based ranking)

這種方法的思路是直接使用你要用的機器學習演算法,針對每個單獨的特徵和響應變數建立預測模型。其實Pearson相關係數等價於線性迴歸裡的標準化迴歸係數。假如某個特徵和響應變數之間的關係是非線性的,可以用基於樹的方法(決策樹、隨機森林)、或者擴充套件的線性模型等。基於樹的方法比較易於使用,因為他們對非線性關係的建模比較好,並且不需要太多的除錯。但要注意過擬合問題,因此樹的深度最好不要太大,再就是運用交叉驗證。

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

from sklearn.cross_validation import cross_val_score, ShuffleSplit
from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor

#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

rf = RandomForestRegressor(n_estimators=20, max_depth=4)
scores = []
for i in range(X.shape[1]):
     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.636, ‘LSTAT’), (0.59, ‘RM’), (0.472, ‘NOX’), (0.369, ‘INDUS’), (0.311, ‘PTRATIO’), (0.24, ‘TAX’), (0.24, ‘CRIM’), (0.185, ‘RAD’), (0.16, ‘ZN’), (0.087, ‘B’), (0.062, ‘DIS’), (0.036, ‘CHAS’), (0.027, ‘AGE’)]

3 線性模型和正則化

單變數特徵選擇方法獨立的衡量每個特徵與響應變數之間的關係,另一種主流的特徵選擇方法是基於機器學習模型的方法。有些機器學習方法本身就具有對特徵進行打分的機制,或者很容易將其運用到特徵選擇任務中,例如迴歸模型,SVM,決策樹,隨機森林等等。說句題外話,這種方法好像在一些地方叫做wrapper型別,大概意思是說,特徵排序模型和機器學習模型是耦盒在一起的,對應的非wrapper型別的特徵選擇方法叫做filter型別。

下面將介紹如何用迴歸模型的係數來選擇特徵。越是重要的特徵在模型中對應的係數就會越大,而跟輸出變數越是無關的特徵對應的係數就會越接近於0。在噪音不多的資料上,或者是資料量遠遠大於特徵數的資料上,如果特徵之間相對來說是比較獨立的,那麼即便是運用最簡單的線性迴歸模型也一樣能取得非常好的效果。

from sklearn.linear_model import LinearRegression
import numpy as np

np.random.seed(0)
size = 5000

#A dataset with 3 features
X = np.random.normal(0, 1, (size, 3))
#Y = X0 + 2*X1 + noise
Y = X[:,0] + 2*X[:,1] + np.random.normal(0, 2, size)
lr = LinearRegression()
lr.fit(X, Y)

#A helper method for pretty-printing linear models
def pretty_print_linear(coefs, names = None, sort = False):
    if names == None:
        names = ["X%s" % x for x in range(len(coefs))]
    lst = zip(coefs, names)
    if sort:
        lst = sorted(lst,  key = lambda x:-np.abs(x[0]))
    return " + ".join("%s * %s" % (round(coef, 3), name)
                                   for coef, name in lst)

print "Linear model:", pretty_print_linear(lr.coef_)

Linear model: 0.984 * X0 + 1.995 * X1 + -0.041 * X2

在這個例子當中,儘管資料中存在一些噪音,但這種特徵選擇模型仍然能夠很好的體現出資料的底層結構。當然這也是因為例子中的這個問題非常適合用線性模型來解:特徵和響應變數之間全都是線性關係,並且特徵之間均是獨立的。

在很多實際的資料當中,往往存在多個互相關聯的特徵,這時候模型就會變得不穩定,資料中細微的變化就可能導致模型的巨大變化(模型的變化本質上是係數,或者叫引數,可以理解成W),這會讓模型的預測變得困難,這種現象也稱為多重共線性。例如,假設我們有個資料集,它的真實模型應該是Y=X1+X2,當我們觀察的時候,發現Y’=X1+X2+e,e是噪音。如果X1和X2之間存線上性關係,例如X1約等於X2,這個時候由於噪音e的存在,我們學到的模型可能就不是Y=X1+X2了,有可能是Y=2X1,或者Y=-X1+3X2。

下邊這個例子當中,在同一個資料上加入了一些噪音,用隨機森林演算法進行特徵選擇。

from sklearn.linear_model import LinearRegression

size = 100
np.random.seed(seed=5)

X_seed = np.random.normal(0, 1, size)
X1 = X_seed + np.random.normal(0, .1, size)
X2 = X_seed + np.random.normal(0, .1, size)
X3 = X_seed + np.random.normal(0, .1, size)

Y = X1 + X2 + X3 + np.random.normal(0,1, size)
X = np.array([X1, X2, X3]).T

lr = LinearRegression()
lr.fit(X,Y)
print "Linear model:", pretty_print_linear(lr.coef_)

Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2

係數之和接近3,基本上和上上個例子的結果一致,應該說學到的模型對於預測來說還是不錯的。但是,如果從係數的字面意思上去解釋特徵的重要性的話,X3對於輸出變數來說具有很強的正面影響,而X1具有負面影響,而實際上所有特徵與輸出變數之間的影響是均等的。

同樣的方法和套路可以用到類似的線性模型上,比如邏輯迴歸。

3.1 正則化模型

正則化就是把額外的約束或者懲罰項加到已有模型(損失函式)上,以防止過擬合併提高泛化能力。損失函式由原來的E(X,Y)變為E(X,Y)+alpha||w||,w是模型係數組成的向量(有些地方也叫引數parameter,coefficients),||·||一般是L1或者L2範數,alpha是一個可調的引數,控制著正則化的強度。當用線上性模型上時,L1正則化和L2正則化也稱為Lasso和Ridge。

3.2 L1正則化/Lasso

L1正則化將係數w的l1範數作為懲罰項加到損失函式上,由於正則項非零,這就迫使那些弱的特徵所對應的係數變成0。因此L1正則化往往會使學到的模型很稀疏(係數w經常為0),這個特性使得L1正則化成為一種很好的特徵選擇方法。

Scikit-learn為線性迴歸提供了Lasso,為分類提供了L1邏輯迴歸。

下面的例子在波士頓房價資料上運行了Lasso,其中引數alpha是通過grid search進行優化的。

from sklearn.linear_model import Lasso
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_boston

boston = load_boston()
scaler = StandardScaler()
X = scaler.fit_transform(boston["data"])
Y = boston["target"]
names = boston["feature_names"]

lasso = Lasso(alpha=.3)
lasso.fit(X, Y)

print "Lasso model: ", pretty_print_linear(lasso.coef_, names, sort = True)

Lasso model: -3.707 * LSTAT + 2.992 * RM + -1.757 * PTRATIO + -1.081 * DIS + -0.7 * NOX + 0.631 * B + 0.54 * CHAS + -0.236 * CRIM + 0.081 * ZN + -0.0 * INDUS + -0.0 * AGE + 0.0 * RAD + -0.0 * TAX

可以看到,很多特徵的係數都是0。如果繼續增加alpha的值,得到的模型就會越來越稀疏,即越來越多的特徵係數會變成0。

然而,L1正則化像非正則化線性模型一樣也是不穩定的,如果特徵集合中具有相關聯的特徵,當資料發生細微變化時也有可能導致很大的模型差異。

3.3 L2正則化/Ridge regression

L2正則化將係數向量的L2範數新增到了損失函式中。由於L2懲罰項中係數是二次方的,這使得L2和L1有著諸多差異,最明顯的一點就是,L2正則化會讓係數的取值變得平均。對於關聯特徵,這意味著他們能夠獲得更相近的對應係數。還是以Y=X1+X2為例,假設X1和X2具有很強的關聯,如果用L1正則化,不論學到的模型是Y=X1+X2還是Y=2X1,懲罰都是一樣的,都是2alpha。但是對於L2來說,第一個模型的懲罰項是2alpha,但第二個模型的是4*alpha。可以看出,係數之和為常數時,各系數相等時懲罰是最小的,所以才有了L2會讓各個係數趨於相同的特點。

可以看出,L2正則化對於特徵選擇來說一種穩定的模型,不像L1正則化那樣,係數會因為細微的資料變化而波動。所以L2正則化和L1正則化提供的價值是不同的,L2正則化對於特徵理解來說更加有用:表示能力強的特徵對應的係數是非零。

回過頭來看看3個互相關聯的特徵的例子,分別以10個不同的種子隨機初始化執行10次,來觀察L1和L2正則化的穩定性。

from sklearn.linear_model import Ridge
from sklearn.metrics import r2_score
size = 100

#We run the method 10 times with different random seeds
for i in range(10):
    print "Random seed %s" % i
    np.random.seed(seed=i)
    X_seed = np.random.normal(0, 1, size)
    X1 = X_seed + np.random.normal(0, .1, size)
    X2 = X_seed + np.random.normal(0, .1, size)
    X3 = X_seed + np.random.normal(0, .1, size)
    Y = X1 + X2 + X3 + np.random.normal(0, 1, size)
    X = np.array([X1, X2, X3]).T


    lr = LinearRegression()
    lr.fit(X,Y)
    print "Linear model:", pretty_print_linear(lr.coef_)

    ridge = Ridge(alpha=10)
    ridge.fit(X,Y)
    print "Ridge model:", pretty_print_linear(ridge.coef_)
    print

Random seed 0 Linear model: 0.728 * X0 + 2.309 * X1 + -0.082 * X2 Ridge model: 0.938 * X0 + 1.059 * X1 + 0.877 * X2

Random seed 1 Linear model: 1.152 * X0 + 2.366 * X1 + -0.599 * X2 Ridge model: 0.984 * X0 + 1.068 * X1 + 0.759 * X2

Random seed 2 Linear model: 0.697 * X0 + 0.322 * X1 + 2.086 * X2 Ridge model: 0.972 * X0 + 0.943 * X1 + 1.085 * X2

Random seed 3 Linear model: 0.287 * X0 + 1.254 * X1 + 1.491 * X2 Ridge model: 0.919 * X0 + 1.005 * X1 + 1.033 * X2

Random seed 4 Linear model: 0.187 * X0 + 0.772 * X1 + 2.189 * X2 Ridge model: 0.964 * X0 + 0.982 * X1 + 1.098 * X2

Random seed 5 Linear model: -1.291 * X0 + 1.591 * X1 + 2.747 * X2 Ridge model: 0.758 * X0 + 1.011 * X1 + 1.139 * X2

Random seed 6 Linear model: 1.199 * X0 + -0.031 * X1 + 1.915 * X2 Ridge model: 1.016 * X0 + 0.89 * X1 + 1.091 * X2

Random seed 7 Linear model: 1.474 * X0 + 1.762 * X1 + -0.151 * X2 Ridge model: 1.018 * X0 + 1.039 * X1 + 0.901 * X2

Random seed 8 Linear model: 0.084 * X0 + 1.88 * X1 + 1.107 * X2 Ridge model: 0.907 * X0 + 1.071 * X1 + 1.008 * X2

Random seed 9 Linear model: 0.714 * X0 + 0.776 * X1 + 1.364 * X2 Ridge model: 0.896 * X0 + 0.903 * X1 + 0.98 * X2

可以看出,不同的資料上線性迴歸得到的模型(係數)相差甚遠,但對於L2正則化模型來說,結果中的係數非常的穩定,差別較小,都比較接近於1,能夠反映出資料的內在結構。

4 隨機森林

隨機森林具有準確率高、魯棒性好、易於使用等優點,這使得它成為了目前最流行的機器學習演算法之一。隨機森林提供了兩種特徵選擇的方法:mean decrease impurity和mean decrease accuracy。

4.1 平均不純度減少 mean decrease impurity

隨機森林由多個決策樹構成。決策樹中的每一個節點都是關於某個特徵的條件,為的是將資料集按照不同的響應變數一分為二。利用不純度可以確定節點(最優條件),對於分類問題,通常採用基尼不純度或者資訊增益,對於迴歸問題,通常採用的是方差或者最小二乘擬合。當訓練決策樹的時候,可以計算出每個特徵減少了多少樹的不純度。對於一個決策樹森林來說,可以算出每個特徵平均減少了多少不純度,並把它平均減少的不純度作為特徵選擇的值。

下邊的例子是sklearn中基於隨機森林的特徵重要度度量方法:

from sklearn.datasets import load_boston
from sklearn.ensemble import RandomForestRegressor
import numpy as np
#Load boston housing dataset as an example
boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]
rf = RandomForestRegressor()
rf.fit(X, Y)
print "Features sorted by their score:"
print sorted(zip(map(lambda x: round(x, 4), rf.feature_importances_), names), 
             reverse=True)

Features sorted by their score: [(0.5298, ‘LSTAT’), (0.4116, ‘RM’), (0.0252, ‘DIS’), (0.0172, ‘CRIM’), (0.0065, ‘NOX’), (0.0035, ‘PTRATIO’), (0.0021, ‘TAX’), (0.0017, ‘AGE’), (0.0012, ‘B’), (0.0008, ‘INDUS’), (0.0004, ‘RAD’), (0.0001, ‘CHAS’), (0.0, ‘ZN’)]

這裡特徵得分實際上採用的是Gini Importance。使用基於不純度的方法的時候,要記住:1、這種方法存在偏向,對具有更多類別的變數會更有利;2、對於存在關聯的多個特徵,其中任意一個都可以作為指示器(優秀的特徵),並且一旦某個特徵被選擇之後,其他特徵的重要度就會急劇下降,因為不純度已經被選中的那個特徵降下來了,其他的特徵就很難再降低那麼多不純度了,這樣一來,只有先被選中的那個特徵重要度很高,其他的關聯特徵重要度往往較低。在理解資料時,這就會造成誤解,導致錯誤的認為先被選中的特徵是很重要的,而其餘的特徵是不重要的,但實際上這些特徵對響應變數的作用確實非常接近的(這跟Lasso是很像的)。

特徵隨機選擇方法稍微緩解了這個問題,但總的來說並沒有完全解決。下面的例子中,X0、X1、X2是三個互相關聯的變數,在沒有噪音的情況下,輸出變數是三者之和。

size = 10000
np.random.seed(seed=10)
X_seed = np.random.normal(0, 1, size)
X0 = X_seed + np.random.normal(0, .1, size)
X1 = X_seed + np.random.normal(0, .1, size)
X2 = X_seed + np.random.normal(0, .1, size)
X = np.array([X0, X1, X2]).T
Y = X0 + X1 + X2

rf = RandomForestRegressor(n_estimators=20, max_features=2)
rf.fit(X, Y);
print "Scores for X0, X1, X2:", map(lambda x:round (x,3),
                                    rf.feature_importances_)

Scores for X0, X1, X2: [0.278, 0.66, 0.062]

當計算特徵重要性時,可以看到X1的重要度比X2的重要度要高出10倍,但實際上他們真正的重要度是一樣的。儘管資料量已經很大且沒有噪音,且用了20棵樹來做隨機選擇,但這個問題還是會存在。

需要注意的一點是,關聯特徵的打分存在不穩定的現象,這不僅僅是隨機森林特有的,大多數基於模型的特徵選擇方法都存在這個問題。

4.2 平均精確率減少 Mean decrease accuracy

另一種常用的特徵選擇方法就是直接度量每個特徵對模型精確率的影響。主要思路是打亂每個特徵的特徵值順序,並且度量順序變動對模型的精確率的影響。很明顯,對於不重要的變數來說,打亂順序對模型的精確率影響不會太大,但是對於重要的變數來說,打亂順序就會降低模型的精確率。

這個方法sklearn中沒有直接提供,但是很容易實現,下面繼續在波士頓房價資料集上進行實現。

from sklearn.cross_validation import ShuffleSplit
from sklearn.metrics import r2_score
from collections import defaultdict

X = boston["data"]
Y = boston["target"]

rf = RandomForestRegressor()
scores = defaultdict(list)

#crossvalidate the scores on a number of different random splits of the data
for train_idx, test_idx in ShuffleSplit(len(X), 100, .3):
    X_train, X_test = X[train_idx], X[test_idx]
    Y_train, Y_test = Y[train_idx], Y[test_idx]
    r = rf.fit(X_train, Y_train)
    acc = r2_score(Y_test, rf.predict(X_test))
    for i in range(X.shape[1]):
        X_t = X_test.copy()
        np.random.shuffle(X_t[:, i])
        shuff_acc = r2_score(Y_test, rf.predict(X_t))
        scores[names[i]].append((acc-shuff_acc)/acc)
print "Features sorted by their score:"
print sorted([(round(np.mean(score), 4), feat) for
              feat, score in scores.items()], reverse=True)

Features sorted by their score: [(0.7276, ‘LSTAT’), (0.5675, ‘RM’), (0.0867, ‘DIS’), (0.0407, ‘NOX’), (0.0351, ‘CRIM’), (0.0233, ‘PTRATIO’), (0.0168, ‘TAX’), (0.0122, ‘AGE’), (0.005, ‘B’), (0.0048, ‘INDUS’), (0.0043, ‘RAD’), (0.0004, ‘ZN’), (0.0001, ‘CHAS’)]

在這個例子當中,LSTAT和RM這兩個特徵對模型的效能有著很大的影響,打亂這兩個特徵的特徵值使得模型的效能下降了73%和57%。注意,儘管這些我們是在所有特徵上進行了訓練得到了模型,然後才得到了每個特徵的重要性測試,這並不意味著我們扔掉某個或者某些重要特徵後模型的效能就一定會下降很多,因為即便某個特徵刪掉之後,其關聯特徵一樣可以發揮作用,讓模型效能基本上不變。

5 兩種頂層特徵選擇演算法

之所以叫做頂層,是因為他們都是建立在基於模型的特徵選擇方法基礎之上的,例如迴歸和SVM,在不同的子集上建立模型,然後彙總最終確定特徵得分。

5.1 穩定性選擇 Stability selection

穩定性選擇是一種基於二次抽樣和選擇演算法相結合較新的方法,選擇演算法可以是迴歸、SVM或其他類似的方法。它的主要思想是在不同的資料子集和特徵子集上執行特徵選擇演算法,不斷的重複,最終彙總特徵選擇結果,比如可以統計某個特徵被認為是重要特徵的頻率(被選為重要特徵的次數除以它所在的子集被測試的次數)。理想情況下,重要特徵的得分會接近100%。稍微弱一點的特徵得分會是非0的數,而最無用的特徵得分將會接近於0。

sklearn在隨機lasso隨機邏輯迴歸中有對穩定性選擇的實現。

from sklearn.linear_model import RandomizedLasso
from sklearn.datasets import load_boston
boston = load_boston()

#using the Boston housing data. 
#Data gets scaled automatically by sklearn's implementation
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

rlasso = RandomizedLasso(alpha=0.025)
rlasso.fit(X, Y)

print "Features sorted by their score:"
print sorted(zip(map(lambda x: round(x, 4), rlasso.scores_), 
                 names), reverse=True)

Features sorted by their score: [(1.0, ‘RM’), (1.0, ‘PTRATIO’), (1.0, ‘LSTAT’), (0.62, ‘CHAS’), (0.595, ‘B’), (0.39, ‘TAX’), (0.385, ‘CRIM’), (0.25, ‘DIS’), (0.22, ‘NOX’), (0.125, ‘INDUS’), (0.045, ‘ZN’), (0.02, ‘RAD’), (0.015, ‘AGE’)]

在上邊這個例子當中,最高的3個特徵得分是1.0,這表示他們總會被選作有用的特徵(當然,得分會收到正則化引數alpha的影響,但是sklearn的隨機lasso能夠自動選擇最優的alpha)。接下來的幾個特徵得分就開始下降,但是下降的不是特別急劇,這跟純lasso的方法和隨機森林的結果不一樣。能夠看出穩定性選擇對於克服過擬合和對資料理解來說都是有幫助的:總的來說,好的特徵不會因為有相似的特徵、關聯特徵而得分為0,這跟Lasso是不同的。對於特徵選擇任務,在許多資料集和環境下,穩定性選擇往往是效能最好的方法之一。

5.2 遞迴特徵消除 Recursive feature elimination (RFE)

遞迴特徵消除的主要思想是反覆的構建模型(如SVM或者回歸模型)然後選出最好的(或者最差的)的特徵(可以根據係數來選),把選出來的特徵放到一遍,然後在剩餘的特徵上重複這個過程,直到所有特徵都遍歷了。這個過程中特徵被消除的次序就是特徵的排序。因此,這是一種尋找最優特徵子集的貪心演算法。

RFE的穩定性很大程度上取決於在迭代的時候底層用哪種模型。例如,假如RFE採用的普通的迴歸,沒有經過正則化的迴歸是不穩定的,那麼RFE就是不穩定的;假如採用的是Ridge,而用Ridge正則化的迴歸是穩定的,那麼RFE就是穩定的。

Sklearn提供了RFE包,可以用於特徵消除,還提供了RFECV,可以通過交叉驗證來對的特徵進行排序。

from sklearn.feature_selection import RFE
from sklearn.linear_model import LinearRegression

boston = load_boston()
X = boston["data"]
Y = boston["target"]
names = boston["feature_names"]

#use linear regression as the model
lr = LinearRegression()
#rank all features, i.e continue the elimination until the last one
rfe = RFE(lr, n_features_to_select=1)
rfe.fit(X,Y)

print "Features sorted by their rank:"
print sorted(zip(map(lambda x: round(x, 4), rfe.ranking_), names))

Features sorted by their rank: [(1.0, ‘NOX’), (2.0, ‘RM’), (3.0, ‘CHAS’), (4.0, ‘PTRATIO’), (5.0, ‘DIS’), (6.0, ‘LSTAT’), (7.0, ‘RAD’), (8.0, ‘CRIM’), (9.0, ‘INDUS’), (10.0, ‘ZN’), (11.0, ‘TAX’), (12.0, ‘B’), (13.0, ‘AGE’)]

6 一個完整的例子

下面將本文所有提到的方法進行實驗對比,資