【持續更新】機器學習特徵工程實用技巧大全
(題圖: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種不同類別,其取值範圍是。
例:顏色、性別、地址、血型、國籍、省、市、郵政編碼。
定性特徵(維基百科):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。
- 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。
,其中表示norm函數。
3.2.3. 區間縮放(scaling)
- sklearn.preprocessing.MaxAbsScaler - scikit-learn 0.18.1 documentation,將一列的數值,除以這一列的最大絕對值。不免疫outlier。
- sklearn.preprocessing.MinMaxScaler - scikit-learn 0.18.1 documentation。不免疫outlier。
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. 定量特徵的簡單變換
- 單獨特徵列乘以一個常數(constant multiplication)或者加減一個常數:對於創造新的有用特徵毫無用處;只能作為對已有特徵的處理。
- 任何針對單獨特徵列的單調變換(如對數):不適用於決策樹類演算法。對於決策樹而言, 、 、 之間沒有差異, 、 、 之間沒有差異,除非發生了舍入誤差。
- 線性組合(linear combination):僅適用於決策樹以及基於決策樹的ensemble(如gradient boosting, random forest),因為常見的axis-aligned split function不擅長捕獲不同特徵之間的相關性;不適用於SVM、線性回歸、神經網路等。
- 多項式特徵(polynomial feature):sklearn.preprocessing.PolynomialFeatures - scikit-learn 0.18.1 documentation。
- 比例特徵(ratio feature):
- 絕對值(absolute value)
- ,,
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. 有效發現和利用數據泄露
數據泄露可以分為兩大類:
- 由於自己的疏忽,在交叉驗證、訓練過程中,產生的數據泄露。這種情況屬於失誤,應當盡量避免。
- 在數據競賽中,找到了理論上不能使用(但是也沒有明令禁止)的額外數據,從而提升分數。
避免第一種數據泄露的方法,可以參考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數據