一.標準化的原因
通常情況下是為了消除量綱的影響。譬如一個百分制的變量與一個5分值的變量在一起怎么比較?只有通過數(shù)據(jù)標準化,都把它們標準到同一個標準時才具有可比性,一般標準化采用的是Z標準化,即均值為0,方差為1,當然也有其他標準化,比如0--1標準化等等,可根據(jù)自己的數(shù)據(jù)分布情況和模型來選擇
二.適用情況
看模型是否具有伸縮不變性。
不是所有的模型都一定需要標準化,有些模型對量綱不同的數(shù)據(jù)比較敏感,譬如SVM等。當各個維度進行不均勻伸縮后,最優(yōu)解與原來不等價,這樣的模型,除非原始數(shù)據(jù)的分布范圍本來就不叫接近,否則必須進行標準化,以免模型參數(shù)被分布范圍較大或較小的數(shù)據(jù)主導。但是如果模型在各個維度進行不均勻伸縮后,最優(yōu)解與原來等價,例如logistic regression等,對于這樣的模型,是否標準化理論上不會改變最優(yōu)解。但是,由于實際求解往往使用迭代算法,如果目標函數(shù)的形狀太“扁”,迭代算法可能收斂得很慢甚至不收斂。所以對于具有伸縮不變性的模型,最好也進行數(shù)據(jù)標準化。
三.三種數(shù)據(jù)變換方法的含義與應用
Rescaling(重縮放/歸一化):通常是指增加或者減少一個常數(shù),然后乘以/除以一個常數(shù),來改變數(shù)據(jù)的衡量單位。例如:將溫度的衡量單位從攝氏度轉化為華氏溫度。
Normalizing(正則化):通常是指除以向量的范數(shù)。例如:將一個向量的歐氏長度等價于1 。在神經(jīng)網(wǎng)絡中,“正則化”通常是指將向量的范圍重縮放至最小化或者一定范圍,使所有的元素都在[0,1]范圍內。通常用于文本分類或者文本聚類中。
Standardizing(標準化):通常是為了消除不同屬性或樣方間的不齊性,使同一樣方內的不同屬性間或同一屬性在不同樣方內的方差減小。例如:如果一個向量包含高斯分布的隨機值,你可能會通過除以標準偏差來減少均值,然后獲得零均值單位方差的“標準正態(tài)”隨機變量。
那么問題是,當我們在訓練模型的時候,一定要對數(shù)據(jù)進行變換嗎?這得視情況而定。很多人對多層感知機有個誤解,認為輸入的數(shù)據(jù)必須在[0,1]這個范圍內。雖然標準化后在訓練模型效果會更好,但實際上并沒有這個要求。但是最好使輸入數(shù)據(jù)中心集中在0周圍,所以把數(shù)據(jù)縮放到[0,1]其實并不是一個好的選擇。
如果你的輸出激活函數(shù)的范圍是[0,1](sigmoid函數(shù)的值域),那你必須保證你的目標值也在這個范圍內。但通常請款下,我們會使輸出激活函數(shù)的范圍適應目標函數(shù)的分布,而不是讓你的數(shù)據(jù)來適應激活函數(shù)的范圍。
當我們使用激活函數(shù)的范圍為[0,1]時,有些人可能更喜歡把目標函數(shù)縮放到[0.1,0.9]這個范圍。我懷疑這種小技巧的之所以流行起來是因為反向傳播的標準化太慢了導致的。但用這種方法可能會使輸出的后驗概率值不對。如果你使用一個有效的訓練算法的話,完全不需要用這種小技巧,也沒有必要去避免溢出(overflow)
四.具體方法及代碼
一)標準化
1.1scale----零均值單位方差
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之后的數(shù)據(jù)零均值,單位方差 X_scaled.mean(axis=0) # column mean: array([ 0., 0., 0.]) X_scaled.std(axis=0) #column standard deviation: array([ 1., 1., 1.])
1.2StandardScaler----計算訓練集的平均值和標準差,以便測試數(shù)據(jù)集使用相同的變換
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用于輸入數(shù)據(jù),變換之后得到的結果同上 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可以用于回歸模型中的目標值處理。
二)歸一化----將數(shù)據(jù)特征縮放至某一范圍(scalingfeatures to a range)
另外一種標準化方法是將數(shù)據(jù)縮放至給定的最小值與最大值之間,通常是0與1之間,可用MinMaxScaler實現(xiàn)。或者將最大的絕對值縮放至單位大小,可用MaxAbsScaler實現(xiàn)。
使用這種標準化方法的原因是,有時數(shù)據(jù)集的標準差非常非常小,有時數(shù)據(jù)中有很多很多零(稀疏數(shù)據(jù))需要保存住0元素。
2.1MinMaxScaler(最小最大值標準化)
公式:X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) ;
X_scaler = X_std/ (max - min) + min
#例子:將數(shù)據(jù)縮放至[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參數(shù)應用至測試數(shù)據(jù) 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.2MaxAbsScaler(絕對值最大標準化)
與上述標準化方法相似,但是它通過除以最大值將訓練集縮放至[-1,1]。這意味著數(shù)據(jù)已經(jīng)以0為中心或者是含有非常非常多0的稀疏數(shù)據(jù)。
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對稀疏數(shù)據(jù)進行標準化
對稀疏數(shù)據(jù)進行中心化會破壞稀疏數(shù)據(jù)的結構,這樣做沒什么意義。但是我們可以對稀疏數(shù)據(jù)的輸入進行標準化,尤其是特征在不同的標準時。MaxAbsScaler和maxabs_scale是專門為稀疏數(shù)據(jù)設計的,也是常用的方法。但是scale和StandardScaler只接受scipy.sparse的矩陣作為輸入,并且必須設置with_centering=False。否則會出現(xiàn)ValueError且破壞稀疏性,而且還會無意中分配更多的內存導致內存崩潰。RobustScaler不適用于稀疏數(shù)據(jù)的輸入,但是你可以用transform 方法。
scalers接受壓縮的稀疏行(Compressed Sparse Rows)和壓縮的稀疏列(Compressed Sparse Columns)的格式(具體參考scipy.sparse.csr_matrix 和scipy.sparse.csc_matrix)。其他的稀疏格式會被轉化成壓縮的稀疏行(Compressed Sparse Rows)格式。為了避免這種不必要的內存拷貝,推薦使用CSR或者CSC的格式。如果數(shù)據(jù)很小,可以在稀疏矩陣上運用toarray 方法。
2.4 對離群點進行標準化
如果你的數(shù)據(jù)有離群點(上一篇我們提到過),對數(shù)據(jù)進行均差和方差的標準化效果并不好。這種情況你可以使用robust_scale和RobustScaler作為替代。它們有對數(shù)據(jù)中心化和數(shù)據(jù)的縮放魯棒性更強的參數(shù)。
三)正則化
3.1L1、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()類實現(xiàn)對訓練集和測試集的擬合和轉換 normalizer=preprocessing.Normalizer().fit(x) print(normalizer) normalizer.transform(x)
四)二值化
4.1特征二值化
特征二值化是把數(shù)值特征轉化成布爾值的過程。這個方法對符合多變量伯努利分布的輸入數(shù)據(jù)進行預測概率參數(shù)很有效。詳細可以見這個例子sklearn.neural_network.BernoulliRBM.
此外,在文本處理中也經(jīng)常會遇到二值特征值(很可能是為了簡化概率推理),即使在實際中正則化后的詞頻或者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.]])
五)對類別特征進行編碼
我們經(jīng)常會遇到一些類別特征,這些特征不是離散型的數(shù)值,而是這樣的:["男性","女性"],["來自歐洲","來自美國","來自亞洲"],["使用Firefox瀏覽器","使用Chrome瀏覽器","使用Safari瀏覽器","使用IE瀏覽器"]等等。這種類型的特征可以被編碼為整型(int),如["男性","來自美國","使用IE瀏覽器"]可以表示成[0,1,3],["女性","來自亞洲","使用Chrome瀏覽器"]可以表示成[1,2,1]。這些整數(shù)式的表示不能直接作為sklearn的參數(shù),因為我們需要的是連續(xù)型的輸入,而且我們通常是有序的翻譯這些特征,而不是所有的特征都是有序化的(譬如瀏覽器就是按人工排的序列)。
將這些類別特征轉化成sklearn參數(shù)中可以使用的方法是:使用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.]])
默認情況下,特征的類別數(shù)量是從數(shù)據(jù)集里自動判斷出來的。當然,你也可以用n_values這個參數(shù)。我們剛剛舉的例子中有兩種性別,三種地名和四種瀏覽器,當我們fit之后就可以將我們的數(shù)據(jù)轉化為數(shù)值了。從結果中來看,第一個數(shù)字代表性別([0,1]代表男性,女性),第二個數(shù)字代表地名([0,1,2]代表歐洲、美國、亞洲),最后一個數(shù)字代表瀏覽器([3,0,1,2]代表四種瀏覽器)
此外,字典格式也可以編碼:Loading features from dicts
OneHotEncoder參數(shù):classsklearn.preprocessing.OneHotEncoder(n_values='auto',categorical_features='all',dtype=
n_values: ‘auto’, int or array of ints
每個特征的數(shù)量
‘auto’ : 從訓練數(shù)據(jù)的范圍中得到
int : 所有特征的最大值(number)
array : 每個特征的最大值(number)
categorical_features: “all” or array of indices or mask:
確定哪些特征是類別特征
‘all’ (默認): 所有特征都是類別特征,意味著所有特征都要進行OneHot編碼
array of indices: 類別特征的數(shù)組索引
mask: n_features 長度的數(shù)組,切dtype = bool
非類別型特征通常會放到矩陣的右邊
dtype: number type, default=np.float
輸出數(shù)據(jù)的類型
sparse: boolean, default=True
設置True會返回稀疏矩陣,否則返回數(shù)組
handle_unknown: str, ‘error’ or ‘ignore’
當一個不明類別特征出現(xiàn)在變換中時,報錯還是忽略
六)缺失值的插補
上篇我們講了五種方法來解決缺失值的問題,其實sklearn里也有一個工具Imputer可以對缺失值進行插補。Imputer類可以對缺失值進行均值插補、中位數(shù)插補或者某行/列出現(xiàn)的頻率最高的值進行插補,也可以對不同的缺失值進行編碼。并且支持稀疏矩陣。
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類的參數(shù):classsklearn.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:會創(chuàng)建一個X的副本
False:在任何合適的地方都會進行插值。
但是以下四種情況,計算設置的copy = Fasle,也會創(chuàng)建一個副本:
1.X不是浮點型數(shù)組
2.X是稀疏矩陣,而且miss_value = 0
3.axis= 0,X被編碼為CSR矩陣
4.axis= 1,X被編碼為CSC矩陣、
舉個實例(在用隨機森林算法之前先用Imputer類進行處理):
import numpy as np from sklearn.datasets import load_boston from sklearn.ensemble import RandomForestRegressor from sklearn.pipeline import Pipeline from sklearn.preprocessing import Imputer from sklearn.cross_validation import cross_val_score rng = np.random.RandomState(0) dataset = load_boston() X_full, y_full = dataset.data, dataset.target n_samples = X_full.shape[0] n_features = X_full.shape[1] # Estimate the score on the entire dataset, with no missing values estimator = RandomForestRegressor(random_state=0, n_estimators=100) score = cross_val_score(estimator, X_full, y_full).mean() print("Score with the entire dataset = %.2f" % score) # Add missing values in 75% of the lines missing_rate = 0.75 n_missing_samples = np.floor(n_samples * missing_rate) missing_samples = np.hstack((np.zeros(n_samples - n_missing_samples, dtype=np.bool), np.ones(n_missing_samples, dtype=np.bool))) rng.shuffle(missing_samples) missing_features = rng.randint(0, n_features, n_missing_samples) # Estimate the score without the lines containing missing values X_filtered = X_full[~missing_samples, :] y_filtered = y_full[~missing_samples] estimator = RandomForestRegressor(random_state=0, n_estimators=100) score = cross_val_score(estimator, X_filtered, y_filtered).mean() print("Score without the samples containing missing values = %.2f" % score) # Estimate the score after imputation of the missing values X_missing = X_full.copy() X_missing[np.where(missing_samples)[0], missing_features] = 0 y_missing = y_full.copy() estimator = Pipeline([("imputer", Imputer(missing_values=0, strategy="mean", axis=0)), ("forest", RandomForestRegressor(random_state=0, n_estimators=100))]) score = cross_val_score(estimator, X_missing, y_missing).mean() print("Score after imputation of the missing values = %.2f" % score)
結果:
Score with the entire dataset = 0.56 Score without the samples containing missing values = 0.48 Score after imputation of the missing values = 0.55
七)生成多項式特征
在輸入數(shù)據(jù)中增加非線性特征可以有效的提高模型的復雜度。簡單且常用的方法就是使用多項式特征(polynomial features),可以得到特征的高階交叉項:
import numpy as np from sklearn.preprocessing import PolynomialFeatures X = np.arange(6).reshape(3, 2) X array([[0, 1], [2, 3], [4, 5]]) poly = PolynomialFeatures(2) poly.fit_transform(X) array([[ 1., 0., 1., 0., 0., 1.], [ 1., 2., 3., 4., 6., 9.], [ 1., 4., 5., 16., 20., 25.]])
然而有時候我們只需要特征的交叉項,可以設置interaction_only=True來得到:
X = np.arange(9).reshape(3, 3) X array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) poly = PolynomialFeatures(degree=3, interaction_only=True) poly.fit_transform(X) array([[ 1., 0., 1., 2., 0., 0., 2., 0.], [ 1., 3., 4., 5., 12., 15., 20., 60.], [ 1., 6., 7., 8., 42., 48., 56., 336.]])
這個方法可能大家在工作中比較少見,但世界上它經(jīng)常用于核方法中,如選擇多項式核時(sklearn.svm.SVC,sklearn.decomposition.KernelPCA)
八)自定義轉換
如果以上的方法覺得都不夠,譬如你想用對數(shù)據(jù)取對數(shù),可以自己用FunctionTransformer自定義一個轉化器,并且可以在Pipeline中使用
import numpy as np from sklearn.preprocessing import FunctionTransformer transformer = FunctionTransformer(np.log1p)#括號內的就是自定義函數(shù) X = np.array([[0, 1], [2, 3]]) transformer.transform(X) array([[ 0. , 0.69314718], [ 1.09861229, 1.38629436]])
告訴你怎么用:
如果你在做一個分類任務時,發(fā)現(xiàn)第一主成分與這個不相關,你可以用FunctionTransformer把第一列除去,剩下的列用PCA:
import matplotlib.pyplot as plt import numpy as np from sklearn.cross_validation import train_test_split from sklearn.decomposition import PCA from sklearn.pipeline import make_pipeline # from sklearn.preprocessing import FunctionTransformer # 如果報錯ImportError: cannot import name FunctionTransformer,可以使用下面的語句 from sklearn.preprocessing import * def _generate_vector(shift=0.5, noise=15): return np.arange(1000) + (np.random.rand(1000) - shift) * noise def generate_dataset(): """ This dataset is two lines with a slope ~ 1, where one has a y offset of ~100 """ return np.vstack(( np.vstack(( _generate_vector(), _generate_vector() + 100, )).T, np.vstack(( _generate_vector(), _generate_vector(), )).T, )), np.hstack((np.zeros(1000), np.ones(1000))) def all_but_first_column(X): return X[:, 1:] def drop_first_component(X, y): """ Create a pipeline with PCA and the column selector and use it to transform the dataset. """ pipeline = make_pipeline( PCA(), FunctionTransformer(all_but_first_column), ) X_train, X_test, y_train, y_test = train_test_split(X, y) pipeline.fit(X_train, y_train) return pipeline.transform(X_test), y_test if __name__ == '__main__': X, y = generate_dataset() plt.scatter(X[:, 0], X[:, 1], c=y, s=50) plt.show() X_transformed, y_transformed = drop_first_component(*generate_dataset()) plt.scatter( X_transformed[:, 0], np.zeros(len(X_transformed)), c=y_transformed, s=50, ) plt.show()
結果:
寫到這里基本上關于數(shù)據(jù)轉化的方法已經(jīng)介紹的差不多了
-
算法
+關注
關注
23文章
4629瀏覽量
93200 -
數(shù)據(jù)轉換
+關注
關注
0文章
88瀏覽量
18026 -
機器學習
+關注
關注
66文章
8438瀏覽量
132953
原文標題:機器學習基礎與實踐(二)----數(shù)據(jù)轉換
文章出處:【微信號:AI_shequ,微信公眾號:人工智能愛好者社區(qū)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論