预处理指对数据进行清洗、转换等处理,使数据更适合机器学习的工具。Scikit 提供了一些预处理的方法,分别是标准化、非线性转换、归一化、二值化、分类特征编码、缺失值插补、生成多项式特征等,如图 1 所示。
为什么要对数据进行标准化处理呢?在表 2 中可以看到收入这一列的数字特别大,而年龄这一列数字相比之下就特别小。因此,在某些机器学习过程中,收入特征就会表现得比较“抢眼”,而影响最终的模型效果。
所以我们要将这种差距去除,让大家在同一起跑线上。因此,将该表转换成另一种形式,如表 3 所示,转换方法为 scale 方法。
在 Scikit 中提供了 scale 方法对数据进行标准化,处理流程如下:
In [1]: from sklearn import preprocessing ...: import numpy as np
下面我们都将以上述表格的数据为例:
In [2]: X= np.array([[7688,32],
...: [5788,29],
...: [4600,25],
...: [8900,35]])
In [3]: X_scaled = preprocessing.scale(X)
In [4]: X_scaled
Out[4]:
array([[ 0.56796035, 0.47301616],
[-0.57518019, -0.33786869],
[-1.28994385, -1.41904849],
[ 1.29716369, 1.28390102]])
In [5]: X_scaled.mean()
Out[5]: -5.551115123125783e-17
In [6]: X_scaled.std()
Out[6]: 1.0
非线性转换类似于标准化处理,是将数据映射到 [0,1] 的均匀分布上。非线性转换将每个特征转换到相同的范围内或者分布内,使异常的数据变得平滑,受异常值的影响就会变小,但是这在一定程度上改变了特征内部和特征之间的距离和
In [1]: from sklearn import preprocessing
...: import numpy as np
In [2]: quantile= preprocessing.QuantileTransformer(random_state=0)
In [3]: X= np.array([[7688,32],
...: [5788,29],
...: [4600,25],
...: [8900,35]])
In [4]: X_trans=quantile.fit_transform(X)
所有的数值都被转换为[0,1]区间内的数字
In [5]: X_trans
Out[5]:
array([[6.66666667e-01, 6.66666667e-01],
[3.33333333e-01, 3.33333333e-01],
[9.99999998e-08, 9.99999998e-08],
[9.99999900e-01, 9.99999900e-01]])
归一化的作用是缩放单个样本,使其具有单位范数。归一化有两种方式,分别是范数“L1”和范数“L2”。
In [1]: from sklearn import preprocessing
...: import numpy as np
In [2]: X= np.array([[7688,32],
...: [5788,29],
...: [4600,25],
...: [8900,35]])
In [3]: X_norm = preprocessing.normalize(X, norm='l2')
要注意归一化针对的是每一行
In [4]: X_norm
Out[4]:
array([[0.99999134, 0.00416229],
[0.99998745, 0.0050103 ],
[0.99998523, 0.0054347 ],
[0.99999227, 0.00393255]])
二值化的作用是将数值型的特征值转换为布尔型。
In [1]: from sklearn import preprocessing
...: import numpy as np
In [2]: X= np.array([[7688,32],
...: [5788,29],
...: [4600,25],
...: [8900,35]])
可以看到所有大于 100 的数字都被编码为 1,所有小于 100 的数字都被编码为0。
In [3]: binary = preprocessing.Binarizer(threshold=100)
In [4]: binary
Out[4]:
array([[1, 0],
[1, 0],
[1, 0],
[1, 0]])
在机器学习过程中,我们经常会遇到字符串形式的特征。这时就需要将这些字符串类型的特征转换为数值型的特征。例如,“老师”“学生”“主任”,我们需要将这些字符串转换为整数,比如将“老师”转换为 0,将“学生”转换为 1,将“主任”转换为 2。
这样的转换会提高机器学习的计算效率,但是这样连续数值的输入会被分类器认为类别之间是有序的,然而实际上“老师”“学生”“主任”之间是无序的,而且没有大小的区别,这时就需要将这些数值进一步转换。
对此的解决思路就是将某个特征的 n 个可能转换为 n 个特征,转换后的特征是 0/1 二值数据。
In [1]: from sklearn import preprocessing
...: import numpy as np
In [2]: enc = preprocessing.OneHotEncoder()
3) 导入模拟的数据
In [3]: X= np.array([[0,4],
...: [1,5],
...: [2,6],
...: [3,7]])
In [4]: enc.fit(X)
Out[4]:
OneHotEncoder(categorical_features='all', dtype=<class 'numpy.float64'>,
handle_unknown='error', n_values='auto', sparse=True)
从 In[3] 中可以看到第一列有 4 种情况,分别为 0、1、2、3,所以第一列会被分为4列。
0表示为[1,0,0,0], 1表示为[0,1,0,0], 2表示为[0,0,1,0], 3表示为[0,0,0,1]。
同样的道理,第二列也被分为 4 列:4 表示为 [1,0,0,0],5 表示为 [0,1,0,0],6 表示为 [0,0,1,0],7 表示为 [0,0,0,1]。
In [5]: enc.transform(X).toarray()
Out[5]:
array([[1., 0., 0., 0., 1., 0., 0., 0.],
[0., 1., 0., 0., 0., 1., 0., 0.],
[0., 0., 1., 0., 0., 0., 1., 0.],
[0., 0., 0., 1., 0., 0., 0., 1.]])
直接获得的数据不一定是完整的数据,里面可能存在缺失的情况。需要对这些有缺失的数据做一些处理,以补全它们。
In [1]: import numpy as np
...: from sklearn.preprocessing import Imputer
In [2]: imp = Imputer(missing_values='NaN', strategy='mean', axis=0)
In [3]: X= np.array([[0,4],
...: [np.NaN,5],
...: [np.NaN,6],
...: [3,7]])
In [4]: imp.fit(X)
Out[4]: Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0)
在 axis=0 的情况下,第一列有两个非空值,他们的均值是 1.5,所以空值全部都用 1.5 填充。
In [5]: imp.transform(X)
Out[5]:
array([[0. , 4. ],
[1.5, 5. ],
[1.5, 6. ],
[3. , 7. ]])
在机器学习中,有些时候数据集的特征很少,这时就需要自己根据已有的特征构造一些新的特征。
In [1]: import numpy as np
...: from sklearn.preprocessing import PolynomialFeatures
In [2]: poly = PolynomialFeatures(2)
In [3]: X= np.array([[0,4],
...: [1,5],
...: [2,6],
...: [3,7]])
4) 训练多项式特征对象
In [4]: poly.fit(X)
Out[4]: PolynomialFeatures(degree=2, include_bias=True, interaction_only=False)
X 的特征已经从 (x1,x2) 转换为如下所示:
In [5]: poly.transform(X)
Out[5]:
array([[ 1., 0., 4., 0., 0., 16.],
[ 1., 1., 5., 1., 5., 25.],
[ 1., 2., 6., 4., 12., 36.],
[ 1., 3., 7., 9., 21., 49.]])