關於標准化(standardization)
數據標准化能將原來的數據進行重新調整(一般也稱為 z-score 規范化方法),以便他們具有標准正態分布的屬性,即 μ=0 和 σ=1。其中,μ 表示平均值,σ 表示標准方差。數據標准化之后的形式可以按照如下公式進行計算:
如果我們是比較兩個不同大小維度的數據,那么將這些數據標准化到以 0 為中心並且標准差為 1 的范圍,這對許多的機器學習算法來說也是一般的要求。比如,從直覺上來說,我們可以將梯度下降看作一個特殊的例子。由於特征值 xj 在權重更新中發揮作用,那么某些權重可能比其他權重更新的速度更快,即:
其中,wj:=wj+Δwj,η 表示學習率,t 表示目標正確分類結果,o 表示模型的輸出分類結果。
其他直觀的例子包括 KNN 算法和一些聚類算法上面,都會使用這種數據標准化的方法。
事實上,我能想到唯一不需要數據標准化的算法就應該是基於決策樹的算法了。我們來看一般的 CART 決策樹算法。在這里我們不深入的分析信息熵的含義,我們可以把這個決策簡單的想象成 is feature x_i >= some_val ?
。從直觀上來講,我們真的不需要來關心數據特征在哪個大小維度(比如,不同數量級,不同領域 —— 這些真的不關心)。
那么,在哪些算法中特征數據標准化是比較重要的呢?比如下面這些算法:
- 對於基於歐幾里得距離的 KNN 算法,如果你想要所有的數據對算法都有貢獻,那么必須進行標准化;
- k-means 算法;
- 邏輯回歸,支持向量機,感知器,神經網絡等,如果你正是在使用梯度下降(上升)來作為優化器,那么采用標准化會讓權重更快的收斂;
- 線性判別分析,PCA,核方法;
另外,我們還要考慮我們的數據是需要進行“標准化(standardize)”還是“歸一化(normalize)”(這里是縮放到 [0, 1] 范圍)。因為有些算法假設我們的數據是以 0 為中心分布的,那么這時候進行標准化還是歸一化就需要自己思考了。例如,如果我們對一個小型多層感知器(利用 tanh 激活函數)進行權重初始化,權重應該是 0 ,或者是以零為中心的小隨機數,這樣能更好的更新模型權重。作為一個經驗法則,我想說的是:如果你不確定對數據進行標准化還是歸一化,那么你就對數據進行標准化吧,至少它不會對數據和結果造成傷害。
標准化(Standardization)
數據的標准化是將數據按比例縮放,使之落入一個小的特定區間,標准化之后的數據可正可負,但是一般絕對值不會太大,一般是 z-score 規范化方法。
歸一化(Normalization)
主要是為了數據處理提出來的,把數據映射到 0~1 范圍之間處理,更加便捷快速,這應該歸到數字信號處理范疇之內。一般方法是最小-最大縮放方法。
關於最小-最大縮放處理
還有一種數據處理的方法是最小 - 最大縮放。在這種方法中,數據被縮放到一個固定的范圍 —— 通常是 0 到 1。與標准化相比,有限范圍的損失值計算最終將得到較小的標准偏差,這也可以抑制一些異常值的影響。
最小 - 最大縮放處理可以通過以下公式完成:
選擇 Z-score 標准化還是最小 - 最大縮放?
具體選擇哪一個數據處理方法沒有很明確的答案,它主要取決於具體的應用程序。
例如,在聚類分析中,為了比較基於特定距離度量的特征數據之間的相似性,數據標准化可能是一個特別重要的方式。另一個比較突出的例子就是主成分分析,我們通常采用標准化來做數據進行縮放。因為我們對最大化方差的方向感興趣。
如何使用 scikit-learn 來實現標准化和歸一化
當然,我們可以利用 Numpy 包來計算數據的 z-score,並使用前面提到的公式來進行標准化。但是,如果我們使用 Python 的開源機器學習庫 scikit-learn 中的預處理模塊來做,會更加便捷。
為了下面更好的討論,我們采用 UCI 機器學習庫中的 “Wine” 數據集來進行代碼編寫。
import pandas as pd import numpy as np df = pd.io.parsers.read_csv( 'https://raw.githubusercontent.com/rasbt/pattern_classification/master/data/wine_data.csv', header=None, usecols=[0,1,2] ) df.columns=['Class label', 'Alcohol', 'Malic acid'] df.head()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Class label | Alcohol | Malic acid |
---|---|---|
0 | 1 | 14.23 |
1 | 1 | 13.20 |
2 | 1 | 13.16 |
3 | 1 | 14.37 |
4 | 1 | 13.24 |
正如我們在上面的表格中所看到的,Alcohol 特征(百分比 / 體積)和 Malic acid(g/l)是在不同大小維度上面進行描述的,所有在比較或者組合這些特征之前,進行數據縮放是非常有必要的。
標准化和最小-最大縮放
from sklearn import preprocessing
std_scale = preprocessing.StandardScaler().fit(df[['Alcohol', 'Malic acid']]) df_std = std_scale.transform(df[['Alcohol', 'Malic acid']]) minmax_scale = preprocessing.MinMaxScaler().fit(df[['Alcohol', 'Malic acid']]) df_minmax = minmax_scale.transform(df[['Alcohol', 'Malic acid']]) print('Mean after standardization:\nAlcohol={:.2f}, Malic acid={:.2f}' .format(df_std[:,0].mean(), df_std[:,1].mean())) print('\nStandard deviation after standardization:\nAlcohol={:.2f}, Malic acid={:.2f}' .format(df_std[:,0].std(), df_std[:,1].std()))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
Mean after standardization:
Alcohol=0.00, Malic acid=0.00
Standard deviation after standardization:
Alcohol=1.00, Malic acid=1.00
print('Min-value after min-max scaling:\nAlcohol={:.2f}, Malic acid={:.2f}' .format(df_minmax[:,0].min(), df_minmax[:,1].min())) print('\nMax-value after min-max scaling:\nAlcohol={:.2f}, Malic acid={:.2f}' .format(df_minmax[:,0].max(), df_minmax[:,1].max()))
- 1
- 2
- 3
- 4
Min-value after min-max scaling:
Alcohol=0.00, Malic acid=0.00
Max-value after min-max scaling:
Alcohol=1.00, Malic acid=1.00
畫圖
%matplotlib inline
from matplotlib import pyplot as plt def plot(): plt.figure(figsize=(8,6)) plt.scatter(df['Alcohol'], df['Malic acid'], color='green', label='input scale', alpha=0.5) plt.scatter(df_std[:,0], df_std[:,1], color='red', label='Standardized ', alpha=0.3) plt.scatter(df_minmax[:,0], df_minmax[:,1], color='blue', label='min-max scaled [min=0, max=1]', alpha=0.3) plt.title('Alcohol and Malic Acid content of the wine dataset') plt.xlabel('Alcohol') plt.ylabel('Malic Acid') plt.legend(loc='upper left') plt.grid() plt.tight_layout() plot() plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
上面的圖包括所有三個不同比例的葡萄酒數據點:原始酒精含量數據(綠色),標准化之后的數據(紅色)和歸一化之后的數據(藍色)。在下面的圖中,我們將放大三個不同的坐標軸。
fig, ax = plt.subplots(3, figsize=(6,14)) for a,d,l in zip(range(len(ax)), (df[['Alcohol', 'Malic acid']].values, df_std, df_minmax), ('Input scale', 'Standardized', 'min-max scaled [min=0, max=1]') ): for i,c in zip(range(1,4), ('red', 'blue', 'green')): ax[a].scatter(d[df['Class label'].values == i, 0], d[df['Class label'].values == i, 1], alpha=0.5, color=c, label='Class %s' %i ) ax[a].set_title(l) ax[a].set_xlabel('Alcohol') ax[a].set_ylabel('Malic Acid') ax[a].legend(loc='upper left') ax[a].grid() plt.tight_layout() plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
自己動手,豐衣足食
當然,我們也可以手動編寫標准化方程和最小-最大縮放。但是,實際正真項目中,還是比較推薦使用 scikit-learn 。比如:
std_scale = preprocessing.StandardScaler().fit(X_train) X_train = std_scale.transform(X_train) X_test = std_scale.transform(X_test)
- 1
- 2
- 3
接下來,我們采用純 Python 代碼來實現這幾個指標,並且也會用 numpy 來進行計算加速。回想一下我們用的幾個參數指標:
純 python
# Standardization x = [1,4,5,6,6,2,3] mean = sum(x)/len(x) std_dev = (1/len(x) * sum([ (x_i - mean)**2 for x_i in x]))**0.5 z_scores = [(x_i - mean)/std_dev for x_i in x] # Min-Max scaling minmax = [(x_i - min(x)) / (max(x) - min(x)) for x_i in x]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Numpy
import numpy as np
# Standardization x_np = np.asarray(x) z_scores_np = (x_np - x_np.mean()) / x_np.std() # Min-Max scaling np_minmax = (x_np - x_np.min()) / (x_np.max() - x_np.min())
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
可視化
為了檢驗我們的代碼是否正常工作,我們通過可視化來進行查看。
from matplotlib import pyplot as plt
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(nrows=2, ncols=2, figsize=(10,5)) y_pos = [0 for i in range(len(x))] ax1.scatter(z_scores, y_pos, color='g') ax1.set_title('Python standardization', color='g') ax2.scatter(minmax, y_pos, color='g') ax2.set_title('Python Min-Max scaling', color='g') ax3.scatter(z_scores_np, y_pos, color='b') ax3.set_title('Python NumPy standardization', color='b') ax4.scatter(np_minmax, y_pos, color='b') ax4.set_title('Python NumPy Min-Max scaling', color='b') plt.tight_layout() for ax in (ax1, ax2, ax3, ax4): ax.get_yaxis().set_visible(False) ax.grid() plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
實戰:PCA 中是否進行數據標准化對分類任務的影響
在文章的前面,我們提到了在 PCA 中對數據進行標准化是至關重要的,因為它是分析不同特征之間的差異。現在,讓我們看看標准化是如何影響 PCA 對整個葡萄酒數據分類結果產生的影響。
接下來,我們主要通過這些步驟進行描述:
- 讀取數據集;
- 將原始數據集拆分為訓練集和測試集;
- 特征數據標准化;
- PCA 降維;
- 訓練朴素貝葉斯分類器;
- 利用標准化數據和不利用標准化數據分別對分類器進行評估;
讀取數據集
import pandas as pd df = pd.io.parsers.read_csv( 'https://raw.githubusercontent.com/rasbt/pattern_classification/master/data/wine_data.csv', header=None, )
- 1
- 2
- 3
- 4
- 5
- 6
將原始數據集拆分為訓練集和測試集
在這一步中,我們將數據隨機的分為一個訓練集和一個測試集,其中訓練集包含整個數據集的 70%,測試集包含整個數據集的 30%。
from sklearn.cross_validation import train_test_split X_wine = df.values[:,1:] y_wine = df.values[:,0] X_train, X_test, y_train, y_test = train_test_split(X_wine, y_wine, test_size=0.30, random_state=12345)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
特征數據標准化
from sklearn import preprocessing
std_scale = preprocessing.StandardScaler().fit(X_train) X_train_std = std_scale.transform(X_train) X_test_std = std_scale.transform(X_test)
- 1
- 2
- 3
- 4
- 5
PCA 降維
現在,我們對標准化的數據和非標准化的數據分別進行 PCA 操作,將數據集轉化為二維特征子空間。在一個真實的應用程序中,我們還會有一個交叉驗證的過程,以便找出一些過度擬合的信息。但是,在這里我們就不做這個過程了,因為我們不是要設計一個完美的分類器,我們在這里只是想要去比較標准化對分類結果的影響。
from sklearn.decomposition import PCA # on non-standardized data pca = PCA(n_components=2).fit(X_train) X_train = pca.transform(X_train) X_test = pca.transform(X_test) # om standardized data pca_std = PCA(n_components=2).fit(X_train_std) X_train_std = pca_std.transform(X_train_std) X_test_std = pca_std.transform(X_test_std)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
讓我們快速的查看一下我們的新特征。如下圖:
from matplotlib import pyplot as plt
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10,4)) for l,c,m in zip(range(1,4), ('blue', 'red', 'green'), ('^', 's', 'o')): ax1.scatter(X_train[y_train==l, 0], X_train[y_train==l, 1], color=c, label='class %s' %l, alpha=0.5, marker=m ) for l,c,m in zip(range(1,4), ('blue', 'red', 'green'), ('^', 's', 'o')): ax2.scatter(X_train_std[y_train==l, 0], X_train_std[y_train==l, 1], color=c, label='class %s' %l, alpha=0.5, marker=m ) ax1.set_title('Transformed NON-standardized training dataset after PCA') ax2.set_title('Transformed standardized training dataset after PCA') for ax in (ax1, ax2): ax.set_xlabel('1st principal component') ax.set_ylabel('2nd principal component') ax.legend(loc='upper right') ax.grid() plt.tight_layout() plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
訓練朴素貝葉斯分類器
接下來,我們使用一個朴素貝葉斯分類器來進行分類任務。也就是說,我們假設每一個特征都是獨立分布的。總而言之,這是一個簡單的分類器,但是具有很好的魯棒性。
貝葉斯規則:
其中:
- ω:表示分類標簽;
- P(ω | x):表示后驗概率;
- P(x | ω):表示先驗概率;
判別規則如下:
我不想在本文介紹很多的貝葉斯內容,如果你對這方面感興趣,可以自己上網看看材料,網上有很多這方面的資料。
from sklearn.naive_bayes import GaussianNB
# on non-standardized data gnb = GaussianNB() fit = gnb.fit(X_train, y_train) # on standardized data gnb_std = GaussianNB() fit_std = gnb_std.fit(X_train_std, y_train)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
利用標准化數據和不利用標准化數據分別對分類器進行評估
from sklearn import metrics pred_train = gnb.predict(X_train) print('\nPrediction accuracy for the training dataset') print('{:.2%}'.format(metrics.accuracy_score(y_train, pred_train))) pred_test = gnb.predict(X_test) print('\nPrediction accuracy for the test dataset') print('{:.2%}\n'.format(metrics.accuracy_score(y_test, pred_test)))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Prediction accuracy for the training dataset
81.45%
Prediction accuracy for the test dataset
64.81%
pred_train_std = gnb_std.predict(X_train_std)
print('\nPrediction accuracy for the training dataset') print('{:.2%}'.format(metrics.accuracy_score(y_train, pred_train_std))) pred_test_std = gnb_std.predict(X_test_std) print('\nPrediction accuracy for the test dataset') print('{:.2%}\n'.format(metrics.accuracy_score(y_test, pred_test_std)))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Prediction accuracy for the training dataset
96.77%
Prediction accuracy for the test dataset
98.15%
正如我們所看到的,在 PCA 之前進行標准化,確實對模型的正確率增加了不少。