数据预处理或称特征预处理,在机器学习中非常重要。
本文聊聊几种常用的特征预处理方法。
标准化
这种方法即为对于某个特征来说,减去其均值并且除以相应的方差。这种方法适用于服从或近似服从标准正态分布(均值为0,方差为1)的数据。之所以要这么做,是为了防止某个变量的方差的阶跟其余变量的方差的阶不在一个数量级上,进而方差学习算法无法从其余变量学到本该学到的知识。
基于scikit-learn的示例如下:
from sklearn import preprocessing
import numpy as np
X = np.array([[ 1., -1., 2.],
... [ 2., 0., 0.],
... [ 0., 1., -1.]])
X_scaled = preprocessing.scale(X)
X_scaled
array([[ 0. ..., -1.22..., 1.33...],
[ 1.22..., 0. ..., -0.26...],
[-1.22..., 1.22..., -1.06...]])
经过变换后的数据均值为0,方差为1。示例如下:
X_scaled.mean(axis=0)
array([ 0., 0., 0.])
X_scaled.std(axis=0)
array([ 1., 1., 1.])
scikit-learn也提供了在训练集中保存均值和方差的功能,这样可以方便在测试集中使用已保存的均值和方差。示例如下,在训练集上:
scaler = preprocessing.StandardScaler().fit(X)
scalerStandardScaler(copy=True, with_mean=True, with_std=True)
scaler.mean_
array([ 1. ..., 0. ..., 0.33...])
scaler.scale_
array([ 0.81..., 0.81..., 1.24...])
scaler.transform(X)
array([[ 0. ..., -1.22..., 1.33...],
[ 1.22..., 0. ..., -0.26...],
[-1.22..., 1.22..., -1.06...]])
在测试集上:
scaler.transform([[-1., 1., 0.]])
array([[-2.44..., 1.22..., -0.26...]])
这种方法还包括了将数据缩放到某个范围之内。通常是缩放为0到1,或者按照使得绝对值的最大值为1的方式来缩放。这种方法主要是为了考虑某个特征具有比较小的标准差的情形,并且保持数据的稀疏性。
缩放为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)
X_train_minmax
array([[ 0.5, 0., 1. ],
[ 1., 0.5, 0.33333333],
[ 0., 1., 0. ]])
这种方法在测试集上的效果如下:
X_test = np.array([[ -3., -1., 4.]])
X_test_minmax = min_max_scaler.transform(X_test)
X_test_minmax
array([[-1.5, 0., 1.66666667]])
如果给定最大值和最小值,则
X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
X_scaled = X_std / (max - min) + min
还有一种方法是对于每个特征直接除以绝对值最大的值或该值的绝对值。示例如下:
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)
X_train_maxabs # doctest +NORMALIZE_WHITESPACE^
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)
X_test_maxabs
array([[-1.5, -1. , 2. ]])
max_abs_scaler.scale_
array([ 2., 1., 2.])
2. 归一化
这种方法可以使得单个样本具有单位范数。这种方法适用于需要衡量两个样本之间相似性并且利用二阶范数的情形,在文本分类中比较常用。示例如下:
X = [[ 1., -1., 2.],
... [ 2., 0., 0.],
... [ 0., 1., -1.]]
X_normalized = preprocessing.normalize(X, norm='l2')
X_normalized
array([[ 0.40..., -0.40..., 0.81...],
[ 1. ..., 0. ..., 0. ...],
[ 0. ..., 0.70..., -0.70...]])
normalizer = preprocessing.Normalizer().fit(X) # fit does nothing
normalizerNormalizer(copy=True, norm='l2')
normalizer.transform(X)
array([[ 0.40..., -0.40..., 0.81...],
[ 1. ..., 0. ..., 0. ...],
[ 0. ..., 0.70..., -0.70...]])
normalizer.transform([[-1., 1., 0.]])
array([[-0.70..., 0.70..., 0. ...]])
3. 二值化
这种方法比较适用于概率推理的场景。示例如下:
X = [[ 1., -1., 2.],
... [ 2., 0., 0.],
... [ 0., 1., -1.]]
binarizer = preprocessing.Binarizer().fit(X) # fit does nothing
binarizerBinarizer(copy=True, threshold=0.0)
binarizer.transform(X)
array([[ 1., 0., 1.],
[ 1., 0., 0.],
[ 0., 1., 0.]])
也可以设定二值化的阈值,示例如下:
binarizer = preprocessing.Binarizer(threshold=1.1)
binarizer.transform(X)
array([[ 0., 0., 1.],
[ 1., 0., 0.],
[ 0., 0., 0.]])
4 编码类别特征
比如给定特征集合 ["male", "female"]
, ["from Europe", "from US", "from Asia"]
,["uses Firefox", "uses Chrome", "uses Safari", "uses Internet Explorer"]则
["male", "from US", "uses Internet Explorer"]可以表示为
[0, 1, 3]
而["female", "from Asia", "uses Chrome"] 可以表示为
[1, 2, 1]
.。
实际中这些编码不能直接用作特征,不过可以通过one-hot 编码来解决这个问题。
示例如下:
enc = preprocessing.OneHotEncoder()
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)
enc.transform([[0, 1, 3]]).toarray()
array([[ 1., 0., 0., 1., 0., 0., 0., 0., 1.]])
5 缺失值
通常可以利用均值,中位数或者出现次数最多的数来代替缺失值。
示例如下:
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. ]]
另外,特别提一下,对于缺失值,也可以利用矩阵补全来解决。
6 生成多项式特征
示例如下:
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.]])
特征 X 由 转变为 。
上面是产生非线性特征,类似的,也可以产生交互性特征。示例如下:
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.]])
特征 X 由 转变为 。
7 自定义转换
示例如下:
import numpy as np
from sklearn.preprocessing import FunctionTransformer
transformer = FunctionTransformer(np.log1p)
X = np.array([[0, 1], [2, 3]])
transformer.transform(X)
array([[ 0. ,0.69314718],
[ 1.09861229,1.38629436]])
参考资料:
http://scikit-learn.org/stable/modules/preprocessing.html
http://statweb.stanford.edu/~candes/papers/MatrixCompletion.pdf
http://web.stanford.edu/~hastie/TALKS/SVD_hastie.pdf
http://pages.cs.wisc.edu/~brecht/papers/09.Recht.ImprovedMC.pdf
https://people.orie.cornell.edu/miketodd/orie6327/lec28.pdf
http://www.jmlr.org/proceedings/papers/v35/hardt14b.pdf