值得收藏!五種常用的特征選擇方法
關(guān)注"Python學(xué)習(xí)與數(shù)據(jù)挖掘"
設(shè)為“星標(biāo)”,第一時間送達(dá)干貨
在許多機(jī)器學(xué)習(xí)相關(guān)的書里,很難找到關(guān)于特征選擇的內(nèi)容,因為特征選擇要解決的問題往往被視為機(jī)器學(xué)習(xí)的一個子模塊,一般不會單獨拿出來討論。
但特征選擇是一個重要的數(shù)據(jù)預(yù)處理過程,特征選擇主要有兩個功能:
減少特征數(shù)量、降維,使模型泛化能力更強(qiáng),減少過擬合 增強(qiáng)對特征和特征值之間的理解
好的特征選擇能夠提升模型的性能,更能幫助我們理解數(shù)據(jù)的特點、底層結(jié)構(gòu),這對進(jìn)一步改善模型、算法都有著重要作用。
本文將結(jié)合Scikit-learn提供的例子介紹幾種常用的特征選擇方法,它們各自的優(yōu)缺點和問題。

英文:Removing features with low variance
這應(yīng)該是最簡單的特征選擇方法了:假設(shè)某特征的特征值只有0和1,并且在所有輸入樣本中,95%的實例的該特征取值都是1,那就可以認(rèn)為這個特征作用不大。如果100%都是1,那這個特征就沒意義了。當(dāng)特征值都是離散型變量的時候這種方法才能用,如果是連續(xù)型變量,就需要將連續(xù)變量離散化之后才能用,而且實際當(dāng)中,一般不太會有95%以上都取某個值的特征存在,所以這種方法雖然簡單但是不太好用??梢园阉鳛樘卣鬟x擇的預(yù)處理,先去掉那些取值變化小的特征,然后再從接下來提到的的特征選擇方法中選擇合適的進(jìn)行進(jìn)一步的特征選擇。

英文:Univariate feature selection。
單變量特征選擇能夠?qū)γ恳粋€特征進(jìn)行測試,衡量該特征和響應(yīng)變量之間的關(guān)系,根據(jù)得分扔掉不好的特征。對于回歸和分類問題可以采用卡方檢驗等方式對特征進(jìn)行測試。
這種方法比較簡單,易于運(yùn)行,易于理解,通常對于理解數(shù)據(jù)有較好的效果(但對特征優(yōu)化、提高泛化能力來說不一定有效);這種方法有許多改進(jìn)的版本、變種。
2.1 Pearson相關(guān)系數(shù)
英文:Pearson Correlation
皮爾森相關(guān)系數(shù)是一種最簡單的,能幫助理解特征和響應(yīng)變量之間關(guān)系的方法,該方法衡量的是變量之間的線性相關(guān)性,結(jié)果的取值區(qū)間為[-1,1],-1表示完全的負(fù)相關(guān)(這個變量下降,那個就會上升),+1表示完全的正相關(guān),0表示沒有線性相關(guān)。
Pearson Correlation速度快、易于計算,經(jīng)常在拿到數(shù)據(jù)(經(jīng)過清洗和特征提取之后的)之后第一時間就執(zhí)行。Scipy的pearsonr方法能夠同時計算相關(guān)系數(shù)和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)))

這個例子中,我們比較了變量在加入噪音之前和之后的差異。當(dāng)噪音比較小的時候,相關(guān)性很強(qiáng),p-value很低。
Scikit-learn提供的f_regrssion方法能夠批量計算特征的p-value,非常方便,參考sklearn的pipeline。
Pearson相關(guān)系數(shù)的一個明顯缺陷是,作為特征排序機(jī)制,他只對線性關(guān)系敏感。如果關(guān)系是非線性的,即便兩個變量具有一一對應(yīng)的關(guān)系,Pearson相關(guān)性也可能會接近0。
x?=?np.random.uniform(-1,?1,?100000)
print?pearsonr(x,?x**2)[0]
更多類似的例子參考sample plots。另外,如果僅僅根據(jù)相關(guān)系數(shù)這個值來判斷的話,有時候會具有很強(qiáng)的誤導(dǎo)性,如Anscombe’s quartet,最好把數(shù)據(jù)可視化出來,以免得出錯誤的結(jié)論。
2.2 互信息和最大信息系數(shù)
英文:Mutual information and maximal information coefficient (MIC)
以上就是經(jīng)典的互信息公式了。想把互信息直接用于特征選擇其實不是太方便:
它不屬于度量方式,也沒有辦法歸一化,在不同數(shù)據(jù)及上的結(jié)果無法做比較; 對于連續(xù)變量的計算不是很方便(X和Y都是集合,x,y都是離散的取值),通常變量需要先離散化,而互信息的結(jié)果對離散化的方式很敏感。
最大信息系數(shù)克服了這兩個問題。它首先尋找一種最優(yōu)的離散化方式,然后把互信息取值轉(zhuǎn)換成一種度量方式,取值區(qū)間在[0,1]。minepy提供了MIC功能。
反過頭來看這個例子,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的統(tǒng)計能力遭到了一些質(zhì)疑,當(dāng)零假設(shè)不成立時,MIC的統(tǒng)計就會受到影響。在有的數(shù)據(jù)集上不存在這個問題,但有的數(shù)據(jù)集上就存在這個問題。
2.3 距離相關(guān)系數(shù)
英文:Distance correlation
距離相關(guān)系數(shù)是為了克服Pearson相關(guān)系數(shù)的弱點而生的。在x和x^2這個例子中,即便Pearson相關(guān)系數(shù)是0,我們也不能斷定這兩個變量是獨立的(有可能是非線性相關(guān));但如果距離相關(guān)系數(shù)是0,那么我們就可以說這兩個變量是獨立的。
R的energy包里提供了距離相關(guān)系數(shù)的實現(xiàn),另外這是Python gist的實現(xiàn)。
#R-code
>?x?=?runif?(1000,?-1,?1)
>?dcor(x,?x**2)
[1]?0.4943864
盡管有MIC和距離相關(guān)系數(shù)在了,但當(dāng)變量之間的關(guān)系接近線性相關(guān)的時候,Pearson相關(guān)系數(shù)仍然是不可替代的。第一、Pearson相關(guān)系數(shù)計算速度快,這在處理大規(guī)模數(shù)據(jù)的時候很重要。第二、Pearson相關(guān)系數(shù)的取值區(qū)間是[-1,1],而MIC和距離相關(guān)系數(shù)都是[0,1]。這個特點使得Pearson相關(guān)系數(shù)能夠表征更豐富的關(guān)系,符號表示關(guān)系的正負(fù),絕對值能夠表示強(qiáng)度。當(dāng)然,Pearson相關(guān)性有效的前提是兩個變量的變化關(guān)系是單調(diào)的。
2.4 基于學(xué)習(xí)模型的特征排序
英文:Model based ranking
這種方法的思路是直接使用你要用的機(jī)器學(xué)習(xí)算法,針對每個單獨的特征和響應(yīng)變量建立預(yù)測模型。其實Pearson相關(guān)系數(shù)等價于線性回歸里的標(biāo)準(zhǔn)化回歸系數(shù)。假如某個特征和響應(yīng)變量之間的關(guān)系是非線性的,可以用基于樹的方法(決策樹、隨機(jī)森林)、或者擴(kuò)展的線性模型等?;跇涞姆椒ū容^易于使用,因為他們對非線性關(guān)系的建模比較好,并且不需要太多的調(diào)試。但要注意過擬合問題,因此樹的深度最好不要太大,再就是運(yùn)用交叉驗證。
在波士頓房價數(shù)據(jù)集上使用sklearn的隨機(jī)森林回歸給出一個單變量選擇的例子:
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))


單變量特征選擇方法獨立的衡量每個特征與響應(yīng)變量之間的關(guān)系,另一種主流的特征選擇方法是基于機(jī)器學(xué)習(xí)模型的方法。有些機(jī)器學(xué)習(xí)方法本身就具有對特征進(jìn)行打分的機(jī)制,或者很容易將其運(yùn)用到特征選擇任務(wù)中,例如回歸模型,SVM,決策樹,隨機(jī)森林等等。說句題外話,這種方法好像在一些地方叫做wrapper類型,大概意思是說,特征排序模型和機(jī)器學(xué)習(xí)模型是耦盒在一起的,對應(yīng)的非wrapper類型的特征選擇方法叫做filter類型。
下面將介紹如何用回歸模型的系數(shù)來選擇特征。越是重要的特征在模型中對應(yīng)的系數(shù)就會越大,而跟輸出變量越是無關(guān)的特征對應(yīng)的系數(shù)就會越接近于0。在噪音不多的數(shù)據(jù)上,或者是數(shù)據(jù)量遠(yuǎn)遠(yuǎn)大于特征數(shù)的數(shù)據(jù)上,如果特征之間相對來說是比較獨立的,那么即便是運(yùn)用最簡單的線性回歸模型也一樣能取得非常好的效果。
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_))

在這個例子當(dāng)中,盡管數(shù)據(jù)中存在一些噪音,但這種特征選擇模型仍然能夠很好的體現(xiàn)出數(shù)據(jù)的底層結(jié)構(gòu)。當(dāng)然這也是因為例子中的這個問題非常適合用線性模型來解:特征和響應(yīng)變量之間全都是線性關(guān)系,并且特征之間均是獨立的。
在很多實際的數(shù)據(jù)當(dāng)中,往往存在多個互相關(guān)聯(lián)的特征,這時候模型就會變得不穩(wěn)定,數(shù)據(jù)中細(xì)微的變化就可能導(dǎo)致模型的巨大變化(模型的變化本質(zhì)上是系數(shù),或者叫參數(shù),可以理解成W),這會讓模型的預(yù)測變得困難,這種現(xiàn)象也稱為多重共線性。例如,假設(shè)我們有個數(shù)據(jù)集,它的真實模型應(yīng)該是Y=X1+X2,當(dāng)我們觀察的時候,發(fā)現(xiàn)Y’=X1+X2+e,e是噪音。如果X1和X2之間存在線性關(guān)系,例如X1約等于X2,這個時候由于噪音e的存在,我們學(xué)到的模型可能就不是Y=X1+X2了,有可能是Y=2X1,或者Y=-X1+3X2。
下邊這個例子當(dāng)中,在同一個數(shù)據(jù)上加入了一些噪音,用隨機(jī)森林算法進(jìn)行特征選擇。
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_))

系數(shù)之和接近3,基本上和上上個例子的結(jié)果一致,應(yīng)該說學(xué)到的模型對于預(yù)測來說還是不錯的。但是,如果從系數(shù)的字面意思上去解釋特征的重要性的話,X3對于輸出變量來說具有很強(qiáng)的正面影響,而X1具有負(fù)面影響,而實際上所有特征與輸出變量之間的影響是均等的。
同樣的方法和套路可以用到類似的線性模型上,比如邏輯回歸。
3.1 正則化模型
正則化就是把額外的約束或者懲罰項加到已有模型(損失函數(shù))上,以防止過擬合并提高泛化能力。損失函數(shù)由原來的E(X,Y)變?yōu)镋(X,Y)+alpha||w||,w是模型系數(shù)組成的向量(有些地方也叫參數(shù)parameter,coefficients),||·||一般是L1或者L2范數(shù),alpha是一個可調(diào)的參數(shù),控制著正則化的強(qiáng)度。當(dāng)用在線性模型上時,L1正則化和L2正則化也稱為Lasso和Ridge。
3.2 L1正則化/Lasso
L1正則化將系數(shù)w的l1范數(shù)作為懲罰項加到損失函數(shù)上,由于正則項非零,這就迫使那些弱的特征所對應(yīng)的系數(shù)變成0。因此L1正則化往往會使學(xué)到的模型很稀疏(系數(shù)w經(jīng)常為0),這個特性使得L1正則化成為一種很好的特征選擇方法。
Scikit-learn為線性回歸提供了Lasso,為分類提供了L1邏輯回歸。
下面的例子在波士頓房價數(shù)據(jù)上運(yùn)行了Lasso,其中參數(shù)alpha是通過grid search進(jìn)行優(yōu)化的。
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))

可以看到,很多特征的系數(shù)都是0。如果繼續(xù)增加alpha的值,得到的模型就會越來越稀疏,即越來越多的特征系數(shù)會變成0。
然而,L1正則化像非正則化線性模型一樣也是不穩(wěn)定的,如果特征集合中具有相關(guān)聯(lián)的特征,當(dāng)數(shù)據(jù)發(fā)生細(xì)微變化時也有可能導(dǎo)致很大的模型差異。
3.3 L2正則化/Ridge regression
L2正則化將系數(shù)向量的L2范數(shù)添加到了損失函數(shù)中。由于L2懲罰項中系數(shù)是二次方的,這使得L2和L1有著諸多差異,最明顯的一點就是,L2正則化會讓系數(shù)的取值變得平均。對于關(guān)聯(lián)特征,這意味著他們能夠獲得更相近的對應(yīng)系數(shù)。還是以Y=X1+X2為例,假設(shè)X1和X2具有很強(qiáng)的關(guān)聯(lián),如果用L1正則化,不論學(xué)到的模型是Y=X1+X2還是Y=2X1,懲罰都是一樣的,都是2alpha。但是對于L2來說,第一個模型的懲罰項是2alpha,但第二個模型的是4*alpha??梢钥闯?,系數(shù)之和為常數(shù)時,各系數(shù)相等時懲罰是最小的,所以才有了L2會讓各個系數(shù)趨于相同的特點。
可以看出,L2正則化對于特征選擇來說一種穩(wěn)定的模型,不像L1正則化那樣,系數(shù)會因為細(xì)微的數(shù)據(jù)變化而波動。所以L2正則化和L1正則化提供的價值是不同的,L2正則化對于特征理解來說更加有用:表示能力強(qiáng)的特征對應(yīng)的系數(shù)是非零。
回過頭來看看3個互相關(guān)聯(lián)的特征的例子,分別以10個不同的種子隨機(jī)初始化運(yùn)行10次,來觀察L1和L2正則化的穩(wěn)
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_))

可以看出,不同的數(shù)據(jù)上線性回歸得到的模型(系數(shù))相差甚遠(yuǎn),但對于L2正則化模型來說,結(jié)果中的系數(shù)非常的穩(wěn)定,差別較小,都比較接近于1,能夠反映出數(shù)據(jù)的內(nèi)在結(jié)構(gòu)。

隨機(jī)森林具有準(zhǔn)確率高、魯棒性好、易于使用等優(yōu)點,這使得它成為了目前最流行的機(jī)器學(xué)習(xí)算法之一。隨機(jī)森林提供了兩種特征選擇的方法:mean decrease impurity和mean decrease accuracy。
4.1 平均不純度減少
英文:mean decrease impurity
隨機(jī)森林由多個決策樹構(gòu)成。決策樹中的每一個節(jié)點都是關(guān)于某個特征的條件,為的是將數(shù)據(jù)集按照不同的響應(yīng)變量一分為二。利用不純度可以確定節(jié)點(最優(yōu)條件),對于分類問題,通常采用基尼不純度或者信息增益,對于回歸問題,通常采用的是方差或者最小二乘擬合。當(dāng)訓(xùn)練決策樹的時候,可以計算出每個特征減少了多少樹的不純度。對于一個決策樹森林來說,可以算出每個特征平均減少了多少不純度,并把它平均減少的不純度作為特征選擇的值。
下邊的例子是sklearn中基于隨機(jī)森林的特征重要度度量方法:
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))

這里特征得分實際上采用的是Gini Importance。使用基于不純度的方法的時候,要記?。?/p>
這種方法存在偏向,對具有更多類別的變量會更有利; 對于存在關(guān)聯(lián)的多個特征,其中任意一個都可以作為指示器(優(yōu)秀的特征),并且一旦某個特征被選擇之后,其他特征的重要度就會急劇下降,因為不純度已經(jīng)被選中的那個特征降下來了,其他的特征就很難再降低那么多不純度了,這樣一來,只有先被選中的那個特征重要度很高,其他的關(guān)聯(lián)特征重要度往往較低。
在理解數(shù)據(jù)時,這就會造成誤解,導(dǎo)致錯誤的認(rèn)為先被選中的特征是很重要的,而其余的特征是不重要的,但實際上這些特征對響應(yīng)變量的作用確實非常接近的(這跟Lasso是很像的)。
特征隨機(jī)選擇方法稍微緩解了這個問題,但總的來說并沒有完全解決。下面的例子中,X0、X1、X2是三個互相關(guān)聯(lián)的變量,在沒有噪音的情況下,輸出變量是三者之和。
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_))

當(dāng)計算特征重要性時,可以看到X1的重要度比X2的重要度要高出10倍,但實際上他們真正的重要度是一樣的。盡管數(shù)據(jù)量已經(jīng)很大且沒有噪音,且用了20棵樹來做隨機(jī)選擇,但這個問題還是會存在。
需要注意的一點是,關(guān)聯(lián)特征的打分存在不穩(wěn)定的現(xiàn)象,這不僅僅是隨機(jī)森林特有的,大多數(shù)基于模型的特征選擇方法都存在這個問題。
4.2 平均精確率減少
英文:Mean decrease accuracy
另一種常用的特征選擇方法就是直接度量每個特征對模型精確率的影響。主要思路是打亂每個特征的特征值順序,并且度量順序變動對模型的精確率的影響。很明顯,對于不重要的變量來說,打亂順序?qū)δP偷木_率影響不會太大,但是對于重要的變量來說,打亂順序就會降低模型的精確率。
這個方法sklearn中沒有直接提供,但是很容易實現(xiàn),下面繼續(xù)在波士頓房價數(shù)據(jù)集上進(jìn)行實現(xiàn)。
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))

在這個例子當(dāng)中,LSTAT和RM這兩個特征對模型的性能有著很大的影響,打亂這兩個特征的特征值使得模型的性能下降了73%和57%。注意,盡管這些我們是在所有特征上進(jìn)行了訓(xùn)練得到了模型,然后才得到了每個特征的重要性測試,這并不意味著我們?nèi)拥裟硞€或者某些重要特征后模型的性能就一定會下降很多,因為即便某個特征刪掉之后,其關(guān)聯(lián)特征一樣可以發(fā)揮作用,讓模型性能基本上不變。

之所以叫做頂層,是因為他們都是建立在基于模型的特征選擇方法基礎(chǔ)之上的,例如回歸和SVM,在不同的子集上建立模型,然后匯總最終確定特征得分。
5.1 穩(wěn)定性選擇
穩(wěn)定性選擇是一種基于二次抽樣和選擇算法相結(jié)合較新的方法,選擇算法可以是回歸、SVM或其他類似的方法。它的主要思想是在不同的數(shù)據(jù)子集和特征子集上運(yùn)行特征選擇算法,不斷的重復(fù),最終匯總特征選擇結(jié)果,比如可以統(tǒng)計某個特征被認(rèn)為是重要特征的頻率(被選為重要特征的次數(shù)除以它所在的子集被測試的次數(shù))。理想情況下,重要特征的得分會接近100%。稍微弱一點的特征得分會是非0的數(shù),而最無用的特征得分將會接近于0。
sklearn在隨機(jī)lasso和隨機(jī)邏輯回歸中有對穩(wěn)定性選擇的實現(xiàn)。
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))

在上邊這個例子當(dāng)中,最高的3個特征得分是1.0,這表示他們總會被選作有用的特征(當(dāng)然,得分會收到正則化參數(shù)alpha的影響,但是sklearn的隨機(jī)lasso能夠自動選擇最優(yōu)的alpha)。接下來的幾個特征得分就開始下降,但是下降的不是特別急劇,這跟純lasso的方法和隨機(jī)森林的結(jié)果不一樣。能夠看出穩(wěn)定性選擇對于克服過擬合和對數(shù)據(jù)理解來說都是有幫助的:總的來說,好的特征不會因為有相似的特征、關(guān)聯(lián)特征而得分為0,這跟Lasso是不同的。對于特征選擇任務(wù),在許多數(shù)據(jù)集和環(huán)境下,穩(wěn)定性選擇往往是性能最好的方法之一。
5.2 遞歸特征消除
遞歸特征消除的主要思想是反復(fù)的構(gòu)建模型(如SVM或者回歸模型)然后選出最好的(或者最差的)的特征(可以根據(jù)系數(shù)來選),把選出來的特征放到一遍,然后在剩余的特征上重復(fù)這個過程,直到所有特征都遍歷了。這個過程中特征被消除的次序就是特征的排序。因此,這是一種尋找最優(yōu)特征子集的貪心算法。
RFE的穩(wěn)定性很大程度上取決于在迭代的時候底層用哪種模型。例如,假如RFE采用的普通的回歸,沒有經(jīng)過正則化的回歸是不穩(wěn)定的,那么RFE就是不穩(wěn)定的;假如采用的是Ridge,而用Ridge正則化的回歸是穩(wěn)定的,那么RFE就是穩(wěn)定的。
Sklearn提供了RFE包,可以用于特征消除,還提供了RFECV,可以通過交叉驗證來對的特征進(jìn)行排序。
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)))


下面將本文所有提到的方法進(jìn)行實驗對比,數(shù)據(jù)集采用Friedman #1 回歸數(shù)據(jù)(這篇論文中的數(shù)據(jù))。數(shù)據(jù)是用這個公式產(chǎn)生的:

X1到X5是由單變量分布生成的,e是標(biāo)準(zhǔn)正態(tài)變量N(0,1)。另外,原始的數(shù)據(jù)集中含有5個噪音變量 X5,…,X10,跟響應(yīng)變量是獨立的。我們增加了4個額外的變量X11,…X14,分別是X1,…,X4的關(guān)聯(lián)變量,通過f(x)=x+N(0,0.01)生成,這將產(chǎn)生大于0.999的關(guān)聯(lián)系數(shù)。這樣生成的數(shù)據(jù)能夠體現(xiàn)出不同的特征排序方法應(yīng)對關(guān)聯(lián)特征時的表現(xiàn)。
接下來將會在上述數(shù)據(jù)上運(yùn)行所有的特征選擇方法,并且將每種方法給出的得分進(jìn)行歸一化,讓取值都落在0-1之間。對于RFE來說,由于它給出的是順序而不是得分,我們將最好的5個的得分定為1,其他的特征的得分均勻的分布在0-1之間。
from?sklearn.datasets?import?load_boston
from?sklearn.linear_model?import?(LinearRegression,?Ridge,?
??????????????????????????????????Lasso,?RandomizedLasso)
from?sklearn.feature_selection?import?RFE,?f_regression
from?sklearn.preprocessing?import?MinMaxScaler
from?sklearn.ensemble?import?RandomForestRegressor
import?numpy?as?np
from?minepy?import?MINE
np.random.seed(0)
size?=?750
X?=?np.random.uniform(0,?1,?(size,?14))
#"Friedamn?#1”?regression?problem
Y?=?(10?*?np.sin(np.pi*X[:,0]*X[:,1])?+?20*(X[:,2]?-?.5)**2?+
?????10*X[:,3]?+?5*X[:,4]?+?np.random.normal(0,1))
#Add?3?additional?correlated?variables?(correlated?with?X1-X3)
X[:,10:]?=?X[:,:4]?+?np.random.normal(0,?.025,?(size,4))
names?=?["x%s"?%?i?for?i?in?range(1,15)]
ranks?=?{}
def?rank_to_dict(ranks,?names,?order=1):
????minmax?=?MinMaxScaler()
????ranks?=?minmax.fit_transform(order*np.array([ranks]).T).T[0]
????ranks?=?map(lambda?x:?round(x,?2),?ranks)
????return?dict(zip(names,?ranks?))
lr?=?LinearRegression(normalize=True)
lr.fit(X,?Y)
ranks["Linear?reg"]?=?rank_to_dict(np.abs(lr.coef_),?names)
ridge?=?Ridge(alpha=7)
ridge.fit(X,?Y)
ranks["Ridge"]?=?rank_to_dict(np.abs(ridge.coef_),?names)
lasso?=?Lasso(alpha=.05)
lasso.fit(X,?Y)
ranks["Lasso"]?=?rank_to_dict(np.abs(lasso.coef_),?names)
rlasso?=?RandomizedLasso(alpha=0.04)
rlasso.fit(X,?Y)
ranks["Stability"]?=?rank_to_dict(np.abs(rlasso.scores_),?names)
#stop?the?search?when?5?features?are?left?(they?will?get?equal?scores)
rfe?=?RFE(lr,?n_features_to_select=5)
rfe.fit(X,Y)
ranks["RFE"]?=?rank_to_dict(map(float,?rfe.ranking_),?names,?order=-1)
rf?=?RandomForestRegressor()
rf.fit(X,Y)
ranks["RF"]?=?rank_to_dict(rf.feature_importances_,?names)
f,?pval??=?f_regression(X,?Y,?center=True)
ranks["Corr."]?=?rank_to_dict(f,?names)
mine?=?MINE()
mic_scores?=?[]
for?i?in?range(X.shape[1]):
????mine.compute_score(X[:,i],?Y)
????m?=?mine.mic()
????mic_scores.append(m)
ranks["MIC"]?=?rank_to_dict(mic_scores,?names)
r?=?{}
for?name?in?names:
????r[name]?=?round(np.mean([ranks[method][name]?
?????????????????????????????for?method?in?ranks.keys()]),?2)
methods?=?sorted(ranks.keys())
ranks["Mean"]?=?r
methods.append("Mean")
print("%s"?%?"".join(methods))
for?name?in?names:
????print("%s%s"?%?(name,?"".join(map(str,?[ranks[method][name]?for?method?in?methods]))))

從以上結(jié)果中可以找到一些有趣的發(fā)現(xiàn):
特征之間存在線性關(guān)聯(lián)關(guān)系,每個特征都是獨立評價的,因此X1,…X4的得分和X11,…X14的得分非常接近,而噪音特征X5,…,X10正如預(yù)期的那樣和響應(yīng)變量之間幾乎沒有關(guān)系。由于變量X3是二次的,因此X3和響應(yīng)變量之間看不出有關(guān)系(除了MIC之外,其他方法都找不到關(guān)系)。這種方法能夠衡量出特征和響應(yīng)變量之間的線性關(guān)系,但若想選出優(yōu)質(zhì)特征來提升模型的泛化能力,這種方法就不是特別給力了,因為所有的優(yōu)質(zhì)特征都不可避免的會被挑出來兩次。
Lasso能夠挑出一些優(yōu)質(zhì)特征,同時讓其他特征的系數(shù)趨于0。當(dāng)如需要減少特征數(shù)的時候它很有用,但是對于數(shù)據(jù)理解來說不是很好用。(例如在結(jié)果表中,X11,X12,X13的得分都是0,好像他們跟輸出變量之間沒有很強(qiáng)的聯(lián)系,但實際上不是這樣的)
MIC對特征一視同仁,這一點上和關(guān)聯(lián)系數(shù)有點像,另外,它能夠找出X3和響應(yīng)變量之間的非線性關(guān)系。
隨機(jī)森林基于不純度的排序結(jié)果非常鮮明,在得分最高的幾個特征之后的特征,得分急劇的下降。從表中可以看到,得分第三的特征比第一的小4倍。而其他的特征選擇算法就沒有下降的這么劇烈。
Ridge將回歸系數(shù)均勻的分?jǐn)偟礁鱾€關(guān)聯(lián)變量上,從表中可以看出,X11,…,X14和X1,…,X4的得分非常接近。
穩(wěn)定性選擇常常是一種既能夠有助于理解數(shù)據(jù)又能夠挑出優(yōu)質(zhì)特征的這種選擇,在結(jié)果表中就能很好的看出。像Lasso一樣,它能找到那些性能比較好的特征(X1,X2,X4,X5),同時,與這些特征關(guān)聯(lián)度很強(qiáng)的變量也得到了較高的得分。

對于理解數(shù)據(jù)、數(shù)據(jù)的結(jié)構(gòu)、特點來說,單變量特征選擇是個非常好的選擇。盡管可以用它對特征進(jìn)行排序來優(yōu)化模型,但由于它不能發(fā)現(xiàn)冗余(例如假如一個特征子集,其中的特征之間具有很強(qiáng)的關(guān)聯(lián),那么從中選擇最優(yōu)的特征時就很難考慮到冗余的問題)。
正則化的線性模型對于特征理解和特征選擇來說是非常強(qiáng)大的工具。L1正則化能夠生成稀疏的模型,對于選擇特征子集來說非常有用;相比起L1正則化,L2正則化的表現(xiàn)更加穩(wěn)定,由于有用的特征往往對應(yīng)系數(shù)非零,因此L2正則化對于數(shù)據(jù)的理解來說很合適。由于響應(yīng)變量和特征之間往往是非線性關(guān)系,可以采用basis expansion的方式將特征轉(zhuǎn)換到一個更加合適的空間當(dāng)中,在此基礎(chǔ)上再考慮運(yùn)用簡單的線性模型。
隨機(jī)森林是一種非常流行的特征選擇方法,它易于使用,一般不需要feature engineering、調(diào)參等繁瑣的步驟,并且很多工具包都提供了平均不純度下降方法。它的兩個主要問題,1是重要的特征有可能得分很低(關(guān)聯(lián)特征問題),2是這種方法對特征變量類別多的特征越有利(偏向問題)。盡管如此,這種方法仍然非常值得在你的應(yīng)用中試一試。
特征選擇在很多機(jī)器學(xué)習(xí)和數(shù)據(jù)挖掘場景中都是非常有用的。在使用的時候要弄清楚自己的目標(biāo)是什么,然后找到哪種方法適用于自己的任務(wù)。當(dāng)選擇最優(yōu)特征以提升模型性能的時候,可以采用交叉驗證的方法來驗證某種方法是否比其他方法要好。
當(dāng)用特征選擇的方法來理解數(shù)據(jù)的時候要留心,特征選擇模型的穩(wěn)定性非常重要,穩(wěn)定性差的模型很容易就會導(dǎo)致錯誤的結(jié)論。對數(shù)據(jù)進(jìn)行二次采樣然后在子集上運(yùn)行特征選擇算法能夠有所幫助,如果在各個子集上的結(jié)果是一致的,那就可以說在這個數(shù)據(jù)集上得出來的結(jié)論是可信的,可以用這種特征選擇模型的結(jié)果來理解數(shù)據(jù)。
長按或掃描下方二維碼,后臺回復(fù):加群,即可申請入群。一定要備注:來源+研究方向+學(xué)校/公司,否則不拉入群中,見諒!
(長按三秒,進(jìn)入后臺)
推薦閱讀

