摘要
随着互联网金融的异军突起,银行业的竞争愈加激烈,防止客户流失和挽留老客户成为各大银行关心的重要问题。本文首先根据已有数据集对各特征进行描述性统计分析,初步了解数据;之后进行数据预处理,包括数据清洗,数据变换、特征选择;再后用SVM、LR、朴素贝叶斯、决策树、RF、XGBoost、LM神经网络进行建模,通过不同性能度量,选出XGBoost为表现最好的模型并进行调参;最后,根据描述性统计和特征重要性为银行挽留客户提出建议。
关键词:客户流失;数据预处理;XGBoost;调参;建议
银行客户流失是指银行的客户不再继续参与原业务、不再重复购买或者终止原先的产品或者服务。近年来,随着互联网金融的异军突起和传统银行业的竞争加剧,银行发展自身潜力、吸引优质顾客、防止客户流失就显得格外重要。研究表明,发展一位新客户所花费的成本要比维持一位老客户的成本多达5到6倍。所以说,在客户流失后,如果企业要去重新发展新客户所需要的成本是巨大的,且大多数新用户产生的利润不如老用户。因此,不管是哪个行业都越来越重视客户流失管理。预测潜在的流失客户、有效挽留和关怀客户是各个企业关心的重要问题之一。
1、通过确定客户流失模型,有效预测客户的流失情况。
2、通过预测模型的建立,提出相应建议提高用户的活跃度,实现挽留关怀客户的有效性,降低开展挽留关怀工作的成本。
本节首先观察样本数据,初步了解属性特征,对各属性进行描述性统计分析,初步探索各属性与客户流失情况的关系;其次进行数据预处理工作,包括异常值处理,数据变换,数据规约等;之后将样本划分为训练集和测试集,观察客户流失情况样本是否均衡,不均衡时需要对训练样本进行均衡化处理;最后,对训练集和测试集进行标准化处理。
本次所使用的数据集是某欧洲银行的数据。数据集一共包含了14个变量,10000个样本,不包含缺失值。本数据集可从superdatascience官网下载。首先先来观察数据情况。 图1 数据集变量预览
在图1展示的数据集中,从左到右,数据集所具有的的变量有编号、用户ID、姓名、信用分、地区、性别、年龄、用户时长(使用银行产品时长)、存贷款情况、使用产品数量、是否有信用卡、是否为活跃用户、估计收入、是否已流失。将数据集特征进行汇总,由表1可以清楚地看出特征变量名称和属性。
表1 数据集特征汇总
我们从表1可以看到,数据集一共包含14个变量,其中第14个变量就是我们的目标变量,目标变量定义的是是否已经流失的分类变量,从而确定了本次数据挖掘的目标是分类。除了目标变量外,特征属性中有7个分类变量,其中有5个是字符类型的,这意味着在数据预处理时首先要处理这些变量才能做下一步分析。其余6个是数值变量,我们接下来要注意考虑数值变量是否存在异常值,是否需要进行数据变换。
下面对各变量首先进行描述性统计分析,观察变量分布情况,进一步了解数据。
本小节主要是通过绘制定量变量的频数分布图来观察变量的分布情况,初步了解各变量分布。
|
|
|
|
|
|
总体来看图2-1,对于信用分这个属性来说,流失和非流失总体呈现出一个偏正态分布,信用分在650-700之间达到峰值。由图2-2可以看出,年龄呈现出一个偏左态分布。该银行35到40岁的客户最多,在60岁以上的客户很少。且给银行的目标用户大部分年龄在25到45岁之间,比较符合多数银行客户年龄的分布情况。对于该银行,图2-3用户使用年数在1到9年的分布均匀,使用10年的人数最多,使用1年的人数较少。我们可以初步猜测该银行忠诚客户较多,但是在发展新客户方面不够重视或者策略需要调整。;对于客户存贷款情况,该样本数据显现的都是存贷款大于0的情况。从图中可以看出,存款在25000以下的占据大部分。在存款大于25000呈现正态分布。在持有产品数方面,该银行大多数用户使用银行的产品数量为1个或2个,拥有3个或4个产品的极少。说明该银行的产品只有少数具有吸引力,银行应该实施相应措施激励用户使用该银行产品。从图2-6可以看出,该银行客户估计收入在0到200000之间,且不同收入客户分布均匀,猜测该银行并没有清晰的目标客户定位,对不同收入层的客户效用没有差别。
对于分类指标,这里展示了不同指标分类的频数分布情况,同时展示了在每种类别下客户流失情况。
|
|
|
|
图3-1中,在地区这个属性可以看出,样本数据集客户来自三个国家:法国、德国、西班牙。该银行法国用户最多,德国和西班牙客户数量几乎相等,但从流失情况来看,德国用户的流失率明显高过其他两国。从性别来看,图3-2显示,银行的男性用户高于女性,但是女性的流失率要高过男性。在客户是否拥有信用卡来看(图3-3),该银行70%的客户有信用卡,没有信用卡客户的流失率在20%以上,有信用卡的流失率在16%。使用该银行信用卡的用户忠诚度更高。由图3-4知,该银行的活跃客户稍高于非活跃用户,活跃用户的流失率明显低于非活跃用户,这也说明活跃客户较非活跃客户忠诚度更高。
图4 客户流失情况
从图4我们可以很明显看到,客户流失情况样本分布很不均衡,未流失与流失用户比例接近4:1。所以接下来在划分训练集和测试集之后,需要对不均衡的测试集样本进行均衡处理。
因为样本不含缺失值,所以不用进行缺失值的处理。数据清洗在这里主要包括两步:删除无关变量和异常值处理。
我们将导入jupter notebook的数据集输出前5个数据观察变量,如图5。
图5 数据集展示
在所有变量中,RowNumber:用户编号,CustomerId:用户ID,Surname:用户姓名这三个变量明显是无关变量,我们将无关变量剔除。
首先根据变量的箱线图来判断是否存在异常值。
|
|
|
|
|
|
由图6可以看出,信用分、年龄、产品数量存在异常值。信用分低于400分是异常值,年龄大于60的属于异常值,产品数为4的也属异常值。处理异常值的方法有很多,其中忽略异常值也是一种方法。由于认为信用分、年龄、产品数量属于重要变量,这里选择不剔除异常值。
对于该数据集的数据变换处理,主要包括对字符型变量的量化、对分类变量的处理、连续属性离散化、样本不均衡处理和数据标准化。
由之前样本数据的观察了解到,地区、性别是字符型变量,无法进行分析,需要将这2个属性进行量化。这里使用转换数值工具包LabelEncoder进行转换。处理后,地区包含0、1、2三个值,性别包括0、1两个值。其中地区变量0代表France,1代表Germany,2代表Spain;性别变量0代表女,1代表男。图7展示了变化后的效果。
图7 量化后数据集
将连续特征离散化对异常数据有很强的鲁棒性,能够增强模型的稳定性,降低了模型过拟合的风险,并且能够提高迭代速度。在之前的异常值检测中我们已知CreditScore和Age两个变量存在异常值,这里将他们进行离散化,将信用得分划分为600分以下、600-650、650-700、700-750、750以上共5个分组,年龄划分为30岁以下、30-40、40-50、50-60、60-70、70-80、80岁以上7个分组。
下面给出分组后的信用分和年龄分布。
|
|
在进行完这些处理后 ,接下来的工作就是对分类变量进行独热编码以及特征矩阵的标准化。但是,考虑到类标签流失与未流失的比例接近1:4,属于不均衡数据。样本不均衡会给结果带来很大影响。在样本不均衡情况下,即使我们最终得到的模型准确度很高也是不可信的。所以,在进行分类任务时,解决样本不均衡问题很重要。解决样本不均衡的方法有很多,目前用的较多的有欠采样、过采样、加权、One-class分类。这里选择过采样的改进方法SMOTE算法来平衡样本。
在对样本处理前,首先简单介绍样本不均衡的处理方法SMOTE算法。
SMOTE(Synthetic Minority Oversampling Technique),合成少数类过采样技术.它是基于随机过采样算法的一种改进方案,由于随机过采样采取简单复制样本的策略来增加少数类样本,这样容易产生模型过拟合的问题,即使得模型学习到的信息过于特别(Specific)而不够泛化(General),SMOTE算法的基本思想是对少数类样本进行分析并根据少数类样本人工合成新样本添加到数据集中,具体如下图所示,算法流程如下。
(1)对于少数类中每一个样本x,以欧氏距离为标准计算它到少数类样本集中所有样本的距离,得到其k近邻。
(2)根据样本不平衡比例设置一个采样比例以确定采样倍率N,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为xn。
(3)对于每一个随机选出的近邻xn,分别与原样本按照如下的公式构建新的样本。
x n e w = x + r a n d ( 0 , 1 ) ( x ^ − x ) x_{new}=x+rand(0,1)(\widehat{x}-x) xnew=x+rand(0,1)(x −x)
图9 SMOTE算法示意图
介绍完SMOTE原理后,我们对数据集进行处理。首先将数据集划分为训练集和样本集,这里将原始数据的80%作为训练集,20%作为样本集然后需要对训练集样本不均衡进行处理。这里随机产生的训练集正负样本比例接近1:5。我们通过SMOTE算法将正样本也就是流失样本扩展到和未流失样本一样多。我们可以看一下SMOTE处理后的效果。
图10 均衡处理后训练样本数量
图10中我们可以看出,SMOTE已经将训练集扩展成了正负样本一致。由于SMOTE算法是基于样本到k近邻之间的距离随机生成的样本,所以过采样后流失样本的离散变量会连续化。Geography、Gender、HasCrCard、IsActiveMember这四个变量是离散变量且为分类变量,但是Smote处理后将他们连续化了,因此需要进行离散化处理。这里划分数据离散化的标准就近原则,即样本数据离哪个指标近就划分到那一类中。
处理完样本不均衡问题后,我们考虑到Geography变量是三分类变量,直接编码(0,1,2)会让计算机直接将变量作为有序数组处理。所以,真正分析的时候要把他们作为虚拟变量处理,性别就是二分类引入一个虚拟变量,(即我们知道一个是0,另一个就是1)可以不变,地区是三分类,要引入两个虚拟变量,即要知道其中两个的类,才能确定另一个,比如France和Spain是0,我们才能确定Germany是1。这里可以直接用sklearn里的OneHotEncoder进行处理。OneHotEncoder是将分类变量的类别生成3列。这里当France为真时(1,0,0),Germany为真时(0,1,0),Spain为真时(0,0,1)。同时,为了避免掉入“虚拟变量陷阱”,我们删除其中一列,这里选择剔除Spain这一列。由于OneHotEncoder工具包会将独热编码后的数据放在最前面,因此此时变量顺序已经发生改变。展示效果如图11。
图11 虚拟变量变换后的数据集
数据的标准化处理,可以在保持列内数据多样性的同时,尽量减少不同类别之间差异的影响,可以让机器公平对待全部特征。同样用sklearn里StandardScaler包处理。这里训练集合测试集要进行相同的处理。
在对单变量分析完之后,我们来观察下特征变量之间的相关性,当特征之间出现强相关性时,我们应该考虑剔除变量强相关性,下面给出了特征变量之间的相关系数矩阵。由于离散变量较多,因此这里采用相关系数矩阵展示。
表2 特征变量相关关系表
Geography | Gender | Tenure | Balance | NumOfProducts | HasCrCard | IsActiveMember | EstimatedSalary | CreditScore_range | age_group |
---|---|---|---|---|---|---|---|---|---|
1.000 | 0.005 | 0.004 | 0.069 | 0.004 | -0.009 | 0.007 | -0.001 | -0.000 | 0.018 |
1.000 | 0.015 | 0.012 | -0.022 | 0.006 | 0.023 | -0.008 | -0.006 | -0.022 | |
1.000 | -0.012 | 0.013 | 0.023 | -0.028 | 0.008 | 0.004 | -0.010 | ||
1.000 | -0.304 | -0.015 | -0.010 | 0.013 | 0.010 | 0.027 | |||
1.000 | 0.003 | 0.010 | 0.014 | 0.009253 | -0.030 | ||||
1.000 | -0.012 | -0.010 | -0.002 | -0.015 | |||||
1.000 | -0.011 | 0.020 | 0.088 | ||||||
1.000 | 0.005 | -0.003 | |||||||
1.000 | -0.003 | ||||||||
1.000 |
由相关系数矩阵可以看出,特征变量之间相关关系较弱,因此我们可以将全部特征纳入。
关于分类算法,目前几大主流基学习器如决策树、支持向量机、朴素贝叶斯、逻辑回归都可以实现,集成学习效果一般较基学习器好,包括随机森林、GBDT、还有比赛使用最多的XGBoost。同时,深度学习也成为数据挖掘上的重要工具,如LM神经网络、BP神经网络、RNN、CNN等。这里使用了决策树、支持向量机、朴素贝叶斯、逻辑回归、XGBoost、LM神经网络进行建模,通过对不同模型的学习能力评估选择能够预测客户流失的最优模型。
通过不同模型对样本训练集的学习,之后用测试集来验证模型的学习能力。这里选用了分类度量指标precision、recall、F1值、准确度、ROC曲线、AUC值、Kappa值来比较各模型。
首先给出各模型的precision、recall、F1值对比,结果见下图。
表3 各模型性能度量
measure | SVM | LR | Tree | RF | XGB | Bayes | LM |
---|---|---|---|---|---|---|---|
precision | 0.79 | 0.80 | 0.80 | 0.85 | 0.86 | 0.80 | 0.84 |
recall | 0.78 | 0.70 | 0.79 | 0.86 | 0.87 | 0.73 | 0.81 |
F1 | 0.78 | 0.73 | 0.79 | 0.85 | 0.86 | 0.75 | 0.82 |
Accuracy | 77.90% | 70.25% | 78.70% | 85.65% | 86.55% | 73.15% | 81.20% |
从上表我们可以看出,XGBoost、随机森林、LM神经网络三种模型所有评价指标都在0.8以上,性能较好。其中XGBoost的四个指标都是最大值,随机森林次之,两者precision、recall、F1值都很接近,准确率相差接近1%,之后是LM神经网络。其余的模型较XGBoost、随机森林、LM神经网络表现差。
我们再来比较一下ROC曲线和AUC值。见图12
图12 ROC曲线
从ROC曲线可以看出,XGBoost包含了其他模型的ROC曲线曲线,最靠近ROC曲线的左上角。并且比较各模型的AUC值,XGBoost也是最大的。从ROC曲线结合AUC值,认为XGBoost表现最好。
最后,再来比较一下各模型的kappa值。Kappa统计量比较的是分类器与仅仅基于随机的分类器的性能,它是根据混淆矩阵计算得出,和混淆矩阵评价保持一致。Kappa统计量的值在-1到1之间,小于0表示分类器性能不如随机分类,大于1表示比随机分类效果好,越接近于1表示分类性能越好。一般kappa值大于0.4才认为具有较好的分类性能。kappa统计量的计算公式是:
k = p 0 − p c 1 − p c k=\frac{p_0-p_c}{1-pc} k=1−pcp0−pc
其中,
p 0 = ∑ i = 1 r x i i N , p c = ∑ i = 1 2 ( x i . x . i ) N 2 p_0=\frac{\sum_{i=1}^rx_{ii}}{N},p_c=\frac{\sum_{i=1}^2(x_{i.}x_{.i})}{N^2} p0=N∑i=1rxii,pc=N2∑i=12(xi.x.i)
xij表示的是混淆矩阵对应的值。
我们用图像直观地表现各模型的kappa表现。
图13 各模型kappa值比较
由kappa值的条形图可以看出,XGBoost、RF、LM的值都超过了0.4,可以认为是较好的分类器。其中XGBoost最大,我们认为XGBoost的分类性能最好。
综合以上评价指标,XGBoost表现最好。下面将针对XGBoost学习器进行调参,使模型表现达到最优。
由上一节的模型评价,确定了本次关于数据集预测银行流失客户的最优算法是XGBoost。同时,XGBoost也是目前在数据挖掘比赛中表现非常好的模型。在本节首先介绍XGBoost算法的原理以及优点,然后结合数据集对XGBoost模型进行调参,获取最优超参数,最后输出模型调参后的性能指标。
XGBoost是boosting算法的其中一种。Boosting算法的思想是将许多弱分类器集成在一起形成一个强分类器。因为XGBoost是一种提升树模型,所以它是将许多树模型集成在一起,形成一个很强的分类器。而所用到的树模型则是CART回归树模型。
XGBoost的算法思想就是不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数,去拟合上次预测的残差。当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数,最后只需要将每棵树对应的分数加起来就是该样本的预测值。
XGBoost的目标函数是:
O b j ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + ∑ k = 1 k Ω ( f k ) + c o n s t a n t   Obj (t)= \sum_{i=1}^n l(y_i,\widehat{y}_i^{(t-1)}+f_t(x_i))+\sum_{k=1}^k\Omega(f_k)+constant\, Obj(t)=i=1∑nl(yi,y i(t−1)+ft(xi))+k=1∑kΩ(fk)+constant
其中,
f t ( x ) = w q ( x ) , w ∈ R T , q : R d → ( 1 , 2 , ⋯ T ) f_t(x)=w_q(x), w\in R^T,q:R^d\rightarrow{(1,2,\cdots T)} ft(x)=wq(x),w∈RT,q:Rd→(1,2,⋯T)
Ω ( f t ) = γ T + 1 2 ∑ j = 1 T w j 2 \Omega(f_t)=\gamma T+\frac{1}{2}\sum_{j=1}^Tw_j^2 Ω(ft)=γT+21j=1∑Twj2
目标函数由两部分构成,第一部分用来衡量预测分数和真实分数的差距,另一部分则是正则化项。正则化项同样包含两部分,T表示叶子结点的个数,w表示叶子节点的分数,q是叶子的索引号。γ可以控制叶子结点的个数,λ可以控制叶子节点的分数不会过大,防止过拟合。
用泰勒展开来近似目标函数:
f ( x + Δ x ) ≃ f ( x ) + f ′ ( x ) Δ x + 1 2 x 2 f(x+\Delta x)\simeq f(x)+f^{'}(x)\Delta x+\frac{1}{2}x^2 f(x+Δx)≃f(x)+f′(x)Δx+21x2
定义
g i = ∂ y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) , h i = ∂ y ^ ( t − 1 ) 2 l ( y i , y ^ ( t − 1 ) ) g_i=\partial_{\widehat{y}^{(t-1)}}l(y_i,\widehat{y}^{(t-1)}),h_i=\partial_{\widehat{y}^{(t-1)}}^2l(y_i,\widehat{y}^{(t-1)}) gi=∂y (t−1)l(yi,y (t−1)),hi=∂y (t−1)2l(yi,y (t−1))
所以,目标函数在泰勒展开下近似为:
O b j ( t ) ≃ ∑ i = 1 n [ l ( y i , y ^ i ( t − 1 ) ) + g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) + c o n s t a n t   Obj^{(t)}\simeq \sum_{i=1}^n[l(y_i,\widehat{y}_i^{(t-1)})+g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)]+\Omega(f_t)+constant\, Obj(t)≃i=1∑n[l(yi,y i(t−1))+gift(xi)+21hift2(xi)]+Ω(ft)+constant
在对正则化新的定义下,我们可以将目标函数改写:
O b j ( t ) ≃ ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) = ∑ i = 1 n [ g i w q ( x i ) + 1 2 h i w q ( x i ) ] + γ T + λ 1 2 ∑ j = 1 T w j 2 = ∑ j = 1 T [ ( ∑ i ∈ I j g i ) w j + 1 2 ( ∑ i ∈ I j h i + λ ) w j 2 ] + λ T   Obj^{(t)}\simeq \sum_{i=1}^n[g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)]+\Omega(f_t)\\=\sum_{i=1}^n[g_iw_{q(x_i)}+\frac{1}{2}h_iw_{q(x_i)}]+\gamma T+\lambda \frac{1}{2}\sum_{j=1}^Tw_j^2\\=\sum_{j=1}^T[(\sum_{i\in{I_j}}g_i)w_j+\frac{1}{2}(\sum_{i\in{I_j}}h_i+\lambda)w_j^2]+\lambda T\, Obj(t)≃i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)=i=1∑n[giwq(xi)+21hiwq(xi)]+γT+λ21j=1∑Twj2=j=1∑T[(i∈Ij∑gi)wj+21(i∈Ij∑hi+λ)wj2]+λT
其中,
I j = { i ∣ q ( x i ) = j } I_j=\left \{i|q(x_i)=j\right\} Ij={i∣q(xi)=j}
为了简化,我们令
G j = ∑ i ∈ I j g i , H j = ∑ i ∈ I j h i G_j=\sum_{i\in{I_j}}g_i,H_j=\sum_{i\in{I_j}}h_i Gj=i∈Ij∑gi,Hj=i∈Ij∑hi
最终公式可以化简为:
O b j ( t ) = ∑ j = 1 T [ ( ∑ i ∈ I j g i ) w j + 1 2 ( ∑ i ∈ I j h i + λ ) w j 2 ] + λ T = ∑ j = 1 T [ G j w j + 1 2 ( H j + λ j 2 ] + γ T   Obj^{(t)}=\sum_{j=1}^T[(\sum_{i\in{I_j}}g_i)w_j+\frac{1}{2}(\sum_{i\in{I_j}}h_i+\lambda)w_j^2]+\lambda T\\=\sum_{j=1}^T[G_jw_j+\frac{1}{2}(H_j+\lambda_j^2]+\gamma T\, Obj(t)=j=1∑T[(i∈Ij∑gi)wj+21(i∈Ij∑hi+λ)wj2]+λT=j=1∑T[Gjwj+21(Hj+λj2]+γT
通过对wj求导等于0,可以得到
w j ∗ = − G j H j + λ w_j^*=-\frac{G_j}{H_j+\lambda} wj∗=−Hj+λGj
将最优解代入可得:
O b j = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T   Obj=-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}+\gamma T\, Obj=−21j=1∑THj+λGj2+γT
(1)精度高。XGBoost的损失函数用到了二阶导数信息,而GBDT只用到一阶;
在大多数情况,数据集都无法避免出现null值的情况,从而导致梯度稀疏,在这种情况下,XGBoost为损失函数指定了默认的梯度方向,间接提升了模型精度和速度。
(2)速度快。XGBoost在生成树的最佳分割点时,放弃了贪心算法,而是采用了一种从若干备选点中选择出最优分割点的近似算法,而且可以多线程搜索最佳分割点。XGBoost还以块为单位优化了特征在内存中的存取,解决了Cache-miss问题,间接提高了训练效率。根据论文所说,通过这些方法优化之后,XGBoost的训练速度比scikit-learn快40倍。
(3)可扩展性高。GBDT的基分类器是CART,而XGBoost的基分类器支持CART,Linear,LR;Xgboost的目标函数支持linear、logistic、softmax等,可以处理回归、二分类,多分类问题。另外,XGBoost还可以自定义损失函数。
(4)防止过拟合。XGBoost在损失函数里加入了正则项,降低模型的方差,使模型更简单,防止过拟合,还能自动处理one-hot特征。
XGBoost的参数很多,我们主要调节的参数如下:
n_estimators:迭代次数,也就是树的个数;
max_depth:树的深度;
min_child_weight:最小叶子节点样本权重和,值较大是可以避免过拟合;
Gamma:指定了节点分裂所需的最小损失函数下降值;
subsample, colsample_bytree:这两个值一般都是0.8;
lambda, alpha:正则化参数;
learning_rat:学习速率。
调参方法选用网格搜索法,在内部设置好交叉验证的折数,这里设置为5,得分函数选择的是AUC,最后让模型输出最佳得分模型。
表4 最优参数
learning_rat | n_estimators | max_depth | min_child_weight | Gamma | subsample | colsample_bytree | lambda | alpha | score |
---|---|---|---|---|---|---|---|---|---|
0.1 | 100 | 7 | 2 | 0 | 0.8 | 0.8 | 3 | 1 | 0.9637 |
最后,对调整参数后的模型再次评估。这里采用仍采用原来的评价准则。最后基于评价准则生成了表5和图14。
表5 precision、recall、F1、kappa、accurancy
category | precision | recall | F1 | Kappa | Accuracy |
---|---|---|---|---|---|
total | 0.86 | 0.87 | 0.86 | 0.55 | 86.95% |
0 | 0.90 | 0.94 | 0.92 | - | - |
1 | 0.70 | 0.57 | 0.62 | - | - |
从类别来看,模型对留存客户的预测更为准确,精确度、召回率、F1值都达到了90%左右,但是对已流失客户的表现不是很好。在所有判定为流失客户中有70%是真实流失的,在所有实际流失的客户中仅判定对了57%。但模型总体的精确度是86%、召回率87%、F1值86%、kappa统计量为0.55,准确度是86.95%。总体预测的准确度还是较高的,较未调参的模型提高了0.4个百分点。
下面看一下ROC曲线。
图14 XGBoost的ROC曲线
观察ROC曲线图,曲线靠近左上角,即在假阳率很低的情况下真阳率较高,并且AUC面积为0.8756,表示模型效果良好,可用于预测。
客户流失的根源在于通过建立客户流失模型使银行能从源头上控制客户流失,防患于未然从而有效的防止客户流失,在日益激烈的市场竞争中,防范客户流失不是“亡羊补牢式”的被动行为,而是贯穿银行经营管理始终的营销策略。防范客户流失、开发新市场、发展新客户都具有重要意义。从营销策略效率考虑,防范客户流失比发展新客户更加经济。主动式的防范客户流失即为银行客户关系管理中长期的策略,重点是通过挽留和关怀客户,提高客户的活跃程度来防止客户向流失状态的转换。由图3-4可知,银行活跃用户的流失率远低于非活跃用户。本节旨在通过分析影响客户流失模型的重要因子,结合之前的描述性分析,为银行挽留和关怀客户提出建议。
通过上一节建立的最佳模型,通过weight(权重:所有树中一个特征被用来分裂数据的次数)的方法来衡量特征的重要性,最后得出的特征重要性排序见图15。
图15 特征重要性排序
特征值越大,说明特征越重要。从图15可以清楚的显示出在XGBoost模型下特征的重要程度。其中,特征重要程度得分超过400的有EstimatedSalary(估计收入)、Balance(存贷款)、Tenure(用户使用年数)、CreditScore_range(信用分分组)、age_group(年龄分组)。400分以下的按得分排序分别是NumOfProducts(产品数量)、IsActiveMenber(是否为活跃用户)、Geography_Germany(地区_德国)、Gender(性别)、Geography_Franch(地区_法国),HasCrCard(是否有信用卡)。
1、目标人群定位。根据特征重要性排序,特征估计收入和存贷款情况是影响预测客户流失的最重要因素。结合之前的特征频数分布图得知,该银行客户各收入层分布均匀,说明银行并没有重视目标收入客户定位,没有明确目标人群,更多客户选择存入较少的钱。目标客户定位有助于银行指定针对性策略,提供适合目标客户的商品和服务,提高银行的竞争力。例如中国工商银行以中等收入阶层为重点目标客户;中国农业银行重点服务“三农”,建立城乡一体化;中国银行更倾向于中高端客户和大型企业。因此,认为该银行有必要明确目标客户群。
2、用户关怀策略。由图15知,客户使用该银行产品年数也是影响模型的重要因素。为了挽留老客户,推出相应的关怀和激励策略十分有必要。对于使用产品年数较长的客户,银行可以采用会员积分制、会员优先服务、老客户感恩回馈等活动,令客户感受到银行对客户的重视;对于银行新用户,可通过各种优惠活动吸引客户使用,如与政府和商家合作推出打折活动,同时也要通过会员积分制激励新用户向老用户的转换。
3、高信用评级激励制度。信用得分也是预测客户流失的重要影响因素。信用得分是银行用来评估客户是否具有获得信用卡的资格。根据图3-3,可以得知该银行没有取得信用卡的用户流失率更高,但是有信用卡的用户流失率也达到了16%左右。因此,为了挽留银行高信用用户,银行可推出一系列信用卡激励策略,一来可以挽留更多高信用用户,二可以刺激其他未办理信用卡的高信用评级用户申请该银行信用卡。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.stats as stats
import seaborn as sns
import math
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from imblearn.over_sampling import SMOTE
from sklearn.metrics import classification_report
from collections import Counter
from sklearn import tree
from sklearn import svm
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
import tensorflow as tf
import keras
from keras.models import Sequential
from keras.layers.core import Dense,Activation
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import roc_curve,roc_auc_score
from sklearn.metrics import cohen_kappa_score
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import GridSearchCV
%matplotlib inline
#导入数据
customers_data = pd.read_csv('G://Churn_Modelling.csv')
X = customers_data.loc[:,['CreditScore', 'Geography', 'Gender', 'Age', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary']]
y=customers_data.loc[:,'Exited']
print(Counter(y))#打印类标签
#字符变量量化
labelencoder1 = LabelEncoder()
X.Geography= labelencoder1.fit_transform(X.Geography)
labelencoder2 = LabelEncoder()
X.Gender = labelencoder2.fit_transform(X.Gender)
#变量频数分布和箱线图
def visual_exploratory(x):
for var in x.select_dtypes(include = [np.number]).columns :
print( var + ' : ')
descStats =x[var].describe()
mu = "%.2e" % descStats['mean']
std = "%.2e" % descStats['std']
maxVal = "%.2e" % descStats['max']
minVal = "%.2e" % descStats['min']
x[var].plot('hist')
titleText = 'Histogram of '+ var +'\n Mean ='+mu+ ' Std='+std+'\n Max='+maxVal+' Min='+minVal
plt.show()
visual_exploratory(customers_data )
# ploting the box plot to visually inspect numeric data
def boxPlot_exploratory(x):
for var in x.select_dtypes(include = [np.number]).columns :
print( var + ' : ')
x.boxplot(column = var)
plt.show()
boxPlot_exploratory(X)
Xgroup=X.copy()
#将信用分进行分组
# Helper function that will create and add a new column tof credit score range the data frame
def creditscore(data):
score = data.CreditScore
score_range =[]
for i in range(len(score)) :
if (score[i] < 600) :
score_range.append(1) # 'Very Bad Credit'
elif ( 600 <= score[i] < 650) :
score_range.append(2) # 'Bad Credit'
elif ( 650 <= score[i] < 700) :
score_range.append(3) # 'Good Credit'
elif ( 700 <= score[i] < 750) :
score_range.append(4) # 'Very Good Credit'
elif score[i] >= 750 :
score_range.append(5) # 'Excellent Credit'
return score_range
# converting the returned list into a dataframe
CreditScore_category = pd.DataFrame({'CreditScore_range': creditscore(Xgroup)})
#年龄分组
def agegroup(data):
age = data.Age
age_range =[]
for i in range(len(age)) :
if (age[i] < 30) :
age_range.append(1) # 'Between 18 and 30 year'
elif ( 30 <= age[i] < 40) :
age_range.append(2) # 'Between 30 and 40 year'
elif ( 40 <= age[i] < 50) :
age_range.append(3) # 'Between 40 and 50 year'
elif ( 50 <= age[i] < 60) :
age_range.append(4) # ''Between 50 and 60 year'
elif ( 60 <= age[i] < 70) :
age_range.append(5) # 'Between 60 and 70 year'
elif ( 70 <= age[i] < 80) :
age_range.append(6) # 'Between 70 and 80 year'
elif age[i] >= 80 :
age_range.append(7) # ''Above 80 year'
return age_range
def visual_exploratory(x):
for var in x.select_dtypes(include = [np.number]).columns :
print( var + ' : ')
descStats =x[var].describe()
mu = "%.2e" % descStats['mean']
std = "%.2e" % descStats['std']
maxVal = "%.2e" % descStats['max']
minVal = "%.2e" % descStats['min']
x[var].plot('hist')
titleText = 'Histogram of '+ var +'\n Mean ='+mu+ ' Std='+std+'\n Max='+maxVal+' Min='+minVal
plt.show()
visual_exploratory(AgeGroup_category )
visual_exploratory(CreditScore_category)
# converting the returned list into a dataframe
AgeGroup_category = pd.DataFrame({'age_group': agegroup(X)})
#插入Xgroup, CreditScore_category,删去'CreditScore','Age'
Xgroup=pd.concat([Xgroup, CreditScore_category],axis=1)
Xgroup=pd.concat([Xgroup,AgeGroup_category],axis=1)
Xgroup=Xgroup.drop(['CreditScore','Age'],axis=1)
#相关性检验
Xcorr=Xgroup
print(Xcorr.corr())
g=sns.pairplot(Xcorr,vars=['Geography','Gender','Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
#划分训练集、测试集
Xgroup_train, Xgroup_test, ygroup_train, ygroup_test = train_test_split(Xgroup, y, test_size = 0.2, random_state =40)
model_smote = SMOTE() # 建立SMOTE模型对象
xgroup_smote, ygroup_smote = model_smote.fit_sample(Xgroup_train, ygroup_train) # 输入数据并作过抽样处理
xgroup_smote = pd.DataFrame(xgroup_smote, columns=[ 'Geography', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group']) # 将数据转换为数据框并命名列名
ygroup_smote = pd.DataFrame(ygroup_smote,columns=['Exited']) # 将数据转换为数据框并命名列名
smotegroup_resampled = pd.concat([xgroup_smote, ygroup_smote],axis=1) # 按列合并数据框
groupby_data_smote = smotegroup_resampled.groupby('Exited').count() # 对label做分类汇总
#smote处理后分类变量离散化
Xgroup_smote_train=pd.DataFrame(Xgroup_smote,columns=['Geography_Franch','Geography_Germany','Geography_Spain', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
length=len(xgroup_smote)
for i in range(length):
if xgroup_smote.Geography[i]>=1.5:
xgroup_smote.Geography[i]=int(2)
elif xgroup_smote.Geography[i]>=0.5 and xgroup_smote.Geography[i]<1.5:
xgroup_smote.Geography[i]=int(1)
else:
xgroup_smote.Geography[i]=int(0)
if xgroup_smote.Gender[i]>=0.5:
xgroup_smote.Gender[i]=1
else:
xgroup_smote.Gender[i]=0
if xgroup_smote.HasCrCard[i]>=0.5:
xgroup_smote.HasCrCard[i]=1
else:
xgroup_smote.HasCrCard[i]=0
if xgroup_smote.IsActiveMember[i]>=0.5:
xgroup_smote.IsActiveMember[i]=1
else:
xgroup_smote.IsActiveMember[i]
#独热编码
Xgroup_smote=xgroup_smote.copy()
onehotencoder = OneHotEncoder(categorical_features = [0])
Xgroup_smote = onehotencoder.fit_transform(Xgroup_smote).toarray()
Xgroup_smote_train=pd.DataFrame(Xgroup_smote,columns=['Geography_Franch','Geography_Germany','Geography_Spain', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
Xgrouphot_test=Xgroup_test.copy()
onehotencoder = OneHotEncoder(categorical_features = [0])
Xgrouphot_test = onehotencoder.fit_transform(Xgrouphot_test).toarray()
Xgrouphot_test=pd.DataFrame(Xgrouphot_test,columns=['Geography_Franch','Geography_Germany','Geography_Spain', 'Gender', 'Tenure', 'Balance', 'NumOfProducts', 'HasCrCard', 'IsActiveMember', 'EstimatedSalary','CreditScore_range','age_group'])
#虚拟变量处理
Xgroup_smote_train=Xgroup_smote_train.drop(['Geography_Spain'],axis=1)
Xgrouphot_test=Xgrouphot_test.drop(['Geography_Spain'],axis=1)
#标准化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
Xgroup_smote_train = sc.fit_transform(Xgroup_smote_train)
Xgrouphot_test = sc.transform(Xgrouphot_test)
#tree
clfgroup = tree.DecisionTreeClassifier()
clfgroup = clfgroup.fit(Xgroup_smote_train,ygroup_smote)
y_predgroup = clfgroup.predict(Xgrouphot_test)
accuracy1 = accuracy_score(ygroup_test, y_predgroup)
print("Accuracy: %.2f%%" % (accuracy1* 100.0))
print(classification_report(ygroup_test, y_predgroup))
#XGBoost
xgb_model2= xgb.XGBClassifier(objective ='binary:logistic',learning_rat= 0.1, n_estimators=100, max_depth= 6, min_child_weight= 1, seed= 0,
subsample=0.8, colsample_bytree= 0.8, gamma=0., reg_alpha= 2, reg_lambda= 1)
xgb_model2.fit(Xgroup_smote_train,ygroup_smote)
# make predictions for test data
predictions2 = xgb_model2.predict(Xgrouphot_test)
# evaluate predictions
accuracy2 = accuracy_score(ygroup_test, predictions2)
print("Accuracy: %.2f%%" % (accuracy2* 100.0))
print(classification_report(ygroup_test, predictions2))
#RF
clfrf = RandomForestClassifier(n_estimators=10, max_depth=None,min_samples_split=2, random_state=0)
clfrf=clfrf.fit(Xgroup_smote_train,ygroup_smote)
predictions_rf = clfrf.predict(Xgrouphot_test)
# evaluate predictions
accuracy_rf = accuracy_score(ygroup_test, predictions_rf)
print("Accuracy: %.2f%%" % (accuracy_rf* 100.0))
print(classification_report(ygroup_test, predictions_rf))
#svm
ygroup_smote_np=np.array(ygroup_smote).reshape(12694,)
sv = svm.SVC(kernel='rbf', C=1, gamma=1,probability=True)
sv=sv.fit(Xgroup_smote_train,ygroup_smote_np)
predictions_sv = sv.predict(Xgrouphot_test)
# evaluate predictions
accuracy_sv = accuracy_score(ygroup_test, predictions_sv)
print("Accuracy: %.2f%%" % (accuracy_sv* 100.0))
print(classification_report(ygroup_test, predictions_sv))
print(confusion_matrix(ygroup_test, predictions_sv))
#LR
lr=LogisticRegression()
lr=lr.fit(Xgroup_smote_train,ygroup_smote_np)
predictions_lr = lr.predict(Xgrouphot_test)
# evaluate predictions
accuracy_lr = accuracy_score(ygroup_test, predictions_lr)
print("Accuracy: %.2f%%" % (accuracy_lr* 100.0))
print(classification_report(ygroup_test, predictions_lr))
#bayes
by=GaussianNB()
by=by.fit(Xgroup_smote_train,ygroup_smote_np)
predictions_by = by.predict(Xgrouphot_test)
# evaluate predictions
accuracy_by = accuracy_score(ygroup_test, predictions_by)
print("Accuracy: %.2f%%" % (accuracy_by* 100.0))
print(classification_report(ygroup_test, predictions_by))
#LM
net=Sequential()
net.add(Dense(input_dim=11,output_dim=10))
net.add(Activation('relu'))
net.add(Dense(input_dim=10,output_dim=1))
net.add(Activation('sigmoid'))
net.compile(loss='mean_squared_error',optimizer='adam')
net.fit(Xgroup_smote_train,ygroup_smote,epochs = 1000, batch_size = 200)
pred_lm= net.predict_classes(Xgrouphot_test).reshape(len(ygroup_test))
prob_lm= net.predict(Xgrouphot_test).reshape(len(ygroup_test))
accuracy_lm = accuracy_score(ygroup_test, pred_lm)
print("Accuracy: %.2f%%" % (accuracy_lm* 100.0))
print(classification_report(ygroup_test, pred_lm)
#ROC
prob_sv=sv.predict_proba(Xgrouphot_test)
prob_lr=lr.predict_proba(Xgrouphot_test)
prob_by=by.predict_proba(Xgrouphot_test)
prob_rf=clfrf.predict_proba(Xgrouphot_test)
prob_xgb=xgb_model2.predict_proba(Xgrouphot_test)
prob_tree=clfgroup.predict_proba(Xgrouphot_test)
fpr_sv, tpr_sv, thresholds_sv = roc_curve(ygroup_test, prob_sv[:,1], drop_intermediate= True)
fpr_lr, tpr_lr, thresholds_lr = roc_curve(ygroup_test, prob_lr[:,1], drop_intermediate= True)
fpr_by, tpr_by, thresholds_by = roc_curve(ygroup_test, prob_by[:,1], drop_intermediate= True)
fpr_rf, tpr_rf, thresholds_rf = roc_curve(ygroup_test, prob_rf[:,1], drop_intermediate= True)
fpr_xgb, tpr_xgb, thresholds_xgb = roc_curve(ygroup_test, prob_xgb[:,1], drop_intermediate= True)
fpr_tree, tpr_tree, thresholds_tree = roc_curve(ygroup_test, prob_tree[:,1], drop_intermediate= True)
fpr_lm, tpr_lm, thresholds_lm= roc_curve(ygroup_test, prob_lm, drop_intermediate= True)
auc_lm=roc_auc_score(ygroup_test,prob_lm)
auc_sv=roc_auc_score(ygroup_test, prob_sv[:,1])
auc_lr=roc_auc_score(ygroup_test, prob_lr[:,1])
auc_by=roc_auc_score(ygroup_test, prob_by[:,1])
auc_rf=roc_auc_score(ygroup_test, prob_rf[:,1])
auc_xgb=roc_auc_score(ygroup_test, prob_xgb[:,1])
auc_tree=roc_auc_score(ygroup_test, prob_tree[:,1])
plt.plot(fpr_sv, tpr_sv,label= "SVM-AUC:%.4f"%auc_sv)
plt.plot(fpr_lr, tpr_lr,label= "LR-AUC:%.4f"%auc_lr)
plt.plot(fpr_by, tpr_by,label= "Bayes-AUC:%.4f"%auc_by)
plt.plot(fpr_rf, tpr_rf,label= "RF-AUC:%.4f"%auc_rf)
plt.plot(fpr_xgb, tpr_xgb,label= "XGB-AUC:%.4f"%auc_xgb)
plt.plot(fpr_tree, tpr_tree,label= "Tree-AUC:%.4f"%auc_tree)
plt.plot(fpr_lm, tpr_lm,label= "LM-AUC:%.4f"%auc_lm)
plt.legend()
plt.plot([0, 1], [0, 1])
plt.xlim(0,1)
plt.ylim(0,1)
plt.title('Roc curve')
plt.show()
#kappa
kappa_SVM=cohen_kappa_score(ygroup_test, predictions_sv)
kappa_LR=cohen_kappa_score(ygroup_test, predictions_lr)
kappa_Tree=cohen_kappa_score(ygroup_test, y_predgroup)
kappa_RF=cohen_kappa_score(ygroup_test, predictions_rf)
kappa_XGB=cohen_kappa_score(ygroup_test, predictions2)
kappa_Bayes=cohen_kappa_score(ygroup_test, predictions_by)
kappa_LM=cohen_kappa_score(ygroup_test, pred_lm)
num_list=[kappa_SVM,kappa_LR,kappa_Tree,kappa_RF,kappa_XGB,kappa_Bayes,kappa_LM]
name_list=['SVM','LR','Tree','RF','XGB','Bayes','LM']
plt.bar(name_list,num_list)
plt.title(u'各模型kappa值对比')
plt.show()
#调参
#n_estimators
cv_params = {'n_estimators': 80,100,120,140,160]}
other_params = {'learning_rat': 0.1, 'n_estimators':100, 'max_depth':6, 'min_child_weight':1,
'subsample':0.8, 'colsample_bytree': 0.8, 'gamma':0., 'reg_alpha':2, 'reg_lambda': 1}
optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
#max_depth、min_child_weight
cv_params = { 'max_depth': [3, 4, 5, 6, 7, 8, 9, 10], 'min_child_weight': [1, 2, 3, 4, 5, 6]}
other_params = {'learning_rat': 0.1, 'n_estimators':100, 'max_depth':6, 'min_child_weight':1,
'subsample':0.8, 'colsample_bytree': 0.8, 'gamma':0., 'reg_alpha':2, 'reg_lambda': 1}
model = xgb.XGBClassifier(**other_params)
optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=4)
optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
#gamma
cv_params = {'gamma': [0,0.1, 0.2, 0.3, 0.4, 0.5, 0.6]}
other_params = {'learning_rate': 0.1, 'n_estimators': 100, 'max_depth': 7, 'min_child_weight': 2, 'seed': 0,
'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0., 'reg_alpha':2, 'reg_lambda': 1}
model = xgb.XGBClassifier(**other_params)
optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=2)
optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
#reg_alpha、'reg_lambda
cv_params = {'reg_alpha': [0.2,0.4,0.6,0.8,1], 'reg_lambda': [3,4,5]}
other_params = {'learning_rate': 0.1, 'n_estimators': 100, 'max_depth': 7, 'min_child_weight': 2, 'seed': 0,
'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0., 'reg_alpha': 2, 'reg_lambda': 1}
model = xgb.XGBClassifier(**other_params)
optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=2)
optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
#learning_rate
cv_params = {'learning_rate': [0.01, 0.05, 0.07, 0.1, 0.2]}
other_params = {'learning_rate': 0.1, 'n_estimators': 100, 'max_depth': 7, 'min_child_weight': 2, 'seed': 0,
'subsample': 0.8, 'colsample_bytree': 0.8, 'gamma': 0., 'reg_alpha': 1, 'reg_lambda': 3}
model = xgb.XGBClassifier(**other_params)
optimized_GBM = GridSearchCV(estimator=model, param_grid=cv_params, scoring='roc_auc', cv=5, verbose=1, n_jobs=2)
optimized_GBM.fit(Xgroup_smote_train,ygroup_smote)
print('参数的最佳取值:{0}'.format(optimized_GBM.best_params_))
print('最佳模型得分:{0}'.format(optimized_GBM.best_score_))
#模型评估
xgb_model_xgb= xgb.XGBClassifier(objective ='binary:logistic',learning_rate=0.1, n_estimators=100, max_depth=7, min_child_weight=2, seed=0,
subsample=0.8, colsample_bytree=0.8, gamma=0., reg_alpha=1, reg_lambda=3)
xgb_model_xgb.fit(Xgroup_smote_train,ygroup_smote)
# make predictions for test data
predictions_xgb = xgb_model_xgb.predict(Xgrouphot_test)
# evaluate predictions
accuracy_xgb = accuracy_score(ygroup_test, predictions_xgb)
print("Accuracy: %.2f%%" % (accuracy_xgb* 100.0))
prob_xgb=xgb_model_xgb.predict_proba(Xgrouphot_test)
fpr, tpr, thresholds = roc_curve(ygroup_test, prob_xgb[:,1], drop_intermediate= True)
plt.plot(fpr, tpr,label= "auc")
auc=roc_auc_score(ygroup_test, prob_xgb[:,1])
plt.legend()
plt.plot([0, 1], [0, 1])
plt.xlim(0,1)
plt.ylim(0,1)
plt.title(auc)
plt.show()
xgb.plot_importance(xgb_model_xgb)
plt.show()
kappa=cohen_kappa_score(ygroup_test, pxgb)