在训练机器学习模型时,为了均衡各类数值特征对于模型的影响程度,加快模型的收敛速度,通常要对数值特征进行缩放、归一化、标准化等操作,下面介绍一下scikit-learn工具包中常用的几种数值特征处理方法。
1、normalize归一化
normalize归一化可以沿着矩阵任意轴进行,如果选择l2归一化,axis=0,就是将每个元素除以元素所在列的l2范数。
normalize函数的参数列表如下:
参数 | 参数描述 |
X | 需要归一化的矩阵 |
norm | 使用哪种归一化方式,L1、L2、Max等 |
axis | 沿着哪个轴方向归一化 |
copy | 是否生成新的拷贝 |
L1归一化表示如下:
L2归一化表示如下:
示例:
import numpy as np
from sklearn import preprocessing
data = np.random.rand(10, 10) * 100
data
array([[78.16323419, 7.49350354, 49.69650393, 56.81082161, 9.99146614,
2.80772256, 37.12871596, 86.40788511, 61.56191883, 97.05294143],
[22.65129438, 40.36108532, 38.5384142 , 11.56585979, 97.79232686,
26.06302914, 7.28807511, 3.81352654, 95.32217769, 43.5822619 ],
[45.61073488, 12.20337497, 58.19307024, 23.50206549, 11.08969354,
41.7826594 , 34.8087407 , 22.51792342, 54.86843145, 38.00780105],
[63.9203881 , 41.93494232, 2.09771219, 70.81039721, 91.85564594,
21.01376502, 2.074977 , 72.97881035, 24.9028654 , 32.63379196],
[26.52935789, 79.57167735, 32.20415608, 97.1739589 , 89.64363271,
8.97169554, 75.48200095, 62.38959941, 63.90995938, 48.05517911],
[57.37290063, 13.55509753, 73.85543964, 5.42533775, 96.15555984,
98.06572975, 70.02672512, 75.30871379, 11.49530931, 15.25904797],
[13.42384882, 0.31386799, 94.53266703, 99.93228266, 8.68249191,
11.8979742 , 94.39501059, 70.37929649, 8.81076432, 84.89065261],
[30.09274618, 49.61306855, 35.55417193, 25.45480588, 57.72702024,
79.40738935, 95.53680102, 69.30517821, 33.17145358, 15.15072447],
[12.38611125, 55.24441364, 68.80182229, 39.81283062, 26.19161967,
28.40487513, 90.34284135, 96.8393213 , 5.99189635, 36.52551797],
[32.19270842, 65.18271248, 54.75696407, 14.08094661, 53.99588207,
57.05199712, 74.85506107, 71.29431704, 5.66216966, 32.41486064]])
沿着axis=0轴进行L2归一化:
经过如下归一化之后,每列元素的平方和为1。
# 经过如下归一化之后,每列元素的平方和为1
data_norm_l2 = preprocessing.normalize(data, norm="l2", axis=0)
np.sum(data_norm_l2 ** 2, axis=0)
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
沿着axis=1轴进行L1归一化:
经过如下归一化之后,每行元素的绝对值之和为1。
# 经过如下归一化之后,每行元素的绝对值之和为1
data_norm_l1 = preprocessing.normalize(data, norm="l1", axis=1)
np.sum(np.abs(data_norm_l1), axis=1)
2、scale缩放
scale函数的参数列表如下:
参数 | 参数描述 |
X | 需要缩放的矩阵 |
axis | 沿着哪个轴方向缩放 |
with_mean | if True 则缩放后均值为0 |
with_std | if True 则缩放后方差为1 |
示例:
经过如下操作之后,每列元素的方差为1,均值为0。
# 经过如下操作之后,每列元素的方差为1,均值为0
data_scale = preprocessing.scale(data, axis=0, with_std=True)
np.mean(data_scale, axis=0), np.std(data_scale, axis=0), np.mean(data, axis=0), np.std(data, axis=0)
array([-4.44089210e-17, 3.05311332e-16, 1.77635684e-16, 8.32667268e-17,
-2.22044605e-16, 1.11022302e-16, 1.11022302e-17, 4.44089210e-17,
2.46330734e-17, 2.77555756e-16]),
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]),
array([51.49281565, 59.4787154 , 47.53953557, 55.68430399, 60.3389103 ,
62.45368944, 34.00746164, 45.45096693, 40.67619051, 60.90908866]),
array([31.16506455, 25.44825364, 27.68806885, 25.31935865, 27.39144467,
29.47623919, 22.07015185, 30.23023828, 24.1993479 , 30.61930749]))
3、min-max缩放
min-max缩放在机器学习应用中常常作用于各个属性列,所以在scikit-learn中,min-max缩放时针对属性列进行的,也就是axis=0的轴方向,经过缩放后的特征数值范围在[0,1]之间取值。min-max缩放公式如下:
示例:
经过如下转换之后,每列元素的数值在[0, 1]之间
# 经过如下转换之后,每列元素的数值在[0, 1]之间
min_max_scaler = preprocessing.MinMaxScaler()
data_min_max = min_max_scaler.fit_transform(data)
np.min(data_min_max, axis=0), np.max(data_min_max, axis=0)
(array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]))
4、standard标准化
standard标准化在机器学习应用中常常作用于各个属性列,所以在scikit-learn中,standard标准化时针对属性列进行的,也就是axis=0的轴方向,经过标准化后的特征数值均值为0,方差为1。standard标准化公式如下:
示例:
经过如下操作之后,每列元素的均值为0,方差为1。
# 经过如下操作之后,每列元素的均值为0,方差为1
standard_scaler = preprocessing.StandardScaler()
data_stand = standard_scaler.fit_transform(data)
np.mean(data_stand, axis=0), np.std(data_stand, axis=0)
(array([ 5.55111512e-17, -3.10862447e-16, -1.27675648e-16, 1.99840144e-16,
-1.24900090e-17, -1.88737914e-16, 2.27595720e-16, 4.44089210e-16,
1.77635684e-16, -7.21644966e-17]),
array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.]))
注意:虽然经过StandardScaler处理之后的数据均值为0,方差为1,但是此时的数据并不一定满足正态分布,StandardScaler并不会改变数据原始的分布。也就是说均值为0,方差为1是标准正态分布的必要不充分条件。如果要将数据分布转换为正态分布,可以使用QuantifleTransformer。
%matplotlib inline
from matplotlib import pyplot as plt
from sklearn import preprocessing
from sklearn.preprocessing import QuantileTransformer
X = np.random.rand(1000) * 100
# StandardScaler先将数据减去均值进行去中心化,然后将方差缩放到1
# StandardScaler并不会改变数据的分布(这点从结果中绘制的柱状图可以看出)
# 注意均值为0,方差为1的不一定就是标准正态分布,甚至都不是正态分布(如下图2所示)
X_scaled = preprocessing.StandardScaler().fit_transform(X.reshape(-1, 1))
X_quantile = QuantileTransformer(output_distribution="normal").fit_transform(X.reshape(-1, 1))
fig, axes = plt.subplots(1, 3)
axes[0].hist(X, bins=30)
axes[0].set_title("original data")
axes[1].hist(X_scaled, bins=30)
axes[1].set_title("scaled data")
axes[2].hist(X_quantile, bins=30)
axes[2].set_title("quantiled data")
print(np.mean(X), np.std(X), np.min(X), np.max(X))
print(np.mean(X_scaled), np.std(X_scaled), np.min(X_scaled), np.max(X_scaled))
print(np.mean(X_quantile), np.std(X_quantile), np.min(X_quantile), np.max(X_quantile))
48.07267719016452 28.86624508675929 0.06382312779740218 99.6132733637035
-6.394884621840901e-17 1.0 -1.6631485639394226 1.7854970751696497
9.800515954339061e-14 1.0198553257234073 -5.199337582605575 5.19933758270342