Xgboost算法原理详解及python实现

Xgboost算法(回归树)

  • 1、算法原理
  • 2、对数据的要求(无需规范化)
  • 3、算法的优缺点
  • 4、XGB、GBDT、LR与RF
  • 5、python代码实现
    • 导入相关包
    • 读取数据并预处理
    • 训练
    • 贝叶斯初步优化
    • 网格搜索调参(一般调参顺序)

1、算法原理

步骤(booststrap sampling):
目标函数: o b j ( t ) = ∑ i = 1 n L ( y i , y ^ i ( t − 1 ) + f t x i ) + Ω f ( t ) + C obj^{(t)}=\sum_{i=1}^nL(y_i,\widehat y_i^{(t-1)}+f_t^{x_i})+\Omega f(t)+C obj(t)=i=1nL(yi,y i(t1)+ftxi)+Ωf(t)+C

Taylor展开: f ( x + Δ x ) ≈ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) ( Δ x ) 2 f(x+\Delta x)\approx f(x)+f'(x)\Delta x+\frac{1}{2}f''(x)(\Delta x)^2 f(x+Δx)f(x)+f(x)Δx+21f(x)(Δx)2

加法训练优化步骤:
{ y ^ i ( 0 ) = 0 y ^ i ( 1 ) = f 1 ( x i ) = y ^ i ( 0 ) + f 1 ( x i ) . . . . . . y ^ i ( t ) = ∑ k = 1 t f k ( x i ) = y ^ i ( t − 1 ) + f t ( x i ) \begin{cases} \widehat y_i^{(0)}=0\\ \widehat y_i^{(1)}=f_1(x_i)=\widehat y_i^{(0)}+f_1(x_i)\\ ......\\ \widehat y_i^{(t)}=\sum_{k=1}^tf_k(x_i)=\widehat y_i^{(t-1)}+f_t(x_i) \end{cases} y i(0)=0y i(1)=f1(xi)=y i(0)+f1(xi)......y i(t)=k=1tfk(xi)=y i(t1)+ft(xi)

目标函数进一步可表示为:
o b j ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t ) ) + ∑ i = 1 t Ω f ( t ) obj^{(t)}=\sum_{i=1}^nl(y_i,\widehat y_i^{(t)})+\sum_{i=1}^t\Omega f(t) obj(t)=i=1nl(yi,y i(t))+i=1tΩf(t)

∑ i = 1 t Ω f ( t ) = Ω f ( t ) + ∑ i = 1 t − 1 Ω f ( t − 1 ) = Ω f ( t ) + c o n s t a t n t \sum_{i=1}^t\Omega f(t)=\Omega f(t)+\sum_{i=1}^{t-1}\Omega f(t-1)=\Omega f(t)+constatnt i=1tΩf(t)=Ωf(t)+i=1t1Ωf(t1)=Ωf(t)+constatnt 其中 ∑ i = 1 t − 1 Ω f ( t − 1 ) / c o n s t a n t \sum_{i=1}^{t-1}\Omega f(t-1)/constant i=1t1Ωf(t1)/constant表示前t-1棵树的复杂度

o b j ( t ) = ∑ i = 1 n l ( y i , y ^ i ( t − 1 ) + f ( t ) ( x i ) ) + Ω f ( t ) + c o n s t a n t obj^{(t)}=\sum_{i=1}^nl(y_i,\widehat y_i^{(t-1)}+f_{(t)}(x_i))+\Omega f(t)+constant obj(t)=i=1nl(yi,y i(t1)+f(t)(xi))+Ωf(t)+constant

y ^ i ( t ) = y ^ i ( t − 1 ) + f ( t ) ( x i ) \widehat y_i^{(t)}=\widehat y_i^{(t-1)}+f_{(t)}(x_i) y i(t)=y i(t1)+f(t)(xi) 其中 f ( t ) ( x i ) f_{(t)}(x_i) f(t)(xi)是第t课树,是唯一一个需要学习的变量,其余都为已知量。 Ω f ( t ) \Omega f(t) Ωf(t)是第t棵树的复杂度。 c o n s t a n t constant constant t − 1 t-1 t1棵树的复杂度

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)}=\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=1n[l(yi,y i(t1))+gift(xi)+21hift2(xi)]+Ωf(t)+constant

其中 g i g_i gi h i h_i hi定义如下:
{ g i = ∂ y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) h i = ∂ 2 y ^ ( t − 1 ) l ( y i , y ^ ( t − 1 ) ) \begin{cases} g_i=\partial\widehat y^{(t-1)}l(y_i,\widehat y^{(t-1)})\\ h_i=\partial^2\widehat y^{(t-1)}l(y_i,\widehat y^{(t-1)}) \end{cases} {gi=y (t1)l(yi,y (t1))hi=2y (t1)l(yi,y (t1))

1、定义树的复杂度 Ω f ( t ) = γ T + 1 2 λ ∑ j = 1 T w j 2 \Omega f(t)=\gamma T+\frac{1}{2}\lambda\sum_{j=1}^{T}w_j^2 Ωf(t)=γT+21λj=1Twj2 其中 T T T为叶子节点数量; λ \lambda λ为一个叶子带来的复杂度; w j 2 w_j^2 wj2为叶子节点 L 2 L_2 L2的范数; j j j为叶子节点数量

2、定义一棵树: 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.....T\}) ft(x)=Wq(x)(wRT,q:Rd{12.....T}) 其中 w w w为一维向量,代表树 q q q各个叶子节点权重; q q q代表一棵树的结构

去常数项 :
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 ) 2 ] + γ 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 \begin{aligned} obj^{(t)}&=\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)}^2]+\gamma T+\frac{1}{2}\lambda\sum_{j=1}^{T}w_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]+\gamma T \end{aligned} obj(t)=i=1n[gift(xi)+21hift2(xi)]+Ωf(t)=i=1n[giwq(xi)+21hiwq(xi)2]+γT+21λj=1Twj2=j=1T[(iIjgi)wj+21(iIjhi+λ)wj2]+γT
∑ j = 1 T \sum_{j=1}^T j=1T将所有训练样本,按叶子节点进行了分组。其中 I j = { i ∣ q ( x i ) = j } 将 属 于 第 I_j=\{i|q(x_i)=j\}将属于第 Ij={iq(xi)=j}j 个 叶 子 节 点 所 有 样 本 个叶子节点所有样本 x_i$,划入到一个叶子节点样本集中

o b j ( t ) = ∑ j = 1 T [ G j w j + 1 2 ( H j + λ ) w j 2 ] + γ T obj^{(t)}=\sum_{j=1}^T[G_jw_j+\frac{1}{2}(H_j+\lambda)w_j^2]+\gamma T obj(t)=j=1T[Gjwj+21(Hj+λ)wj2]+γT

G j = ∑ i ∈ I j g i G_j=\sum_{i \in I_j}g_i Gj=iIjgi:叶子节点 j j j所包含样本一阶偏导数累加之和(常量); H j = ∑ i ∈ I j h i H_j=\sum_{i \in I_j}h_i Hj=iIjhi:叶子节点 j j j所包含样本二阶偏导数累加之和(常量)

求解:
{ w j ∗ = − G j H j + λ ( 每 个 叶 子 节 点 权 重 s c o r e ) o b j = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T ( 第 t 棵 树 带 来 的 最 小 损 失 ) \begin{cases} w_j^*=-\frac{G_j}{H_j+\lambda} (每个叶子节点权重score)\\ obj=-\frac{1}{2}\sum_{j=1}^T\frac{G_j^2}{H_j+\lambda}+\gamma T(第t棵树带来的最小损失) \end{cases} {wj=Hj+λGj(score)obj=21j=1THj+λGj2+γTt

分裂指标(分割点在Gain最大且大于0):
G a i n = o b j L + R − ( o b j L + o b j R ) = [ − 1 2 ( G L + G R ) 2 H L + H R + λ + γ ] − [ − 1 2 ( G L ) 2 H L + λ + ( G R ) 2 H R + λ + 2 λ ] \begin{aligned} Gain&=obj_{L+R}-(obj_{L}+obj_{R})\\ &=[-\frac{1}{2}\frac{(G_L+G_R)^2}{H_L+H_R+\lambda}+\gamma]-[-\frac{1}{2}\frac{(G_L)^2}{H_L+\lambda}+\frac{(G_R)^2}{H_R+\lambda}+2\lambda] \end{aligned} Gain=objL+R(objL+objR)=[21HL+HR+λ(GL+GR)2+γ][21HL+λ(GL)2+HR+λ(GR)2+2λ]
Gain>0则obj下降了;Gain<0分裂失败

2、对数据的要求(无需规范化)

不需要归一化,能自动处理缺失值

3、算法的优缺点

一、优点:

  • 许多策略防止过拟合(正则化、Shrinkage、样本抽样与列抽样)
  • 目标函数利用了二阶导数(1、增加精度;2、能够自定义损失函数。二阶泰勒展开可以近似大量损失函数)
  • 支持并行化
  • 支持设置样本权重,该权重体现在一阶导数g和二阶导数h通过调整权重可以去关注一些样本
  • 添加了对稀疏数据处理(缺失值处理)
  • 精度高

二、缺点:

  • 虽然利用了预排序和近似算法能降低寻找最佳分割点的计算量,但在节点分裂过程中仍需要遍历数据集
  • 预排序消耗两倍内存

4、XGB、GBDT、LR与RF

XGB与GBDT的不同之处
1、XGB在目标函数中加了正则化项(相当于预剪枝,不易过拟合)
2、XGB不仅用了一阶导数而且还使用了二阶导数
3、支持并行化
4、XGB的基分类器除了CART还可以是线性分类器
5、缺失值处理,自动学习出他的默认分裂方向
6、支持列抽样

XGB为什么使用泰勒二阶展开
1、精准性:更为精准的逼近真实的损失函数
2、可扩展性:损失函数支持自定义,只需要新的损失函数二阶可导

XGB为什么可以并行训练
特征维度并行:在训练之前,每个特征按特征值进行预排序,并存储Block结构,在后面查找分割点重复使用(并行查找)

XGB为什么快
1、分块并行:训练前每个特征值排序并存储Block结构,后面查找分割点重复使用,支持并行查找每个特征分割点
2、候选分割点:每个特征采用常数个分位点作为候选分割点
3、CPU cache命中优化:使用缓存预取的方法,对每个线性分配一个连续buffer,读取每个Block中样本的梯度信息并存入连续buffer中
4、Block处理优化:Block预先放入内存,Block按列进行解压缩,将Block划分到不同硬盘来提高吞吐

XGB防止过拟合的方法
1、目标函数添加了正则项(叶子节点数+叶子节点权重 L 2 L_2 L2正则化)
2、列抽样(训练的时候只用一部分特征)
3、子采样(每轮计算可以不使用全部样本)
4、Shrinkage(学习率|步长,为了给后面训练流出更多的学习空间)

XGB如何处理缺失值
1、在特征上寻找分割点时不考虑缺失值,只考虑non—missing值
2、训练时缺失值自定义划分方向放到右子结点
3、预测时分别划分到左叶子节点与右叶子节点各计算一遍,选择分裂后增益最大的方向

XGB一棵树停止生长的条件
1、Gain<0停止分裂
2、达到树的最大深度
3、最小样本权重和(引入一次分裂后,重新计算生成左右2个叶子结点的样本权重和,如果任意一个叶子节点的样本权重低于某一个阈值,放弃本次分裂)
4、最小样本数量(叶子)

XGB如何处理不平衡数据
1、采用AUC评估模型性能,可以通过scale_pos_weight来平衡正、负样本权重(关注AUC)
2、通过上采样与下采样
3、如果在意正确率,不能平衡数据(破坏真实分布),应该设置max_delta_step为一个有限数字帮助收敛(基模型为LR时有效)

GBDT与LR
1、LR可解释性强,可并行化,需要大量特征工程
2、GBDT非线性,特征表达能力强,无法并行,易过拟合
3、高维稀疏场景下,LR比GBDT好

XGB如何剪枝
1、正则化
2、Gain的阈值
3、最小样本权重和
4、最大深度

XGB如何评价特征重要性
1、weight:该特征在所有树中被用作分割样本特征的总次数
2、gain:该特征在其出现过的所有树中产生平均增益
3、cover:在其出现过的所有树中平均覆盖范围(一个特征作为分割点,影响的样本数量。即有多少个样本经过该特征分割到2个子节点)

XGB过拟合如何解决
1、控制模型复杂度(max_depth、min_child_weight等参数)
2、增加随机性(subsample和colsample_bytree)
3、减少学习率,同时增加迭代次数(estimator)

RF与GBDT的区别
1、集成学习:RF属于baggig,GBDT属于boosting
2、偏差-方差:RF降低方差,GBDT降低偏差
3、训练样本:RF抽样,GBDT全部样本
4、并行:RF可并行,GBDT不可并行
5、泛化能力:RF不易过拟合,GBDT易过拟合
6、异常值:RF对异常值不敏感,GBDT对异常值敏感

5、python代码实现

导入相关包

import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder#分类变量编码包
from sklearn.metrics import roc_auc_score,roc_curve,auc,precision_score,f1_score,accuracy_score,classification_report,recall_score
from sklearn.model_selection import train_test_split
from xgboost.sklearn import XGBClassifier
import matplotlib.pylab as plt
import xgboost as xgb

import os
os.chdir('E:/wyz/Desktop/XGB/')

读取数据并预处理

data = pd.read_excel('ceshi.xlsx',sheet_name = 'Sheet2')
#分类变量编码
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
str_variable = list(data.dtypes[data.dtypes.values == object].index)
for col in str_variable:    
    data[col] = le.fit_transform(data[col].astype(str))
#划分数据集
y = data_model['target']
x = data_model.drop('target', axis=1)
x_train, x_test, y_train, y_test = train_test_split(x, y,random_state=0,train_size=0.7)

训练

#通过交叉验证的方法来获取最优的迭代次数(树的数量)
def modelfit1(alg,train_x,train_y,test_x,test_y,useTrainCV=True,cv_folds=5,early_stopping_rounds=20):
    if useTrainCV:
        xgb_param = alg.get_xgb_params()#获取xgb参数
        xgtrain = xgb.DMatrix(train_x.values,label=train_y.values)#传入x和y
        cvresult = xgb.cv(xgb_param,
                          xgtrain,#训练数据
                          num_boost_round=alg.get_params()['n_estimators'],#树的数量
                          nfold=cv_folds,#交叉验证
                          metrics='auc',#评价指标
                          early_stopping_rounds=early_stopping_rounds,#连续迭代多少次auc不在下降
#                          verbose_eval=10,#每隔1轮打印一次评价指标
                          show_stdv=False )#不打印标准差
        alg.set_params(n_estimators=cvresult.shape[0])#得到最优的树的数量
    #训练集上的拟合
    alg.fit(train_x,train_y,eval_metric='auc')#传入x和y
    #训练集上的预测
    train_class = alg.predict(train_x)#输出0和1
    train_prob = alg.predict_proba(train_x)[:,1]#输出1的概率
    #测试集上的预测
    test_class = alg.predict(test_x)#输出0和1
    test_prob = alg.predict_proba(test_x)[:,1]#输出1的概率
    #准确率与auc
    print('训练集准确率: %.4f'%accuracy_score(train_y,train_class))##准确率,预测为类别(normalize=False返回预测正确的个数)
    print('测试集准确率: %.4f'%accuracy_score(test_y,test_class))#准确率,预测为类别(normalize=False返回预测正确的个数)
    print('训练集AUC: %.4f'%roc_auc_score(train_y,train_prob))#AUC,预测为概率
    print('测试集AUC: %.4f'%roc_auc_score(test_y,test_prob))#AUC,预测为概率
    #print(alg.feature_importances_)#变量重要性
    return alg

贝叶斯初步优化

#预测结果
from sklearn.model_selection import cross_val_score
from bayes_opt import BayesianOptimization
#定义贝叶斯优化的f
def model_target(learning_rate,n_estimators,max_depth,min_child_weight,subsample,colsample_bytree,reg_alpha,gamma):
    val = cross_val_score(
                     XGBClassifier(
                     learning_rate =learning_rate,#学习率
                     n_estimators=int(n_estimators),#最多的树的数量
                     max_depth=int(max_depth),#每棵树的最大深度
                     min_child_weight=min_child_weight,#叶子节点最小的样本权重和
                     subsample=subsample,#行抽样
                     colsample_bytree=colsample_bytree,#列抽样
                     objective= 'binary:logistic',
                     reg_alpha=reg_alpha,#正则化
                     gamma=gamma,#当损失函数减少时才会分裂
                     scale_pos_weight=1,#数值大于0,在样本的类非常不均衡时使用有助于快速收敛
                     seed=27),x_train,y_train,scoring='roc_auc',cv=5).mean()
    return val
model_bo = BayesianOptimization(
                              model_target,
                              {'learning_rate':(0.01,0.01),
                               'n_estimators': (100, 1000),
                               'max_depth': (2, 4),
                               'min_child_weight': (0,1),
                               'subsample': (0.6, 1),
                               'colsample_bytree':(0.6,1),
                               'reg_alpha': (0.001, 10),
                               'gamma':(0.0,0.0)})
model_bo.maximize()

贝叶斯进阶优化

#预测结果
from sklearn.model_selection import cross_val_score
from bayes_opt import BayesianOptimization
#定义贝叶斯优化的f
def model_target(learning_rate,n_estimators,max_depth,min_child_weight,subsample,colsample_bytree,reg_alpha,gamma):
    val = cross_val_score(
                     XGBClassifier(
                     learning_rate =learning_rate,#学习率
                     n_estimators=int(n_estimators),#最多的树的数量
                     max_depth=int(max_depth),#每棵树的最大深度
                     min_child_weight=min_child_weight,#叶子节点最小的样本权重和
                     subsample=subsample,#行抽样
                     colsample_bytree=colsample_bytree,#列抽样
                     objective= 'binary:logistic',
                     reg_alpha=reg_alpha,#正则化
                     gamma=gamma,#当损失函数减少时才会分裂
                     scale_pos_weight=1,#数值大于0,在样本的类非常不均衡时使用有助于快速收敛
                     seed=27),x_train,y_train,scoring='roc_auc',cv=5).mean()
    return val
model_bo = BayesianOptimization(
                              model_target,
                              {'learning_rate':(0.01,0.01),
                               'n_estimators': (100, 1000),
                               'max_depth': (2, 4),
                               'min_child_weight': (0,1),
                               'subsample': (0.6, 1),
                               'colsample_bytree':(0.6,1),
                               'reg_alpha': (0.001, 10),
                               'gamma':(0.0,0.0)})
model_bo.maximize()

网格搜索调参(一般调参顺序)

  • 寻找最优的树深度与最优的样本权重和
  • 寻找最优gamma值
  • 寻找subsample和colsample_bytree值
  • reg_alpha正则化
  • 降低学习率获取,增加更多的树

XGB参数详解

参数 默认值及输入类型 介绍
booster 默认值:gbtree
输入:gbtree、gblinear
在每次迭代中选择模型的类型,有2个选项:gbtree:基于树的模型gblinear:基于回归的模型
silent 默认值:0
输入:0、1
激活Silent mode就设置为1,即正在运行的消息不会被打印。默认为0,好处就是帮助我们理解模型运行状况
nthread 默认值:使用所有的核 这个参数用于并行处理,系统中的核的数量如果想运行所有的核,就不用再输入nthread的值,因为默认情况就是使用所有核。还有另外两个参数是由XGBoost自动设置的,下面继续探索Booster参数
eta 默认值:0.3 与GBM中的eta类似。在每一步中收缩权重使得模型更加稳健。通常设置值为:0.01−0.2
min_child_weight 默认值:1 孩子节点中最小的样本权重和。如果一个叶子节点的样本权重和小于min_child_weight则拆分过程结束。在现行回归模型中,这个参数是指建立每个模型所需要的最小样本数。该成熟越大算法越conservative这与GBM中的min_child_leaf类似,但不完全相同,XGBoost指 min “sum of weights” of observations 而 GBM 为 min “number of observations”。可用于控制过拟合。太高的值可能导致欠拟合,应使用CV进行调参
max_depth 默认值:6 与GBM一样,定义了一棵树的最大深度。用于控制过拟合,因为较高的深度会使模型对一些样本学习到特定关系,而这种关系又不是泛化的。适合用CV进行调整值的大小。通常设置值为:3−10
max_leaf_nodes 树中节点或树叶的最大数量。有时可以代替max_depth。 如:二叉树,深度“n”将产生最大2 ^ n个叶。如果这样,GBM可以忽略max_depth
gamma 默认值:0 只有当损失函数以正值减少时,节点才会分割。 Gamma指定了进行分割时所需的最小损失的减少量。使算法比较保守。 Gamma值可以根据损失函数调整大小
max_delta_step 默认值:0 如果max_delta_step设置为0,表示没有约束。 可以取正值。这个参数不是必须要设定的。在逻辑回归中,当类别比例非常不平衡时,这个参数很有用
subsample 默认值:1 与GBM取子样本一样,都是对总体进行随机采样出子样本占总体的比例。较低的值使算法比较保守,可以防止过度拟合,但太小的值可能会导致欠拟合。通常设置值为:0.5−1
colsample_bytree 默认值:1 类似于GBM中的max_features。 表示随机抽取的列数占总列数的比例。通常设置值为:0.5−1
colsample_bylevel 默认值:1 表示每个层中用于拆分时的列数占比(相当于选出的列数的再比例)。这个参数不常用,因为subsample和 colsample_bytree可以替代这个参数的作用
lambda 默认值:1 L2对权重正则化(Ridge回归也是L2)这用于XGBoost的正则化部分。 虽然许多数据科学家一般不用它,但是减少过拟合的时候还是要用一下的
alpha 默认值:0 L1对权重正则化(类似于Lasso回归的L1)维度较高时使用,可以运行得更快
scale_pos_weight 默认值:1 数值大于0,在样本的类非常不均衡时使用有助于快速收敛
seed 默认值:0 种子随机数。使采样的结果与之前相同以及参数调整
objective 默认值:reg:linear 这个参数定义了要最小化的损失函数。 有如下选择:
objective [default=reg:linear] 这个参数定义了要最小化的损失函数。 有如下选择:
binary:logistic:用于二分类的逻辑回归,返回值为概率,非类别。
multi:softmax:使用softmax目标的多类分类返回预测类(不是概率)。 还需设置一个num_class(number of classes)参数来定义类的数量。
multi:softprob: 与softmax相同,但返回的是每个样本属于每个类的预测概率而不是类别。
eval_metric [ default according to objective ] 默认值为rmse用于回归,错误率用于分类。可选值有:
1、rmse – root mean square error
2、mae – mean absolute error
3、logloss– negative log-likelihood
4、error – Binary classification error rate (0.5 threshold)
5、merror – Multiclass classification error rate
6、mlogloss – Multiclass logloss
7、auc: Area under the curve

你可能感兴趣的:(机器学习算法(分类),算法,python,机器学习)