boost原理与sklearn源码_集成学集习Boosting—Xgboost原理和参数

d23c37a7ce22fb435e3c5eee994aaf4d.png

XGBoost本质上还是一个GBDT,但是速度和效率发挥到极致,所以叫X(Extreme)GBoosted,两者都是boosting方法。跟GBDT不同的是,XGBoost在目标函数上增加了正则项,以此来控制模型的复杂度。在对损失函数求解时进行了二阶泰勒展开,而且XGBoost块结构可以很好的支持并行计算。

一,原理理解

1.1,目标函数

相比GBDT,Xgboost考虑了树的复杂度来防止模型过拟合,因此在目标函数中增加惩罚项,也称正则项。目标函数如下

8bd42b71df99d2bccd8d1c9bcb5491de.png

用g和h替换上一轮学习的目标函数的一阶和二阶导数

8654c10b5c45cf91dcfb09ee3af930d1.png

因此,目标函数可以转换为

c140a9a3815d6fe5cdad79a17162d273.png

1.2,树的复杂度

GBoost定义每棵树的复杂度包含叶子节点数和叶子节点的取值,对于XGB,每个叶子节点上有一个预测分数,也称为叶子权重,是在这个叶子节点上的样本的取值,用w表示。

5798ffbafbf21cbe378cf1856ddf68ad.png

在陈天奇自己的论文上只注明了L2正则项,但是在python的Xgboost包和sklearn中,都包含了L1正则的可调节参数,一般使L1正则的参数置为0

把树的复杂度加到目标函数中得到

7abf25f8ac5ba8ae8a50c81e518e0d79.png

用G和H表示每个叶子节点上的一阶导数之和和二阶导数之和

d4c114a439a5d6383bd91e6584c0ad31.png

因此,目标函数可以转换为

3985659b1f9cd804824ee4b38ac4e05c.png

上面的式子是一元二次函数的形式,求目标函数最小值,则每个叶子节点最终的拟合值为

ffcfa306fd1b7ebb666eecd513ea5dd1.png

其中G和H为这片叶子样本的一阶导数之和和二阶导数之和,因此,最终目标函数

dc8eb907d4338aeba64b65af577b097c.png

2,树的形状确定--分裂准则

弱学习器的分裂过程使用了贪心算法,每次分裂生成左右两个叶子,需要使下面式子的值大于0,使以下式子的值最大时为最佳分裂点

8f65fa8ce48cec8513106b18fff18f36.png

Gain也可以作为feature_importance_的计算方法之一。树分裂也可表达为

1ae0eef372f8df276b691446fafc8b11.png

8e426a26ba6995b6043d053150f154c1.png即为参数中gamma,可用来调节模型复杂度

3,贪心算法和近似算法

XGBoost中采用预排序的方法,计算过程当中是按照value的排序,逐个数据样本来计算划分收益,这样的算法能够精确的找到最佳划分值。贪心算法遍历所有特征以及所有分割点,每次选最好的那个,但是这种计算代价太大,当数据量和特征维度都比较多的时候,计算起来比较复杂,所以陈天奇用了一种近似算法,分裂时选一些候选点,在候选点里面找到最佳分裂点进行分裂,找寻候选点的方式是让损失在左右两个叶子上分布得均匀一些,陈天奇用损失函数的二次导数h代表每个样本的权重,让分裂后每个箱子的二次导数之和都差不多,控制分裂的候选点个数是由min_child_weight来进行控制。

寻找候选点有两种方法:

  • 全局近似:构建树开始时提出所有候选分裂点,树的每一层采用相同的候选,一般适用于层次较少的树

  • 局部近似:每次分裂时都分裂提出各自的候选分裂点,适用于层次较深的树

Xgboost 在处理带缺失值的特征时,先对非缺失的样本进行排序,对该特征缺失的样本先不处理,然后在遍历每个分裂点时,将这些缺失样本分别划入左子树和右子树来计算损失然后求最优。如果训练样本中没有缺失值,而预测过程中出现了缺失值,那么样本会被默认分到右子树。

二,参数对比

Xgboost提供了原生接口和sklearn接口,参数大同小异,如果要使用Xgboost的原生接口,需要先安装xgboost包,安装命令如下:

pip install xgboost -i https://pypi.tuna.tsinghua.edu.cn/simple

两个接口的参数对比,字有点小,打开图片

90f567c0f9c96eb8842d344cc050e378.png

三,常用调包命令

例子用到的数据是在kaggle上下载的信用卡欺诈的样本《creditcard.csv》

  • 导入常用包

#分析import pandas as pdimport numpy as np#可视化import matplotlib.pyplot as pltimport seaborn as sns#建模import xgboost as xgb #原生接口from xgboost import XGBClassifier as XGBC #sklearn接口from sklearn.model_selection import GridSearchCV,train_test_split#评估from sklearn.metrics import accuracy_score,roc_auc_score as auc,recall_score as recall,confusion_matrix as cm #样本不均衡适用

原数据样本有284807条数据,正负样本分布极不平衡,正样本比例仅为0.17%,在建模之前先做了欠抽样,将样本减低到25092条,样本比例为1.96%,提高模型的运行速率。我也做了实验,可以把全部样本导进模型,模型效果跟抽样后效果差不多,只是运行速率较慢。

  • 样本拆分

Xtrain,Xtest,Ytrain,Ytest=train_test_split(data,label,test_size=0.3,random_state=20200517)#如调用xgboost的原生接口,需要把数据装进DMatrix里面DTrain=xgb.DMatrix(Xtrain,label=Ytrain)DTest=xgb.DMatrix(Xtest)num_boost_round=200#xgboost的原生接口需要把模型参数单独生命,初始参数param={'eta':0.05       ,'objective':'binary:logistic'       ,'gamma':0       ,'eval_metrics':'auc'       }
  • 创建一个函数,对比参数调节前后的评价指标变化

def cvplot(DTrain,param,num_boost_round,metrics,nfold,cvresult):    '''    DTrain:训练数据集    param:调整的训练参数    num_boost_round:弱学习器个数    metrics:评价准则    nfold:交叉验证折数    cvresult:对比的调参结果    '''        cvresult_adjust=xgb.cv(param,DTrain,num_boost_round=num_boost_round,metrics=metrics,nfold=nfold)        plt.figure(5*20)    plt.plot(range(1,num_boost_round+1),cvresult.iloc[:,0],c='red',label='Train-Orginal')    plt.plot(range(1,num_boost_round+1),cvresult.iloc[:,2],c='darkred',label='Test-Orginal')    plt.plot(range(1,num_boost_round+1),cvresult_adjust.iloc[:,0],c='blue',label='Train-Adjust')    plt.plot(range(1,num_boost_round+1),cvresult_adjust.iloc[:,2],c='skyblue',label='Test-Adjust')    plt.legend()    plt.show()    return cvresult_adjust
  • 原生接口的交叉验证--xgb.cv

#粗选学习率和学习器个数,确定范围cvresult_vv1=xgb.cv(param,DTrain,num_boost_round=num_boost_round,metrics='auc',nfold=5)#平衡不平衡样本-测试scale_pos_weight作用param['scale_pos_weight']=(Ytrain.count()-Ytrain.sum())/Ytrain.sum()cvresult_vv2=cvplot(DTrain,param,num_boost_round,metrics='auc',nfold=5,cvresult=cvresult_vv1)

加入scale_pos_weight之后可以看到模型的收敛速度确实比较快

0d40be21ac384831af628591f105b3c4.png

  • 网格搜索法--GridSearchCV

param_grid_tree={'max_depth':range(2,9,2)            ,'min_child_weight':range(1,6,2)}grid_search=GridSearchCV(estimator=XGBC(learing_rate=0.05                                        ,n_estimators=200                                        ,scale_pos_weight=param['scale_pos_weight']                                        )                        ,param_grid=param_grid_tree,cv=5,scoring='roc_auc')grid_search.fit(Xtrain,Ytrain)grid_search.best_params_grid_search.best_estimator_grid_search.best_score_

‍可以用这种方式来调整模型复杂度,包括max_depth,min_child_weight,gamma和正则项等

  • 训练模型

  • 原生接口

#训练xg_cl=xgb.train(params=param,dtrain=DTrain,num_boost_round=140)#预测测试集#原生接口只输出预测概率preds = xg_cl.predict(DTest)preditLabel=[round(values) for values in preds]#评估accuracy_score(Ytest,preditLabel)   #准确率recall(Ytest,preditLabel)           #召回率cm(Ytest,preditLabel,labels=[1,0])  #混淆矩阵auc(Ytest,preditLabel)             #auc    #特征重要性'''重要性指标:weight - 该特征在所有树中被用作分割样本的特征的次数。gain - 分裂时在所有树中的平均增益。cover - 每个特征在分裂时结点处的平均二阶导数'''xgb.plot_importance(xg_cl,importance_type='weight')feature_score=xg_cl.get_score(importance_type='weight')

  • sklearn接口

#训练xbg_model= XGBC(learing_rate=0.01                ,n_estimators=140                ,scale_pos_weight=param['scale_pos_weight']                ,max_depth=4                ,min_child_weight=1                 ,gamma=0                 ,objective='binary:logistic'                 ,random_state=2020)xbg_model.fit(Xtrain,Ytrain)#预测测试集y_pred_test = xbg_model.predict(Xtest)y_prob_test = xbg_model.predict_proba(Xtest)#评估accuracy_score(Ytest,y_pred_test)recall(Ytest,y_pred_test)cm(Ytest,y_pred_test,labels=[1,0])auc(Ytest,y_pred_test)#特征重要性'''重要性指标:weight - 该特征在所有树中被用作分割样本的特征的次数。gain - 分裂时在所有树中的平均增益。cover - 每个特征在分裂时结点处的平均二阶导数'''xgb.plot_importance(xbg_model,importance_type='gain')#model.feature_importances_的重要性排名默认使用gainfeature_score=xbg_model.feature_importances_  #等于原生接口的get_score
  • 保存模型

""" 第一种, pickle"""import picklepickle.dump(xg_cl, open('D:/bike/机器学习/xgboost/output/xg_cl.pkl', 'wb'))model1 = pickle.load(open('D:/bike/机器学习/xgboost/output/xg_cl.pkl', 'rb'))model1.predict(DTrain)"""第二种, sklearn的joblib"""#joblib更适合大数据量的模型,且只能往硬盘存储,不能往字符串存储from sklearn.externals import joblibjoblib.dump(xbg_model, 'D:/bike/机器学习/xgboost/output/xbg_model.pkl')model2 = joblib.load('D:/bike/机器学习/xgboost/output/xbg_model.pkl')model2.predict(Xtrain)
  • 自定义目标函数(这一段代码是直接网上copy的哈哈哈)

#定义目标函数,返回一阶和二阶导数def logregobj(pred, dtrain):    labels = dtrain.get_label()    pred = 1.0 / (1+np.exp(-pred))    # sigmoid函数    grad = pred - labels    hess = pred * (1-pred)    return grad, hess     # 返回一阶导数和二阶导数def evalerror(pred, dtrain):    labels = dtrain.get_label()    return 'error', float(sum(labels!=(pred>0.0)))/len(labels)    # 自定义目标函数训练model = xgb.train(param, dtrain, num_round, watch_list, logregobj, evalerror)# 交叉验证xgb.cv(param, dtrain, num_round, nfold=5, seed=3, obj=logregobj, feval=evalerror)

三,跟GBDT的对比

2506c312db6e61b0457b8ccc0d7e253a.png

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值