特征工程之特征表達


          在特征工程之特征選擇中,我們講到了特征選擇的一些要點。本篇我們繼續討論特征工程,不過會重點關注於特征表達部分,即如果對某一個特征的具體表現形式做處理。主要包括缺失值處理,特殊的特征處理比如時間和地理位置處理,離散特征的連續化和離散化處理,連續特征的離散化處理幾個方面。

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方法很方便的得到樣本離散化后的特征,然后使用獨熱編碼即可。 

         具體的一個示例代碼如下:

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=0.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])
print (X_train_new.todense())

       輸出是:

[[0. 1. 1. 0.]
 [1. 0. 0. 1.]
 [1. 0. 0. 1.]
 [1. 0. 0. 1.]
 [0. 1. 1. 0.]]

6. 小結

       本文總結了特征表達的一些具體方法, 但是特征表達的方法便不止於上文中的方法,畢竟這是工程實踐。但是上文中的方法是比較普遍的,希望可以給大家一些幫助和啟發。 下一篇我們討論特征預處理和分類類別不平衡的問題處理。

 

(歡迎轉載,轉載請注明出處。歡迎溝通交流: liujianping-ok@163.com)             


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM