【持續更新】機器學習特徵工程實用技巧大全

(題圖:Rene Magritte - The Empire of Light)

(2017/10/6 已更新:減少了空行數量)

與其說是教程類的科普,不如說是一篇經驗向的個人筆記,所以細節上比較懶。其實,我更打算把這篇文章做成一個索引,能夠引用原版文檔的就引用文檔,盡量不重複翻譯,畢竟各類文檔本身信息量最充足、更新最及時。簡潔是本文的第一原則。

一些基礎的暫時就不補充了,主要涉及一些奇技淫巧。純粹是經驗之談,如有不準確之處,請多多包涵;如能幫忙指正,感激不盡。如果覺得內容少的話,等幾個月,說不定就養肥了~

想到一點寫一點,有思路才更新。有其他點子的話,可以在評論區補充或者私信。鏈接分享隨意,全文轉載前請私信。

目錄

  • 0. 概述
  • 1. 探索性數據分析(EDA)
  • 2. 預處理(Preprocessing)
  • 3. 特徵工程:處理已有特徵
  • 3.1. 定性特徵
  • 3.2. 定量特徵
  • 3.3. 時間特徵
  • 3.4 缺失值處理
  • 4. 特徵工程:創造新特徵
  • 5. 特徵選擇
  • 6. 數據泄露(Leakage)
  • 7. 參考文獻

0. 概述

特徵工程feature engineering):利用領域知識現有數據,創造出新的特徵,用於機器學習演算法;可以手動(manual)或自動(automated)。神經網路的自動特徵工程,常常不適用於現實中其他的複雜任務。因此,本文主要針對數據挖掘以及傳統的機器學習,不會涉及圖像識別、自然語言處理等深度學習領域。

俗話說:數據與特徵工程決定了模型的上限,改進演算法只不過是逼近這個上限而已。

特徵工程(維基百科):Feature engineering

1. 探索性數據分析(EDA,Exploratory Data Analysis)

plot,plot,plot,重要的事情說三遍

2. 預處理(Preprocessing)

我們需要將訓練集表示為矩陣X(大小:n_samples * n_features)和矢量y(長度:n_samples)。矢量y可以被轉換為矩陣Y(大小:n_samples * n_classes)。

scikit-learn完整的API reference:API Reference - scikit-learn 0.18.1 documentation

常用函數:

  • sklearn.preprocessing.LabelBinarizer - scikit-learn 0.18.1 documentation,用於one vs all的label encoding,類似於啞編碼,生成一個(n_examples * n_classes)大小的0~1矩陣,每個樣本僅對應一個label。
  • sklearn.preprocessing.MultiLabelBinarizer - scikit-learn 0.18.1 documentation,用於label encoding,生成一個(n_examples * n_classes)大小的0~1矩陣,每個樣本可能對應多個label。
  • sklearn.preprocessing.LabelEncoder - scikit-learn 0.18.1 documentation,用於label encoding,生成一個(n_examples)大小的0~(n_classes-1)矢量,每個樣本僅對應一個label。

3. 特徵工程:處理已有特徵

3.1. 定性特徵

定性特徵,表示某個數據點屬於某一個類別,或具有某一種類的特性。一列定性特徵,默認用自然數表示(可以用LabelEncoder將字元串轉化為自然數)。如果一列定性特徵里有K種不同類別,其取值範圍是{0, 1, 2, 3, ..., K-1}

例:顏色、性別、地址、血型、國籍、省、市、郵政編碼。

定性特徵(維基百科):Categorical variable

3.1.1. 自然數編碼

默認的編碼方式(見上,使用LabelEncoder可以得到),消耗內存小,訓練時間快,但是特徵的質量不高。

3.1.2. 啞編碼(One-hot Encoding)

如果定性特徵本身有順序(例:優秀、良好、合格、不合格),那麼可以保留單列自然數編碼。如果定性特徵沒有明顯的順序(例:紅、黃、藍),則可以使用以下方法:

sklearn.preprocessing.OneHotEncoder - scikit-learn 0.18.1 documentation,用於定性特徵的啞編碼(One-Hot Encoding)。運行結果與LabelBinarizer相似,不過在參數以及輸入輸出的格式上有細微的差別,參見文檔。輸出的矩陣是稀疏的,含有大量的0。

統計學中,啞編碼的變種還有effects coding、contrast coding、nonsense coding等編碼方式,但在數據挖掘中都不常用。

3.1.3. 聚類編碼

和啞編碼相比,聚類編碼試圖充分利用每一列0與1的信息表達能力。聚類編碼時一般需要特定的專業知識(domain knowledge),例如ZIP碼可以根據精確度分層為ZIP3、ZIP4、ZIP5、ZIP6,然後按層次進行編碼。

(我個人尚未使用過這種方法,僅在讀論文的時候看到了類似的思路,所以暫時不知道對於各種演算法而言效果如何。)

3.1.4. 平均數編碼(mean encoding)

平均數編碼(mean encoding),針對高基數定性特徵有監督編碼。當一個定性特徵列包括了極多不同類別時(如家庭地址,動輒上萬)時,可以採用。優點:和啞編碼相比,節省內存、減少演算法計算時間、有效增強模型表現。

平均數編碼:針對高基數定性特徵(類別特徵)的數據預處理/特徵工程 - 知乎專欄

3.1.5. 只出現一次的類別

在定性特徵列里,有時會有一些類別,在訓練集和測試集中總共只出現一次,例如特別偏僻的郊區地址。此時,保留其原有的自然數編碼意義不大,不如將所有頻數為1的類別合併到同一個新的類別下

注意:如果特徵列的頻數需要被當做一個新的特徵加入數據集,請在上述合併之前提取出頻數特徵。

3.2. 定量特徵

定量特徵(numerical feature),可以是連續的(continuous),也可以是離散的(discrete),一般表示為一個實數值。

例:年齡、價格、身高、體重、測量數據。

不同演算法對於定量特徵的處理要求不同。下文中的一些數據處理方法(3.2.1、3.2.2、3.2.3),因為是針對某一特徵列的單調變換,所以不會對基於決策樹的演算法(隨機森林、gbdt)產生任何影響。一般而言,決策樹類演算法不需要預處理定量特徵。

3.2.1. 標準化(Standardization)

  • sklearn.preprocessing.StandardScaler - scikit-learn 0.18.1 documentation,轉換為Z-score,使定量特徵列的算數平均為0,方差(以及標準差)為1。不免疫outlier。

x = frac{x - mu}{sigma}

  • sklearn.preprocessing.RobustScaler - scikit-learn 0.18.1 documentation。如果定量特徵列中存在數值極大或極小的outlier(通過EDA發現),應該使用更穩健(robust)的統計數據:用中位數而不是算術平均數,用分位數(quantile)而不是方差。這種標準化方法有一個重要的參數:(分位數下限,分位數上限),最好通過EDA的數據可視化確定。免疫outlier。

3.2.2. 歸一化(Normalization)

  • sklearn.preprocessing.Normalizer - scikit-learn 0.18.1 documentation,把每一行數據歸一化,使之有unit norm,norm的種類可以選l1、l2或max。不免疫outlier。

vec{x} = frac{vec{x}}{l(vec{x})},其中l表示norm函數。

3.2.3. 區間縮放(scaling)

  • sklearn.preprocessing.MaxAbsScaler - scikit-learn 0.18.1 documentation,將一列的數值,除以這一列的最大絕對值。不免疫outlier。

x = frac{x}{max(|X|)}

  • sklearn.preprocessing.MinMaxScaler - scikit-learn 0.18.1 documentation。不免疫outlier。

x = frac{x - min(X)}{max(X) - min(X)}

3.3. 其他特徵工程(待補全)

時間特徵:年、月、日、時、分、秒,這是一年的第n天,這是一年的第n周,這是一周的第n天,etc

地理特徵:經度、緯度

用戶特徵:如用戶名,每個種類對應一個不同的實體

  • NA indicator column
  • 時間序列:把昨天的特徵加入今天的特徵,或者把和昨天相比,特徵數值的改變數加入今天的特徵。
  • 連續特徵離散化(這對於決策樹類型的模型沒太多意義)。一種挺有趣的變種,就是限制浮點數特徵的精度。
  • clipping:可以用pandas dataframe的.clip(low, upper)方法,把特徵值的取值限制在一定範圍內
  • 一開始就把所有的特徵一股腦地扔進模型,費時費力,容易被一些沒用的特徵誤導
  • 除非萬不得已,不要用PCA或者LDA降維,直接減原始特徵就行了。
  • 像lightgbm和xgboost這種gbdt,一般都自帶cv,early stopping和feature importance,用來同時驗證FE是墜吼的,省時間。

3.4 缺失值處理

LightGBM和XGBoost都能將NaN作為數據的一部分進行學習,所以不需要處理缺失值。

其他情況下,我們需要使用

4. 特徵工程:創造新特徵

4.1. 定量特徵的簡單變換

  1. 單獨特徵列乘以一個常數(constant multiplication)或者加減一個常數:對於創造新的有用特徵毫無用處;只能作為對已有特徵的處理。
  2. 任何針對單獨特徵列的單調變換(如對數):不適用於決策樹類演算法。對於決策樹而言,XX^3X^5 之間沒有差異, |X|X^2X^4 之間沒有差異,除非發生了舍入誤差。
  3. 線性組合(linear combination):僅適用於決策樹以及基於決策樹的ensemble(如gradient boosting, random forest),因為常見的axis-aligned split function不擅長捕獲不同特徵之間的相關性;不適用於SVM、線性回歸、神經網路等。
  4. 多項式特徵(polynomial feature):sklearn.preprocessing.PolynomialFeatures - scikit-learn 0.18.1 documentation。
  5. 比例特徵(ratio feature):X_1 / X_2
  6. 絕對值(absolute value)
  7. max(X_1, X_2)min(X_1, X_2)X_1 xor X_2

4.2. 定性特徵與定量特徵的組合

用N1和N2表示定量特徵,用C1和C2表示定性特徵,利用pandas的groupby操作,可以創造出以下幾種有意義的新特徵:(其中,C2還可以是離散化了的N1)

median(N1)_by(C1) 中位數nmean(N1)_by(C1) 算術平均數nmode(N1)_by(C1) 眾數nmin(N1)_by(C1) 最小值nmax(N1)_by(C1) 最大值nstd(N1)_by(C1) 標準差nvar(N1)_by(C1) 方差nfreq(C2)_by(C1) 頻數nnfreq(C1) 這個不需要groupby也有意義n

僅僅將已有的定性和定量特徵進行以上的有效組合,就能夠大量增加優秀的可用特徵

將這種方法和線性組合等基礎特徵工程方法結合(僅用於決策樹),可以得到更多有意義的特徵,如:

N1 - median(N1)_by(C1)nN1 - mean(N1)_by(C1)n

4.3. 用基因編程創造新特徵

Welcome to gplearn』s documentation!

基於genetic programming的symbolic regression,具體的原理和實現參見文檔。目前,python環境下最好用的基因編程庫為gplearn。基因編程的兩大用法:

  • 轉換(transformation):把已有的特徵進行組合轉換,組合的方式(一元、二元、多元運算元)可以由用戶自行定義,也可以使用庫中自帶的函數(如加減乘除、min、max、三角函數、指數、對數)。組合的目的,是創造出和目標y值最「相關」的新特徵。這種相關程度可以用spearman或者pearson的相關係數進行測量。spearman多用於決策樹(免疫單特徵單調變換),pearson多用於線性回歸等其他演算法。
  • 回歸(regression):原理同上,只不過直接用於回歸而已。

4.4. 用決策樹創造新特徵

在決策樹系列的演算法中(單棵決策樹、gbdt、隨機森林),每一個樣本都會被映射到決策樹的一片葉子上。因此,我們可以把樣本經過每一棵決策樹映射後的index(自然數)或one-hot-vector(啞編碼得到的稀疏矢量)作為一項新的特徵,加入到模型中。

具體實現:apply()以及decision_path()方法,在scikit-learn和xgboost里都可以用。

決策樹、基於決策樹的ensemble

  • spearman correlation coefficient

線性模型、SVM、神經網路

  • 對數(log)
  • pearson correlation coefficient

6. 數據泄露(Data Leakage)

在數據挖掘中,數據泄露leakage)指的是:本來不應該出現在X里的、和目標y有關的數據,出現在了X中。如此一來,機器學習演算法就會有好到不真實的表現。

Kaggle Wiki鏈接:Leakage | Kaggle

6.1. 數據泄露的種類以及影響分析

  • 測試集數據被泄露到訓練集:過擬合,模型在現實中的表現遠不如test accuracy;測試集失去意義。
  • 正確的預測(y)被泄露到測試集:嚴重過擬合,訓練出的模型毫無用處,比賽組織者的極大失敗
  • 未來的信息被泄露到過去:時間序列相關,現實中模型將無法有效根據過去情況預測未來。
  • 模型可以獲得一些不該獲得的信息,比如和目標變數有較大關係的變數、現實里接觸不到的變數。例子:y是「病人是否患有癌症」,但是X包括了「病人是否接受腫瘤切除手術」。
  • 反向工程,去匿名化,去除數據集中的隨機打亂操作,社會工程學。這種行為是數據比賽明令禁止的,而且在現實中也涉嫌侵犯隱私。例子:反向工程「隨機的」用戶編碼,得出用戶的真名。
  • 第三方信息。例子:已知坐標,利用geocoder類型的服務推出所在城市;在預測金融市場時加入第三方的政策新聞的特徵。

6.2. 有效發現和利用數據泄露

數據泄露可以分為兩大類:

  1. 由於自己的疏忽,在交叉驗證、訓練過程中,產生的數據泄露。這種情況屬於失誤,應當盡量避免。
  2. 在數據競賽中,找到了理論上不能使用(但是也沒有明令禁止)的額外數據,從而提升分數。

避免第一種數據泄露的方法,可以參考kaggle的各類比賽。假設有大量數據,我們可以把未處理的數據分為訓練集測試集,其中,測試集包括Public LB和Private LB兩部分。

  • 在模型的訓練、選擇和交叉驗證時,我們只能接觸訓練集。
  • 在對自己的模型非常自信時,可以偶爾在Public LB上驗證。
  • 只有模型即將被用於正式商業用途時,才能看模型在Private LB上的表現。

交叉驗證誤差、public LB誤差、private LB誤差:如果後者的誤差值顯著高於前者,那麼需要考慮過擬合或第一類數據泄露。

第二類的數據泄露,屬於旁門左道。本質上,這相當於在模型訓練階段,幹了數據收集階段的工作。搜集原始數據,或是自己提供數據舉辦競賽(試圖避免他人利用數據泄露)時,可以參考這種思路。

  • 文件夾的創造時間。
  • 看似亂碼的字元串(如各類id)可能有統計分布的規律。
  • 地理位置信息:如果提供了坐標,則可反向地理編碼,得出相關地理信息。

這類數據可能會導致過擬合。

大家在收藏的時候也不要忘記點贊,你們的贊是我更新的動力。

覺得有用的話,可以把鏈接分享出去讓更多人看見。

如果你們有其他特徵工程的技巧,歡迎在評論區分享。

(順便,厚顏無恥地求一個關注?我是 @光喻

推薦閱讀:

深度學習入門:Tensorflow實戰Digit Recognizer(一)
kaggle小黃車競賽 得分:0.41038
Kaggle—So Easy!百行代碼實現排名Top 5%的圖像分類比賽
Zillow簡介(二)如何重構經紀行業的產業鏈?
用Python下載Kaggle數據

TAG:机器学习 | Kaggle | 数据挖掘 |