機器學習中,有哪些特徵選擇的工程方法? - bonelee - 博客園?

www.cnblogs.com

使用第三方庫imblearn處理樣本不均衡問題?

blog.csdn.net

特徵工程是數據分析中最耗時間和精力的一部分工作,它不像演算法和模型那樣是確定的步驟,更多是工程上的經驗和權衡。因此沒有統一的方法。這裡只是對一些常用的方法做一個總結。本文關注於特徵選擇部分。後面還有兩篇會關注於特徵表達和特徵預處理。

一、特徵選擇

1. 特徵的來源

    在做數據分析的時候,特徵的來源一般有兩塊,一塊是業務已經整理好各種特徵數據,我們需要去找出適合我們問題需要的特徵;另一塊是我們從業務特徵中自己去尋找高級數據特徵。我們就針對這兩部分來分別討論。

2. 選擇合適的特徵

    我們首先看當業務已經整理好各種特徵數據時,我們如何去找出適合我們問題需要的特徵,此時特徵數可能成百上千,哪些纔是我們需要的呢?

    第一步是找到該領域懂業務的專家,讓他們給一些建議。比如我們需要解決一個藥品療效的分類問題,那麼先找到領域專家,向他們諮詢哪些因素(特徵)會對該藥品的療效產生影響,較大影響的和較小影響的都要。這些特徵就是我們的特徵的第一候選集。

    這個特徵集合有時候也可能很大,在嘗試降維之前,我們有必要用特徵工程的方法去選擇出較重要的特徵結合,這些方法不會用到領域知識,而僅僅是統計學的方法。

    最簡單的方法就是方差篩選。方差越大的特徵,那麼我們可以認為它是比較有用的。如果方差較小,比如小於1,那麼這個特徵可能對我們的演算法作用沒有那麼大。最極端的,如果某個特徵方差為0,即所有的樣本該特徵的取值都是一樣的,那麼它對我們的模型訓練沒有任何作用,可以直接捨棄。在實際應用中,我們會指定一個方差的閾值,當方差小於這個閾值的特徵會被我們篩掉。sklearn中的VarianceThreshold類可以很方便的完成這個工作。

    特徵選擇方法有很多,一般分為三類:第一類過濾法比較簡單,它按照特徵的發散性或者相關性指標對各個特徵進行評分,設定評分閾值或者待選擇閾值的個數,選擇合適特徵。上面我們提到的方差篩選就是過濾法的一種。第二類是包裝法,根據目標函數,通常是預測效果評分,每次選擇部分特徵,或者排除部分特徵。第三類嵌入法則稍微複雜一點,它先使用某些機器學習的演算法和模型進行訓練,得到各個特徵的權值係數,根據權值係數從大到小來選擇特徵。類似於過濾法,但是它是通過機器學習訓練來確定特徵的優劣,而不是直接從特徵的一些統計學指標來確定特徵的優劣。下面我們分別來看看3類方法。

2.1 過濾法選擇特徵

    上面我們已經講到了使用特徵方差來過濾選擇特徵的過程。除了特徵的方差這第一種方法,還有其他一些統計學指標可以使用。

    第二個可以使用的是相關係數。這個主要用於輸出連續值的監督學習演算法中。我們分別計算所有訓練集中各個特徵與輸出值之間的相關係數,設定一個閾值,選擇相關係數較大的部分特徵。

    第三個可以使用的是假設檢驗,比如卡方檢驗。卡方檢驗可以檢驗某個特徵分佈和輸出值分佈之間的相關性。個人覺得它比比粗暴的方差法好用。如果大家對卡方檢驗不熟悉,可以參看這篇卡方檢驗原理及應用,這裡就不展開了。在sklearn中,可以使用chi2這個類來做卡方檢驗得到所有特徵的卡方值與顯著性水平P臨界值,我們可以給定卡方值閾值, 選擇卡方值較大的部分特徵。

    除了卡方檢驗,我們還可以使用F檢驗和t檢驗,它們都是使用假設檢驗的方法,只是使用的統計分佈不是卡方分佈,而是F分佈和t分佈而已。在sklearn中,有F檢驗的函數f_classif和f_regression,分別在分類和回歸特徵選擇時使用。

    第四個是互信息,即從信息熵的角度分析各個特徵和輸出值之間的關係評分。在決策樹演算法中我們講到過互信息(信息增益)。互信息值越大,說明該特徵和輸出值之間的相關性越大,越需要保留。在sklearn中,可以使用mutual_info_classif(分類)和mutual_info_regression(回歸)來計算各個輸入特徵和輸出值之間的互信息。

    以上就是過濾法的主要方法,個人經驗是,在沒有什麼思路的 時候,可以優先使用卡方檢驗和互信息來做特徵選擇

2.2 包裝法選擇特徵

    包裝法的解決思路沒有過濾法這麼直接,它會選擇一個目標函數來一步步的篩選特徵。

    最常用的包裝法是遞歸消除特徵法(recursive feature elimination,以下簡稱RFE)。遞歸消除特徵法使用一個機器學習模型來進行多輪訓練,每輪訓練後,消除若干權值係數的對應的特徵,再基於新的特徵集進行下一輪訓練。在sklearn中,可以使用RFE函數來選擇特徵。

    我們下面以經典的SVM-RFE演算法來討論這個特徵選擇的思路。這個演算法以支持向量機來做RFE的機器學習模型選擇特徵。它在第一輪訓練的時候,會選擇所有的特徵來訓練,得到了分類的超平面wx˙+b=0wx˙+b=0後,如果有n個特徵,那麼RFE-SVM會選擇出ww中分量的平方值w2iwi2最小的那個序號i對應的特徵,將其排除,在第二類的時候,特徵數就剩下n-1個了,我們繼續用這n-1個特徵和輸出值來訓練SVM,同樣的,去掉w2iwi2最小的那個序號i對應的特徵。以此類推,直到剩下的特徵數滿足我們的需求為止。

2.3 嵌入法選擇特徵

    嵌入法也是用機器學習的方法來選擇特徵,但是它和RFE的區別是它不是通過不停的篩掉特徵來進行訓練,而是使用的都是特徵全集。在sklearn中,使用SelectFromModel函數來選擇特徵。

    最常用的是使用L1正則化和L2正則化來選擇特徵。在之前講到的用scikit-learn和pandas學習Ridge回歸第6節中,我們講到正則化懲罰項越大,那麼模型的係數就會越小。當正則化懲罰項大到一定的程度的時候,部分特徵係數會變成0,當正則化懲罰項繼續增大到一定程度時,所有的特徵係數都會趨於0. 但是我們會發現一部分特徵係數會更容易先變成0,這部分係數就是可以篩掉的。也就是說,我們選擇特徵係數較大的特徵。常用的L1正則化和L2正則化來選擇特徵的基學習器是邏輯回歸。

    此外也可以使用決策樹或者GBDT。那麼是不是所有的機器學習方法都可以作為嵌入法的基學習器呢?也不是,一般來說,可以得到特徵係數coef或者可以得到特徵重要度(feature importances)的演算法纔可以做為嵌入法的基學習器。

3. 尋找高級特徵

    在我們拿到已有的特徵後,我們還可以根據需要尋找到更多的高級特徵。比如有車的路程特徵和時間間隔特徵,我們就可以得到車的平均速度這個二級特徵。根據車的速度特徵,我們就可以得到車的加速度這個三級特徵,根據車的加速度特徵,我們就可以得到車的加加速度這個四級特徵。。。也就是說,高級特徵可以一直尋找下去。

    在Kaggle之類的演算法競賽中,高分團隊主要使用的方法除了集成學習演算法,剩下的主要就是在高級特徵上面做文章。所以尋找高級特徵是模型優化的必要步驟之一。當然,在第一次建立模型的時候,我們可以先不尋找高級特徵,得到以後基準模型後,再尋找高級特徵進行優化。

    尋找高級特徵最常用的方法有:

    若干項特徵加和: 我們假設你希望根據每日銷售額得到一週銷售額的特徵。你可以將最近的7天的銷售額相加得到。

    若干項特徵之差: 假設你已經擁有每週銷售額以及每月銷售額兩項特徵,可以求一週前一月內的銷售額。

    若干項特徵乘積: 假設你有商品價格和商品銷量的特徵,那麼就可以得到銷售額的特徵。

    若干項特徵除商: 假設你有每個用戶的銷售額和購買的商品件數,那麼就是得到該用戶平均每件商品的銷售額。

    當然,尋找高級特徵的方法遠不止於此,它需要你根據你的業務和模型需要而得,而不是隨便的兩兩組合形成高級特徵,這樣容易導致特徵爆炸,反而沒有辦法得到較好的模型。個人經驗是,聚類的時候高級特徵盡量少一點,分類回歸的時候高級特徵適度的多一點。

4. 特徵選擇小結

    特徵選擇是特徵工程的第一步,它關係到我們機器學習演算法的上限。因此原則是盡量不錯過一個可能有用的特徵,但是也不濫用太多的特徵。

二、特徵表達

1. 缺失值處理

特徵有缺失值是非常常見的,大部分機器學習模型在擬合前需要所有的特徵都有值,不能是空或者NULL。那麼如果有缺失值我們需要怎麼處理呢?

   首先我們會看是該特徵是連續值還是離散值。如果是連續值,那麼一般有兩種選擇,一是選擇所有有該特徵值的樣本,然後取平均值,來填充缺失值,另一種是取中位數來填充缺失值。如果是離散值,則一般會選擇所有有該特徵值的樣本中最頻繁出現的類別值,來填充缺失值。在sklearn中,可以使用preprocessing.Imputer來選擇這三種不同的處理邏輯做預處理。

2. 特殊的特徵處理

  有些特徵的默認取值比較特殊,一般需要做了處理後才能用於演算法。比如日期時間,比如顯示20180519,這樣的值一般沒辦法直接使用。那麼一般需要如何變換呢?

對於時間原始特徵,處理方法有很多,這裡只舉例幾種有代表性的方法。 第一種是使用連續的時間差值法,即計算出所有樣本的時間到某一個未來時間之間的數值差距,這樣這個差距是UTC的時間差,從而將時間特徵轉化為連續值。第二種方法是根據時間所在的年,月,日,星期幾,小時數,將一個時間特徵轉化為若干個離散特徵,這種方法在分析具有明顯時間趨勢的問題比較好用。第三種是權重法,即根據時間的新舊得到一個權重值。比如對於商品,三個月前購買的設置一個較低的權重,最近三天購買的設置一個中等的權重,在三個月內但是三天前的設置一個較大的權重。當然,還有其他的設置權重的方法,這個要根據要解決的問題來靈活確定。

對地理特徵,比如「廣州市天河區XX街道XX號」,這樣的特徵我們應該如何使用呢?處理成離散值和連續值都是可以的。如果是處理成離散值,則需要轉化為多個離散特徵,比如城市名特徵,區縣特徵,街道特徵等。但是如果我們需要判斷用戶分佈區域,則一般處理成連續值會比較好,這時可以將地址處理成經度和緯度的連續特徵。

3. 離散特徵的連續化處理

   有很多機器學習演算法只能處理連續值特徵,不能處理離散值特徵,比如線性回歸,邏輯回歸等。那麼想使用邏輯回歸,線性回歸時這些值只能丟棄嗎?當然不是。我們可以將離散特徵連續化處理。

   最常見的離散特徵連續化的處理方法是獨熱編碼one-hot encoding。處理方法其實比較簡單,比如某特徵的取值是高,中和低,那麼我們就可以創建三個取值為0或者1的特徵,將高編碼為1,0,0這樣三個特徵,中編碼為0,1,0這樣三個特徵,低編碼為0,0,1這樣三個特徵。也就是說,之前的一個特徵被我們轉化為了三個特徵。sklearn的OneHotEncoder可以幫我們做這個處理。

   第二個方法是特徵嵌入embedding。這個一般用於深度學習中。比如對於用戶的ID這個特徵,如果要使用獨熱編碼,則維度會爆炸,如果使用特徵嵌入就維度低很多了。對於每個要嵌入的特徵,我們會有一個特徵嵌入矩陣,這個矩陣的行很大,對應我們該特徵的數目。比如用戶ID,如果有100萬個,那麼嵌入的特徵矩陣的行就是100萬。但是列一般比較小,比如可以取20。這樣每個用戶ID就轉化為了一個20維的特徵向量。進而參與深度學習模型。在tensorflow中,我們可以先隨機初始化一個特徵嵌入矩陣,對於每個用戶,可以用tf.nn.embedding_lookup找到該用戶的特徵嵌入向量。特徵嵌入矩陣會在反向傳播的迭代中優化。

   此外,在自然語言處理中,我們也可以用word2vec將詞轉化為詞向量,進而可以進行一些連續值的後繼處理。

4. 離散特徵的離散化處理

   離散特徵有時間也不能直接使用,需要先進行轉化。比如最常見的,如果特徵的取值是高,中和低,那麼就算你需要的是離散值,也是沒法直接使用的。

   對於原始的離散值特徵,最常用的方法也是獨熱編碼,方法在第三節已經講到。

   第二種方法是虛擬編碼dummy coding,它和獨熱編碼類似,但是它的特點是,如果我們的特徵有N個取值,它只需要N-1個新的0,1特徵來代替,而獨熱編碼會用N個新特徵代替。比如一個特徵的取值是高,中和低,那麼我們只需要兩位編碼,比如只編碼中和低,如果是1,0則是中,0,1則是低。0,0則是高了。目前虛擬編碼使用的沒有獨熱編碼廣,因此一般有需要的話還是使用獨熱編碼比較好。

   此外,有時候我們可以對特徵進行研究後做一個更好的處理。比如,我們研究商品的銷量對應的特徵。裡面有一個原始特徵是季節春夏秋冬。我們可以將其轉化為淡季和旺季這樣的二值特徵,方便建模。當然有時候轉化為三值特徵或者四值特徵也是可以的。

   對於分類問題的特徵輸出,我們一般需要用sklearn的LabelEncoder將其轉化為0,1,2,...這樣的類別標籤值。

5. 連續特徵的離散化處理

   對於連續特徵,有時候我們也可以將其做離散化處理。這樣特徵變得高維稀疏,方便一些演算法的處理。

   對常用的方法是根據閾值進行分組,比如我們根據連續值特徵的分位數,將該特徵分為高,中和低三個特徵。將分位數從0-0.3的設置為高,0.3-0.7的設置為中,0.7-1的設置為高。

   當然還有高級一些的方法。比如使用GBDT。在LR+GBDT的經典模型中,就是使用GDBT來先將連續值轉化為離散值。那麼如何轉化呢?比如我們用訓練集的所有連續值和標籤輸出來訓練GBDT,最後得到的GBDT模型有兩顆決策樹,第一顆決策樹有三個葉子節點,第二顆決策樹有4個葉子節點。如果某一個樣本在第一顆決策樹會落在第二個葉子節點,在第二顆決策樹落在第4顆葉子節點,那麼它的編碼就是0,1,0,0,0,0,1,一共七個離散特徵,其中會有兩個取值為1的位置,分別對應每顆決策樹中樣本落點的位置。在sklearn中,我們可以用GradientBoostingClassifier的 apply方法很方便的得到樣本離散化後的特徵,然後使用獨熱編碼即可。

三、特徵預處理

1. 特徵的標準化和歸一化

     由於標準化和歸一化這兩個詞經常混用,所以本文不再區別標準化和歸一化,而通過具體的標準化和歸一化方法來區別具體的預處理操作。

    z-score標準化:這是最常見的特徵預處理方式,基本所有的線性模型在擬合的時候都會做 z-score標準化。具體的方法是求出樣本特徵x的均值mean和標準差std,然後用(x-mean)/std來代替原特徵。這樣特徵就變成了均值為0,方差為1了。在sklearn中,我們可以用StandardScaler來做z-score標準化。當然,如果我們是用pandas做數據預處理,可以自己在數據框裡面減去均值,再除以方差,自己做z-score標準化。

    max-min標準化:也稱為離差標準化,預處理後使特徵值映射到[0,1]之間。具體的方法是求出樣本特徵x的最大值max和最小值min,然後用(x-min)/(max-min)來代替原特徵。如果我們希望將數據映射到任意一個區間[a,b],而不是[0,1],那麼也很簡單。用(x-min)(b-a)/(max-min)+a來代替原特徵即可。在sklearn中,我們可以用MinMaxScaler來做max-min標準化。這種方法的問題就是如果測試集或者預測數據裏的特徵有小於min,或者大於max的數據,會導致max和min發生變化,需要重新計算。所以實際演算法中, 除非你對特徵的取值區間有需求,否則max-min標準化沒有 z-score標準化好用。

    L1/L2範數標準化:如果我們只是為了統一量綱,那麼通過L2範數整體標準化也是可以的,具體方法是求出每個樣本特徵向量x? x→的L2範數||x? ||2||x→||2,然後用x? /||x? ||2x→/||x→||2代替原樣本特徵即可。當然L1範數標準化也是可以的,即用x? /||x? ||1x→/||x→||1代替原樣本特徵。通常情況下,範數標準化首選L2範數標準化。在sklearn中,我們可以用Normalizer來做L1/L2範數標準化。

    此外,經常我們還會用到中心化,主要是在PCA降維的時候,此時我們求出特徵x的平均值mean後,用x-mean代替原特徵,也就是特徵的均值變成了0, 但是方差並不改變。這個很好理解,因為PCA就是依賴方差來降維的。

    雖然大部分機器學習模型都需要做標準化和歸一化,也有不少模型可以不做做標準化和歸一化,主要是基於概率分佈的模型,比如決策樹大家族的CART,隨機森林等。當然此時使用標準化也是可以的,大多數情況下對模型的泛化能力也有改進。

2. 異常特徵樣本清洗

    我們在實際項目中拿到的數據往往有不少異常數據,有時候不篩選出這些異常數據很可能讓我們後面的數據分析模型有很大的偏差。那麼如果我們沒有專業知識,如何篩選出這些異常特徵樣本呢?常用的方法有兩種。

    第一種是聚類,比如我們可以用KMeans聚類將訓練樣本分成若干個簇,如果某一個簇裏的樣本數很少,而且簇質心和其他所有的簇都很遠,那麼這個簇裡面的樣本極有可能是異常特徵樣本了。我們可以將其從訓練集過濾掉。

    第二種是異常點檢測方法,主要是使用iForest或者one class SVM,使用異常點檢測的機器學習演算法來過濾所有的異常點。

    當然,某些篩選出來的異常樣本是否真的是不需要的異常特徵樣本,最好找懂業務的再確認一下,防止我們將正常的樣本過濾掉了。

3. 處理不平衡數據

    這個問題其實不算特徵預處理的部分,不過其實它的實質還是訓練集中各個類別的樣本的特徵分佈不一致的問題,所以這裡我們一起講。

    我們做分類演算法訓練時,如果訓練集裏的各個類別的樣本數量不是大約相同的比例,就需要處理樣本不平衡問題。也許你會說,不處理會怎麼樣呢?如果不處理,那麼擬合出來的模型對於訓練集中少樣本的類別泛化能力會很差。舉個例子,我們是一個二分類問題,如果訓練集裏A類別樣本佔90%,B類別樣本佔10%。 而測試集裏A類別樣本佔50%, B類別樣本佔50%, 如果不考慮類別不平衡問題,訓練出來的模型對於類別B的預測準確率會很低,甚至低於50%。

    如何解決這個問題呢?一般是兩種方法:權重法或者採樣法。

    權重法是比較簡單的方法,我們可以對訓練集裏的每個類別加一個權重class weight。如果該類別的樣本數多,那麼它的權重就低,反之則權重就高。如果更細緻點,我們還可以對每個樣本加權重sample weight,思路和類別權重也是一樣,即樣本數多的類別樣本權重低,反之樣本權重高。sklearn中,絕大多數分類演算法都有class weight和 sample weight可以使用。

    如果權重法做了以後發現預測效果還不好,可以考慮採樣法。

    採樣法常用的也有兩種思路,一種是對類別樣本數多的樣本做子採樣, 比如訓練集裏A類別樣本佔90%,B類別樣本佔10%。那麼我們可以對A類的樣本子採樣,直到子採樣得到的A類樣本數和B類別現有樣本一致為止,這樣我們就只用子採樣得到的A類樣本數和B類現有樣本一起做訓練集擬合模型。第二種思路是對類別樣本數少的樣本做過採樣, 還是上面的例子,我們對B類別的樣本做過採樣,直到過採樣得到的B類別樣本數加上B類別原來樣本一起和A類樣本數一致,最後再去擬合模型。

    上述兩種常用的採樣法很簡單,但是都有個問題,就是採樣後改變了訓練集的分佈,可能導致泛化能力差。所以有的演算法就通過其他方法來避免這個問題,比如SMOTE演算法通過人工合成的方法來生成少類別的樣本。方法也很簡單,對於某一個缺少樣本的類別,它會隨機找出幾個該類別的樣本,再找出最靠近這些樣本的若干個該類別樣本,組成一個候選合成集合,然後在這個集合中不停的選擇距離較近的兩個樣本,在這兩個樣本之間,比如中點,構造一個新的該類別樣本。舉個例子,比如該類別的候選合成集合有兩個樣本(x1,y),(x2,y)(x1,y),(x2,y),那麼SMOTE採樣後,可以得到一個新的訓練樣本(x1+x22,y)(x1+x22,y),通過這種方法,我們可以得到不改變訓練集分佈的新樣本,讓訓練集中各個類別的樣本數趨於平衡。我們可以用imbalance-learn這個Python庫中的SMOTEENN類來做SMOTE採樣。

註:本篇本章轉載劉建平博客園

推薦閱讀:

相關文章