如何進行特徵選擇(代碼篇)| 機器學習你會遇到的“坑”

轉載自:https://baijiahao.baidu.com/s?id=1604162212692449062&wfr=spider&for=pc

僅做記錄,侵刪。

我們在這裏首先會對數據的多餘特徵和無關特徵做可視化,以便我們更好的理解特徵選擇的動機,接着分別用過濾法,包裹法,和嵌入法這些特徵選擇的方法做出代碼展示,同時觀察測試集上泛化誤差來體現出特徵選擇的優越性,最後我們試一試將其結合起來會不會取得更好的效果。

 

我們上一篇的所用的糖尿病數據有一個遺留問題,那就是性別到底與糖尿病的惡化程度有沒有關係?換而言之,它到底是不是一個無關特徵?

 

 

 

 

 

 

 

from sklearn import datasets

import seaborn as snsimport matplotlib.pyplot as pltimport numpy as np#讀取數據data=datasets.load_diabetes()X=data['data'][:,np.newaxis,1]y=data['target']#作圖sns.set(style='darkgrid')plt.plot(X,y,'.k',markersize=5)plt.xlabel('Sex')plt.ylabel('quantitativemeasure of disease progression')plt.legend()plt.show()

 

 

我們從圖中可以看出來,在每個性別上,我們的目標均呈現出均勻分佈的趨勢,很可能性別就是一個無關特徵,但是,我們最好利用相關係數法來對所有的特徵做一個檢驗:

 

 

 

 

 

 

from sklearn import datasets

fromsklearn.feature_selection import f_regressionimport seaborn as snsimport matplotlib.pyplot as pltimport numpy as np#讀取數據data=datasets.load_diabetes()X=data['data']y=data['target']# 計算相關係數score=f_regression(X,y)[0]#作圖sns.set(style='darkgrid')sns.barplot(score,data['feature_names'])plt.xlabel('Score')plt.ylabel('features')plt.legend()plt.show()

 

 

相關係數衡量的是特徵與目標的線性相關性,從圖中可以看出,性別與目標的相關係數非常小,那麼其餘的諸如‘age’,'s1','s2'該不該去除呢,我們在對特徵做互信息篩選:

 

 

 

 

 

 

......

fromsklearn.feature_selection import mutual_info_regressionscore=mutual_info_regression(X,y,discrete_features=False,random_state=0)......

 

 

除此之外,我們還可以選用Spearman相關係數:

 

 

 

 

 

 

from scipy.stats import spearmanr

score=[]forn in range(10): score.append(np.abs(spearmanr(X[:,n],y)[0]))

 

 

可以看出,‘age’,‘sex’,‘s1’,‘s2’仍然是得分最低的四個特徵,而‘age’,‘sex’,‘s2’仍然是得分最低的三個特徵。我們就可以說,互信息和Pearson係數,以及Spearman係數取得了一定程度的一致性。那麼經過這樣的過濾,我們對特徵的重要程度有了認識,到底要挑選多少特徵進入最終的模型取決於多少特徵可以達到最好的性能。

 

直到這裏,我們做的只是檢驗特徵與目標的相關性,來達到剔除無關特徵的作用,但是除此之外,我們的特徵選擇還有一個重要的任務,就是剔除多餘特徵。針對我們的數據,我們可能會直覺上認爲,年齡和血壓有一定的關係:

 

 

 

 

 

 

plt.plot(X[:,0],X[:,3],'.k')

 

 

但如果我們只從樣本空間去可視化,我們無法做到精確判斷,比如這幅圖我們無法得到什麼信息。更好的做法是,我們對每個特徵與每個特徵做相關係數,得到相關性矩陣(注意,這時候我們只對特徵進行處理,與目標i無關):

 

 

 

 

 

 

from scipy.stats import spearmanr

import seaborn as snsfrom sklearn import datasets

data=datasets.load_diabetes()X=data['data']

score_mat=np.abs(spearmanr(X)[0])

sns.set(style='white')

sns.heatmap(score_mat,annot=True,center=0)plt.show()

 

 

方格的顏色越淺,相關程度越高。我們可以注意到一個有趣的事實:我們所關注的年齡和血壓的相關度只有0.35,遠遠低於's1'血清與's2'血清的相關度,後者高達0.88,而's3'血清和's4'血清的相關度也有0.79.爲了直觀的理解這種相關度,我們可以對高相關度的's1'血清和's2'血清做圖:

 

 

 

 

 

 

plt.plot(X[:,4],X[:,5],'.k')

 

 

兩者呈現出很強的線性關係!那麼我們就可以斷定這是多餘特徵的特徵,我們只需要保留其中一個。

 

由上可見,過濾法主要有兩大任務:通過檢驗特徵之間的相關度來找出多餘特徵,通過檢驗特徵與目標的相關度來找出無關特徵。

 

接下來,包裹法相對就好理解得多,它把全部的特徵丟進集合,利用貪心策略。我們可以只利用線性模型觀察不同的特徵數對模型擬合能力的影響,根據理論我們說特徵越少,模型的參數也就越少,那麼模型的擬合能力也會越弱。但是,特徵選擇的目的是爲了讓模型的泛化能力更強,所以我們要把特徵數當作超參數,通過交叉驗證的辦法來觀察測試集的上的泛化誤差:

 

 

 

 

 

 

from sklearn.feature_selection import RFECV

import seaborn as snsimport matplotlib.pyplot as pltimport numpy as npfrom sklearn.linear_model import LinearRegressionfromsklearn.model_selection import KFolddata=datasets.load_diabetes()X=data['data']y=data['target']lr=LinearRegression()scorer='neg_mean_squared_error'rfecv = RFECV(estimator=lr, step=1, cv=KFold(5),scoring=scorer)rfecv.fit(X, y)sns.set(style='darkgrid')plt.xlabel("Number offeatures selected")plt.ylabel("Cross validationscore (MSE)")plt.plot(range(1, len(rfecv.grid_scores_) +1), -rfecv.grid_scores_,'r',label="Optimal number offeatures : %d"% rfecv.n_features_)plt.legend()plt.show()

 

 

利用包裹法我們可以挑選出最佳的特徵子集包含的特徵數爲6,這樣的特徵子集的泛化能力比全部特徵集要好,最佳特徵子集的MSE大約爲2946.88,而全部特徵的MSE大約爲2993,說明對數據進行特徵選擇可以顯著提高模型的性能。我們可以通過如下代碼查看哪幾個特徵得以保留:

 

 

 

 

 

 

np.array(data['feature_names'])[rfecv.support_]#這裏面用到了numpy數組的布爾值切分方法

 

包裹法非常的簡單粗暴,直接利用特徵子集的表現來達到挑選特徵的作用,但它固定了學習器,如果我們用linear regression挑選完特徵,也只能說明,這些特徵是在linear regression上最佳的組合,而不能放入到更廣泛的模型中,比如,向量機和貝葉斯模型。

 

而嵌入法嵌入在了學習器裏面,訓練過程和特徵選擇同時完成,這樣的嵌入避免了學習器與特徵選擇相分離的麻煩,因爲包裹法所挑選的特徵可能會隨着學習器的改變而改變。

 

我們在《過擬合問題(代碼篇)》中有過這樣一幅圖(最佳的

約爲0.057):

 

 

表示了模型的泛化誤差隨着正則化係數的變化,從中選擇使得泛化誤差最小的.而最佳的在參數空間對應着某些特徵的消失:

 

 

 

 

 

 

coefs_lasso=[]

alphas=np.linspace(0.01,0.5,1000)fora in alphas: lasso=Lasso(alpha=a) lasso.fit(X,y) coefs_lasso.append((lasso.coef_))

 

 

正則化是非常好用的辦法,因爲它參與模型的優化,但不依賴於模型本身,所以在很多模型中都可以嵌入它。如果它嵌入Linear Regression,就是我們前面反覆討論過的形式,可如果它嵌入了向量機,一樣可以起到特徵選擇的作用。

 

從模型的角度來看特徵選擇,它本質就是將我們模型輸入變量的維度減少,從而獲得更好的性能,更快的訓練速度,甚至更好的解釋性。

 

那麼還有沒有其他方法可以達到類似的目的呢?我們將在下一篇介紹降維與特徵選擇的不同,以及我們如何恰當的應用降維。

 

 

 

 

 

 

課堂TIPS

 

 

 

本文的數據與《過擬合問題(代碼篇)》的數據相同,主要是針對迴歸問題。如果是分類問題,那麼過濾法中的相關係數和互信息法也要做相應的改變,我們更應該用sklearn.featureselection的fclassif和mutualinfoclassif,以及chi2。

 

本文采用的spearman係數法來自Scipy的統計模塊。除此之外,常用的過濾法所需要的函數,在Scipy裏幾乎都可以找到。

 

從理論上來說,過濾法所剔除的特徵應該與包裹法剔除的特徵相同,但在實際情況中,因爲數據的分佈未知,過濾法的很多數學假設也就不再成立,甚至現實中的數據太過龐大,關係太過複雜,過濾法剔除的特徵往往與包裹法的不同。

 

包裹法雖然原理簡單,算法好用,但實際中特徵往往很多,計算量也會變得非常大。所以正則化方法是被人普遍使用,而過濾法則往往作爲一個具有參考價值的方法,並不會把過濾作爲特徵選擇的終點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章