Pipelines
背景
Pipelines直译过来就是管道,为什么要用呢?我认为就是在数据处理过程中,很多步骤都是重复或者类似的,比如特征选择处理、归一化、分类等等,pipeline可以帮助我们减少这些重复的内容,更加专注于选择组合而不用重复设计代码
接下来我们开始理解
Pipelines使用步骤
在sklearn中一个完整的Pipeline示例步骤如下:
(1)首先,对数据进行预处理,比如缺失值的处理;
(2)数据的标准化;
(3)降维;
(4)特征选择算法;
(5)分类或者预测或者聚类算法(估计器,estimator)。
实际上,调用pipeline的fit方法,是用前n-1个变换器处理特征,之后传递给最后的评估器(estimator)进行训练。pipeline会继承最后一个评估器(estimator)的所有方法,输出一个模型出来。
Pipelines初步演示
我们参考一个例子,看下pipeline在只做【数据标准化→不降维→使用SVM模型】的情况
我们用的是一个数字分类的数据集
# 导入库
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report
import matplotlib.pyplot as plt
# 导入数据
digits = datasets.load_digits()
X = digits.data
y = digits.target
# 数据可视化
plt.gray()
plt.matshow(digits.images[0]) # 显示图片
plt.show()
# 数据拆分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=33)
# 模型串联
pipeline = Pipeline([
('scaler', StandardScaler()), # 数据标准化
('svm', SVC()) # SVM分类器
])
# 模型训练
pipeline.fit(X_train, y_train)
# 预测
y_pred = pipeline.predict(X_test)
# 结果分析
accuracy = accuracy_score(y_test, y_pred)
print("预测准确率:", accuracy)
report = classification_report(y_test, y_pred)
print("分类报告:\n", report)
输出结果如下
我们看到了Pipeline实现了【数据标准化→不降维→使用SVM模型】的模型训练,并且拿训练完的模型去预测,获得一定的效果。
并且,我们可以进一步看到Pipeline会按顺序一步步执行管道里的内容
理解Pipeline源码
我们来看看源码,主要分为
- Pipeline类
- FeautreUnion类
piepline偏向于流程控制,FeautreUnion倾向于并发计算
我们先理解下Pipeline类在干什么
一、Pipeline类
Pipeline主要方法
Pipline的方法都是执行各个学习器中对应的方法,如果该学习器没有该方法,就会报错。
假设该Pipeline共有n个学习器: 首先假设:
pl=Pipeline([
('Normal',Normalizer()),
('PCA',PCA()),
('SVC',SVC())
])
pl.transform:依次执行各个学习器的transform方法。
pl.fit:依次对前n-1个学习器执行fit和transform方法,第n个学习器(最后一个学习器)执行fit方法。
pl.predict:执行第n个学习器的predict方法。
pl.score:执行第n个学习器的score方法。
pl.set_params:设置第n个学习器的参数。
pl.get_param:获取第n个学习器的参数。
Pipelines原理与源码解析
Pipeline of transforms with a final estimator.
Sequentially apply a list of transforms and a final estimator.
Intermediate steps of the pipeline must be 'transforms', that is, they
must implement fit and transform methods.
The final estimator only needs to implement fit.
The transformers in the pipeline can be cached using ``memory`` argument.
The purpose of the pipeline is to assemble several steps that can be
cross-validated together while setting different parameters.
For this, it enables setting parameters of the various steps using their
names and the parameter name separated by a '__', as in the example below.
A step's estimator may be replaced entirely by setting the parameter
with its name to another estimator, or a transformer removed by setting
to None.
pipeline是以一个元组列表,每个元素为(key, value),其中 key 是你给这个步骤起的名字, value 是一个评估器对象,每一个可以称为一个步骤。
pipeline管道按顺序应用转换列表每个元素中对转换器,给出最后的末尾转换器。管道中前n-1的对象都会经历拟合(fit)和转换(transform),最后一个转换器只需要进行拟合操作
可以看到管道的最终目的是为了给出一个转换器,也就是我们所说的模型。通常我们都会把训练数据塞进管道,让训练数据经历上述的转换,最后得到一个训练完成的模型。
首先来看Pipeline的初始化方法:
sklearn.pipeline.Pipeline(steps, memory=None, verbose=False)
steps: 步骤,使用(key, value)列表来构建,其中 key 是你给这个步骤起的名字(可以随便起), value 是一个对象(也是评估器,Estimators)。
memory: 内存参数,当需要保存Pipeline中间的"transformer"时,才需要用到memory参数,默认None。
举个例子,当我们使用pipeline时,会这么用
pl_svm=Pipeline([
# 要给每个步骤起名,然后再调用模型
('Normal',Normalizer()),
('PCA',PCA()),
('SVC',SVC())
])
这里steps会存储我们例子中元组列表的内容
首先看Pipeline类的初始化方法
def __init__(self, steps, *, memory=None, verbose=False):
self.steps = steps
self.memory = memory
self.verbose = verbose
self._validate_steps()
接下来两个方法,
get_params:一个是用来获取评估器的参数的
set_params:一个是用来给评估器的参数赋值
def get_params(self, deep=True):
"""Get parameters for this estimator.
Parameters
----------
deep : bool, default=True
If True, will return the parameters for this estimator and
contained subobjects that are estimators.
Returns
-------
params : mapping of string to any
Parameter names mapped to their values.
"""
return self._get_params('steps', deep=deep)
def set_params(self, **kwargs):
"""Set the parameters of this estimator.
Valid parameter keys can be listed with ``get_params()``.
Returns
-------
self
"""
self._set_params('steps', **kwargs)
return self
下面是验证函数,主要干了以下几件事情
- 拆分列表里的名字与评估器,检查是否合法
- 规定前n-1个是转换器,要进行fit/ transform方法,第n个(最后一个)是评估器,只能 fit 方法
- 遍历每个转换器,检查它们是否具有fit或fit_transform和transform方法。如果没有这些方法,则会引发TypeError错误
def _validate_steps(self):
names, estimators = zip(*self.steps)
# validate names
self._validate_names(names