机器学习实践:基于XGBoost的分类方法

序言

XGBoost是陈天奇等人开发的一个开源机器学习项目,高效地实现了GBDT算法并进行了算法和工程上的许多改进,被广泛应用在Kaggle竞赛及其他许多机器学习竞赛中,并取得了不错的成绩。

XGBoost本质上是一个GBDT(Gradient Boosting Decision Tree,梯度提升决策树),GBDT使用了Boosting的思想,其基本原理是所有弱分类器的结果相加等于预测值,然后下一个弱分类器去拟合上一个预测的残差(这个残差就是预测值与真实值之间的误差)1

相比GBDT,XGBoost在工程实现上做了大量的优化,力争把速度和效率发挥到极致,所以叫X(Extreme)GBoost。

原理

例子引入

假设我们要预测一家人对电子游戏的喜好程度,已知喜好程度打分真实值:男孩2.9,女孩-0.8,爷爷-1.9,奶奶-1.9,妈妈-0.1。考虑到年龄因素,年轻人更可能喜欢电子游戏;考虑到性别因素,男性更喜欢电子游戏,故先根据年龄大小区分小孩和大人,然后再通过性别区分是男是女,逐一给各人在电子游戏喜好程度上打分,训练出第一颗树,如下图所示。
3pi87.png
接着再根据电脑使用频率进行分类打分,训练出第二颗树,如下图所示。
3p8gO.png
而我们最终的预测分数是将两棵树的预测值相加。例如,小男孩的分数是2+0.9=2.9,爷爷的分数是-1-0.9=-1.9。

这与GBDT的原理异曲同工,第二颗树对男孩的预测值是男孩的真实值与第一颗树预测值的差值(0.9=2.9-2),即残差,这正好对应了上文提到的“下一个弱分类器去拟合上一个预测的残差”。最终的预测结果是所有分类器的结果相加。

总而言之,XGBoost的核心算法思想基本就是:

  1. 不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数f(x),去拟合上次预测的残差;
  2. 当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数;
  3. 最后只需要将每棵树对应的分数加起来就是该样本的预测值2

目标函数

  • XGBoost与GBDT比较大的不同就是目标函数的定义。在第t轮,我们需要寻找一个f,来最小化下面的目标函数:
    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 \boldsymbol{Obj^{(t)}=\sum_{i=1}^n l\Big(y_i, \hat{y}_i^{(t-1)}+f_t(x_i)\Big)+\Omega(f_t)+constant} Obj(t)=i=1nl(yi,y^i(t1)+ft(xi))+Ω(ft)+constant
    其中第一项中的 l ( ) l(\quad) l()为损失函数;第二项为正则项,控制树的复杂度,防止过拟合;第三项为常数。在这个式子中, y ^ i ( t − 1 ) + f t ( x i ) = y ^ i ( t ) \hat{y}_i^{(t-1)}+f_t(x_i)=\hat{y}_i^{(t)} y^i(t1)+ft(xi)=y^i(t),即第t轮的预测值,表示是由上一轮预测值加上本轮结果得到。

  • 接下来,用泰勒展开三项来近似我们的目标函数:
    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 \boldsymbol{Obj^{(t)}\cong\sum_{i=1}^n \Big[l(y_i, \hat{y}_i^{(t-1)})+g_if_t(x_i)+\frac{1}{2}h_if_t^2(x_i)\Big]+\Omega(f_t)+constant} Obj(t)i=1n[l(yi,y^i(t1))+gift(xi)+21hift2(xi)]+Ω(ft)+constant
    其中正则项 Ω ( 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 Ω(ft)=γT+21λj=1Twj2 w w w是叶结点权重(分数), T T T是叶结点个数。

  • 接下来求解这个目标函数,最终化为:
    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 \boldsymbol{Obj^{(t)}=\sum_{j=1}^T\Big[(\sum_{i\in I_j} g_i)\cdot w_j+\frac{1}{2}(\sum_{i\in I_j} h_i+\lambda)\cdot w_j^2\Big]+\gamma T} Obj(t)=j=1T[(iIjgi)wj+21(iIjhi+λ)wj2]+γT
    其中 I j = { i ∣ q ( x i ) = j } I_j=\{i|q(x_i)=j\} Ij={iq(xi)=j}代表映射到叶结点 j j j的样本 x i x_i xi的所有下标, q q q表示从样本到叶结点的映射(代表了树结构)。(推导过程不作赘述,参考# XGBoost原理

  • 最终我们将关于树模型的迭代转化为关于树的叶节点的迭代,并求出最优的叶节点分数,令 ∂ O b j ( t ) / ∂ w j = 0 \partial Obj^{(t)}/\partial w_j=0 Obj(t)/wj=0,得 w j = − ∑ i ∈ I j g i ∑ i ∈ I j h i + λ w_j=-\frac{\sum_{i\in I_j} g_i}{\sum_{i\in I_j} h_i+\lambda} wj=iIjhi+λiIjgi。将叶节点的最优值带入目标函数,得到一个可看做树结构的打分函数:
    L ( t ) ( q ) = − 1 2 ∑ j = 1 T ( ∑ i ∈ I j g i ) 2 ∑ i ∈ I j h i + λ + γ T \boldsymbol{L^{(t)}(q)=-\frac{1}{2}\sum_{j=1}^T \frac{(\sum_{i\in I_j} g_i)^2}{\sum_{i\in I_j} h_i+\lambda}+\gamma T} L(t)(q)=21j=1TiIjhi+λ(iIjgi)2+γT
    我们的任务就是找到一个最好的树结构 q q q使得这个函数最小,得分越小,说明结构越好。

树的生成

很显然,一棵树的生成是由一个节点一分为二,然后不断分裂最终形成为整棵树。对于一个叶子节点如何进行分裂,XGBoost做法是枚举所有不同树结构的贪心法

不断地枚举不同树的结构,然后利用打分函数来寻找出一个最优结构的树,接着加入到模型中,不断重复这样的操作。这个寻找的过程使用的就是贪心算法。选择一个feature分裂,计算loss function最小值,然后再选一个feature分裂,又得到一个loss function最小值,枚举完,找一个效果最好的,把树给分裂,就得到了小树苗。

限于篇幅,更多的实现细节参考# XGBoost原理

代码实践

XGBoost算法的实现以鸢尾花数据集为例。Iris鸢尾花数据集是一个经典数据集,在统计学习和机器学习领域都经常被用作示例。数据集内包含 3 类共 150 条记录,每类各 50 个数据,每条记录都有 4 项特征:花萼长度、花萼宽度、花瓣长度、花瓣宽度,可以通过这4个特征预测鸢尾花卉属于(iris-setosa,iris-versicolour,iris-virginica)中的哪一品种3。部分数据截图如下:
3Ccqw.png

下面的示例代码4采用直接调用xgboost包的方式。

#导入sklearn和xgboost包
from sklearn.datasets import load_iris 
from sklearn.model_selection import train_test_split 
import xgboost as xgb 

#用于显示正确率
def show_accuracy(a, b, tip): 
	acc = a.ravel() == b.ravel() 
	print (acc) 
	print (tip + '正确率:\t', float(acc.sum()) / a.size) 
	
if __name__ == "__main__": 
	#加载iris数据集 
	data=load_iris() 
	X=data.data 
	Y=data.target
	#划分训练集和测试集 
	X_train,X_test,y_train,y_test=train_test_split(X,Y,test_size=0.25,random_state=1) 
	#数据转换为数据矩阵类DMatrix
	data_train = xgb.DMatrix(X_train,label=y_train) 
	data_test = xgb.DMatrix(X_test,label=y_test) 
	#设置参数 
	param = {'max_depth': 3, 'eta': 1, 'silent': 1, 'objective': 'multi:softmax','num_class': 3} 
	watchlist = [(data_test, 'eval'), (data_train, 'train')] 
	#训练模型
	bst = xgb.train(param, data_train, num_boost_round=4, evals=watchlist) 
	#测试
	y_hat = bst.predict(data_test) 
	show_accuracy(y_hat, y_test, 'XGBoost ')

param各项参数的含义可参考4
运行结果如下:
3CATG.png

XGBoost的代码实现还可以使用xgboost包的sklearn接口xgboost.XGBClassifier(),利用函数参数设置模型参数,限于篇幅不再赘述,读者可以参考链接Github:XGBoost.ipynb

总结

XGBoost是一种GBDT的实现,是一个优秀的分类算法,相比GBDT有许多优势,如:

  1. 显式地加入了正则项来控制模型的复杂度,有利于防止过拟合;
  2. 对代价函数进行二阶泰勒展开,可以同时使用一阶和二阶导数;
  3. 支持多种类型的基分类器;
  4. 能够自动学习出缺失值的处理策略。

参考文献


  1. Github:ML-NLP/Machine Learning/3.2 GBDT/3.2 GBDT.md ↩︎

  2. cnblogs:XGBoost算法 ↩︎

  3. 鸢尾花(iris)数据集分析 ↩︎

  4. 机器学习之XGBoost代码实现 ↩︎ ↩︎

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值