
1.3 案例实战
本节介绍本案例的开发运行环境搭建、数据集准备、特征工程、建模代码编写、项目运行及模型评估等。
1.3.1 运行环境搭建
首先安装机器学习套件Anaconda。Anaconda是一个开源的Python发行版本,包含Conda、Python等180多个科学包及其依赖项。Anaconda自带一个名为Conda的虚拟环境管理器,可以在同一个机器上安装不同版本的机器学习虚拟环境,并能够在不同虚拟环境之间切换。同时,Anaconda也具有跨平台属性,支持Windows、Linux、MacOS三种操作系统。Anaconda官网下载地址为https://www.anaconda.com/products/individual#Downloads,下载界面如图1-21所示。

图1-21 下载Anaconda机器学习套件
安装时会出现如图1-22所示的界面,此时注意勾选环境变量选项,并将安装路径指定到C:\Anaconda。特别说明的是,本书作者的安装路径是C:\Anaconda,读者也可安装到其他路径,只需在后续试验中修改相应路径即可。

图1-22 Anaconda安装过程
安装完成后,在命令行下执行以下语句,创建名为automl的虚拟环境并安装Python 3.7版本:

激活虚拟环境,执行以下语句:

此时,命令行将出现“(automl)”提示,表示已进入该虚拟环境。执行以下命令安装相关框架,不显示报错即可:

值得注意的是,上述两个安装命令由于依赖包的版本冲突,不能安装在一个虚拟环境下。至此,运行环境搭建完毕。
1.3.2 数据集准备
本案例中,我们根据业务的先验知识选择以下相关特征来对每个客户预测其手机银行是否月活。值得注意的是,我们是用当月的各项特征指标来预测下月是否月活,因此每行数据由“客户号、当月各项特征、是否手机银行下月活跃”三部分组成,完整数据字段为:
客户号、客户九项资产月日均、年龄、开户月份数、性别、婚姻、职业、学历、最近一次交易距今天数、风险评级、活动睡眠标志、资产评级、持有产品数、持有贷款、持有信用卡、是否签约电子渠道、是否签约快捷支付、本月累计交易金额、本月累计交易笔数、本月累计现金交易金额、本月累计现金交易笔数、本月累计代发金额、本月累计代发笔数、本月累计网银金额、本月累计网银笔数、本月累计柜面金额、本月累计柜面笔数、是否手机银行下月活跃。
观察数据发现,风险评级和职业两个特征的缺失(或不准确)情况比较严重,我们需要舍弃这两个字段,而交易占比更能反映客户的交易偏好,我们需要将交易笔数和金额转换为占比,并将剩余的少数缺失字段填充数字0,数据预处理过程如下:

对于数值类型的特征,并非值越大越重要,所以需要进行分箱操作。根据每个特征列的数据分布,由算法来决定最优的分箱边界,将原始特征划分到某个类别,然后将类别数据送入模型,从而规范不同特征取值范围不一致的问题。本案例采用决策树分箱算法来决定具体的分箱数量和分箱边界。
如图1-23所示,决策树分箱的任务是:在自变量x和因变量y之间,如何将x进行特征分类,使得y的类别数(熵)最小。熵是衡量混乱程度的指标。数据的类别越多,熵值就越大;数据的类别越少,熵值就越小。熵增定律告诉我们:在自然状态下,世界会往更混乱、更无序的方向(即熵增方向)发展。数据建模在本质上是对数据进行某种管理,使其变得规范、有序。这是将无序变为有序的过程,其目标是熵增最小化。决策树分箱的思路是把想要离散化(即分箱)表示的单个自变量用树模型来拟合因变量,通过熵增最小化原则来依次选择不同层次的特征分裂,从而获得最大的信息增益,最大程度地降低数据混乱,从而获得最优的分类。
在决策树分箱算法中,每个节点进行一次特征分裂,相当于在对应的数据块中切一刀,如图1-24所示。每切一刀,就计算一次信息增益。比如原始数据集y的熵值为S1,x特征有5个取值,5个取值的概率占比分别为L1、L2、L3、L4、L5,分别计算5个取值的结果集熵值,为R1、R2、R3、R4、R5,那么选择x特征来作为分支节点的熵值,S2=L1×R1+L2×R2+L3×R3+L4×R4+L5×R5,信息增益为S1-S2。决策树分箱算法会按信息增益从大到小的顺序依次从上到下决定各层节点的特征分裂边界值,从而使得落在叶子节点的y具有最小的熵值。

图1-23 决策树分箱示意图

图1-24 决策树分箱的特征分裂过程
分箱代码如下:

接下来遍历数据集中的每列特征,将数值型字段进行决策树分箱,即一列特征裂变为多列特征(即分几箱)。本案例设定最多裂变为5列,然后把原特征列替换为裂变后的onehot编码分箱类别。比如,算法决定将某列特征分为3箱,则该列特征分裂为3列,取值为“0, 0, 1”“0, 1, 0”“1, 0, 0”。


对于类别数不多的文字类特征,我们直接将其转化为onehot编码。比如性别字段取值为“男”“女”“未知”三个值,分别将其编码为“0, 0, 1”“0, 1, 0”“1, 0, 0”,使用pandas的get_dummies()函数实现:

生成原特征的onehot编码后,原特征列不再需要,应删除。处理完所有其他字段后,将数据写入features.txt文件,该文件由82列数据组成,每列用逗号分隔,其格式为:

其中,客户号是唯一主键,每列feature和监督数据“是否手机银行下月活跃”经过上述处理后,其取值要么为0,要么为1。本节代码详见下载文件feature.py。至此,完成数据集准备工作。
1.3.3 特征选择代码实战
首先使用Feature_selector来选择特征。设置特征列和监督数据。注意在读取数据集文件时,将唯一主键作为索引,这样它不会参与到特征选择过程中:

显示共线特征,代码如下:


此时会得到图1-25所示的共线特征可视化热图。

图1-25 共线特征可视化热图
得到关联度大于80%的特征列表:

共线性较大的特征,自变量之间存在较强的线性相关关系,导致模型预测能力下降,增加对模型结果的解释成本。因此,我们需要将其删除。

训练梯度提升机,计算零重要性特征:


上述代码详见下载文件f_select.py。执行后将绘制标准化特征重要性分数,得到以下重要特征排序,如图1-26所示。

图1-26 Feature_selector选择的重要特征
其中,虚线表示累计重要性达到90%时需要超过50个特征。features.txt中有80个特征,平均特征重要性分数为1/80=0.0125,可以看到,多数特征的重要性已超过平均分数。我们在fs.ops中剔除零重要性特征和低重要性特征,剩下的就是重要特征。注意,低重要性特征与我们设置的90%阈值有关,这是由cumulative_importance=0.9参数决定的。调整这个参数,将得到不同的低重要性特征选择。
我们再来对比一下Boruta的选择结果。首先导入库包:

读取数据集,X为所有的feature特征列,y为监督数据列:

Boruta库使用随机森林算法来选择特征。于是实例化一个随机森林类,开启并发模式,指定balanced模式设置y值自动将权重与输入数据中的类频率成反比地调整为n_samples/(n_classes*np.bincount(y)),树深度为5:

向BorutaPy框架传入随机森林实例化对象,根据数据集大小自动设置学习器数量,指定perc参数来设置影子特征和原始特征比较的阈值,perc越低,就会有越多的不相关特征被选为相关的,但也会有较少相关的特征被遗漏,两者需要权衡。

传入数据集,训练特征选择的随机森林:

输出特征选择结果及特征:

上述代码详见下载文件Boruta1.py,执行结果如图1-27所示。
可以看到,在80个待选特征中,Boruta选择了72个重要特征,与Feature_selector的选择结果基本一致(注意Feature_selector的选择结果由重要性阈值决定)。我们选择部分排名靠前的非共线重要特征,按3:1的比例划分训练集和测试集,分别形成features.train和features.test文件:



图1-27 Boruta特征选择结果
1.3.4 自动化建模代码实战
特征工程完成后,进入自动化建模环节,这是在事先设定的模型空间和参数空间中搜索最优解的过程。使用Flaml框架的0.9.5版本,首先导入库包:

实例化一个自动机器学习类:

设置自动机器学习的相关参数。time_budget参数是模型训练的秒数,metric参数是度量值,accuracy表示按精度进行优化,task参数指定分类任务或回归任务,classification表示本例是分类任务。log_file_name参数指定将训练过程记录到哪个日志文件中,训练过程数据包括每次搜索的计算开销、时间开销、验证机损失、模型参数等。estimator_list参数指定学习器列表,即指定模型空间,本例不作设置就表示使用默认的所有学习器进行学习。Flaml支持的学习器有LightGBM、随机森林(RF)、CATBoost、XGBoost、Extra_tree(极端随机树)、XGB_Limitdepth、LRL1(带L1正则化的逻辑回归)等。

然后从训练集文件获取特征矩阵X_train和监督向量y_train,设置index_col=0,将唯一主键设置为索引,这样它就不会参与模型训练:

将特征矩阵X_train、监督向量y_train和模型参数放到框架中运行,启动Flaml的自动模型搜索机制,在多个学习器和模型参数的默认空间中搜索最佳学习器和最佳模型参数:

Flaml框架搜索过程如图1-28所示。

图1-28 Flaml框架搜索过程
最后使用predict()函数对测试集进行预测,得到测试集每个样本的预测类别,并计算模型性能指标:


经过10min训练后,Flaml搜索到的最佳学习器为LightGBM,在测试集上的准确率为86.73%,auc指标为0.74,如图1-29所示。

图1-29 Flaml框架搜索结果
上述代码详见下载文件flaml_classify.py。接下来,我们使用AutoGluon框架的0.3.1版本进行对比。首先导入库包:

读取训练集:

设置模型训练秒数:

我们指定auc值作为模型评估指标,它被定义为roc曲线下方的面积,该值越大越好。模型在每次迭代后,会在数据集上计算评估指标,观察这个指标可以了解模型的优化方向。

接下来开始启动AutoGluon的自动学习过程,它将使用CATBoost、ExtraTree、LightGBM、NeuralNet、RandomForestEntr、XGBoost等单一模型进行集成学习,这些模型的训练权重文件将被存储到path参数指定的路径下,eval_metric参数指定模型评估指标,fit()函数将训练集数据输入框架,它将以TabularPredictor()实例的label列作为监督数据进行监督学习,presets='best_quality'指定模型将获得最佳预测准确性,num_bag_folds=5表示将进行5折交叉验证:


从测试集获取数据:

分别使用predict()函数和predict_proba()函数来进行预测,前者输出每个样本的预测类别,后者输出每个预测类别的概率。

使用evaluate_predictions()函数来得到模型的各项性能指标,如图1-30所示。

图1-30 AutoGluon模型性能评估
我们得到的auc指标为0.90,准确率指标为0.86,对比Flaml的结果,AutoGluon略好。
1.3.5 自动化调参代码实战
下面使用贝叶斯优化来对梯度提升决策树(GBDT)分类模型进行超参数调优。首先导入库包:

其中,GradientBoostingClassifier是scikit-learn框架封装的梯度提升决策树的分类类,梯度提升决策树还有一个回归类GradientBoostingRegressor。读取训练数据集文件:


我们先用默认参数来对数据集进行5折交叉验证,即将数据集切分为5个数据块,每次使用其中一块作为验证集,其他4块作为训练集,取5次训练得到的auc指标的平均值来衡量模型性能:

我们得到的result值为0.865768。下面开始进行贝叶斯优化,首先定义要优化的函数,该函数是一个参数不确定的梯度提升决策树分类在数据集上进行5折交叉验证后的auc指标的平均值:

接下来指定传入梯度提升决策树分类的每个参数的取值范围,贝叶斯优化算法将在此范围内寻找最佳参数:

其中,n_estimators参数指定弱学习器的最大迭代次数,min_samples_split参数指定节点划分所需最小样本数,max_features参数指定最大特征数,max_depth参数指定决策树最大深度。然后调用maximize()函数来最大化目标函数值,我们从初始5个点开始,经过200次采样,最终获得在当前采样点下的最佳模型参数:

使用最佳模型参数来构建一个梯度提升决策树分类实例,其中round_up是整数四舍五入函数:


我们再对训练集计算5折交叉验证后的auc平均值:

此时result已经提升到0.872804的成绩,高于以前的0.865768。代码详见下载文件autog.py。至此,我们分别尝试了Flaml、AutoGluon、贝叶斯优化三种自动化模型技术。我们取三种方法预测值的并集作为下月手机银行月活客户的营销目标,以此为基础开展营销工作。