using-pipelines-for-multiple-preprocessing-steps

用管线命令处理多个步骤

管线命令不经常用,但是很有用。它们可以把多个步骤组合成一个对象执行。这样可以更方便灵活地调节和控制整个模型的配置,而不只是一个一个步骤调节。

Getting ready

这是我们把多个数据处理步骤组合成一个对象的第一部分。在scikit-learn里称为pipeline。这里我们首先通过计算处理缺失值;然后将数据集调整为均值为0,标准差为1的标准形。

让我们创建一个有缺失值的数据集,然后再演示pipeline的用法:

In [1]:
from sklearn import datasets
import numpy as np
mat = datasets.make_spd_matrix(10)
masking_array = np.random.binomial(1, .1, mat.shape).astype(bool)
mat[masking_array] = np.nan
mat[:4, :4]
Out[1]:
array([[ 1.05419595,  1.42287309,  0.02368264, -0.8505244 ],
       [ 1.42287309,  5.09704588,         nan, -2.46408728],
       [ 0.02368264,  0.03614203,  0.63317494,  0.09792298],
       [-0.8505244 , -2.46408728,  0.09792298,  2.04110849]])

How to do it...

如果不用管线命令,我们可能会这样实现:

In [2]:
from sklearn import preprocessing
impute = preprocessing.Imputer()
scaler = preprocessing.StandardScaler()
mat_imputed = impute.fit_transform(mat)
mat_imputed[:4, :4]
Out[2]:
array([[ 1.05419595,  1.42287309,  0.02368264, -0.8505244 ],
       [ 1.42287309,  5.09704588,  0.09560571, -2.46408728],
       [ 0.02368264,  0.03614203,  0.63317494,  0.09792298],
       [-0.8505244 , -2.46408728,  0.09792298,  2.04110849]])
In [5]:
mat_imp_and_scaled = scaler.fit_transform(mat_imputed)
mat_imp_and_scaled[:4, :4]
Out[5]:
array([[  1.09907483e+00,   2.62635324e-01,  -3.88958755e-01,
         -4.80451718e-01],
       [  1.63825210e+00,   2.01707858e+00,  -7.50508486e-17,
         -1.80311396e+00],
       [ -4.08014393e-01,  -3.99538476e-01,   2.90716556e+00,
          2.97005140e-01],
       [ -1.68651124e+00,  -1.59341549e+00,   1.25317595e-02,
          1.88986410e+00]])

现在我们用pipeline来演示:

In [6]:
from sklearn import pipeline
pipe = pipeline.Pipeline([('impute', impute), ('scaler', scaler)])

我们看看pipe的内容。和前面介绍一致,管线命令定义了处理步骤:

In [7]:
pipe
Out[7]:
Pipeline(steps=[('impute', Imputer(axis=0, copy=True, missing_values='NaN', strategy='mean', verbose=0)), ('scaler', StandardScaler(copy=True, with_mean=True, with_std=True))])

然后在调用pipefit_transform方法,就可以把多个步骤组合成一个对象了:

In [10]:
new_mat = pipe.fit_transform(mat)
new_mat[:4, :4]
Out[10]:
array([[  1.09907483e+00,   2.62635324e-01,  -3.88958755e-01,
         -4.80451718e-01],
       [  1.63825210e+00,   2.01707858e+00,  -7.50508486e-17,
         -1.80311396e+00],
       [ -4.08014393e-01,  -3.99538476e-01,   2.90716556e+00,
          2.97005140e-01],
       [ -1.68651124e+00,  -1.59341549e+00,   1.25317595e-02,
          1.88986410e+00]])

可以用Numpy验证一下结果:

In [11]:
np.array_equal(new_mat, mat_imp_and_scaled)
Out[11]:
True

完全正确!本书后面的主题中,我们会进一步展示管线命令的威力。不仅可以用于预处理步骤中,在降维、算法拟合中也可以很方便的使用。

How it works...

前面曾经提到过,每个scikit-learn的算法接口都类似。pipeline最重要的函数也不外乎下面三个:

  • fit
  • transform
  • fit_transform

具体来说,如果管线命令有N个对象,前N-1个对象必须实现fittransform,第N个对象至少实现fit。否则就会出现错误。

如果这些条件满足,管线命令就会运行,但是不一定每个方法都可以。例如,pipe有个inverse_transform方法就是这样。因为由于计算步骤没有inverse_transform方法,一运行就有错误:

In [12]:
pipe.inverse_transform(new_mat)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-12-62edd2667cae> in <module>()
----> 1 pipe.inverse_transform(new_mat)

d:\programfiles\Miniconda3\lib\site-packages\sklearn\utils\metaestimators.py in <lambda>(*args, **kwargs)
     35             self.get_attribute(obj)
     36         # lambda, but not partial, allows help() to work with update_wrapper
---> 37         out = lambda *args, **kwargs: self.fn(obj, *args, **kwargs)
     38         # update the docstring of the returned function
     39         update_wrapper(out, self.fn)

d:\programfiles\Miniconda3\lib\site-packages\sklearn\pipeline.py in inverse_transform(self, X)
    265         Xt = X
    266         for name, step in self.steps[::-1]:
--> 267             Xt = step.inverse_transform(Xt)
    268         return Xt
    269 

AttributeError: 'Imputer' object has no attribute 'inverse_transform'

但是,scalar对象可以正常运行:

In [13]:
scaler.inverse_transform(new_mat)[:4, :4]
Out[13]:
array([[ 1.05419595,  1.42287309,  0.02368264, -0.8505244 ],
       [ 1.42287309,  5.09704588,  0.09560571, -2.46408728],
       [ 0.02368264,  0.03614203,  0.63317494,  0.09792298],
       [-0.8505244 , -2.46408728,  0.09792298,  2.04110849]])

只要把管线命令设置好,它就会如愿运行。它就是一组for循环,对每个步骤执行fittransform,然后把结果传递到下一个变换操作中。

使用管线命令的理由主要有两点:

  • 首先是方便。代码会简洁一些,不需要重复调用fittransform
  • 其次,也是更重要的作用,就是使用交叉验证。模型可以变得很复杂。如果管线命令中的一个步骤调整了参数,那么它们必然需要重新测试;测试一个步骤参数的代码管理成本是很低的。但是,如果测试5个步骤的全部参数会变都很复杂。管线命令可以缓解这些负担。