機器學習-- 資料轉換

weixin_33866037發表於2018-11-17

一.標準化的原因

通常情況下是為了消除量綱的影響。譬如一個百分制的變數與一個5分值的變數在一起怎麼比較?只有通過資料標準化,都把它們標準到同一個標準時才具有可比性,一般標準化採用的是Z標準化,即均值為0,方差為1,當然也有其他標準化,比如0--1標準化等等,可根據自己的資料分佈情況和模型來選擇

二.適用情況

看模型是否具有伸縮不變性。

 不是所有的模型都一定需要標準化,有些模型對量綱不同的資料比較敏感,譬如SVM等。當各個維度進行不均勻伸縮後,最優解與原來不等價,這樣的模型,除非原始資料的分佈範圍本來就不叫接近,否則必須進行標準化,以免模型引數被分佈範圍較大或較小的資料主導。但是如果模型在各個維度進行不均勻伸縮後,最優解與原來等價,例如logistic regression等,對於這樣的模型,是否標準化理論上不會改變最優解。但是,由於實際求解往往使用迭代演算法,如果目標函式的形狀太“扁”,迭代演算法可能收斂得很慢甚至不收斂。所以對於具有伸縮不變性的模型,最好也進行資料標準化。

三.三種資料變換方法的含義與應用

Rescaling(重縮放/歸一化):通常是指增加或者減少一個常數,然後乘以/除以一個常數,來改變資料的衡量單位。例如:將溫度的衡量單位從攝氏度轉化為華氏溫度。

Normalizing(正則化):通常是指除以向量的範數。例如:將一個向量的歐氏長度等價於1 。在神經網路中,“正則化”通常是指將向量的範圍重縮放至最小化或者一定範圍,使所有的元素都在[0,1]範圍內。通常用於文字分類或者文字聚類中。

Standardizing(標準化):通常是為了消除不同屬性或樣方間的不齊性,使同一樣方內的不同屬性間或同一屬性在不同樣方內的方差減小。例如:如果一個向量包含高斯分佈的隨機值,你可能會通過除以標準偏差來減少均值,然後獲得零均值單位方差的“標準正態”隨機變數。

那麼問題是,當我們在訓練模型的時候,一定要對資料進行變換嗎?這得視情況而定。很多人對多層感知機有個誤解,認為輸入的資料必須在[0,1]這個範圍內。雖然標準化後在訓練模型效果會更好,但實際上並沒有這個要求。但是最好使輸入資料中心集中在0周圍,所以把資料縮放到[0,1]其實並不是一個好的選擇。

如果你的輸出啟用函式的範圍是0,1,那你必須保證你的目標值也在這個範圍內。但通常請款下,我們會使輸出啟用函式的範圍適應目標函式的分佈,而不是讓你的資料來適應啟用函式的範圍。

當我們使用啟用函式的範圍為[0,1]時,有些人可能更喜歡把目標函式縮放到[0.1,0.9]這個範圍。我懷疑這種小技巧的之所以流行起來是因為反向傳播的標準化太慢了導致的。但用這種方法可能會使輸出的後驗概率值不對。如果你使用一個有效的訓練演算法的話,完全不需要用這種小技巧,也沒有必要去避免溢位(overflow)

四.具體方法及程式碼

一)標準化

1.1 scale----零均值單位方差

from sklearn import preprocessing 
import numpy as np  
#raw_data
X = np.array([[1., -1., 2.], [2., 0., 0.], [0., 1., -1.]])  
X_scaled = preprocessing.scale(X) 
#output
X_scaled = [[ 0.         -1.22474487  1.33630621]
             [ 1.22474487  0.         -0.26726124]
             [-1.22474487  1.22474487 -1.06904497]]
#scaled之後的資料零均值,單位方差
X_scaled.mean(axis=0)  # column mean: array([ 0.,  0.,  0.])  
X_scaled.std(axis=0)  #column standard deviation: array([ 1.,  1.,  1.])

1.2 StandardScaler----計算訓練集的平均值和標準差,以便測試資料集使用相同的變換

scaler = preprocessing.StandardScaler().fit(X) 
#out:
 StandardScaler(copy=True, with_mean=True, with_std=True)
scaler.mean_  
#out: 
array([ 1.,  0. ,  0.33333333])  
scaler.std_ 
#out:
 array([ 0.81649658,  0.81649658,  1.24721913]) 
#測試將該scaler用於輸入資料,變換之後得到的結果同上
scaler.transform(X)
 #out: 
array([[ 0., -1.22474487,  1.33630621],  [ 1.22474487, 0. , -0.26726124],  [-1.22474487,1.22474487, -1.06904497]])  
scaler.transform([[-1., 1., 0.]])  
#scale the new data, out: 
array([[-2.44948974,  1.22474487, -0.26726124]])

注:1)若設定with_mean=False 或者 with_std=False,則不做centering 或者scaling處理。

2)scale和StandardScaler可以用於迴歸模型中的目標值處理。
二)歸一化----將資料特徵縮放至某一範圍(scalingfeatures to a range)

另外一種標準化方法是將資料縮放至給定的最小值與最大值之間,通常是0與1之間,可用MinMaxScaler實現。或者將最大的絕對值縮放至單位大小,可用MaxAbsScaler實現。

使用這種標準化方法的原因是,有時資料集的標準差非常非常小,有時資料中有很多很多零(稀疏資料)需要儲存住0元素。

2.1 MinMaxScaler(最小最大值標準化)

公式:X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) ;

X_scaler = X_std/ (max - min) + min

#例子:將資料縮放至[0, 1]間
X_train = np.array([[1., -1., 2.], [2., 0., 0.], [0., 1., -1.]])
min_max_scaler = preprocessing.MinMaxScaler() 
X_train_minmax = min_max_scaler.fit_transform(X_train)  
#out: 
array([[ 0.5       ,  0.        ,  1.        ], 
[ 1.        ,  0.5       ,  0.33333333],        
[ 0.        ,  1.        ,  0.        ]])
#將上述得到的scale引數應用至測試資料
X_test = np.array([[ -3., -1., 4.]])  
X_test_minmax = min_max_scaler.transform(X_test) #out: array([[-1.5 ,  0. , 1.66666667]])
#可以用以下方法檢視scaler的屬性
min_max_scaler.scale_        #out: array([ 0.5 ,  0.5,  0.33...])
min_max_scaler.min_         #out: array([ 0.,  0.5,  0.33...])

2.2 MaxAbsScaler(絕對值最大標準化)

與上述標準化方法相似,但是它通過除以最大值將訓練集縮放至[-1,1]。這意味著資料已經以0為中心或者是含有非常非常多0的稀疏資料。

X_train = np.array([[ 1., -1.,  2.],
                     [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])
max_abs_scaler = preprocessing.MaxAbsScaler()
X_train_maxabs = max_abs_scaler.fit_transform(X_train)
# out: 
array([[ 0.5, -1.,  1. ], [ 1. , 0. ,  0. ],       [ 0. ,  1. , -0.5]])
X_test = np.array([[ -3., -1.,  4.]])
X_test_maxabs = max_abs_scaler.transform(X_test) 
#out: 
array([[-1.5, -1. ,  2. ]])
max_abs_scaler.scale_  
#out: 
array([ 2.,  1.,  2.])

其實在scale模組裡,也提供了這兩種方法: minmax_scale和maxabs_scale
2.3 對稀疏資料進行標準化

對稀疏資料進行中心化會破壞稀疏資料的結構,這樣做沒什麼意義。但是我們可以對稀疏資料的輸入進行標準化,尤其是特徵在不同的標準時。MaxAbsScaler 和 maxabs_scale是專門為稀疏資料設計的,也是常用的方法。但是scale 和 StandardScaler只接受scipy.sparse的矩陣作為輸入,並且必須設定with_centering=False。否則會出現 ValueError且破壞稀疏性,而且還會無意中分配更多的記憶體導致記憶體崩潰。RobustScaler不適用於稀疏資料的輸入,但是你可以用 transform 方法。

scalers接受壓縮的稀疏行(Compressed Sparse Rows)和壓縮的稀疏列(Compressed Sparse Columns)的格式(具體參考scipy.sparse.csr_matrix 和scipy.sparse.csc_matrix)。其他的稀疏格式會被轉化成壓縮的稀疏行(Compressed Sparse Rows)格式。為了避免這種不必要的記憶體拷貝,推薦使用CSR或者CSC的格式。如果資料很小,可以在稀疏矩陣上運用toarray 方法。

2.4 對離群點進行標準化

如果你的資料有離群點(上一篇我們提到過),對資料進行均差和方差的標準化效果並不好。這種情況你可以使用robust_scale 和 RobustScaler 作為替代。它們有對資料中心化和資料的縮放魯棒性更強的引數。

三)正則化

3.1 L1、L2正則化

x=np.array([[1.,-1.,2.],
            [2.,0.,0.],
            [0.,1.,-1.]])
x_normalized=preprocessing.normalize(x,norm='l2')
print(x_normalized)

# 可以使用processing.Normalizer()類實現對訓練集和測試集的擬合和轉換
normalizer=preprocessing.Normalizer().fit(x)
print(normalizer)
normalizer.transform(x)

注:稀疏資料輸入:

normalize 和 Normalizer 既接受稠密資料(dense array-like),也接受稀疏矩陣(from scipy.sparse)作為輸入

稀疏資料需要轉換成壓縮的稀疏行(Compressed Sparse Rows)格式(詳見scipy.sparse.csr_matrix),為了避免不必要的記憶體拷貝,推薦使用CSR。

四)二值化

4.1特徵二值化

特徵二值化是把數值特徵轉化成布林值的過程。這個方法對符合多變數伯努利分佈的輸入資料進行預測概率引數很有效。詳細可以見這個例子sklearn.neural_network.BernoulliRBM.

此外,在文字處理中也經常會遇到二值特徵值(很可能是為了簡化概率推理),即使在實際中正則化後的詞頻或者TF-IDF的值通常只比未正則化的效果好一點點。

對於 Normalizer,Binarizer工具類通常是在Pipeline階段(sklearn.pipeline.Pipeline)的前期過程會用到。下面舉一個具體的例子:

#input
X = [[ 1., -1.,  2.],
         [ 2.,  0.,  0.],
         [ 0.,  1., -1.]]
#binary
binarizer = preprocessing.Binarizer().fit(X)  # fit does nothing
binarizer
Binarizer(copy=True, threshold=0.0)
#transform
binarizer.transform(X)
#out:
array([[ 1.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  1.,  0.]])     

# 調整閾值
binarizer = preprocessing.Binarizer(threshold=1.1)
binarizer.transform(X)
#out:
array([[ 0.,  0.,  1.],
       [ 1.,  0.,  0.],
       [ 0.,  0.,  0.]])

注:稀疏資料輸入:

binarize 和 Binarizer 既接受稠密資料(dense array-like),也接受稀疏矩陣(from scipy.sparse)作為輸入

稀疏資料需要轉換成壓縮的稀疏行(Compressed Sparse Rows)格式(詳見scipy.sparse.csr_matrix),為了避免不必要的記憶體拷貝,推薦使用CSR。

五)對類別特徵進行編碼

我們經常會遇到一些類別特徵,這些特徵不是離散型的數值,而是這樣的:["男性","女性"],["來自歐洲","來自美國","來自亞洲"],["使用Firefox瀏覽器","使用Chrome瀏覽器","使用Safari瀏覽器","使用IE瀏覽器"]等等。這種型別的特徵可以被編碼為整型(int),如["男性","來自美國","使用IE瀏覽器"]可以表示成[0,1,3],["女性","來自亞洲","使用Chrome瀏覽器"]可以表示成[1,2,1]。這些整數式的表示不能直接作為sklearn的引數,因為我們需要的是連續型的輸入,而且我們通常是有序的翻譯這些特徵,而不是所有的特徵都是有序化的(譬如瀏覽器就是按人工排的序列)。

將這些類別特徵轉化成sklearn引數中可以使用的方法是:使用one-of-K或者one-hot編碼(獨熱編碼OneHotEncoder)。它可以把每一個有m種類別的特徵轉化成m中二值特徵。舉例如下:

enc = preprocessing.OneHotEncoder()
#input
enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])  
OneHotEncoder(categorical_features='all', dtype=<... 'float'>,handle_unknown='error', n_values='auto', sparse=True)
#transform
enc.transform([[0, 1, 3]]).toarray()
#out
array([[ 1.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  1.]])

預設情況下,特徵的類別數量是從資料集裡自動判斷出來的。當然,你也可以用n_values這個引數。我們剛剛舉的例子中有兩種性別,三種地名和四種瀏覽器,當我們fit之後就可以將我們的資料轉化為數值了。從結果中來看,第一個數字代表性別([0,1]代表男性,女性),第二個數字代表地名([0,1,2]代表歐洲、美國、亞洲),最後一個數字代表瀏覽器([3,0,1,2]代表四種瀏覽器)

此外,字典格式也可以編碼: Loading features from dicts

OneHotEncoder引數:class sklearn.preprocessing.OneHotEncoder(n_values='auto', categorical_features='all', dtype=<class 'float'>, sparse=True, handle_unknown='error')

n_values : ‘auto’, int or array of ints

每個特徵的數量

‘auto’ : 從訓練資料的範圍中得到

int : 所有特徵的最大值(number)

array : 每個特徵的最大值(number)

categorical_features: “all” or array of indices or mask :

確定哪些特徵是類別特徵

‘all’ (預設): 所有特徵都是類別特徵,意味著所有特徵都要進行OneHot編碼

array of indices: 類別特徵的陣列索引

mask: n_features 長度的陣列,切dtype = bool

非類別型特徵通常會放到矩陣的右邊

dtype : number type, default=np.float

輸出資料的型別

sparse : boolean, default=True

設定True會返回稀疏矩陣,否則返回陣列

handle_unknown : str, ‘error’ or ‘ignore’

當一個不明類別特徵出現在變換中時,報錯還是忽略

六)缺失值的插補

sklearn裡也有一個工具Imputer可以對缺失值進行插補。Imputer類可以對缺失值進行均值插補、中位數插補或者某行/列出現的頻率最高的值進行插補,也可以對不同的缺失值進行編碼。並且支援稀疏矩陣。

import numpy as np
from sklearn.preprocessing import Imputer
#用均值插補缺失值
imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
imp.fit([[1, 2], [np.nan, 3], [7, 6]])
Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0)
X = [[np.nan, 2], [6, np.nan], [7, 6]]
print(imp.transform(X))                           
[[ 4.          2.        ]
 [ 6.          3.666...]
 [ 7.          6.        ]]

#對稀疏矩陣進行缺失值插補
import scipy.sparse as sp
X = sp.csc_matrix([[1, 2], [0, 3], [7, 6]])
imp = Imputer(missing_values=0, strategy='mean', axis=0)
imp.fit(X)
Imputer(axis=0, copy=True, missing_values=0, strategy='mean', verbose=0)
X_test = sp.csc_matrix([[0, 2], [6, 0], [7, 6]])
print(imp.transform(X_test))                      
[[ 4.          2.        ]
 [ 6.          3.666...]
 [ 7.          6.        ]]

在稀疏矩陣中,缺失值被編碼為0儲存為矩陣中,這種格式是適合於缺失值比非缺失值多得多的情況。此外,Imputer類也可以用於Pipeline中

Imputor類的引數:class sklearn.preprocessing.Imputer(missing_values='NaN', strategy='mean', axis=0, verbose=0, copy=True)

missing_values : int或"NaN",預設NaN(String型別)

strategy : string, 預設為mean,可選則mean、median、most_frequent

axis :int, 預設為0(axis = 0,對列進行插值;axis= 1,對行進行插值)

verbose : int, 預設為0

copy : boolean, 預設為True

True:會建立一個X的副本

False:在任何合適的地方都會進行插值。但是以下四種情況,計算設定的copy = Fasle,也會建立一個副本:

1.X不是浮點型陣列

2.X是稀疏矩陣,而且miss_value = 0

3.axis= 0,X被編碼為CSR矩陣

4.axis= 1,X被編碼為CSC矩陣

相關文章