前些天在 100-Days-Of-ML-Code 上回答了一個關於資料拆分與特徵縮放的順序先後的一個issue,感覺挺有爭議性的,故單獨拎出來做下筆記說明。我的觀點是:機器學習工程中,應該先進行資料劃分,再進行特徵縮放。出於嚴謹性,本篇文章是從機器學習-資料探勘方面進行資料拆分與特徵縮放的順序問題闡述,同時也歡迎大家一起討論這個問題。
問題闡述
關於資料拆分與特徵縮放的順序先後問題,一般會在工程中遇到,具體表現為:
先資料拆分再特徵縮放
from sklearn.preprocessing import StandardScaler,MinMaxScaler from sklearn.model_selection import train_test_split X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.1) sc = StandardScaler() X_train = sc.fit_transform(X_train) X_test = sc.transform(X_test)
先資料縮放再資料拆分
from sklearn.preprocessing import StandardScaler,MinMaxScaler sc = StandardScaler() X_transform = sc.fit_transform(X) X_train,X_test,y_train,y_test = train_test_split(X_transform,y,test_size=0.1)
論點闡述
首先先來看下我們常用的兩種 sklearn 上的特徵縮放:StandardScaler()與MinMaxScaler()
從圖中可以看出StandardScalar涉及到了均值μ與標準差σ,而MinMaxScaler則涉及到了最大值max與最小值min。這些引數的取值都得考慮到全域性樣本的,什麼意思呢?我們來看下兩者的輸出結果:
先資料拆分再特徵縮放先資料縮放再資料拆分
可以很明顯看出,兩種不同的操作順序輸出的資料是完全不同的,也就是說樣本的分佈是完全不同的(很重要!後面闡述要用到),那這種差異性在現實工程中會有什麼影響?要解答這個問題,首先我們首先需要了解fit_transform()方法,fit_transform()你可以理解為fit()方法和transform()方法的pipeline,進行特徵縮放時我們的順序是
- 先fit獲得相應的引數值(可以理解為獲得特徵縮放規則)
- 再用transform進行轉換
fit_transform方法就是先執行fit()方法再執行transform()方法,所以每執行一次就會採用新的特徵縮放規則,我們可以將訓練集的特徵縮放規則應用到測試集上,可以將測試集的特徵縮放規則應用到訓練集上(不過一般很少這麼做),但是透過全部資料集(訓練集+測試集)fit到的的特徵縮放規則是沒有模型訓練意義的。
這裡我們舉一個例子:假設農業部要求我們用LR模型來對花型別進行分類,我們經過學習得到了一個LR模型,模型上線後,現在需要對新的花資料進行預測分類(此時我們可以把舊花資料看做訓練集,新花資料看做測試集):
- 按照先資料拆分再特徵縮放的做法是:先將舊花資料fit出特徵縮放規則,接著將其transform到新花資料上,接著對應用舊花資料特徵縮放規則的新花資料進行預測分類;
- 按照先資料縮放再資料拆分的做法是:將新舊花資料合併為一個總資料集,接著對總資料集進行fit_transform操作,最後再把新花資料切分出來進行預測分類;
重點!!!
這時候問題來了,“我們經過學習得到了一個LR模型”,請問我們學習的資料是什麼?舊花資料 OR 新舊花合併資料?答案肯定是舊花資料啊,更為詳細地講,是應用舊花資料特徵縮放規則的舊花資料,這時候第二種做法的問題就出來了,我們這個LR模型是根據應用舊花資料特徵縮放規則的舊花資料的分佈學習到的這條分類線
而此時你卻將這條分類線去應用在應用新舊花資料特徵縮放規則的新花資料上,根據上方我們得到的論點“兩種不同的操作順序輸出的樣本的分佈是完全不同”,兩種完全不同的分佈,你用根據其中一種分佈學習得到分類線對另一種分佈來說是完全沒有使用意義的,因為兩者根本可以說是根據不同的資料學習而來的,所以有些時候第二種做法效果可能會很好也可能會很糟糕,這就像你拿牛資料學習的LR模型去預測花的分類一樣。而機器學習的前身就是統計學,而統計學的一個樣本基本原則就是樣本同質性(homogenetic)。
總結
>>> from sklearn.datasets import load_iris >>> from sklearn.model_selection import train_test_split >>> iris = load_iris() >>> X, y = iris.data, iris.target >>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) >>> quantile_transformer = preprocessing.QuantileTransformer(random_state=0) >>> X_train_trans = quantile_transformer.fit_transform(X_train) >>> X_test_trans = quantile_transformer.transform(X_test) >>> np.percentile(X_train[:, 0], [0, 25, 50, 75, 100]) array([ 4.3, 5.1, 5.8, 6.5, 7.9])
這裡我貼的是sklearn的一段官方demo程式碼,可以看出sklearn的演示程式碼也是遵從先資料拆分再特徵縮放的順序進行的操作,先fit到X_train的特徵縮放規則,再將其應用在X_test上,這也從一個小方面驗證了我的觀點吧(雖然我也不喜歡不嚴謹的舉例論證方法)。所以綜上所述,我的觀點是在進行資料探勘方面的工作時,面對特徵縮放環節,應該先進行資料拆分再進行特徵縮放。
才學疏淺,歡迎評論指導
歡迎前往我的個人小站:www.wengjj.ink