對於特徵工程是數據分析中最費時費力的一部分的說法,應該是沒人有異議的,特徵工程很重要,可卻沒有明晰的體系,在西瓜書裡也只有一個章節講了特徵選擇,沒有像算法模型一樣有統一的體系,甚至連基本的概念術語都沒有統一,標準化與歸一化分不清理還亂,啞變量也叫虛擬變量,這些都讓特徵工程很難得學習,本文把特徵工程相關學習做下記錄。
特徵工程籠統的分為三個塊,三部分很難說得上前後順序或者遞進關係。
- 特徵表示
- 特徵預處理
- 特徵選擇
第一部分特徵表示
特徵表示,是對特徵具體表現形式做處理,包括缺失值處理,連續特徵的離散化,離散特徵的連續化(亦即,特徵編碼)
1.缺失值處理
對特徵缺失值的處理,主要處理方式是填充,數值特徵一般用均值填充,分類特徵一般是頻繁值來填充。sklearn中使用Imputer而來處理。
<code> #導入SimpleImputer from sklearn.impute import SimpleImputer import numpy as np #平均值填充 imp_mean = SimpleImputer(missing_values=np.nan, strategy='mean') imp_mean.fit_transform([[1,2,np.nan], [3,np.nan,4], [np.nan,5,6]]) #中位數填充 imp_median = SimpleImputer(missing_values='na', strategy='median') imp_median.fit_transform([['na',1,2,3], [4,5,6,7], [8,9,10,'na'], [11,12,13,14]]) #最頻繁值填充,對數值特徵就是眾數 imp_most = SimpleImputer(missing_values='未知', stragtegy='most_frequent') imp_most.fit_transform([['廣州','男',30], ['未知','女',23], ['上海','男',34], ['上海','男',24], ['北京','未知',33]])/<code>
2.離散特徵處理
由於不少的模型是無法直接處理離散特徵,例如性別(男,女)、城市(北京,上海),這些離散特徵在K近鄰、邏輯迴歸等模型都是不能直接使用的,這是需要將離散特徵進行編碼,數值化表示處理。
sklearn中主要的是編碼方式有LabelEncoder,OneHotEncoder,ordinalEncoder,三者的核心其實是相同的,但實現細節不同。
LabelEncoder是最容易理解的,例如季節特徵有春夏秋冬四個特徵值,就編碼為0,1,2,3
<code> from sklearn.preprocessing import LabelEncoder labeler = LabelEncoder() labeler.fit_transform(['春','夏', '秋', '冬']) /<code>
*LabelEncoder通常用來轉換目標變量
OrdinalEncoder 的處理方式和LabelEncoder相同,不同的地方是OrdinalEncoder提供參數調整編碼映射,主要是用來處理X變量特徵的。
<code> from sklearn.preprocessing import OrdinalEncoder enc = OrdinalEncoder() X = [['男', '青年'],['女','中年'],['男','少年']] enc.fit_transform(X)/<code>
OneHotEncoder 的編碼方式是將特徵的每個唯一值轉化一個二值化的虛擬特徵。性別特徵轉化為性別-男、性別-女兩個。
<code> from sklearn.preprocessing import OneHotEncoder enc = OneHotEncoder() X = [['男', 'A'], ['女','C'], ['男','B']] enc.fit_transform(X) /<code>
自然語言處理是離散特徵處理中的一種特殊情況,是按特徵值頻次來做的編碼,有時是直接根據出現頻次的詞頻,或者是出現頻次權重。
3.連續特徵表示處理
連續特徵有時需要做離散化的處理,目前個人還沒找到這樣處理的理論邏輯,可能適合理由是高維稀疏解,部分算法需要例如決策樹。另外連續特徵做離散化處理也多是根據經驗,例如連續的時間特徵抽離出天中小時數,周幾等。
連續特徵的處理主要的思路是基於閾值分桶,收入分成中低高,閾值的確定就變成了特徵處理質量的關鍵,一般是基於經驗或專家意見,如收入分桶就按照收入分類標準;除了這類經驗性的閾值確定外,還可以使用機器學習算法來確定
<code> from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.ensemble import GradientBoostingClassifier from sklearn.preprocessing import OneHotEncoder X, y = make_classification(n_samples=10) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.5) gbc = GradientBoostingClassifier(n_estimators=2) one_hot = OneHotEncoder() gbc.fit(X_train, y_train) X_train_new = one_hot.fit_transform(gbc.apply(X_train)[:,:,0]) /<code>
第二部分特徵預處理
特徵表示是將單個的特徵類型進行轉化,連續與離散的轉化,經過特徵表示處理之後的特徵是可以用來進行模型訓練了,但是各個特徵的取值範圍都不相同,年齡特徵的範圍可能是0-100,而收入特徵的範圍是0-100000,這對於一些模型性能影響很大(訓練速度,擬合性能),所以需要做特徵的預處理來統一特徵量綱
特徵“正常化”(Normalization)
這部分有個很大問題就是Normalize、standard和標準化、歸一化以及正則化難解難分了
Normalize按詞根是正常化的意思,Standard是標準化的意思,本來這兩詞在sklearn中對應方法是沒問題的
但奈何漢語太博大了,特徵縮放(Feature scaling)操作都是為了統一量綱,所以將所有的特徵縮放(MinMaxScaler,MaxAbsScaler,StandardScaler)操作叫做標準化在漢語語境也可以說通(按統一標準轉化);歸一化也是一樣既代指Normalization,也代指Normalizer。
這個問題希望業界早點統一吧
特徵正常化包括對於特徵操作的MaxAbsScaler,MinMaxScaler,StandardScaler和對樣本方向操作的Normalizer
<code> from sklearn.preprocessing import MaxAbsScaler, MinMaxScaler, StandardScaler mas = MaxAbsScaler() mas.fit_transfrom(X) scaler = MinMaxScaler() scaler.fit_transform(X) Std_scaler = StandardScaler() std_scaler.fit_transform(X)/<code>
Normalizer與前三者的一個不同之處在於,Normalizer是針對樣本方向的處理,也是做前三者都是在列方向的處理,Normalizer是在行方向的處理。
Normalizer是用L1或L2範數縮放單個樣本,這種處理方式主要是在向量空間模型使用,如文本分類內容聚類。
第三部分特徵選擇
在將特徵進行編碼分段及標準化之後,我們可以得到一份規整的數據集,面對大量的特徵,我們做出選擇,篩除一些特徵
特徵選擇主要有三類方法,過濾法特徵選擇、包裝法特徵選擇、嵌入法特徵選擇
1.過濾法特徵選擇
過濾法是按照特徵的發散性或者相關性指標對各個特徵進行評分,設定閾值選擇合適特徵,具體來說,常用的幾種方式是,方差、相關係數、假設檢驗、互信息
基於方差 的過濾選擇,是通過計算各特徵的方差,設定方差閾值進行選擇,sklearn中的實現方法是VarianceThreshold
<code> from sklearn.feature_selection import VarianceThreshold X = [[0,0,1],[0,1,0],[1,0,0],[0,1,1],[0,1,0],[0,1,1]] sel = VarianceThreshold(threshold=(.8*(1-.8))) sel.fit_transform(X) #方差過濾的手動實現 def varianceSelect(X,threshold): import numpy as np var = np.var(X, axis=0) return X[:,var>threshold]/<code>
基於假設檢驗過濾,是以“特徵與響應變量沒有關係”作為零假設進行檢驗,既然是假設檢驗就涉及F分佈、T分佈、卡方分佈,都對應的是sklearn中的f_classif、f_regression、chi2
<code> from sklearn.datasets import load_iris from sklearn.feature_selection import SelectKBest from sklearn.feature_selection import chi2 X, y = load_iris(return_X_y=True) X.shape out:(150, 4) X_new = SelectKBest(chi2, k=2).fit_transform(X, y) X_new.shape (150, 2)/<code>
基於互信息的過濾,是從信息熵的角度分析各個特徵和輸出值之間的關係評分,互信息值越大,說明該特徵和輸出值之間的相關性越大,越需要保留。對應sklearn中mutual_info_regression、mutual_info_classif
基於假設檢驗和互信息的,都可以通過SelectKBest來使用
2.包裝法特徵選擇
包裝法更像是一種解決策略,它沒有過濾法那麼直接。包裝法將學習模型的性能作為特徵選擇的評價標準,也就通過多次訓練模型找出最佳特徵,因此這種方法的計算開銷更大。
常用的包裝法是遞歸消除特徵法(RFE),遞歸消除特徵法使用一個機器學習模型來進行多輪訓練,每輪訓練後,消除若干權值係數的對應的特徵,再基於新的特徵集進行下一輪訓練。在sklearn中,可以使用RFE函數來選擇特徵。
3.嵌入法特徵選擇
嵌入法是將特徵選擇過程與學習模型訓練過程融合一體,在訓練過程中自動進行特徵選擇。在sklearn中是使用SelectFromModel。
嵌入法的常用方式是使用L1、L2正則化做特徵選擇,一個例子是Ridge迴歸中,正則化懲罰項越大,那麼模型的係數就會越小。當正則化懲罰項大到一定的程度的時候,部分特徵係數會變成0,當正則化懲罰項繼續增大到一定程度時,所有的特徵係數都會趨於0