目录
1 使用sklearn进行数据挖掘
1.1 数据挖掘的步骤
1.2 数据初貌
1.3 关键技术
2 Pipeline的作用
3 Pipeline的参数与使用
3.1 function
3.2 用法(例子)
3.3 示例
3.3.1 Pipeline的调用
3.3.2 Pipeline参数改变
3.3.3 Pipeline与GridSearch
4 FeatureUnion的作用
4.1 整体并行处理
4.2 部分并行处理
4.3 示例
4.3.1 FeatureUnion的调用
4.3.2 FeatureUnion的参数变更
5 FeatureUnion与pipeline的结合使用
数据挖掘通常包括数据采集,数据分析,特征工程,训练模型,模型评估等步骤。使用sklearn工具可以方便地进行特征工程和模型训练工作,在《使用sklearn做单机特征工程》中,最后留下了一些疑问:特征处理类都有三个方法fit、transform和fit_transform,fit方法居然和模型训练方法fit同名(不光同名,参数列表都一样),这难道都是巧合?
显然,这不是巧合,这正是sklearn的设计风格。我们能够更加优雅地使用sklearn进行特征工程和模型训练工作。此时,不妨从一个基本的数据挖掘场景入手:
我们使用sklearn进行虚线框内的工作(sklearn也可以进行文本特征提取)。通过分析sklearn源码,我们可以看到除训练,预测和评估以外,处理其他工作的类都实现了3个方法:fit、transform和fit_transform。从命名中可以看到,fit_transform方法是先调用fit然后调用transform,我们只需要关注fit方法和transform方法即可。
transform方法主要用来对特征进行转换。从可利用信息的角度来说,转换分为无信息转换和有信息转换。无信息转换是指不利用任何其他信息进行转换,比如指数、对数函数转换等。有信息转换从是否利用目标值向量又可分为无监督转换和有监督转换。无监督转换指只利用特征的统计信息的转换,统计信息包括均值、标准差、边界等等,比如标准化、PCA法降维等。有监督转换指既利用了特征信息又利用了目标值信息的转换,比如通过模型选择特征、LDA法降维等。
通过总结常用的转换类,我们得到下表:
包 | 类 | 参数列表 | 类别 | fit方法有用 | 说明 |
sklearn.preprocessing | StandardScaler | 特征 | 无监督 | Y | 标准化 |
sklearn.preprocessing | MinMaxScaler | 特征 | 无监督 | Y | 区间缩放 |
sklearn.preprocessing | Normalizer | 特征 | 无信息 | N | 归一化 |
sklearn.preprocessing | Binarizer | 特征 | 无信息 | N | 定量特征二值化 |
sklearn.preprocessing | OneHotEncoder | 特征 | 无监督 | Y | 定性特征编码 |
sklearn.preprocessing | Imputer | 特征 | 无监督 | Y | 缺失值计算 |
sklearn.preprocessing | PolynomialFeatures | 特征 | 无信息 | N | 多项式变换(fit方法仅仅生成了多项式的表达式) |
sklearn.preprocessing | FunctionTransformer | 特征 | 无信息 | N | 自定义函数变换(自定义函数在transform方法中调用) |
sklearn.feature_selection | VarianceThreshold | 特征 | 无监督 | Y | 方差选择法 |
sklearn.feature_selection | SelectKBest | 特征/特征+目标值 | 无监督/有监督 | Y | 自定义特征评分选择法 |
sklearn.feature_selection | SelectKBest+chi2 | 特征+目标值 | 有监督 | Y | 卡方检验选择法 |
sklearn.feature_selection | RFE | 特征+目标值 | 有监督 | Y | 递归特征消除法 |
sklearn.feature_selection | SelectFromModel | 特征+目标值 | 有监督 | Y | 自定义模型训练选择法 |
sklearn.decomposition | PCA | 特征 | 无监督 | Y | PCA降维 |
sklearn.lda | LDA | 特征+目标值 | 有监督 | Y | LDA降维 |
不难看到,只有有信息的转换类的fit方法才实际有用,显然fit方法的主要工作是获取特征信息和目标值信息,在这点上,fit方法和模型训练时的fit方法就能够联系在一起了:都是通过分析特征和目标值,提取有价值的信息,对于转换类来说是某些统计量,对于模型来说可能是特征的权值系数等。另外,只有有监督的转换类的fit和transform方法才需要特征和目标值两个参数。fit方法无用不代表其没实现,而是除合法性校验以外,其并没有对特征和目标值进行任何处理,Normalizer的fit方法实现如下:
def fit(self, X, y=None):
"""Do nothing and return the estimator unchanged
This method is just there to implement the usual API and hence
work in pipelines.
"""
X = check_array(X, accept_sparse='csr')
return self
基于这些特征处理工作都有共同的方法,那么试想可不可以将他们组合在一起?在本文假设的场景中,我们可以看到这些工作的组合形式有两种:流水线式和并行式。基于流水线组合的工作需要依次进行,前一个工作的输出是后一个工作的输入;基于并行式的工作可以同时进行,其使用同样的输入,所有工作完成后将各自的输出合并之后输出。sklearn提供了包pipeline来完成流水线式和并行式的工作。
在此,我们仍然使用IRIS数据集来进行说明。为了适应提出的场景,对原数据集需要稍微加工:
from numpy import hstack, vstack, array, median, nan
from numpy.random import choice
from sklearn.datasets import load_iris
#特征矩阵加工
#使用vstack增加一行含缺失值的样本(nan, nan, nan, nan)
#使用hstack增加一列表示花的颜色(0-白、1-黄、2-红),花的颜色是随机的,意味着颜色并不影响花的分类
iris.data = hstack((choice([0, 1, 2], size=iris.data.shape[0]+1).reshape(-1,1), vstack((iris.data, array([nan, nan, nan, nan]).reshape(1,-1)))))
#目标值向量加工
#增加一个目标值,对应含缺失值的样本,值为众数
iris.target = hstack((iris.target, array([median(iris.target)])))
并行处理,流水线处理,自动化调参,持久化是使用sklearn优雅地进行数据挖掘的核心。并行处理和流水线处理将多个特征处理工作,甚至包括模型训练工作组合成一个工作(从代码的角度来说,即将多个对象组合成了一个对象)。在组合的前提下,自动化调参技术帮我们省去了人工调参的反锁。训练好的模型是贮存在内存中的数据,持久化能够将这些数据保存在文件系统中,之后使用时无需再进行训练,直接从文件系统中加载即可。
Pipeline可以将许多算法模型串联起来,可以用于把多个estamitors级联成一个estamitor,比如将特征提取、归一化、分类组织在一起形成一个典型的机器学习问题工作流。Pipleline中最后一个之外的所有estimators都必须是变换器(transformers),最后一个estimator可以是任意类型(transformer,classifier,regresser),若最后一个estimator是个分类器,则整个pipeline就可以作为分类器使用,如果最后一个estimator是个聚类器,则整个pipeline就可以作为聚类器使用。
实际上,调用pipeline的fit方法,是用前n-1个变换器处理特征,之后传递给最后的estimator训练。pipeline继承最后一个estimator的所有方法。
由此带来两点直接好处:
Pipeline的方法都是执行各个学习器中对应的方法,如果该学习器没有该方法,会报错,假设该Pipeline共有n个学习器。
步骤:1.首先对数据进行预处理,比如缺失值的处理
2.数据的标准化
3.降维
4.特征选择算法
5.分类或者预测或者聚类算法(估计器,estimator)
Pipeline是使用(key, value)对的列表构建的,其中key是包含要给予此步骤的名称的字符串,value是对应的不同学习器。
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()的简称,亦或者是代称
pipe.fit(iris.data,iris.target)
输出:
Pipeline(memory=None,
steps=[('sc', StandardScaler(copy=True, with_mean=True, with_std=True)), ('pca', PCA(copy=True, iterated_power='auto', n_components=None, random_state=None,
svd_solver='auto', tol=0.0, whiten=False)), ('svc', SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
kernel='rbf', max_iter=-1, probability=False, random_state=None,
shrinking=True, tol=0.001, verbose=False))])
其次可以通过make_pipeline函数实现pipeline的调用:它是Pipeline类的简单实现,只需传入每个step的类实例即可,不需自己命名,自动将类的小写设为该step的名:
from sklearn.linear_model import ElasticNet, Lasso, BayesianRidge, LassoLarsIC
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import RobustScaler #用来解决离群点
new_pipeline=make_pipeline(RobustScaler(), Lasso(alpha =0.0005, random_state=1))
输出:
Pipeline(memory=None,
steps=[('robustscaler', RobustScaler(copy=True, quantile_range=(25.0, 75.0), with_centering=True,
with_scaling=True)), ('lasso', Lasso(alpha=0.0005, copy_X=True, fit_intercept=True, max_iter=1000,
normalize=False, positive=False, precompute=False, random_state=1,
selection='cyclic', tol=0.0001, warm_start=False))])
可以通过set_params重新设置每个类里边需传入的参数,设置方法为step的name__parma=参数值:
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()的简称,亦或者是代称
pipe.set_params(sc__copy=False)
#改变参数的格式为 学习器简称__该学习器对应参数名=参数值
pipe.fit(iris.data,iris.target)
模型调参利器 gridSearchCV(网格搜索):https://blog.csdn.net/weixin_41988628/article/details/83098130
GridSearchCV,它存在的意义就是自动调参,只要把参数输进去,就能给出最优化的结果和参数。但是这个方法适合于小数据集,一旦数据的量级上去了,很难得出结果。这个时候就是需要动脑筋了。数据量比较大的时候可以使用一个快速调优的方法——坐标下降。它其实是一种贪心算法:拿当前对模型影响最大的参数调优,直到最优化;再拿下一个影响最大的参数调优,如此下去,直到所有的参数调整完毕。这个方法的缺点就是可能会调到局部最优而不是全局最优,但是省时间省力,巨大的优势面前,还是试一试吧,后续可以再拿bagging再优化。
通常算法不够好,需要调试参数时必不可少。比如SVM的惩罚因子C,核函数kernel,gamma参数等,对于不同的数据使用不同的参数,结果效果可能差1-5个点,sklearn为我们提供专门调试参数的函数grid_search。
举例为pipeline与GridSearch在pca与svc中的使用:
from sklearn.model_selection import GridSearchCV
#以下格式为 学习器简写__该学习器对应的某个参数名=可选参数值范围
params = dict(pca__n_components=[2, 5, 10],
svc__C=[0.1, 10, 100])
grid_search = GridSearchCV(pipe, param_grid=params)
FeatureUnion把若干个transformer object组合成一个新的estimators。这个新的transformer组合了他们的输出,一个FeatureUnion对象接受一个transformer对象列表。
在训练阶段,每一个transformer都在数据集上独立的训练。在数据变换阶段,多有的训练好的Trandformer可以并行的执行。他们输出的样本特征向量被以end-to-end的方式拼接成为一个更大的特征向量。
FeatureUnion和Pipeline可以组合使用来创建更加复杂的模型。
注意:FeatureUnion无法检查两个transformers是否产生了相同的特征输出,它仅仅产生了一个原来互相分离的特征向量的集合。确保其产生不一样的特征输出是调用者的事情。
FeatureUnion与pipeline的区别
并行处理使得多个特征处理工作能够并行地进行。根据对特征矩阵的读取方式不同,可分为整体并行处理和部分并行处理。
pipeline包提供了FeatureUnion类来进行整体并行处理:
from numpy import log1p
from sklearn.preprocessing import FunctionTransformer
from sklearn.preprocessing import Binarizer
from sklearn.pipeline import FeatureUnion
#新建将整体特征矩阵进行对数函数转换的对象
step2_1 = ('ToLog', FunctionTransformer(log1p))
#新建将整体特征矩阵进行二值化类的对象
step2_2 = ('ToBinary', Binarizer())
#新建整体并行处理对象
#该对象也有fit和transform方法,fit和transform方法均是并行地调用需要并行处理的对象的fit和transform方法
#参数transformer_list为需要并行处理的对象列表,该列表为二元组列表,第一元为对象的名称,第二元为对象
step2 = ('FeatureUnion', FeatureUnion(transformer_list=[step2_1, step2_2, step2_3]))
整体并行处理有其缺陷,在一些场景下,我们只需要对特征矩阵的某些列进行转换,而不是所有列。pipeline并没有提供相应的类(仅OneHotEncoder类实现了该功能),需要我们在FeatureUnion的基础上进行优化:
from sklearn.pipeline import FeatureUnion, _fit_one_transformer, _fit_transform_one, _transform_one
from sklearn.externals.joblib import Parallel, delayed
from scipy import sparse
import numpy as np
#部分并行处理,继承FeatureUnion
class FeatureUnionExt(FeatureUnion):
#相比FeatureUnion,多了idx_list参数,其表示每个并行工作需要读取的特征矩阵的列
def __init__(self, transformer_list, idx_list, n_jobs=1, transformer_weights=None):
self.idx_list = idx_list
FeatureUnion.__init__(self, transformer_list=map(lambda trans:(trans[0], trans[1]), transformer_list), n_jobs=n_jobs, transformer_weights=transformer_weights)
#由于只部分读取特征矩阵,方法fit需要重构
def fit(self, X, y=None):
transformer_idx_list = map(lambda trans, idx:(trans[0], trans[1], idx), self.transformer_list, self.idx_list)
transformers = Parallel(n_jobs=self.n_jobs)(
#从特征矩阵中提取部分输入fit方法
delayed(_fit_one_transformer)(trans, X[:,idx], y)
for name, trans, idx in transformer_idx_list)
self._update_transformer_list(transformers)
return self
#由于只部分读取特征矩阵,方法fit_transform需要重构
def fit_transform(self, X, y=None, **fit_params):
transformer_idx_list = map(lambda trans, idx:(trans[0], trans[1], idx), self.transformer_list, self.idx_list)
result = Parallel(n_jobs=self.n_jobs)(
#从特征矩阵中提取部分输入fit_transform方法
delayed(_fit_transform_one)(trans, name, X[:,idx], y,
self.transformer_weights, **fit_params)
for name, trans, idx in transformer_idx_list)
Xs, transformers = zip(*result)
self._update_transformer_list(transformers)
if any(sparse.issparse(f) for f in Xs):
Xs = sparse.hstack(Xs).tocsr()
else:
Xs = np.hstack(Xs)
return Xs
#由于只部分读取特征矩阵,方法transform需要重构
def transform(self, X):
transformer_idx_list = map(lambda trans, idx:(trans[0], trans[1], idx), self.transformer_list, self.idx_list)
Xs = Parallel(n_jobs=self.n_jobs)(
#从特征矩阵中提取部分输入transform方法
delayed(_transform_one)(trans, name, X[:,idx], self.transformer_weights)
for name, trans, idx in transformer_idx_list)
if any(sparse.issparse(f) for f in Xs):
Xs = sparse.hstack(Xs).tocsr()
else:
Xs = np.hstack(Xs)
return Xs
在本文提出的场景中,我们对特征矩阵的第1列(花的颜色)进行定性特征编码,对第2、3、4列进行对数函数转换,对第5列进行定量特征二值化处理。使用FeatureUnionExt类进行部分并行处理的代码如下:
from numpy import log1p
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import FunctionTransformer
from sklearn.preprocessing import Binarizer
#新建将部分特征矩阵进行定性特征编码的对象
step2_1 = ('OneHotEncoder', OneHotEncoder(sparse=False))
#新建将部分特征矩阵进行对数函数转换的对象
step2_2 = ('ToLog', FunctionTransformer(log1p))
#新建将部分特征矩阵进行二值化类的对象
step2_3 = ('ToBinary', Binarizer())
#新建部分并行处理对象
#参数transformer_list为需要并行处理的对象列表,该列表为二元组列表,第一元为对象的名称,第二元为对象
#参数idx_list为相应的需要读取的特征矩阵的列
step2 = ('FeatureUnionExt', FeatureUnionExt(transformer_list=[step2_1, step2_2, step2_3], idx_list=[[0], [1, 2, 3], [4]]))
FeatureUnion对象实例使用(key, value)构成的list来构造,key是你自己起的transformation的名称,value是一个estimator对象。
from sklearn.pipeline import FeatureUnion
from sklearn.decomposition import PCA
from sklearn.decomposition import KernelPCA
estimators = [('linear_pca', PCA()), ('kernel_pca', KernelPCA())]
combined = FeatureUnion(estimators)
from sklearn.pipeline import FeatureUnion
from sklearn.decomposition import PCA
from sklearn.decomposition import KernelPCA
estimators = [('linear_pca', PCA()), ('kernel', KernelPCA())]
combined = FeatureUnion(estimators)
combined.set_params(kernel__alpha=0.6)##注意这里是两个下划线不然会报错
print(combined)
# Author: Andreas Mueller
#
# License: BSD 3 clause
from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.grid_search import GridSearchCV
from sklearn.svm import SVC
from sklearn.datasets import load_iris
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest
iris = load_iris()
X, y = iris.data, iris.target
# This dataset is way to high-dimensional. Better do PCA:
pca = PCA(n_components=2)
# Maybe some original features where good, too?
selection = SelectKBest(k=1)
# Build estimator from PCA and Univariate selection:
combined_features = FeatureUnion([("pca", pca), ("univ_select", selection)])
# Use combined features to transform dataset:
X_features = combined_features.fit(X, y).transform(X)
svm = SVC(kernel="linear")
# Do grid search over k, n_components and C:
pipeline = Pipeline([("features", combined_features), ("svm", svm)])
param_grid = dict(features__pca__n_components=[1, 2, 3],
features__univ_select__k=[1, 2],
svm__C=[0.1, 1, 10])
grid_search = GridSearchCV(pipeline, param_grid=param_grid, verbose=10)
grid_search.fit(X, y)
print(grid_search.best_estimator_)
sklearn.pipeline
.Pipeline:https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html
使用sklearn优雅地进行数据挖掘:https://www.cnblogs.com/jasonfreak/p/5448462.html
sklearn :Pipeline 与 FeatureUnion入门指南:https://blog.csdn.net/qq_35992440/article/details/89918305