一文速学-XGBoost模型算法原理以及实现+Python项目实战_xgboost金融领域模型实战

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

一、XGBoost模型概述

1.发展历史

  1. 2014年:XGBoost由陈天奇在《XGBoost: A Scalable Tree Boosting System》一文中首次提出。
  2. 2015年:XGBoost在Kaggle竞赛中大放异彩,成为数据科学家和机器学习工程师的首选算法之一。
  3. 2016年:XGBoost发布了C++和Python两个版本,支持更多的特征工程和模型调优功能,极大地提高了算法的效率和可扩展性。
  4. 2017年:XGBoost获得了KDD Cup 2017竞赛中的多个奖项,并且成为Spark MLlib中的重要组件。
  5. 2018年:XGBoost在Microsoft Azure ML Studio中被引入,成为Azure Machine Learning的核心组件之一。
  6. 2019年:XGBoost发布了GPU版本,可以在GPU上加速模型训练和预测,大大提高了算法的计算速度。
  7. 2020年:XGBoost被应用在各种领域,如金融、医疗、自然语言处理和图像识别等,成为机器学习领域的一个重要里程碑。

这里通过简述的发展史可以得到我们可以通过python调用此算法,而且也可以通过调用GPU提高了算法的计算速度。由此可见XGBoost算法的受欢迎程度。

2.算法改进之处

XGBoost是一种基于梯度提升决策树(Gradient Boosting Decision Tree,GBDT)的机器学习算法,旨在优化和加速GBDT的训练过程,并提高模型的准确性和泛化能力。

我们拿这个XGBoost与其他GBDT的算法进行对比:

算法差异点GBDTXGBoostLightGBMCatBoost
弱学习器CART回归树1.CART回归树 2.线性学习器 3.Dart树Leaf-wise树对称树
寻找分裂点贪心算法近似算法直方图算法预排序算法
稀疏值处理稀疏感知算法EFB(互斥特征捆绑)
类别特征不直接支持,可自行编码后输入模型同GBDT直接支持,GS编码直接支持,Ordered TS编码
并行支持不可以可以可以可以

XGBoost的算法原理较于GBDT算法的改进包括以下几个方面:

1.损失函数

XGBoost采用泰勒展开式(Taylor expansion)来近似损失函数,其中损失函数可以是回归问题中的均方误差(MSE)或分类问题中的交叉熵(cross-entropy)。XGBoost通过二阶泰勒展开式考虑了损失函数的一、二阶导数,从而提高了模型的预测精度。

2.分裂点选择

XGBoost使用贪心算法来选择最优的分裂点(split point),即使得损失函数最小的分裂点。在寻找最优分裂点的过程中,XGBoost通过对特征值进行排序来加速计算,同时引入了直方图(histogram)和近似算法(approximate algorithm)来进一步提高计算效率。

3.剪枝策略

XGBoost采用与CART决策树类似的剪枝策略,通过设定叶子节点的最小权重和最大深度来控制模型的复杂度,并避免过拟合。剪枝策略可以在模型训练过程中或之后进行。

4.正则化

XGBoost还采用正则化方法来控制模型的复杂度,避免过拟合。具体来说,XGBoost支持两种正则化方法:L1正则化和L2正则化。通过调节正则化参数的值,可以实现对模型复杂度的灵活控制。

5.学习率

XGBoost还引入了学习率(learning rate)的概念,用于控制每次迭代时模型参数的更新幅度,避免过拟合。学习率通常设置为小于1的值,例如0.1或0.01。

6.提前停止

为了避免过拟合和提高模型的训练效率,XGBoost还支持提前停止(early stopping)功能。该功能会在训练过程中监测验证集的损失函数,当连续若干次迭代中验证集的损失函数没有下降时,就停止训练,避免过拟合和浪费计算资源。

二、XGBoost算法原理

在了解了XGBoost与GBDT算法差距之后我们不妨和学习GBDT算法一样整体来一遍清楚哪些计算节点增加了新的处理。

首先基础的流程还是和GBDT一致的:

1.初始化构造目标函数

将所有样本的权重设置为相等的值,建立一个初始模型作为基准模型,可以设置为简单的平均值或者是中位数,而XGBoost中加入了正则项,用来控制基学习器树的结构,目标函数定义如下:

例如建立一个弱分类器F0(x)=argmin_{c}\sum_{i=1}^{N}L(y_{i},c),c即为平均值。但是我们在每次迭代的过程中添加一个新函数f(x):

  • \hat{y}_{i}^{(t)}是第t次迭代的预测值。
  • \hat{y}_{t}^{(t-1)}t-1次迭代的预测值。
  • f_{t}(x_{i})是第t颗树,也就是我们第t次迭代需要得到的树。

也就是t个模型的预测值等于前t个模型的预测值+当前正在训练第t个模型的预测值。

那么上述公式简化为:

Obj{t}=\sum_{i=1}{n}l(y_{i},\hat{y}_{i}{(t)})+\sum_{k=1}{t}\Omega (f_{k})

2.目标函数变换

我们可以讲上述公式变换:

首先将算法模型y_{i}{t}=y_{i}{t-1}+f_{t}(x_{i})带入取代y_{i}^{t},将后面基学习器树的复杂度进行拆分,拆成前t-1棵树的复杂度加上当前模型树的复杂度,又因为我们当时正在训练第t棵树,所以针对于前k棵树都是常量,所以现在我们的目标函数又可以写成:

Obj{t}=\sum_{i=1}{n}l(y_{i},\hat{y}_{i}^{(t-1)}+f_{t}(x_{i}))+\Omega (f_{t})+constant

这里我们考虑平方损失,此时目标函数又可以变形为:

Obj{(t)}=\sum_{i=1}{n}(2(y_{i}-\hat{y}_{i}{(t-1)})f_{t}(x_{i})+f_{t}(x_{i})+f_{t}(x_{i}){2})+\Omega (f_{t})+constant

根据上面我们就构造好了目标函数,但是为了将其进行简化,我们将其进行泰勒二阶展开

泰勒二阶展开式一般形式如下:

f(x+\Delta x)=f(x)+f{}'(x)\Delta x+\frac{1}{2}f{}'{}'(x)\Delta x^{2}

此时我们定义f(x)=\sum_{i=1}{n}l(y_{i},\hat{y}_{i}{(t)}),\Delta x=f_{t}.

目标函数利用泰勒展开式就可以变成:

Obj{t}=\sum_{i=1}{n}l(y_{i},\hat{y}_{i}{(t-1)}+g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f{}'{}'(x_{i}){(2)})+\Omega (f_{t})+constant

其中

因为我们的g_{i}h_{i}都是和前t-1个学习器相关,所以都为常数,那么简化后的目标函数就为:

min\sum_{i=1}{n}[L(y_{i},y_{i}{t-1})+g_{i}f_{t}(x_{i})+\frac{1}{2}h_{i}f_{t}^{2}(x_{i})]+\Omega (f_{t})

变换优势总结
  1. 变换后的目标函数可以被二阶泰勒展开,使得目标函数在每个节点的损失函数可以被表示为关于该节点预测值的二次函数,这样可以在计算最优分裂点时,使用牛顿法或拟牛顿法等高效的优化算法来求解。
  2. 变换后的目标函数可以解决梯度爆炸和梯度消失问题,这是由于使用指数函数和对数函数进行变换,将目标函数的值范围映射到了一个合适的区间,从而避免了数值溢出和数值不稳定的情况。
  3. 变换后的目标函数可以解决分类问题的类别不平衡问题,这是由于在分类问题中,负样本数量通常远大于正样本数量,导致算法容易偏向于负样本。通过将目标函数变换为指数损失函数或对数损失函数,可以对正负样本进行加权,从而解决类别不平衡问题。

3.将树引入目标函数

那么得到了想要的目标函数,我们现在就需要将树引入到我们的目标计算函数中,去取代f_{t}(x_{i})\Omega (k_{t}),我们依旧参照GBDT的计算原理和过程,也就是XGBoost的预测原理。

使用f_{t}(x)=\omega _{q}(x),\omega \epsilon R{T},q:R{d}->{1,2,3,...,T}

  • \omega代表树中叶子节点的权重
  • q代表的是树的结构

对于决策树的例子大家可以参考我的上篇文章:

一文速学-GBDT模型算法原理以及实现+Python项目实战

这里不再复述,仅讲XGBoost改动,我们知道单独的使用GBDT模型,容易出现过拟合,在实际应用中往往使用 GBDT+LR的方式做模型训练。一般情况下,我们的树模型越深越茂密那么复杂度越高,或者叶子节点值越大模型复杂度越高。

在XGBoost算法的实现中,是采用下式来衡量模型复杂度的:

\Omega (f_{k})=\alpha T+\frac{1}{2}\lambda \sum_{j=1}{J}w_{j}{2}

其中T代表叶子节点个数,\sum_{j=1}{J}w_{j}{2}:各个叶子节点值的求和,\alpha ,\lambda:超参数,控制惩罚程度。

那么我们将原目标函数的f_{t}(x_{i})\Omega (k_{t})给取代掉:

min\sum_{i=1}{n}[g_{j}w_{q(x_{i})}+\frac{1}{2}h_{i}w_{q(x_{i})}{2}]+\alpha T+\frac{1}{2}\lambda \sum_{j=1}^{J}\omega _{j}^{2}

那么此时我们定义在叶子结点j中的实例的集合为:

计算损失函数时是以样本索引来遍历的\sum_{i=1}^{n}总共n个样本,计算所有叶子节点样本的损失可以为:

\sum_{i\epsilon I_{j}}^{}g_{i}w_{j}

所以此时目标函数就可以写为:

\sum_{j=1}^{J}[(\sum_{i\epsilon I_{j}}^{}g_{i})w_{j}+(\frac{1}{2}\sum_{i\epsilon I_{j}}{}h_{i})w_{j}{2}]+\alpha T+\frac{1}{2}\lambda \sum_{j=1}{J}w_{j}{2}

=\sum_{j=1}^{J}[(\sum_{i\epsilon I_{j}}^{}g_{i})w_{j}+\frac{1}{2}(\sum_{i\epsilon I_{j}}h_{i}+\lambda )w_{j}^{2}]+\alpha T

大家如果能看到这里已经差不多能将此目标函数推导出来了,但是这里我们还需要获得最优叶子结点的值,也就是最小值。此时我们需要将上述公式再度变换:

G_{j}=\sum_{i\epsilon I_{j}}^{}g_{i}

H_{j}+\lambda =\sum_{i\epsilon I_{j}}^{}h_{i}+\lambda

此时目函数就为G_{j}w_{j}+\frac{1}{2}(H_{j}+\lambda )w_{j}^{2}是一个二次函数,对于二次函数这里补充一个大家或许真的的知识点,也就是得到这个函数的最小值。我们需要先求导去零得到极值点的x,再带入原方程。

那么对其求导很容易得到x=-\frac{G}{H},当x=-\frac{G}{H}时对应方程的值为-\frac{1}{2}*\frac{G^{2}}{H}.

接着我们定义:

  • G_{j}=\sum_{i\epsilon I_{j}}^{}g_{i}
  • H_{j}=\sum_{i\epsilon I_{j}}^{}h_{i}

那么目标函数就为:

那么到这一步目标函数最优结构就确定了,但是针对于一棵树,模型的结构可能有很多种,此刻我们需要选择最优树结构,XGBoost算法采用的动态规划为贪心算法,去构建一颗最优的树。

不知道大家看到现在还有几个愿意看下去贪心算法构建最优树的,但是XGBoost算法的计算原理就是如此,要了解其计算过程这点是绕不过的,该学习了解的还是得下点功夫。

4.构建最优树(贪心算法)

贪心算法的模板下面给出:

  1. 确定问题的优化目标,即需要优化的指标。
  2. 将问题分解成若干个子问题,每个子问题都可以采取贪心策略求解。
  3. 对于每个子问题,定义一个局部最优解,然后利用贪心策略选择当前状态下的最优解,更新全局最优解。
  4. 对于更新后的全局最优解,判断是否满足问题的终止条件。如果满足,算法终止;如果不满足,继续执行步骤3。

而对于树模型的优劣程度我们是根据信息增益来判断的,也就是使用熵来进行评估不确定度,CART中使用的是基尼系数的概念。对于XGBoost算法来说我们比较Obj的大小差距就可拟作信息增益Obj_{new}-Obj_{old}

我们是要获得最大信息增益,就需要遍历所以特征计算增益,选取增益大的进行切分构造树模型max=Obj_{new}-Obj_{old}

根据这个原理我们就可以构造一棵使目标函数最小的树模型,然后按照这个逻辑,一次构造K棵树,然后最终将所有的树加权就是我们最终的模型:

f_{K}(x)=f_{K-1}(x)+f_{k}(x)=\sum_{k=1}^{K}f_{k}(x)

讲到现在XGBoost已经讲完损失函数和分裂点选择两个核心的变动,还有另外四个改进,暂时先不作很深入的讲解,剪枝策略就详细介绍在RF算法中,正则化和提前停止策略我将会在下一篇详细讲述。下面进入实战实现环节。

三、XGBoost实战-贷款违约预测模型

或许大家觉得公式推导很繁琐,但是如果不做以上推导的话,无论是面试还是答辩无法证实你确实会使用XGBoost算法,而且面对XGBoost算法调优以及参数改进都是比较困难的。而且XGBoost一般直接使用自带的XGBoost库Python编程就好了,很少自行构造XGBoost树,大家可以参照我上篇文章GBDT流程一样构造XGBoost算法结构树。

对于贷款违约预测这个比较经典的课题,我们很容易拿到现成的数据,对于建模来讲,我们最缺的就是数据,而阿里天池提供了一份开源贷款违约数据,大家直接下载即可。

贷款违约预测

那么我们就开始实现逐步进行建模。

1.数据背景及描述

赛题以预测用户贷款是否违约为任务,数据集报名后可见并可下载,该数据来自某信贷平台的贷款记录,总数据量超过120w,包含47列变量信息,其中15列为匿名变量。为了保证比赛的公平性,将会从中抽取80万条作为训练集,20万条作为测试集A,20万条作为测试集B,同时会对employmentTitle、purpose、postCode和title等信息进行脱敏。

字段表
FieldDescription
id为贷款清单分配的唯一信用证标识
loanAmnt贷款金额
term贷款期限(year)
interestRate贷款利率
installment分期付款金额
grade贷款等级
subGrade贷款等级之子级
employmentTitle就业职称
employmentLength就业年限(年)
homeOwnership借款人在登记时提供的房屋所有权状况
annualIncome年收入
verificationStatus验证状态
issueDate贷款发放的月份
purpose借款人在贷款申请时的贷款用途类别
postCode借款人在贷款申请中提供的邮政编码的前3位数字
regionCode地区编码
dti债务收入比
delinquency_2years借款人过去2年信用档案中逾期30天以上的违约事件数
ficoRangeLow借款人在贷款发放时的fico所属的下限范围
ficoRangeHigh借款人在贷款发放时的fico所属的上限范围
openAcc借款人信用档案中未结信用额度的数量
pubRec贬损公共记录的数量
pubRecBankruptcies公开记录清除的数量
revolBal信贷周转余额合计
revolUtil循环额度利用率,或借款人使用的相对于所有可用循环信贷的信贷金额
totalAcc借款人信用档案中当前的信用额度总数
initialListStatus贷款的初始列表状态
applicationType表明贷款是个人申请还是与两个共同借款人的联合申请
earliesCreditLine借款人最早报告的信用额度开立的月份
title借款人提供的贷款名称
policyCode公开可用的策略_代码=1新产品不公开可用的策略_代码=2
n系列匿名特征匿名特征n0-n14,为一些贷款人行为计数特征的处理

这里借用一下贷款违约预测学习笔记submarineas博主 的图片

数据展示:

2.数据质量校验

使用pandas进行数据分析,也就是查看数据中的空缺值,重复值和异常值,以及相对应的数据描述。

import pandas as pd
df=pd.read_csv("train.csv")
test=pd.read_csv("testA.csv")

基本数据情况:

df.shape
(800000, 47)
test.shape
(200000, 46)

df.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 800000 entries, 0 to 799999
Data columns (total 47 columns):
 #   Column              Non-Null Count   Dtype  
---  ------              --------------   -----  
 0   id                  800000 non-null  int64  
 1   loanAmnt            800000 non-null  float64
 2   term                800000 non-null  int64  
 3   interestRate        800000 non-null  float64
 4   installment         800000 non-null  float64
 5   grade               800000 non-null  object 
 6   subGrade            800000 non-null  object 
 7   employmentTitle     799999 non-null  float64
 8   employmentLength    753201 non-null  object 
 9   homeOwnership       800000 non-null  int64  
 10  annualIncome        800000 non-null  float64
 11  verificationStatus  800000 non-null  int64  
 12  issueDate           800000 non-null  object 
 13  isDefault           800000 non-null  int64  
 14  purpose             800000 non-null  int64  
 15  postCode            799999 non-null  float64
 16  regionCode          800000 non-null  int64  
 17  dti                 799761 non-null  float64
 18  delinquency_2years  800000 non-null  float64
 19  ficoRangeLow        800000 non-null  float64
 20  ficoRangeHigh       800000 non-null  float64
 21  openAcc             800000 non-null  float64
 22  pubRec              800000 non-null  float64
 23  pubRecBankruptcies  799595 non-null  float64
 24  revolBal            800000 non-null  float64
 25  revolUtil           799469 non-null  float64
 26  totalAcc            800000 non-null  float64
 27  initialListStatus   800000 non-null  int64  
 28  applicationType     800000 non-null  int64  
 29  earliesCreditLine   800000 non-null  object 
 30  title               799999 non-null  float64
 31  policyCode          800000 non-null  float64
 32  n0                  759730 non-null  float64
 33  n1                  759730 non-null  float64
 34  n2                  759730 non-null  float64
 35  n3                  759730 non-null  float64
 36  n4                  766761 non-null  float64
 37  n5                  759730 non-null  float64
 38  n6                  759730 non-null  float64
 39  n7                  759730 non-null  float64
 40  n8                  759729 non-null  float64
 41  n9                  759730 non-null  float64
 42  n10                 766761 non-null  float64
 43  n11                 730248 non-null  float64
 44  n12                 759730 non-null  float64
 45  n13                 759730 non-null  float64
 46  n14                 759730 non-null  float64
dtypes: float64(33), int64(9), object(5)
查看重复值:
df[df.duplicated()==True]#打印重复值

缺失值统计
# nan可视化
missing = df.isnull().sum()/len(df)
missing = missing[missing > 0]
missing.sort_values(inplace=True)
missing.plot.bar()

异常值分析-MAD异常值识别法:

这里仅检测为数值的特征,对该方法不清楚的可以去看看我的另一篇文章一文速学(六)-数据分析之Pandas异常值检测及处理操作各类方法详解+代码展示-数据分析之Pandas异常值检测及处理操作各类方法详解+代码展示"):

3.特征类别处理

此时我们可以发现有五个特征为object特征,对于此特征我们需要进行特征转换,大家可以参考我的一文速学-特征数据类别分析与预处理方法详解+Python代码,处理定类特征。

1.grade

该特征为贷款等级,总共有A,B,C,D,E,F,G七个等级,使用OneHot Encoding方法可行:

pd.get_dummies(df.grade,drop_first=False) 

2.subGrade

该特征为贷款等级之子级,命名规则为贷款等级加上[1,2,3,4,5]这五个等级,这显然用OneHot Encoding方法就不行了,而且与上个grade方法存在信息冗余,因此可以将此特征与上个特征grade特征进行合并处理,通过适当加入一定的量纲即可。当然有更好的方法,我这里便于大家理解,采用一下方法:

df_grade=pd.get_dummies(df.grade,drop_first=False) 
def shine_convert(x):
    x=x*2
    return x/10 
se_subGrade=df.subGrade.str[1:].astype(int).apply(shine_convert)

for i in range(df_grade.shape[0]):
    data=df_grade.iloc[i,:]
    non_zero_index = data != 0
    data.loc[non_zero_index] += se_subGrade[i]
    print(data)

这里就实现了在定类数据上面再作等级的划分的条件。当然跑80w数据我这电脑以及够慢了,这里并没有跑完,大家可以去尝试一下。

4.XGBoost模型训练

数据处理的问题我这里不演示太多,毕竟主要是写XGBoost的运用,文章太长了也容易造成知识疲劳,这里我们就直接使用XGBoost算法,大家默认数据已经是处理好的就好了。这里详细讲一下XGBoost算法调参该如何调。

1.xgboost.get_config()

获取全局配置的当前值。
全局配置由可在全局范围中应用的参数集合组成。有关全局配置中支持的参数的完整列表:Global Configuration

import xgboost as xgb
xgb.get_config()

2.树的最大深度以及最小叶子节点样本权重

首先对这个值为树的最大深度以及最小叶子节点样本权重和这个组合进行调整。最大深度控 制了树的结构,最小叶子节点样本权重这个参数用于避免过拟合。当它的值较大时,可以避免模 型学习到局部的特殊样本。但是如果这个值过高,会导致欠拟合。

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2.树的最大深度以及最小叶子节点样本权重

首先对这个值为树的最大深度以及最小叶子节点样本权重和这个组合进行调整。最大深度控 制了树的结构,最小叶子节点样本权重这个参数用于避免过拟合。当它的值较大时,可以避免模 型学习到局部的特殊样本。但是如果这个值过高,会导致欠拟合。

[外链图片转存中…(img-xmZmskA9-1715375914959)]
[外链图片转存中…(img-d7uQr2f9-1715375914959)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 18
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值