python机器学习15:建立算法的管道模型

1.管道模型的概念及用法

1.1管道模型的基本概念

我们先来做个试验,来引入管道模型
加入我们要用某个数据集进行模型训练的话,大概做的做法就会像下面这样。
首先,要载入数据集,这里我们继续使用make_blobs来生成数据集,然后对数据集进行预处理(假设模型需要),输入代码如下:

##导入数据集生成器
from sklearn.datasets import make_blobs
##导入数据集拆分工具
from sklearn.model_selection import train_test_split
##导入预处理工具
from sklearn.preprocessing import StandardScaler
##导入多层感知器神经网络
from sklearn.neural_network import MLPClassifier
##导人画图工具
import matplotlib.pyplot as plt
#生成样本数量200 分类为2 ,标准差为5的数据集
X, y = make_blobs(n_samples=200,centers=2,cluster_std=5)
##将数据集拆分为训练集和测试集
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=38)
##对数据进行预处理
scaler = StandardScaler().fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
##将处理后的数据形态进行打印
print(X_train_scaled.shape)
print(X_test_scaled.shape)

在这段代码中,我么你选择使用MLP多层感知神经网络作为下一步要用的分类器模型(因为MLP是典型的需要进行数据预处理的算法模型),用StandardScaler作为数据预处理的工具,用make_blobs生成样本数量为200,分类数为2,准备差为5的数据集。
和以往一样,我们用train_test_split工具将数据集拆分为训练集和测试集,运行代码,得到如下结果:
(150, 2)
(50, 2)
结果分析:从结果中可以看出,训练集中的数据样本为150个,测试集中的样本数据为50个,特征数都是2个。
下面我们来看一下未经过处理的训练集和经过处理的数据差别,输入代码如下:

# #原始的训练集
plt.scatter(X_train[:,0],X_train[:,1],c=y_train,cmap=plt.cm.cool)
# #经过预处理的训练集
plt.scatter(X_train_scaled[:,0],X_train_scaled[:,1],edgecolor='k',c=y_train,cmap=plt.cm.cool)
# #添加图题
plt.title("compare data")
# #显示图片
plt.show()

运行代码,将得到如下图所示的结果:
预处理前后的训练集对比
结果分析:从图中可以看到,StandardScaler将训练集的数据变得更加“聚拢”,这样有利于我们使用神经网络模型进行拟合。
接下来,我们要用到前面所学到的网格搜索来确定MLP的最优参数,在本例中,我们选择的参数我hidden_layer_sizes和alpha这两个来进行试验,输入代码如下:

# #导人网格搜索
from sklearn.model_selection import GridSearchCV
# #设定网格搜索的模型参数字典hidden_layer_ sizes JJ alpha
param_dict = {'hidden_layer_sizes':[(50,),(100,),(100,100)],'alpha':[0.0001,0.001,0.01,0.1]}
#建立神经网络模型
mlp = MLPClassifier(max_iter=1600,random_state=38)
# #建立网格搜索模型
grid = GridSearchCV(mlp,param_dict,cv=3)
# #拟合数据
grid.fit(X_train_scaled,y_train)
# #打印模型得分和最佳参数
print('模型最佳得分:{}'.format(grid.best_score_))
print('最佳参数:{}'.format(grid.best_params_))

这段代码中,我们想测试的是MLP的隐藏层为(50,),(100,)和(100,100)以及alpha值为0.0001,0.001,0.01,0.1时,哪个参数的组合可以让模型的得分最高,为了避免让模型提示最大迭代次数太小,我们把max_iter参数设置为1600。运行代码,得到如下图所示结果:
进行网格搜索得到的模型最佳分数和最佳参数
结果分析:从结果看出,当alpha参数等于0.0001时,有1个50个节点的隐藏层,即hidden_layer_sizes参数为(50,)时,模型评分最高,为0.85,总的来说还可以。
按之前的步骤来做,下一步就可以去拟合测试集了,输入代码如下:

print('测试集得分:{}'.format(grid.score(X_test_scaled,y_test)))

运行代码,可以得到如下所示结果:
测试集得分:0.92
结果分析:测试集上模型得分是0.94,看起来还不错。但是我们忽略了一个问题。
问题就是我们在进行数据预处理的时候,用StandardScaler拟合了训练数据集X_train,而后用这个拟合的scaler去分别转化了X_train和X_test,这一步没有问题。但是,我们在进行网格搜索的时候,是用了X_train来拟合GridSearchCV。如果大家还记得我们在网格搜索中讲的内容,就会发现,在这一步,由于交叉验证是会把数据集分成若干份,然后依次作为训练集和测试集给模型评分,并且找到最高分。
那么问题来了,这里我们把X_train_Scaled进行了拆分,那么拆出来的每一部分,都是基于X_train本身对于StandardScaler拟合后再对自身进行转换。相当于我们用交叉验证生成的测试集拟合了StandardScaler后,再用这个scale人转换这个测试集自身,如下图所示:
错误的预处理方法
在图中可以看到,这样的做法是错误的。我们在交叉验证中,将训练集又拆分成了training fold和validation fold,但用StandardScaler进行预处理的时候,是使用training fold和validation fold一起进行的拟合。这样一来,交叉验证的得分就是不准确的了。
那该如何解决这个问题呢?
首先要明确这个问题在于交叉验证时,所用的经过预处理的数据不对,所以我们要保证交叉验证时,所用的预处理数据是正确的才行。解决办法就是在交叉验证的过程中,数据拆分的时候,再进行数据预处理。这样就不会出现拆分的数据错误的情况了。
如何能让它先拆分数据,再进行预处理,这就要引入一开始就要介绍的管道模型(Pipeline)来解决了。
首先来看一下Pipeline的基本用法,输入代码如下:

#导入管道模型
from sklearn.pipeline import Pipeline
##建立包含预处理和神经网络的管道模型
pipeline = Pipeline([('scaler',StandardScaler()),
                     ('mlp',MLPClassifier(max_iter=1600,random_state=38))])
# #用管道模型对训练集进行拟合
pipeline.fit(X_train,y_train)
# 的丁印 道模 的分数
print('使用管道模型的MLP模型评分:{:.2f}'.format(pipeline.score(X_test,y_test)))

在这段代码中,我们导入了scikit-lean中的Pipeline类,然后在这条“流水线”中“安装”了两个“设备”,一个是用来进行数据预处理的StandardScaler,另一个是最大迭代次数为1600的MLP多层感知神经网络。然后我们用管道模型pipeline来拟合训练数据集,并对测试集进行评分,运行代码,打印结果如下:
使用管道模型的MLP模型评分:0.92
结果分析:可以看到,管道模型在测试数据集的得分达到了0.94,和之前的分数基本没有什么差别。

1.2使用管道模型进行网格搜索

上一小节引入了管道模型,并且介绍了如何使用管道模型将数据与处理和模型训练打包在一起,下面来介绍如何使用管道模型进行网格搜索。
使用管道模型进行网格搜索的示例,输入代码如下:

##设 参数字典
params = {'mlp__hidden_layer_sizes':[(50,),(100,),(100,100)],'mlp__alpha':[0.0001,0.001,0.01,0.1]}
# #将管道模型加入网格搜索
grid = GridSearchCV(pipeline,param_grid=params,cv=3)
# #对训练集进行拟合
grid.fit(X_train,y_train)
# #打印模型交叉验证分数、最佳参数和测试集得分
print('交叉验证最高分:{:.2f}'.format(grid.best_score_))
print('模型最优参数:{}'.format(grid.best_params_))
print('测试集得分:{}'.format(grid.score(X_test,y_test)))

运行代码,得到如下图所示的结果:
将管道模型加入网格搜索的模型评分
结果分析:从图中可以看到,管道模型在交叉验证集中的最高分是0.85,而在MLP的最优参数是alpha等于0.0001,而hidden_layer_sizes的数值为(50,)。这个参数下测试集的得分达到了0.94.
在上面这段代码中,发现我们传给parmas的方法发生了一点变化,那就是我们在hidden_layer_sizes和alpha前面都添加了mlp_这样一个前缀。为什么要这样呢?这是因为pipeline中会有多个算法,我们需要让pipeline知道这个参数是传给哪一个算法的。否则程序就会报错。
通过使用管道模型,我们改变了交叉验证的方式,如下图所示
使用管道模型后数据预处理的方式
对比此图和之前错误的图,我么你可以看到,在前面我们没有使用pipeline的时候,GridSearchCVC会把经过scaler处理的数据拆分成若干个训练集与验证机。而使用了pipeline之后,相当于每次模型会先拆分训练集和验证集,然后单独对训练集和验证集分别进行预处理,再由网格搜索寻找最优参数。这样就避免了我们在前文中所说的错误操作。
我们可以使用如下一段代码来透视一下pipeline所进行处理的过程:
pipeline.steps
运行代码,得到如下图所示的结果:
管道模型中的步骤
结果分析:
从图中的结果可以看到,pipeline.steps把包含在管道模型中的数据预处理scaler和多层感知神经网络MLP的全部参数返回给我们,这就如同是流水线的工作流程一样。从这个结果中也可以看出,在GridsearchCV进行每一步交叉验证之前,pipeline都会对训练集和验证集进行StandardScaler预处理操作。

2.使用管道模型对股票涨幅进行回归分析

2.1数据集准备

数据集使用的是前面一篇《python机器学习之模型评估与优化
》博客中所讲解的通过证券交易软件导出的股票数据,具体导出方法,请看模型评估与优化这一篇博客。
接下来,我们输入代码如下:

#导入pandas
import pandas as pd
stocks = pd.read_csv('C:\\Users\\1003342\\Desktop\\study\\dataset_20190622.csv',encoding='gbk')
#定义数据集中的特征X和目标y
X = stocks.loc[:,'现价':'流通股(亿)'].values
y = stocks['涨幅%']
#验证数据集形态
print(X.shape,y.shape)

运行结果:(3636, 27) (3636,)
结果分析:从图中看到数据已经加载成功,数据集中共有3421支股票,每支股票包含27个特征。接下来可以先尝试用MLP多层感知神经网络来进行回归分析,看模型的表现如何。当然这里还是使用交叉验证cross_val_score来进行评分的工作。
输入代码:

import numpy as np
for i in range(3636):
    arr2=X[i]
    for j in range(len(arr2)):
        if np.isnan(arr2[j]):
            arr2[j] = 0
from sklearn.model_selection import cross_val_score
from sklearn.neural_network import MLPRegressor
scores = cross_val_score(MLPRegressor(random_state=38),X,y,cv=3)
print ('模型平均分:{:.2f}'.format(scores.mean()))

运行代码,得到结果:模型平均分:-17764822391.68
结果分析:这是一个令人揪心的结果,模型的表现实在是够糟糕,得分居然达到-3000多万。这是什么原因呢?我们说过MLP对数据预处理的要求很高,而原始数据集中各个特征的数量级差得又比较远,因此使用原数据集进行训练,结果必然很差。所以接下来,我们要建立一个管道模型,将数据与处理和MLP模型打包进去。

2.2建立包含预处理和MLP模型的管道模型

这里再引入一个知识点,在sklearn中,可以使用make_pipeline来便捷地建立管道模型,看如下代码:

#导入make_pipeline模块
from sklearn.pipeline import make_pipeline
#对比两种方法的语法
piepline = Pipeline([('scaler',StandardScaler),('mlp',MLPRegressor(random_state=38))])
pipe = make_pipeline(StandardScaler(),MLPRegressor(random_state=38))
print(piepline.steps)
print(pipe.steps)

从这段代码中,我们对比了使用Pipelien建立管道模型和使用make_pipeline建立管道模型。这两种方法的结果是完全一样的。但是make_pipeline要相对更简洁一些。我们不需要在make_pipeline中指定每个步骤的名称,直接把每个步骤中我们希望用到的功能模块传进去就可以了。运行代码,得到如下图所示的结果:
对比两种建立管道模型的方式
结果分析:
结果向我们返回了两种方法建立的管道模型中的步骤,从参数看,两种方法得到的结果是完全一致的。下面我们继续尝试用交叉验证cross_val_score来给模型评分,输入代码如下:

#进行交叉验证
scores = cross_val_score(pipe,X,y,cv=6)
print('模型平均分:{}'.format(scores.mean()))

和之前我们直接使用MLP多层感知神经网络不同的是,这次我们用来进行评分的模型是刚刚建立好的管道模型pipe,也就是说在交叉验证中,每次都会先对数据集进行StandardScaler预处理,再拟合MLP回归模型。然后看看结果如何,运行代码:
结果:模型平均分:0.3824022619996266
结果分析:虽然0.38并不是一个理想的分数,但是对比之前没有经过预处理的-3000多万分,可以说模型还是表现得天壤之别。接下来,我们会继续向管道模型中添加新的步骤,并介绍如何调用每个模块中的属性。

2.3向管道模型添加特征选择步骤

我们在之前的章节中介绍过使用随机森林模型对股票数据集进行特征选择。下面我们尝试使用pipeline管道模型将特征选择的部分也添加进来。输入代码如下:

#导入特征选择模块
from sklearn.feature_selection import SelectFromModel
#导入RFE工具
from sklearn.feature_selection import RFE
#导入随机森林模型
from sklearn.ensemble import RandomForestRegressor
#建立管道模型
# pipe = make_pipeline(StandardScaler(),
#                     SelectFromModel(RandomForestRegressor(random_state=38)),
#                     MLPRegressor(random_state=38))
pipe = make_pipeline(StandardScaler(),
                    RFE(RandomForestRegressor(n_estimators=100,random_state=38)),
                    MLPRegressor(random_state=38))
pipe.steps

我们把selectFromModel这个步骤也加进了make_pipeline中,为了让多次运行的结果能够保持一致,也将随机森林的random_state进行指定,我们这里指定为38。
运行代码,管道模型pipeline会把其中的所有步骤反馈如下:
管道模型pipeline的所有步骤反馈
结果分析:我们可以从结果中看到pipeline中每个步骤所使用的的模型参数,这里不再展开解释,下面就使用交叉验证发来给管道模型进行评分,输入代码如下:

#使用交叉验证进行评分
scores = cross_val_score(pipe,X,y,cv=3)
#打印模型分数
print('管道模型评分:{}'.format(scores.mean()))

运行代码结果:
管道模型评分:0.5453733730647278
结果分析:对比之前没有添加特征选择的管道模型,这次的pipeline得分虽然也不高,但比上一有了显著的提升。当然针对不同数据集我们可以在管道模型中增加更多的步骤,以便提高模型的性能表现。
同样的,我们还可以提取管道模型中每个步骤的属性,例如SelectFromModel步骤中,模型选择了哪些特征,输入代码如下:

#使用管道模拟数据
pipe.fit(X,y)
#查询哪些特征被选择
mask = pipe.named_steps['rfe'].get_support()
#打印特征选择的结果
print(mask)

运行代码,会得到如下的结果:
在SelectFromModel步骤中模型所选择的特征
结果分析:从结果中看到,pipeline可以把管道模型中特征选择SelectFromModel的选择结果返回给我们。
除了进行交叉验证之外,pipeline也可以用于网络搜索来寻找最佳模型以及模型最佳参数。

3.使用管道模型进行模型选择和参数调优

本节中,我们一起探索,如何使用管道模型选择相对好的算法模型,以及找到模型中更优的参数

3.1使用管道模型进行模型选择

这部分内容主要讨论的是,我们应该如何利用管道模型从若干算法中找到适合我们的数据集算法。比如我们想知道,对于股票数据集来说,是使用随机森林算法好一些,还是使用MLP多层感知神经网络好一些,就可以利用管道模型来进行对比。有趣的地方在于,我们知道MLP需要对数据进行良好的预处理,而随机森林并不需要这么做。因此我们要在设置好管道模型的参数字典,输入代码如下:

#定义参数字典
params = [{'reg':[MLPRegressor(random_state=38)],'scaler':[StandardScaler(),None]},
          {'reg':[RandomForestRegressor(random_state=38)],'scaler':[None]}]
#对pipeline实例化
pipe = Pipeline([('scaler',StandardScaler()),('reg',MLPRegressor())])
grid = GridSearchCV(pipe,params,cv=3)
grid.fit(X,y)
print('最佳模型是:{}'.format(grid.best_params_))
print('模型最佳得分是:{}'.format(grid.best_score_))

这段代码中,我们定义了一个字典的列表params,作为pipeline的参数。在参数中,我们指定对MLP模型使用StandardScaler,而RandomForest不适用StandardScaler,所以scaler这一项对应的值是None,运行代码,会得到如下让人喜出望外的结果:
通过管道模型选出最佳模型
结果分析:从结果中可以看到,随机森林的得分比之前MLP的得分高出很多,看来选择一个适合的模型真的很重要。经过网格搜索的评估,在MLP和随机森林二者之间,随机森林的表现要更好一些,分数达到0.85分,这还是我们没有调整参数的情况下。下面,我们再试试用网格搜索和管道模型进行模型选择的同时,一并寻找更有参数。

3.2使用管道模型寻找最优参数

在前面我们已经学习了如何用网格搜索来进行参数调优,也掌握了如何使用管道模型进行模型选择。不过相信大家会有个新问题:在上一个例子中,我么你对比的两个模型使用的基本都是默认参数,如MLP的隐藏层,我们会使用缺省值(100,),而随机森林我们使用的n_estimators也默认的10个。那如果我们修改了参数,会不会MLP的表现超过随机森林呢?
带着这个问题,一起来进行试验。我们可以通过在网格搜索中扩大搜索空间,将需要进行对比的模型参数,也放进管道模型中进行对比,输入代码如下:

#在参数字典中增加MLP隐藏层和随机森林中estimator数量的选项
params = [{'reg':[MLPRegressor(random_state=38)],'scaler':[StandardScaler(),None],'reg__hidden_layer_sizes':[(50,),(100,),(100,100)]},
          {'reg':[RandomForestRegressor(random_state=38)],'scaler':[None],'reg__n_estimators':[10,50,100]}]
#对pipeline实例化
pipe = Pipeline([('scaler',StandardScaler()),('reg',MLPRegressor())])
grid = GridSearchCV(pipe,params,cv=3)
grid.fit(X,y)
print('最佳模型是:{}'.format(grid.best_params_))
print('模型最佳得分是:{}'.format(grid.best_score_))

从代码中可以看到,除了在上一例中我们给管道模型设置的参数params之外,这次我们把几个想要试验的参数,也包含在了params的字典当中,一个是MLP的隐藏层数量,我们传入一个列表,分别是10,50和100.接下来我们就让GridSearchCV去遍历两个模型中所有给出的备选参数,看看结果会有什么变化。运行代码,看看网格搜索返回的结果如下:
使用管道寻找最佳模型和更优参数
结果分析:这次管道所选的最佳模型的结果仍然随机森林,相比未加入参数选择,这次经过选择的最佳参数得分有所提升。
思考:如果继续多提供一些参数供管道模型进行选择的话,如让随机森林的n_estimators数量可以选择500或1000,那么结果可能还会不一样,我们实验一下,输入代码如下:

#导入随机森林模型
from sklearn.ensemble import RandomForestRegressor

#在参数字典中增加MLP隐藏层和随机森林中estimator数量的选项
params = [{'reg':[MLPRegressor(random_state=38)],'scaler':[StandardScaler(),None],'reg__hidden_layer_sizes':[(10,),(100,),(100,100)]},
          {'reg':[RandomForestRegressor(random_state=38)],'scaler':[None],'reg__n_estimators':[100,500,1000]}]
#对pipeline实例化
pipe = Pipeline([('scaler',StandardScaler()),('reg',MLPRegressor())])
grid = GridSearchCV(pipe,params,cv=3)
grid.fit(X,y)    
print('最佳模型是:{}'.format(grid.best_params_))
print('模型最佳得分是:{}'.format(grid.best_score_))

这次把随机森林n_estimators参数的选项设置为100,500,和1000.随便把MLP模型的max_iter最大迭代次数增加到1000,这次模型拟合的时间会相对更长一些,毕竟n_estimators达到500或1000,是非常消耗计算资源的。
在大约等了1min,网格搜索向我们返回结果如下:
调整模型参数再次通过pipeline管道选择模型
结果分析:这一次随机森林依旧保持领先,增加了n_estimators数量的随机森林,模型得分再一次提高,模型逐渐变得成熟。

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

曾牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值