機器學習中,數據歸一化是非常重要,如果不進行數據歸一化,可能會導致模型壞掉或者訓練出一個奇怪的模型。
爲什麼要進行數據歸一化
現在有一個訓練數據集,包含兩個樣本,內容如下:
腫瘤大小(cm) | 發現時間(day) | |
---|---|---|
樣本1 | 1 | 200 |
樣本2 | 5 | 100 |
以 k-近鄰算法爲例,“發現時間”的數值比“腫瘤大小”的數值大很多,樣本間的距離被“發現時間”主導,訓練出來的模型主要由“發現時間”影響,甚至“腫瘤大小”的影響可忽略不計。
解決方法就是將是數據映射到同一尺度,這就是數據歸一化。
數據歸一化的兩個常用方式爲:最值歸一化和均值方差歸一化。
最值歸一化(normalization)
最值歸一化就是將數據映射到 0~1 之間,適用於數據分佈有明顯邊界的情況。將樣本的特徵值減去該特徵的最小值,再除以該特徵的取值區間,對應的數學公式爲:
$$ x_{scale} = \frac{x-x_{min}}{x_{max}-x_{min}} $$
使用 np.random
生成一個 50*2 的二維整形數組,並轉換成浮點型:
import numpy as np
X = np.random.randint(0, 100, size=(50, 2))
X = np.array(X, dtype=float)
對於第一列數據,$x_{min}$ = np.min(X[:, 0])
,$x_{max}$ = np.max(X[:, 0])
:
X[:, 0] = (X[:, 0] - np.min(X[:, 0])) / (np.max(X[:, 0]) - np.min(X[:, 0]))
第二列數據同理:
X[:, 1] = (X[:, 1] - np.min(X[:, 1])) / (np.max(X[:, 1]) - np.min(X[:, 1]))
此時樣本的所有特徵值都在 0~1 之間。
均值方差歸一化(standardization)
均值方差歸一化就是把所有數據歸一到均值爲0、方差爲1的分佈中。對於數據分佈有無明顯邊界都適用。數學公式爲:
$$ x_{scale} = \frac{x-x_{mean}}{s} $$
$x_{mean}$:特徵均值,$s$:特徵方差。
同樣使用 np.random
生成一個 50*2 的二維整形數組,並轉換成浮點型:
X2 = np.random.randint(0, 100, size=(50, 2))
X2 = np.array(X2, dtype=float)
對於第一列數據,$x_{mean}$ = np.mean(X2[:, 0])
,$s$ = np.std(X2[:, 0])
:
X2[:, 0] = (X2[:, 0] - np.mean(X2[:, 0])) / np.std(X2[:, 0])
第二列數據同理:
X2[:, 1] = (X2[:, 1] - np.mean(X2[:, 1])) / np.std(X2[:, 1])
可以查看 X2 各列的均值非常接近0,方差非常接近1:
# np.mean(X2[:, 0])
-4.440892098500626e-18
# np.mean(X2[:, 1])
-1.2878587085651815e-16
# np.std(X2[:, 0])
0.9999999999999999
# np.std(X2[:, 1])
0.9999999999999999
對測試數據集進行歸一化處理
前面都是在對訓練數據集進行歸一化處理,而對測試數據集的歸一化處理有所不同。由於測試數據是在模擬真實環境,而在真實環境中很難拿到所有的測試數據的均值和方差,此時將測試數據集也進行上面的操作是錯誤的,正確的方法是利用訓練數據集歸一化的數據。
如測試數據集的最值歸一化處理爲:
$$ test_{scale} = \frac{test-min_{train}}{max_{train}-min_{train}} $$
測試數據集的均值方差歸一化處理爲:
$$ test_{scale} = \frac{test-mean_{train}}{s_{train}} $$
以均值方差歸一化處理爲例,Scikit Learn 中封裝了 StandardScaler 類用於訓練數據集和測試數據集的歸一化處理。
以鳶尾花的數據爲例:
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
iris = datasets.load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
StandardScaler 類位於 preprocessing 模塊中:
from sklearn.preprocessing import StandardScaler
standardScaler = StandardScaler()
將訓練數據傳入 fit()
方法中,該方法會保存訓練數據的方差和均值,並返回 StandardScaler 實例本身:
standardScaler.fit(X_train)
其中 mean_
、scale_
屬性保存了均值和方差:
# standardScaler.mean_
array([5.83416667, 3.08666667, 3.70833333, 1.17 ])
# standardScaler.scale_
array([0.81019502, 0.44327067, 1.76401924, 0.75317107])
接着可以向 transform()
方法中傳入訓練數據和測試數據獲取歸一化處理後的數據:
X_train = standardScaler.transform(X_train)
X_test = standardScaler.transform(X_test)