为什么需要Pipeline?
在日常机器学习项目开发中,可能会经过数据缩放、特征组合以及模型学习拟合等过程;并且,当问题更为复杂时,所应用到的算法以及模型则较为繁杂。
与此同时,在忽略一些细节的前提下,可以通过将这些数据处理步骤结合成一条算法链,以更加高效地完成整个机器学习流程;
Pipeline是什么?
在sklearn中,所谓pipeline,就是由一系列数据转换步骤或待拟合模型(如果有,则模型必须处于管道末端)构成的加工链条。
Pipeline有什么好处?
sklearn中Pipeline有以下妙用:
- 便捷性和封装性:直接调用fit和predict方法来对pipeline中的所有算法模型进行训练和预测。
- 联合的参数选择:你可以通过网格搜索(grid search,一种调参手段)选择管道中所有评估器的参数。
- 安全性:训练转换器和预测器使用的是相同样本,管道有助于防止来自测试数据的统计数据泄露到交叉验证的训练模型中。
Pipeline的原理
pipeline可以将许多算法模型串联起来,形成一个典型的机器学习问题工作流。
Pipeline处理机制就像是把所有模型塞到一个管子里,然后依次对数据进行处理,得到最终的分类结果。
例如,模型1可以是一个数据标准化处理,模型2可以是特征选择模型或者特征提取模型,模型3可以是一个分类器或者预测模型(模型不一定非要三个,根据自己实际需要),如下图所示。
在sklearn中Pipleline中最后一个之外的所有评估器都必须是变换器,最后一个评估器可以是任意类型(transformer,classifier,regresser),若最后一个评估器(estimator)是分类器,则整个pipeline就可以作为分类器使用,如果最后一个评估器(estimator)是个回归器,则整个pipeline就可以作为回归器使用。
备注:
- Estimator:估计器,所有的机器学习算法模型,都被称为估计器。
- Transformer:转换器,比如标准化。转换器的输出可以放入另一个转换器或估计器中作为输入。
在sklearn中一个完整的Pipeline示例步骤如下:
- 首先,对数据进行预处理,比如缺失值的处理
- 数据的标准化
- 降维
- 特征选择算法
- 分类或者预测或者聚类算法(估计器,estimator)
实际上,调用pipeline的fit方法,是用前n-1个变换器处理特征,之后传递给最后的评估器(estimator)进行训练。pipeline会继承最后一个评估器(estimator)的所有方法。
sklearn中Pipeline的用法
sklearn.pipeline.Pipeline(steps, memory=None, verbose=False) 复制代码
参数详解:
steps
: 步骤,使用(key, value)列表来构建,其中 key 是你给这个步骤起的名字, value 是一个评估器对象。memory
: 内存参数,当需要保存Pipeline中间的"transformer"时,才需要用到memory参数,默认None。
Pipeline类的方法
Pipline的方法都是执行各个学习器中对应的方法,如果该学习器没有该方法,就会报错。
假设该Pipeline共有n个学习器:
- transform:依次执行各个学习器的transform方法。
- fit:依次对前n-1个学习器执行fit和transform方法,第n个学习器(最后一个学习器)执行fit方法。
- predict:执行第n个学习器的predict方法。
- score:执行第n个学习器的score方法。
- set_params:设置第n个学习器的参数。
- get_param:获取第n个学习器的参数。
Pipeline的案例
模块化特征转换
步骤说明:
- 步骤一:先用 StandardScaler 对数据集每一列做标准化处理(transformer),
- 步骤二:再用 PCA 主成分分析进行特征降维(transformer),
- 步骤三:最后再用 SVC 模型分类(Estimator)。
训练得到的是一个模型,可通过pipe.predict(X)
直接用来预测,预测时,数据会从步骤一开始进行转换,避免了模型用来预测的数据还要额外写代码实现。还可通过pipe.score(X,Y)
得到这个模型在X训练集上的正确率。
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC from sklearn.decomposition import PCA from sklearn.datasets import load_iris iris=load_iris() pipe=Pipeline([('sc', StandardScaler()),('pca',PCA()),('svc',SVC())]) # ('sc', StandardScaler()) sc为自定义转换器名称, StandardScaler()为执行标准化任务的转换器 pipe.fit(iris.data, iris.target) # 预测 print(pipe.predict(iris.data)) # 评估模型的分数(准确率) print(pipe.score(iris.data, iris.target)) 复制代码
运行结果:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2] 0.9733333333333334 复制代码
除此之外,我们还可以使用make_pipeline
函数,它是Pipeline类的简单实现,只需传入每个step的类实例即可,不需自己命名,它自动将类的小写设为该step的名。
from sklearn.pipeline import make_pipeline from sklearn.naive_bayes import GaussianNB make_pipeline(StandardScaler(),GaussianNB()) 复制代码
运行结果:
Pipeline(steps=[('standardscaler', StandardScaler()), ('gaussiannb', GaussianNB())]) 复制代码
自动化网格搜索(Grid Search)
Pipeline还可以结合GridSearch来对参数进行选择。
步骤说明:
- 步骤一:首先,使用TfidfVectorizer进行特征抽取。
- 步骤二:然后,使用 SVC 模型分类。
- 步骤三:最后,通过GridSearchCV使用交叉验证进行网格搜索。
from sklearn.datasets import fetch_20newsgroups from sklearn.model_selection import train_test_split from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.svm import SVC from sklearn.pipeline import Pipeline from sklearn.model_selection import GridSearchCV import numpy as np # 下载数据 news = fetch_20newsgroups(subset='all') # 切分数据集 X_train,X_test,y_train,y_test = train_test_split(news.data[:3000],news.target[:3000],test_size=0.25,random_state=33) # 文本特征抽取 vec = TfidfVectorizer() X_count_train = vec.fit_transform(X_train) X_count_test = vec.transform(X_test) # 使用pipeline简化系统搭建流程,将文本抽取与分类器模型串联起来 clf = Pipeline([('vect',TfidfVectorizer(stop_words='english')),('svc',SVC())]) # 这里经pipeline进行特征处理、SVC模型训练之后,得到的直接就是训练好的分类器clf parameters = { 'svc__gamma': np.logspace(-2,1,4), 'svc__C': np.logspace(-1,1,3), 'vect__analyzer': ['word'] } # GridSearchCV使用交叉验证进行网格搜索,其中,n_jobs=-1代表使用计算机的全部CPU gs = GridSearchCV(clf, parameters, verbose=2, refit=True, cv=3, n_jobs=-1) gs.fit(X_train,y_train) # 通过交叉验证获取最好的超参数和模型评估最好的分数 print (gs.best_params_, '---',gs.best_score_) # 评估最好的模型的分数(准确率) print (gs.score(X_test, y_test)) 复制代码
运行结果:
Fitting 3 folds for each of 12 candidates, totalling 36 fits {'svc__C': 10.0, 'svc__gamma': 0.1, 'vect__analyzer': 'word'} --- 0.7888888888888889 0.8226666666666667 复制代码
可以看到,parameters变量里面的key都有一个前缀,不难发现,这个前缀其实就是在Pipeline中定义的操作名。二者相结合,使我们的代码变得十分简洁。
关于网格搜索(Grid Search)的说明:
网格搜索是一种调优方法,在参数列表中进行穷举搜索,对每种情况进行训练,找到最优的参数;由此可知,这种方法的主要缺点是比较耗时,参数越多,候选值越多,耗费时间越长!所以,一般情况下,先定一个大范围,然后再细化。
特征组合
FeatureUnion
将多个转换器(transformer)组合成一个新的转换器(transformer)对象,在拟合过程中,每个转换器都独立地拟合数据,并行进行,并且输出的特征矩阵被并排排列在一个大的矩阵中。
一个FeatureUnion对象接受一个transformer对象列表。FeatureUnion
能和 Pipeline
组合使用。
注意:
FeatureUnion无法检查两个transformers是否产生了相同的特征输出,它仅仅产生了一个原来互相分离的特征向量的集合。确保其产生不一样的特征输出是调用者的事情。
步骤说明:
- 步骤一:分别通过StandardScaler和FunctionTransformer进行特征处理。
- 步骤二:然后将StandardScaler处理后的特征与FunctionTransformer处理后的特征进行组合。
from sklearn.pipeline import FeatureUnion from sklearn.preprocessing import StandardScaler from sklearn.preprocessing import FunctionTransformer from numpy import log1p # 原始矩阵数据形状 print(iris.data.shape) step1=('Standar', StandardScaler()) step2=('ToLog', FunctionTransformer(log1p)) steps=FeatureUnion(transformer_list=[step1,step2]) print(steps) data=steps.fit_transform(iris.data) # 经过特征组合之后的矩阵数据形状 print(data.shape) # 查看第一行数据 print(data[0]) 复制代码
运行结果:
(150, 4) FeatureUnion(transformer_list=[('Standar', StandardScaler()), ('ToLog', FunctionTransformer(func=<ufunc 'log1p'>))]) (150, 8) [-0.90068117 1.01900435 -1.34022653 -1.3154443 1.80828877 1.5040774 0.87546874 0.18232156] 复制代码