三、數(shù)據(jù)預(yù)處理與特征工程
由于面對現(xiàn)實問題時废登,其所給的數(shù)據(jù)往往都是比較拉跨的淹魄,所以這門技術(shù)就是在建模之前要該干的事情:數(shù)據(jù)預(yù)處理和特征工程;
-
流程:
提取/創(chuàng)造特征非常難钳宪,且大多數(shù)是基于經(jīng)驗的揭北。
-
sklearn中的數(shù)據(jù)預(yù)處理和特征工程:
(1)模塊preprocessing:幾乎包含數(shù)據(jù)預(yù)處理的所有內(nèi)容;
(2)模塊Impute:填補缺失值專用吏颖;
(3)模塊feature_selection:包含特征選擇的各種方法的實踐搔体;
(4)模塊decomposition:包含降維算法。
3.1 數(shù)據(jù)預(yù)處理 Preprocessing & Impute
3.1.1 數(shù)據(jù)無量綱化
- 無量綱化: 將不同規(guī)格的數(shù)據(jù)轉(zhuǎn)換到同一規(guī)格半醉,或不同分布的數(shù)據(jù)轉(zhuǎn)換到某個特定分布的需求疚俱;
- 類別: 分為 線性 和 非線性 兩種方式。其中 線性的無量綱化 包括中心化(Zero-centered或者Mean?subtraction)處理和縮放處理(Scale)缩多;
- 中心化本質(zhì): 讓所有記錄減去一個固定值呆奕,即讓數(shù)據(jù)樣本數(shù)據(jù)平移到某個位置;
- 縮放本質(zhì):通過除以一個固定值衬吆,將數(shù)據(jù)固定在某個范圍之中梁钾,取對數(shù)也算是一種縮放處理;
-
數(shù)據(jù)歸一化(Normalization逊抡,又稱Min-Max Scaling): 當數(shù)據(jù)(x)按照最小值中心化后姆泻,再按極差(最大值 - 最小值)縮放,數(shù)據(jù)移動了最小值個單位冒嫡,并且會被收斂到[0,1]之間的過程(并不是正則化)拇勃。歸一化之后的數(shù)據(jù)服從正態(tài)分布,公式如下:
在sklearn當中孝凌,我們使用preprocessing.MinMaxScaler來實現(xiàn)這個功能方咆。MinMaxScaler有一個重要參,feature_range蟀架,控制我們希望把數(shù)據(jù)壓縮到的范圍瓣赂,默認是[0,1]:
from sklearn.preprocessing import MinMaxScaler
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
#不太熟悉numpy的小伙伴榆骚,能夠判斷data的結(jié)構(gòu)嗎?
#如果換成表是什么樣子钩述?
import pandas as pd
pd.DataFrame(data)
此時pd的值為:
#實現(xiàn)歸一化
#實例化
scaler = MinMaxScaler()
#fit寨躁,在這里本質(zhì)是生成min(x)和max(x)
scaler = scaler.fit(data)
#通過接口導出結(jié)果
result = scaler.transform(data)
result
此時result的值為:
歸一化后發(fā)現(xiàn)數(shù)據(jù)分布一致,說明其所含信息相似/一致
#訓練和導出結(jié)果一步達成
result_ = scaler.fit_transform(data)
#將歸一化后的結(jié)果逆轉(zhuǎn)(就是復(fù)原成歸一前的樣子)
scaler.inverse_transform(result)
#使用MinMaxScaler的參數(shù)feature_range實現(xiàn)將數(shù)據(jù)歸一化到[0,1]以外的范圍中
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
scaler = MinMaxScaler(feature_range=[5,10]) #依然實例化
result = scaler.fit_transform(data) #fit_transform一步導出結(jié)果
result
最后那段代碼運行的結(jié)果:
當X中的特征數(shù)量非常多的時候(一般不會)牙勘,fit會報錯并表示,數(shù)據(jù)量太大了我計算不了
此時使用partial_fit作為訓練接口:scaler = scaler.partial_fit(data)
- 使用numpy實現(xiàn)歸一化(就是用上面那個歸一化的公式):
import numpy as np
X = np.array([[-1, 2], [-0.5, 6], [0, 10], [1, 18]])
#歸一化所禀,X.min/max(axis=0/1)返回的是每一列/行中的最小/大值
X_nor = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
X_nor
#逆轉(zhuǎn)歸一化
X_returned = X_nor * (X.max(axis=0) - X.min(axis=0)) + X.min(axis=0)
X_returned
但是這玩意兒在實際應(yīng)用中一般都是直接用MinMaxScaler了
- 數(shù)據(jù)標準化(Standardization方面,又稱Z-score normalization): 當數(shù)據(jù)(x)按均值(μ)中心化后,再按標準差(σ)縮放色徘,數(shù)據(jù)就會服從為均值為0恭金,方差為1的正態(tài)分布(即標準正態(tài)分布 ,標準化)褂策。對應(yīng)的公式為:
from sklearn.preprocessing import StandardScaler
data = [[-1, 2], [-0.5, 6], [0, 10], [1, 18]]
#實例化
scaler = StandardScaler()
#fit横腿,本質(zhì)是生成均值和方差
scaler.fit(data)
#查看均值的屬性mean_
scaler.mean_
#array([-0.125, 9. ])
#查看方差的屬性var_(都是按列算的)
scaler.var_
#array([ 0.546875, 35. ])
#通過接口導出結(jié)果
x_std = scaler.transform(data)
此時x_std的值為:
標準化之后就會變成均值為0,方差為1的數(shù)據(jù)
#導出的結(jié)果是一個數(shù)組斤寂,用mean()查看均值
x_std.mean()
#0.0
#用std()查看方差
x_std.std()
#1.0
#使用fit_transform(data)一步達成結(jié)果
scaler.fit_transform(data)
#使用inverse_transform逆轉(zhuǎn)標準化
scaler.inverse_transform(x_std)
對于StandardScaler和MinMaxScaler來說耿焊,空值NaN會被當做是缺失值,在 fit的時候忽略 遍搞,在transform的時候保持缺失NaN的狀態(tài)顯示 罗侯。在fit接口中,依然只允許導入至少二維數(shù)組溪猿,一維數(shù)組導入會報錯钩杰。通常來說,我們輸入的X會是我們的特征矩陣诊县,現(xiàn)實案例中特征矩陣不太可能是一維所以不會存在這個問題讲弄。
-
StandardScaler和MinMaxScaler選哪個?(我不到耙廊)
看情況避除。大多數(shù)機器學習算法中,會選擇StandardScaler來進行特征縮放抗悍,因為MinMaxScaler對異常值非常敏感驹饺。在PCA,聚類缴渊,邏輯回歸赏壹,支持向量機,神經(jīng)網(wǎng)絡(luò)這些算法中衔沼,StandardScaler往往是最好的選擇蝌借。
MinMaxScaler在不涉及距離度量昔瞧、梯度、協(xié)方差計算以及數(shù)據(jù)需要被壓縮到特定區(qū)間時使用廣泛菩佑,比如數(shù)字圖像處理中量化像素強度時自晰,都會使用MinMaxScaler將數(shù)據(jù)壓縮于[0,1]區(qū)間之中。
建議先試試看StandardScaler稍坯,效果不好換MinMaxScaler酬荞。
一大堆無量綱化:
3.1.2 缺失值
數(shù)據(jù)挖掘之中,常常會有重要的字段缺失值很多瞧哟,但又不能舍棄字段的情況混巧。因此,數(shù)據(jù)預(yù)處理中非常重要的一項就是處理缺失值:
#導入數(shù)據(jù)(泰坦尼克號數(shù)據(jù))
import pandas as pd
#后邊那個index_col表示將指定的列作為索引勤揩,這里取第0列咧党,不然就會再額外創(chuàng)建一列作為索引
data = pd.read_csv(r"E:\Involution\sklearn\03數(shù)據(jù)預(yù)處理和特征工程\Narrativedata.csv",index_col=0)
data.head()
這個數(shù)據(jù)有三個特征:一個數(shù)值型,兩個字符型陨亡,標簽也是字符型
查看數(shù)據(jù)信息:
- impute.SimpleImputer: class sklearn.impute.SimpleImputer (missing_values=nan, strategy=’mean’, fill_value=None, verbose=0,copy=True)傍衡,這個類是專門用來填補缺失值的,共有四個參數(shù):
copy一般都是True负蠕,不希望修改原數(shù)據(jù)
#填補年齡
#sklearn當中特征矩陣必須是二維蛙埂,取出Age列對象并轉(zhuǎn)為二維
Age = data.loc[:,"Age"].values.reshape(-1,1)
Age[:20]
提取并轉(zhuǎn)換后的數(shù)據(jù)長這樣:
from sklearn.impute import SimpleImputer
#實例化,默認均值填補
imp_mean = SimpleImputer()
#用中位數(shù)填補
imp_median = SimpleImputer(strategy="median")
#用0填補
imp_0 = SimpleImputer(strategy="constant",fill_value=0)
#fit_transform一步完成調(diào)取結(jié)果
imp_mean = imp_mean.fit_transform(Age)
imp_median = imp_median.fit_transform(Age)
imp_0 = imp_0.fit_transform(Age)
imp_mean[:20]
imp_median[:20]
imp_0[:20]
填補后的數(shù)組內(nèi)容為:
中位數(shù)和均值比較接近虐急,說明數(shù)據(jù)分布比較均勻
#在這里我們使用中位數(shù)填補Age
data.loc[:,"Age"] = imp_median
data.info()
此時年齡的空缺值都填補上
#使用眾數(shù)填補Embarked(用的是字符串箱残,所以用眾數(shù))
Embarked = data.loc[:,"Embarked"].values.reshape(-1,1)
imp_mode = SimpleImputer(strategy = "most_frequent")
data.loc[:,"Embarked"] = imp_mode.fit_transform(Embarked)
data.info()
這樣一來這組數(shù)據(jù)就完整了(現(xiàn)實案例中可以把這兩條空值刪了就好)
- 用Pandas和Numpy進行填補其實更加簡單:
import pandas as pd
data = pd.read_csv(r"E:\Involution\sklearn\03數(shù)據(jù)預(yù)處理和特征工程\Narrativedata.csv\Narrativedata.csv",index_col=0)
data.head()
#.fillna 在DataFrame里面直接進行填補
data.loc[:,"Age"] = data.loc[:,"Age"].fillna(data.loc[:,"Age"].median())
#.dropna(axis=0)刪除所有有缺失值的行,.dropna(axis=1)刪除所有有缺失值的列
#參數(shù)inplace止吁,為True表示在原數(shù)據(jù)集上進行修改被辑,為False表示生成一個復(fù)制對象,不修改原數(shù)據(jù)敬惦,默認False
data.dropna(axis=0,inplace=True)
刪掉了缺失的兩行之后:
3.1.3 處理分類型特征:編碼與啞變量
專門拿來處理分類型特征的盼理,因為在機器學習中,大多數(shù)算法(譬如邏輯回歸俄删,支持向量機SVM宏怔,k近鄰算法)都只能夠處理數(shù)值型數(shù)據(jù),不能處理文字畴椰,在sklearn當中臊诊,除了專用來處理文字的算法,其他算法在fit的時候全部要求輸入數(shù)組或矩陣斜脂,也不能夠?qū)胛淖中蛿?shù)據(jù)(其實手寫決策樹和普斯貝葉斯可以處理文字抓艳,但是sklearn中規(guī)定必須導入數(shù)值型)
- preprocessing.LabelEncoder: 標簽專用,能夠將分類轉(zhuǎn)換為分類數(shù)值帚戳;
from sklearn.preprocessing import LabelEncoder
#要輸入的是標簽玷或,不是特征矩陣儡首,所以允許一維,一般默認是最后一列
y = data.iloc[:,-1]
#實例化
le = LabelEncoder()
#導入數(shù)據(jù)
le = le.fit(y)
#transform接口調(diào)取結(jié)果
label = le.transform(y)
轉(zhuǎn)換后的label長這樣:
#屬性.classes_查看標簽中究竟有多少類別
le.classes_
#查看獲取的結(jié)果label
label
#array(['No', 'Unknown', 'Yes'], dtype=object)
#也可以直接fit_transform一步到位
le.fit_transform(y)
#使用inverse_transform可以逆轉(zhuǎn)
le.inverse_transform(label)
#讓標簽等于我們運行出來的結(jié)果
data.iloc[:,-1] = label
data.head()
#如果不需要教學展示的話我會這么寫:
from sklearn.preprocessing import LabelEncoder
data.iloc[:,-1] = LabelEncoder().fit_transform(data.iloc[:,-1])
- preprocessing.OrdinalEncoder:特征專用偏友,能夠?qū)⒎诸愄卣鬓D(zhuǎn)換為分類數(shù)值(不能導入一維數(shù)組)
from sklearn.preprocessing import OrdinalEncoder
#接口categories_對應(yīng)LabelEncoder的接口classes_蔬胯,一模一樣的功能
data_ = data.copy()
data_.head()
#查看每個特征中有多少個類別,這里指明了從第1列到最后一列之間的索引(不包含這兩列)
OrdinalEncoder().fit(data_.iloc[:,1:-1]).categories_
#[array(['female', 'male'], dtype=object), array(['C', 'Q', 'S'], dtype=object)]
data_.iloc[:,1:-1] = OrdinalEncoder().fit_transform(data_.iloc[:,1:-1])
data_.head()
這樣就一口氣完成了多個特征類的轉(zhuǎn)變位他,到此為止氛濒,data中所有數(shù)據(jù)全都是數(shù)字型的了
-
preprocessing.OneHotEncoder:獨熱編碼,創(chuàng)建啞變量
盡管已經(jīng)將把分類變量Sex和Embarked都轉(zhuǎn)換成數(shù)字對應(yīng)的類別了鹅髓,但是這種轉(zhuǎn)會對么泼橘?我們來思考三種不同性質(zhì)的分類數(shù)據(jù):
1) 艙門(S,C迈勋,Q)
三種取值S,C醋粟,Q是相互獨立的靡菇,彼此之間完全沒有聯(lián)系,表達的是S≠C≠Q(mào)的概念米愿。這是名義變量厦凤。
2) 學歷(小學,初中育苟,高中)
三種取值不是完全獨立的较鼓,我們可以明顯看出,在性質(zhì)上可以有高中>初中>小學這樣的聯(lián)系违柏,學歷有高低博烂,但是學歷取值之間卻不是可以計算的,我們不能說小學 + 某個取值 = 初中漱竖。這是有序變量禽篱。
3) 體重(>45kg,>90kg馍惹,>135kg)
各個取值之間有聯(lián)系躺率,且是可以互相計算的,比如120kg - 45kg = 90kg万矾,分類之間可以通過數(shù)學計算互相轉(zhuǎn)換悼吱。這是有距變量
算法會把艙門,學歷這樣的分類特征良狈,都誤會成是體重這樣的分類特征后添。這是說,我們把分類轉(zhuǎn)換成數(shù)字的時候们颜,忽略了數(shù)字中自帶的數(shù)學性質(zhì)吕朵,所以給算法傳達了一些不準確的信息猎醇,而這會影響我們的建模。
類別OrdinalEncoder可以用來處理有序變量努溃,但對于名義變量硫嘶,我們只有使用啞變量的方式來處理,才能夠 盡量向算法傳達最準確的信息 :
這樣的變化(有點像向量)梧税,讓算法能夠徹底領(lǐng)悟敞恋,原來三個取值是沒有可計算性質(zhì)的,是“有你就沒有我”的不等概念块攒。
在我們的數(shù)據(jù)中纷闺,性別和艙門,都是這樣的名義變量凳谦。因此我們需要使用獨熱編碼忆畅,將兩個特征都轉(zhuǎn)換為啞變量
data.head()
from sklearn.preprocessing import OneHotEncoder
X = data.iloc[:,1:-1]
#下面那個auto可以讓函數(shù)自己去找對應(yīng)的類,不用自己輸了(來人福利)
enc = OneHotEncoder(categories='auto').fit(X)
result = enc.transform(X).toarray()
result
有5列是因為性別2+艙門3共有5列數(shù)據(jù)
#依然可以直接一步到位尸执,但為了給大家展示模型屬性家凯,所以還是寫成了三步
OneHotEncoder(categories='auto').fit_transform(X).toarray()
#依然可以還原,DataFrame讓數(shù)據(jù)以表格形式展現(xiàn)如失,好看些
pd.DataFrame(enc.inverse_transform(result))
#通過下面這個來告訴你上面那個矩陣中绊诲,哪個變量是屬于哪個特征的
enc.get_feature_names()
#array(['x0_female', 'x0_male', 'x1_C', 'x1_Q', 'x1_S'], dtype=object)
result
result.shape
#concat是連接函數(shù),axis=1,表示跨行進行合并褪贵,也就是將量表左右相連掂之,如果是axis=0,就是將量表上下相連
newdata = pd.concat([data,pd.DataFrame(result)],axis=1)
newdata.head()
#刪除原來的那兩列(axis=1)脆丁,如果是行的畫就是axis=0
newdata.drop(["Sex","Embarked"],axis=1,inplace=True)
#修改列的名字
newdata.column = ["Age","Survived","Female","Male","Embarked_C","Embarked_Q","Embarked_S"]
newdata.head()
特征可以做啞變量世舰,標簽也可以嗎?可以偎快,使用類sklearn.preprocessing.LabelBinarizer可以對做啞變量冯乘,許多算法都可以處理多標簽問題(比如說決策樹),但是這樣的做法在現(xiàn)實中不常見
三種方法的用法晒夹、參數(shù)裆馒、屬性和接口
- 數(shù)據(jù)類型以及常用的統(tǒng)計量:挺有用的
3.1.4 處理連續(xù)型特征:二值化與分段
-
sklearn.preprocessing.Binarizer:
二值化: 把連續(xù)型分類變量轉(zhuǎn)換為二分類的方法,大于閾值的值映射為1丐怯,而小于或等于閾值的值映射為0喷好。默認閾值為0時,特征中所有的正值都映射到1读跷。二值化是對文本計數(shù)數(shù)據(jù)的常見操作梗搅,它還可以用作考慮布爾隨機變量的估計器的預(yù)處理步驟(例如,使用貝葉斯設(shè)置中的伯努利分布建模)
#將年齡二值化
data_2 = data.copy()
from sklearn.preprocessing import Binarizer
#類為特征專用,所以不能使用一維數(shù)組
X = data_2.iloc[:,0].values.reshape(-1,1)
transformer = Binarizer(threshold=30).fit_transform(X)
transformer
#把原數(shù)據(jù)中對應(yīng)的列換掉
data_2.iloc[;,0] = transformer
- preprocessing.KBinsDiscretizer:這是將連續(xù)型變量劃分為分類變量的類无切,能夠?qū)⑦B續(xù)型變量排序后按順序分箱后編碼荡短。總共包含三個重要參數(shù)(最常用的就是默認的那幾個):
那個onehot-dense一般不用哆键,不學了
from sklearn.preprocessing import KBinsDiscretizer
X = data.iloc[:,0].values.reshape(-1,1)
est = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform')
est.fit_transform(X)
#查看轉(zhuǎn)換后分的箱:變成了一列中的三箱掘托,其中ravel是降維函數(shù);set是把數(shù)據(jù)導入籍嘹,重復(fù)的去掉闪盔,可以用來看有多少類
set(est.fit_transform(X).ravel())
#{0.0, 1.0, 2.0}
#查看轉(zhuǎn)換后分的箱:變成了啞變量
est = KBinsDiscretizer(n_bins=3, encode='onehot', strategy='uniform')
est.fit_transform(X).toarray()
一般來說只用剛剛調(diào)整encoder和要分的箱數(shù)(可以用shift+tab來查看幫助)
3.2 特征選擇 feature_selection
特征工程共有三種手藝:
-
三件重要的事情: 跟數(shù)據(jù)提供者開會!跟數(shù)據(jù)提供者開會辱士!還他丫的是跟數(shù)據(jù)提供者開會泪掀!(申遺)
根據(jù)我們的目標,用業(yè)務(wù)常識來選擇特征颂碘。來看完整版泰坦尼克號數(shù)據(jù)中的這些特征:
其中是否存活是我們的標簽异赫。很明顯,以判斷“是否存活”為目的头岔,票號祝辣,登船的艙門,乘客編號明顯是 無關(guān)特征切油,可以直接刪除 。姓名名惩,艙位等級澎胡,船艙編號,也基本可以判斷是相關(guān)性比較低的特征娩鹉。性別攻谁,年齡,船上的親人數(shù)量弯予,這些應(yīng)該是相關(guān)性比較高的特征
- 第一步戚宦,理解業(yè)務(wù): 當無法依賴對業(yè)務(wù)的理解來選擇特征時,有四種方法可以用來選擇特征:過濾法锈嫩,嵌入法受楼,包裝法,和降維算法
#導入數(shù)據(jù)呼寸,讓我們使用digit recognizor數(shù)據(jù)來一展身手
import pandas as pd
data = pd.read_csv(r"E:\Involution\sklearn\03數(shù)據(jù)預(yù)處理和特征工程\digit recognizor.csv")
不難看出這玩意兒還挺大的
X = data.iloc[:,1:]
y = data.iloc[:,0]
X.shape
#(42000, 784)
"""
雖然只有42000行艳汽,但是有784維,算是很高的了(一個特征就是一維对雪,維度就是指特征的數(shù)量)
這個數(shù)據(jù)量相對夸張河狐,如果使用支持向量機和神經(jīng)網(wǎng)絡(luò),很可能會直接跑不出來(有可能升維)。使用KNN跑一次大概需要半個小時馋艺。用這個數(shù)據(jù)舉例栅干,能更夠體現(xiàn)特征工程的重要性。
"""
3.2.1 Filter過濾法
過濾方法通常用作預(yù)處理步驟捐祠,特征選擇完全獨立于任何機器學習算法(就是在用算法前就可以把最佳特征子集選出來)碱鳞。它是根據(jù)各種統(tǒng)計檢驗中的分數(shù)以及相關(guān)性的各項指標來選擇特征:
3.2.1.1 方差過濾
(1)VarianceThreshold
- 介紹: 這是通過特征本身的方差來篩選特征的類。比如一個特征本身的方差很小雏赦,就表示樣本在這個特征上基本沒有差異劫笙,可能特征中的大多數(shù)值都一樣,甚至整個特征的取值都相同星岗,那這個特征對于樣本區(qū)分沒有什么作用填大。所以無論接下來的特征工程要做什么,都要優(yōu)先消除方差為0的特征俏橘;
- 參數(shù)threshold: 表示方差的閾值允华,表示舍棄所有方差小于threshold的特征,不填默認為0寥掐,即刪除所有的記錄都相同的特征靴寂;
from sklearn.feature_selection import VarianceThreshold
#實例化,不填參數(shù)默認方差為0
selector = VarianceThreshold()
#獲取刪除不合格特征之后的新特征矩陣
X_var0 = selector.fit_transform(X)
#也可以直接寫成 X = VairanceThreshold().fit_transform(X)
X_var0.shape
#(42000, 708)
已經(jīng)刪除了方差為0的特征召耘,但是依然剩下了708多個特征百炬,明顯還需要進一步的特征選擇。然而污它,如果我們知道我們需要多少個特征剖踊,方差也可以幫助我們將特征選擇一步到位。比如說衫贬,我們希望留下一半的特征德澈,那可以設(shè)定一個讓特征總數(shù)減半的方差閾值,只要找到特征方差的中位數(shù)固惯,再將這個中位數(shù)作為參數(shù)threshold的值輸入就好了:
#通過numpy來選取中位數(shù)
import numpy as np
#查看每一列的方差
X.var()
#np.median()就是選取一大堆值中的中位數(shù)梆造;VarianceThreshold()相當于在實例化,
X_fsvar = VarianceThreshold(np.median(X.var().values)).fit_transform(X)
#這個就是拿來提取方差值的
X.var().values
#這個是取中位數(shù)的
np.median(X.var().values)
#1352.286703180131
X_fsvar.shape
#(42000, 392)
這里想要取前多少個數(shù)就通過把var().values排序然后來查看對應(yīng)的第n個方差并輸入到VarianceThreshold中就可以了
當特征是二分類時葬毫,特征的取值就是伯努利隨機變量镇辉,這些變量的方差可以計算為:
其中X是特征矩陣,p是二分類特征中的一類在這個特征中所占的概率
#若特征是伯努利隨機變量贴捡,假設(shè)p=0.8摊聋,即二分類特征中某種分類占到80%以上的時候刪除特征
X_bvar = VarianceThreshold(.8 * (1 - .8)).fit_transform(X)
X_bvar.shape
#(42000, 685)
#685個中沒有占80%以上的分類了
(2)方差過濾對模型的影響
在這里,我為大家準備了KNN和隨機森林分別在方差過濾前和方差過濾后運行的效果和運行時間的對比:
- KNN是K近鄰算法中的分類算法栈暇,其原理是 利用每個樣本到其他樣本點的距離來判斷每個樣本點的相似度麻裁,然后對樣本進行分類 。KNN必須遍歷每個特征和每個樣本,因而 特征越多煎源,KNN的計算也就會越緩慢色迂。由于這一段代碼對比運行時間過長,所以我為大家貼出了代碼和結(jié)果:
1. 導入模塊并準備數(shù)據(jù):
#KNN vs 隨機森林在不同方差過濾效果下的對比
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn.model_selection import cross_val_score
import numpy as np
X = data.iloc[:,1:]
y = data.iloc[:,0]
X_fsvar = VarianceThreshold(np.median(X.var().values)).fit_transform(X)
我們從模塊neighbors導入KNeighborsClassfier縮寫為KNN手销,導入隨機森林縮寫為RFC歇僧,然后導入交叉驗證模塊和numpy。其中未過濾的數(shù)據(jù)是X和y锋拖,使用中位數(shù)過濾后的數(shù)據(jù)是X_fsvar诈悍,都是我們之前已經(jīng)運行過的代碼
2. KNN方差過濾前
#======【TIME WARNING:35mins +】======#
cross_val_score(KNN(),X,y,cv=5).mean()
#python中的魔法命令,可以直接使用%%timeit來計算運行這個cell中的代碼所需的時間
#為了計算所需的時間兽埃,需要將這個cell中的代碼運行很多次(通常是7次)后求平均值侥钳,因此運行%%timeit的時間會遠遠超過cell中的代碼單獨運行的時間
#======【TIME WARNING:4 hours】======#
%%timeit
cross_val_score(KNN(),X,y,cv=5).mean()
3. KNN方差過濾后
#======【TIME WARNING:20 mins+】======#
cross_val_score(KNN(),X_fsvar,y,cv=5).mean()
#======【TIME WARNING:2 hours】======#
%%timeit
cross_val_score(KNN(),X,y,cv=5).mean()
可以看出,對于KNN柄错,過濾后的效果十分明顯:準確率稍有提升舷夺,但平均運行時間減少了10分鐘*,特征選擇過后算法的效率上升了1/3售貌。那隨機森林又如何呢给猾?
4. 隨機森林方差過濾前
cross_val_score(RFC(n_estimators=10,random_state=0),X,y,cv=5).mean()
5. 隨機森林方差過濾后
cross_val_score(RFC(n_estimators=10,random_state=0),X_fsvar,y,cv=5).mean()
首先可以觀察到的是,隨機森林的準確率略遜于KNN颂跨,但運行時間卻連KNN的1%都不到敢伸,只需要十幾秒鐘(因為兩種算法本身的運算量就有很大區(qū)別);
其次恒削,方差過濾后详拙,隨機森林的準確率也微弱上升,但運行時間卻幾乎是沒什么變化蔓同,依然是11秒鐘;
主要對象: 需要遍歷特征或升維的算法們蹲诀;
主要目的: 在維持算法表現(xiàn)的前提下斑粱,幫助算法們降低計算成本(減少時間);
對受影響的算法來說脯爪,我們可以將方差過濾的影響總結(jié)如下:
在我們的對比當中则北,我們使用的方差閾值是特征方差的中位數(shù),因此屬于閾值比較大痕慢,過濾掉的特征比較多的情況尚揣;方差過濾不一定能幫模型表現(xiàn)更好
我們可以觀察到,無論是KNN還是隨機森林掖举,在過濾掉一半特征之后快骗,模型的精確度都上升了。這說明被我們過濾掉的特征在當前隨機模式(random_state = 0)下大部分是噪音。那我們就可以保留這個去掉了一半特征的數(shù)據(jù)方篮,來為之后的特征選擇做準備名秀;反之亦反,這種情況就放棄過濾/選擇其他方式過濾藕溅。
(3)選取超參數(shù)threshold
-
我們怎樣知道匕得,方差過濾掉的到底時噪音還是有效特征呢?過濾后模型到底會變好還是會變壞呢巾表?
每個數(shù)據(jù)集不一樣汁掠,只能自己去嘗試。但現(xiàn)實中集币,我們只會使用閾值為0或者閾值很小的方差過濾考阱,來為我們優(yōu)先消除一些明顯用不到的特征,然后我們會選擇更優(yōu)的特征選擇方法繼續(xù)削減特征數(shù)量惠猿。
3.2.1.2 相關(guān)性過濾
我們希望選出與標簽相關(guān)且有意義的特征羔砾,因為這樣的特征能夠為我們提供大量信息偶妖。如果特征與標簽無關(guān)姜凄,那只會白白浪費我們的計算內(nèi)存,可能還會給模型帶來噪
音趾访。Sklearn中有三種常用的方法來評判特征與標簽之間的相關(guān)性:
(1)卡方過濾
- 作用: 是專門針對離散型標簽(即分類問題)的相關(guān)性過濾态秧;
- feature_selection.chi2: 計算每個非負特征和標簽之間的卡方統(tǒng)計量(需要數(shù)據(jù)預(yù)處理,變成>0)扼鞋,并依照卡方統(tǒng)計量由高到低為特征排名申鱼;
- feature_selection.SelectKBest:可以輸入評分標準來選出前K個分數(shù)最高的特征的類,我們可以借此除去最可能獨立于標簽云头,與我們分類目的無關(guān)的特征捐友;
如果卡方檢驗檢測到某個特征中所有的值都相同,會提示我們使用方差先進行方差過濾溃槐。
在這里匣砖,我們使用threshold=中位數(shù)時完成的方差過濾的數(shù)據(jù)來做卡方檢驗(如果方差過濾后模型的表現(xiàn)反而降低了,那我們就不會使用方差過濾后的數(shù)據(jù)昏滴,而是使用原數(shù)據(jù)):
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.model_selection import cross_val_score
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#假設(shè)在這里我一直我需要300個特征
#SelectKBest的第一個參數(shù)是模型依賴的統(tǒng)計量猴鲫,這里是卡方chi2;
#第二個參數(shù)k表示選取前k個特征中卡方值最高的特征
X_fschi = SelectKBest(chi2, k=300).fit_transform(X_fsvar, y)
X_fschi.shape
#(42000, 300)
#然后來驗證下模型的效果
cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
#0.9344761904761905
可以看出模型的效果降低了谣殊,這說明我們在設(shè)定k=300的時候刪除了與模型相關(guān)且有效的特征拂共,我們的K值設(shè)置得太小,要么我們需要調(diào)整K值姻几,要么我們必須放棄相關(guān)性過濾宜狐;反之亦反势告。
(2)選取超參數(shù)K
那如何設(shè)置一個最佳的K值呢?在現(xiàn)實數(shù)據(jù)中肌厨,數(shù)據(jù)量很大培慌,模型很復(fù)雜的時候,我們也許不能先去跑一遍模型看看效果柑爸,而是希望最開始就能夠選擇一個最優(yōu)的超參數(shù)k.第一種方法就是學習曲線:
#======【TIME WARNING: 5 mins】======#
%matplotlib inline
import matplotlib.pyplot as plt
score = []
for i in range(390,200,-10):
X_fschi = SelectKBest(chi2, k=i).fit_transform(X_fsvar, y)
once = cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
score.append(once)
plt.plot(range(350,200,-10),score)
plt.show()
通過這條曲線吵护,我們可以觀察到,隨著K值的不斷增加表鳍,模型的表現(xiàn)不斷上升馅而,這說明,K越大越好譬圣,數(shù)據(jù)中所有的特征都是與標簽相關(guān)的瓮恭。
但是運行這條曲線的時間同樣也是非常地長,接下來我們就來介紹一種更好的選擇k的方法:看p值選擇k厘熟;
- 卡方檢驗的本質(zhì): 推測兩組數(shù)據(jù)之間的差異屯蹦,其檢驗的原假設(shè)是”兩組數(shù)據(jù)是相互獨立的”∩蹋卡方檢驗返回卡方值和P值兩個統(tǒng)計量登澜,其中卡方值很難界定有效的范圍,而p值飘庄,我們一般使用0.01或0.05(好耶)作為顯著性水平脑蠕,即p值判斷的邊界:
小于0.05/設(shè)定值就是相關(guān),否則相互獨立跪削;從特征工程的角度谴仙,我們希望 選取卡方值很大,p值小于0.05的特征 碾盐,也就是和標簽相關(guān)聯(lián)
chivalue, pvalues_chi = chi2(X_fsvar,y)
chivalue
pvalues_chi
大部分p都是小于0.05的晃跺,說明和標簽相關(guān),這就意味著不能刪除
#k取多少毫玖?我們想要消除所有p值大于設(shè)定值掀虎,比如0.05或0.01的特征:
k = chivalue.shape[0] - (pvalues_chi > 0.05).sum()
#X_fschi = SelectKBest(chi2, k=填寫具體的k).fit_transform(X_fsvar, y)
#cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
上面這串代碼可以高效的去除不相關(guān)的特征,但是這里全是0孕豹,所以沒用
可以觀察到,所有特征的p值都是0十气,這說明對于digit recognizor這個數(shù)據(jù)集來說励背,方差驗證已經(jīng)把所有和標簽無關(guān)的特征都剔除了,或者這個數(shù)據(jù)集本身就不含與標簽無關(guān)的特征砸西。
在這種情況下叶眉,舍棄任何一個特征址儒,都會舍棄對模型有用的信息,而使模型表現(xiàn)下降衅疙,因此在我們對計算速度感到滿意時莲趣,我們不需要使用相關(guān)性過濾來過濾我
們的數(shù)據(jù)。
如果我們認為運算速度太緩慢饱溢,那我們可以酌情刪除一些特征喧伞,但前提是,我們必須犧牲模型的表現(xiàn)绩郎。接下來潘鲫,我們試試看用其他的相關(guān)性過濾方法驗證一下我們在這個數(shù)據(jù)集上的結(jié)論;
(3)F檢驗
- 定義: F檢驗肋杖,又稱ANOVA溉仑,方差齊性檢驗,是用來捕捉每個特征與標簽之間的線性關(guān)系的過濾方法状植。它即可以做回歸也可以做分類浊竟;
- feature_selection.f_classif(F檢驗分類): 用于標簽是離散型變量的數(shù)據(jù);
-
含feature_selection.f_classif(F檢驗分類):用于標簽是連續(xù)型變量的數(shù)據(jù)津畸;
和卡方檢驗一樣振定,這兩個類需要和類SelectKBest連用,并且我們也可以直接通過輸出的統(tǒng)計量來判斷我們到底要設(shè)置一個什么樣的K洼畅。需要注意的是吩案,F(xiàn)檢驗在數(shù)據(jù)服從正態(tài)分布時效果會非常穩(wěn)定,因此如果使用F檢驗過濾帝簇,我們會先將數(shù)據(jù)轉(zhuǎn)換成服從正態(tài)分布的方式徘郭; -
本質(zhì): 尋找兩組數(shù)據(jù)之間的線性關(guān)系,其原假設(shè)是”數(shù)據(jù)不存在顯著的線性關(guān)系“丧肴。它返回F值和p值兩個統(tǒng)計量残揉;
和卡方過濾一樣,我們希望選取p值小于0.05或0.01的特征芋浮,這些特征與標簽時顯著線性相關(guān)的抱环,而p值大于0.05或0.01的特征則被我們認為是和標簽沒有顯著線性關(guān)系的特征,應(yīng)該被刪除纸巷。以F檢驗的分類為例镇草,我們繼續(xù)在數(shù)字數(shù)據(jù)集上來進行特征選擇:
from sklearn.feature_selection import f_classif
F, pvalues_f = f_classif(X_fsvar,y)
F
pvalues_f
k = F.shape[0] - (pvalues_f > 0.05).sum()
k
#392,說明這里所有的特征全都是相關(guān)的
#X_fsF = SelectKBest(f_classif, k=填寫具體的k).fit_transform(X_fsvar, y)
#cross_val_score(RFC(n_estimators=10,random_state=0),X_fsF,y,cv=5).mean()
得到的結(jié)論和我們用卡方過濾得到的結(jié)論一模一樣:沒有任何特征的p值大于0.01,所有的特征都是和標簽相關(guān)的瘤旨,因此我們不需要相關(guān)性過濾梯啤;
(4)互信息法
- 功能: 用來捕捉每個特征與標簽之間的任意關(guān)系(包括線性和非線性關(guān)系)的過濾方法;
-
feature_selection.mutual_info_classif(互信息分類) 和feature_selection.mutual_info_classif(互信息分類) :和F檢驗相似存哲,它既
可以做回歸也可以做分類因宇,并且包含上面這兩個類七婴。他們倆的用法和參數(shù)都和F檢驗一模一樣,不過互信息法比F檢驗更加強大察滑,F(xiàn)檢驗只能夠找出線性關(guān)系打厘,而互信息法可以找出任意關(guān)系 - 返回值: 不返回p值或F值類似的統(tǒng)計量,它返回“每個特征與目標之間的互信息量的估計”贺辰,這個估計量在[0,1]之間取值户盯,為0則表示兩個變量獨立,為1則表示兩個變量完全相關(guān)魂爪;
from sklearn.feature_selection import mutual_info_classif as MIC
result = MIC(X_fsvar,y)
全都>0先舷,說明沒有任何特征和標簽相互獨立
k = result.shape[0] - sum(result <= 0)
#X_fsmic = SelectKBest(MIC, k=填寫具體的k).fit_transform(X_fsvar, y)
#cross_val_score(RFC(n_estimators=10,random_state=0),X_fsmic,y,cv=5).mean()
當然了,無論是F檢驗還是互信息法滓侍,大家也都可以使用學習曲線蒋川,只是使用統(tǒng)計量的方法會更加高效。當統(tǒng)計量判斷已經(jīng)沒有特征可以刪除時撩笆,無論用學習曲線如何跑捺球,刪除特征都只會降低模型的表現(xiàn)。當然了夕冲,如果數(shù)據(jù)量太龐大氮兵,模型太復(fù)雜,我們還是可以犧牲模型表現(xiàn)來提升模型速度歹鱼,一切都看大家的具體需求泣栈;
3.2.1.3 過濾法總結(jié)
通常來說,我會建議弥姻,先使用方差過濾南片,然后使用互信息法來捕捉相關(guān)性,不過了解各種各樣的過濾方式也是必要的庭敦。所有信息被總結(jié)在下表:
3.2.2 Embedded嵌入法
- 定義: 是一種讓算法自己決定使用哪些特征的方法疼进,即特征選擇和算法訓練同時進行;
- 流程圖:
先輸入全部特征秧廉,然后一直進行選取特征子集和算法+模型評估的循環(huán)伞广,多次重復(fù)后可以得到各個特征的權(quán)值系數(shù),并根據(jù)權(quán)值系數(shù)從大到小選擇特征然后選擇權(quán)值系數(shù)大的疼电,這樣就可以列出各個特征對樹的建立的貢獻嚼锄,我們就可以基于這種貢獻的評估,找出對模型建立最有用的特征
因此蔽豺,相比于過濾法区丑,嵌入法的結(jié)果會更加精確到模型的效用本身悍募,對于提高模型效力有更好的效果患久。并且侄柔,由于考慮特征對模型的貢獻仲吏,因此無關(guān)的特征(需要相關(guān)性過濾的特征)和無區(qū)分度的特征(需要方差過濾的特征)都會因為缺乏對模型的貢獻而被刪除掉濒析,可謂是過濾法的進化版(可以直接用嵌入法)正什;
缺點: (1)過濾法中使用的統(tǒng)計量可以使用統(tǒng)計知識和常識來查找范圍(如p值應(yīng)當?shù)陀陲@著性水平0.05),而嵌入法中使用的權(quán)值系數(shù)卻沒有這樣的范圍可找——>權(quán)值系數(shù)為0的特征對模型絲毫沒有作用号杏,但當大量特征都對模型有貢獻且貢獻不一時婴氮,我們就很難去界定一個有效的臨界值。此時盾致,模型權(quán)值系數(shù)就是我們的超參數(shù)主经,我們或許需要學習曲線,或者根據(jù)模型本身的某些性質(zhì)去判斷這個超參數(shù)的最佳值究竟應(yīng)該是多少庭惜;
(2)嵌入法引入了算法來挑選特征罩驻,因此其計算速度也會和應(yīng)用的算法有很大的關(guān)系。如果采用計算量很大护赊,計算緩慢的算法惠遏,嵌入法本身也會非常耗時耗力。并且骏啰,在選擇完畢之后节吮,我們還是需要自己來評估模型;feature_selection.SelectFromModel
class sklearn.feature_selection.SelectFromModel (estimator, threshold=None, prefit=False, norm_order=1,max_features=None)
這是一個元變換器,可以與任何在擬合后具有coef_判耕,feature_importances_屬性或參數(shù)中可選懲罰項的評估器一起使用(比如隨機森林和樹模型就具有屬性feature_importances_透绩,邏輯回歸就帶有l(wèi)1和l2懲罰項,線性支持向量機也支持l2懲罰項)壁熄。
對于有feature_importances_的模型來說帚豪,若重要性低于提供的閾值參數(shù),則認為這些特征不重要并被移除请毛。feature_importances_的取值范圍是[0,1]志鞍,如果設(shè)置閾值很小,比如0.001方仿,就可以刪除那些對標簽預(yù)測完全沒貢獻的特征固棚。如果設(shè)置得很接近1,可能只有一兩個特征能夠被留下仙蚜;
邏輯回歸的時候會仔細講一遍
重點要考慮的是前兩個參數(shù)
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
#隨機森林實例化此洲,因為這樣之后才能放進SelectFromModel函數(shù)來運行
RFC_ = RFC(n_estimators =10,random_state=0)
X_embedded = SelectFromModel(RFC_,threshold=0.005).fit_transform(X,y)
#在這里我只想取出來有限的特征。0.005這個閾值對于有780個特征的數(shù)據(jù)來說委粉,是非常高的閾值呜师,
#因為平均每個特征只能夠分到大約0.001的feature_importances_
X_embedded.shape
#(42000, 47)
#模型的維度明顯被降低了
#同樣的,我們也可以畫學習曲線來找最佳閾值
#======【TIME WARNING:10 mins】======#
import numpy as np
import matplotlib.pyplot as plt
RFC_.fit(X,y).feature_importances_
threshold = np.linspace(0,(RFC_.fit(X,y).feature_importances_).max(),20)
score = []
for i in threshold:
X_embedded = SelectFromModel(RFC_,threshold=i).fit_transform(X,y)
once = cross_val_score(RFC_,X_embedded,y,cv=5).mean()
score.append(once)
plt.plot(threshold,score)
plt.show()
橫坐標是閾值贾节,取得越大砍的就越多汁汗,反之亦反衷畦;從圖像上來看,隨著閾值越來越高知牌,模型的效果逐漸變差祈争,被刪除的特征越來越多,信息損失也逐漸變大角寸。
但是在0.00134之前菩混,模型的效果都可以維持在0.93以上,因此我們可以從中挑選一個數(shù)值來驗證一下模型的效果:
X_embedded = SelectFromModel(RFC_,threshold=0.00067).fit_transform(X,y)
X_embedded.shape
#(42000, 324)
cross_val_score(RFC_,X_embedded,y,cv=5).mean()
#0.9391190476190475
可以看出扁藕,特征個數(shù)瞬間縮小到324多沮峡,這比我們在方差過濾的時候選擇中位數(shù)過濾出來的結(jié)果392列要小,并且交叉驗證分數(shù)0.9399高于方差過濾后的結(jié)果0.9388亿柑。這是由于嵌入法比方差過濾更具體到模型的表現(xiàn)的緣故邢疙,換一個算法,使用同樣的閾值望薄,效果可能就沒有這么好了秘症;
#======【TIME WARNING:10 mins】======#
score2 = []
for i in np.linspace(0,0.00134,20):
X_embedded = SelectFromModel(RFC_,threshold=i).fit_transform(X,y)
once = cross_val_score(RFC_,X_embedded,y,cv=5).mean()
score2.append(once)
plt.figure(figsize=[20,5])
plt.plot(np.linspace(0,0.00134,20),score2)
plt.xticks(np.linspace(0,0.00134,20))
plt.show()
查看結(jié)果,果然0.00067并不是最高點式矫,真正的最高點0.000564已經(jīng)將模型效果提升到了94%以上
用0.000564來跑一跑我們的SelectFromModel:
X_embedded = SelectFromModel(RFC_,threshold=0.000564).fit_transform(X,y)
X_embedded.shape
#(42000, 340)
cross_val_score(RFC_,X_embedded,y,cv=5).mean()
#0.9392857142857144
#=====【TIME WARNING:2 min】=====#
#我們可能已經(jīng)找到了現(xiàn)有模型下的最佳結(jié)果乡摹,如果我們調(diào)整一下隨機森林的參數(shù)呢?
cross_val_score(RFC(n_estimators=100,random_state=0),X_embedded,y,cv=5).mean()
#0.9634285714285715
得出的特征數(shù)目依然小于方差篩選采转,并且模型的表現(xiàn)也比沒有篩選之前更高聪廉,已經(jīng)完全可以和計算一次半小時的KNN相匹敵(KNN的準確率是96.58%),接下來再對隨機森林進行調(diào)參故慈,準確率應(yīng)該還可以再升高不少板熊;
在嵌入法下,我們很容易就能夠?qū)崿F(xiàn)特征選擇的目標:減少計算量察绷,提升模型表現(xiàn)干签。因此,比起要思考很多統(tǒng)計量的過濾法來說拆撼,嵌入法可能是更有效的一種方法容劳。
然而,在算法本身很復(fù)雜的時候闸度,過濾法的計算遠遠比嵌入法要快竭贩,所以大型數(shù)據(jù)中,我們還是會優(yōu)先考慮過濾法莺禁;
3.3 Wrapper包裝法
- 特點:與嵌入法十分相似留量,它也是依賴于算法自身的選擇,比如coef_屬性或feature_importances_屬性來完成特征選擇。但不同的是楼熄,我們往往使用一個目標函數(shù)作為黑盒來 幫助我們選取特征 忆绰,而不是自己輸入某個評估指標或統(tǒng)計量的閾值;
- 流程:包裝法在初始特征集上訓練評估器可岂,并且通過coef_屬性或通過feature_importances_屬性獲得每個特征的重要性较木。然后,從當前的一組特征中修剪最不重要的特征青柄。在修剪的集合上遞歸地重復(fù)該過程,直到最終到達所需數(shù)量的要選擇的特征(也就每次只選用上一次剩下的特征预侯,這也使得所需要的計算成本是最高的(因為其他兩種方法都是一次解決所有問題))
注意致开,在這個圖中的“算法”,指的不是我們最終用來導入數(shù)據(jù)的分類或回歸算法(即不是隨機森林)萎馅,而是專業(yè)的數(shù)據(jù)挖掘算法双戳,即我們的目標函數(shù)。這些數(shù)據(jù)挖掘算法的核心功能就是選取最佳特征子集
是遞歸特征消除法(Recursive feature elimination, 簡寫為RFE): 最典型的目標函數(shù)糜芳,是一種貪婪的優(yōu)化算法飒货,旨在找到性能最佳的特征子集:
操作流得: 反復(fù)創(chuàng)建模型,并在 每次迭代 時保留最佳特征或剔除最差特征峭竣,下一次迭代 時塘辅,它會使用上一次建模中沒有被選中的特征來構(gòu)建下一個模型,直到所有特征都耗盡為止皆撩。 然后扣墩,它根據(jù)自己保留或剔除特征的順序來對特征進行排名,最終選出一個最佳子集扛吞;
包裝法的效果是所有特征選擇方法中最利于提升模型表現(xiàn)的呻惕,它可以使用很少的特征達到很優(yōu)秀的效果。此外,在特征數(shù)目相同時,包裝法和嵌入法的效果能夠匹敵,不過它比嵌入法算得更見緩慢虏杰,所以也不適用于太大型的數(shù)據(jù)——>包裝法yyds;feature_selection.RFE: class sklearn.feature_selection.RFE (estimator, n_features_to_select=None, step=1, verbose=0)
前兩個參數(shù)比較重要:estimator是需要填寫的實例化后的評估器质况,n_features_to_select是想要選擇的特征個數(shù)囤捻;
還有倆重要的屬性:.support_返回所有的特征的是否最后被選中的布爾矩陣;.ranking_返回特征的按數(shù)次迭代中綜合重要性的排名,越前面越重要;
類feature_selection.RFECV會在交叉驗證循環(huán)中執(zhí)行RFE以找到最佳數(shù)量的特征,增加參數(shù)cv,其他用法都和RFE一模一樣:
from sklearn.feature_selection import RFE
RFC_ = RFC(n_estimators =10,random_state=0)
#step說明每迭代一次刪除n個特征
selector = RFE(RFC_, n_features_to_select=340, step=50).fit(X, y)
#support返回所有特征,加和就是求需要的特征數(shù)目
selector.support_.sum()
selector.ranking_
X_wrapper = selector.transform(X)
cross_val_score(RFC_,X_wrapper,y,cv=5).mean()
#0.9379761904761905
同樣也可以對包裝法畫學習曲線,通過學習曲線來知道選多少個特征才能最好:
#======【TIME WARNING: 15 mins】======#
score = []
for i in range(1,751,50):
X_wrapper = RFE(RFC_,n_features_to_select=i, step=50).fit_transform(X,y)
once = cross_val_score(RFC_,X_wrapper,y,cv=5).mean()
score.append(once)
plt.figure(figsize=[20,5])
plt.plot(range(1,751,50),score)
plt.xticks(range(1,751,50))
plt.show()
明顯能夠看出,在包裝法下面,應(yīng)用50個特征時,模型的表現(xiàn)就已經(jīng)達到了90%以上,比嵌入法和過濾法都高效很多。我們可以放大圖像嚼吞,尋找模型變得非常穩(wěn)定的點來畫進一步的學習曲線(就像我們在嵌入法中做的那樣)
如果我們此時追求的是最大化降低模型的運行時間炒刁,我們甚至可以直接選擇50作為特征的數(shù)目,這是一個在縮減了94%的特征的基礎(chǔ)上疾瓮,還能保證模型表現(xiàn)在90%以上的特征組合,不可謂不高效;
在特征數(shù)目相同時,包裝法能夠在效果上匹敵嵌入法未巫。由于包裝法效果和嵌入法相差不多,在更小的范圍內(nèi)使用學習曲線,我們也可以將包裝法的效果調(diào)得很好
——>包裝法是最容易在最小的特征數(shù)下找到最佳的模型表現(xiàn)
3.4 特征選擇總結(jié)
經(jīng)驗來說,過濾法更快速,但更粗糙冠胯。包裝法和嵌入法更精確,比較適合具體到算法去調(diào)整,但計算量比較大,運行時間長航缀。
總結(jié)一下就是:當數(shù)據(jù)量很大的時候灿巧,優(yōu)先使用方差過濾和互信息法調(diào)整,再上其他特征選擇方法零院。使用邏輯回歸時打洼,優(yōu)先使用嵌入法。使用支持向量機時搔扁,優(yōu)先使用包裝法苛聘。迷茫的時候网梢,從過濾法走起巡社,看具體數(shù)據(jù)具體分析,反正有學習曲線和模型評估舟茶。
其實特征選擇只是特征工程中的第一步胀瞪。真正的高手,往往使用特征創(chuàng)造或特征提取來尋找高級特征朴肺。特征工程非常深奧,雖然我們?nèi)粘挪略?赡苡玫讲欢啵鋵嵥浅C烂?/p>