xgboost的原理和实战

转载自

https://blog.csdn.net/github_38414650/article/details/76061893

以及

https://blog.csdn.net/u011630575/article/details/79418138

和csdn各博主的学习资料

要了解xgboost是什么,首先要明白两个概念

1.xgboost是很多CART回归树的集成

2.boosting集成学习,由多个相关联的决策树联合决策,什么叫相关联,举个例子,有一个样本[数据->标签]是[(2,4,5)-> 4],第一棵决策树用这个样本训练得预测为3.3,那么第二棵决策树训练时的输入,这个样本就变成了[(2,4,5)-> 0.7],也就是说,下一棵决策树输入样本会与前面决策树的训练和预测相关

与之对比的是random foreast(随机森林)算法,各个决策树是独立的、每个决策树在样本堆里随机选一批样本,随机选一批特征进行独立训练,各个决策树之间没有啥毛线关系。

那看下xgboost

我们的目标是希望建立K个回归树,使得树群的预测值尽量接近真实值(准确率)而且有尽量大的泛化能力(更为本质的东西),从数学角度看这是一个泛函最优化,多目标,看下目标函数:

                                                           L(ϕ)=∑il(y^i−yi)+∑kΩ(fk)

其中ii表示第i个样本,l((y^i−yi)l((y^i−yi)表示第ii个样本的预测误差,误差越小越好,不然你算得上预测么?后面∑kΩ(fk)∑kΩ(fk)表示树的复杂度的函数,越小复杂度越低,泛化能力越强,这意味着啥不用我多说。表达式为

                                                            Ω(f)=γT+12λ||w||^2

T表示叶子节点的个数,w表示节点的数值(这是回归树的东西,分类树对应的是类别)

之前在boosting和bagging的文章中说到了,bagging的目的是降低方差,提高泛化能力,而boosting则是为了降低偏差,提高准确率,从xgboost的目标函数看来,前半部分是减小误差,也就是提高准确率,后半部分是为了减小复杂度,也即是决策树里的剪枝,所以是提高泛化能力。

直观上看,目标要求预测误差尽量小,叶子节点尽量少,节点数值尽量不极端(这个怎么看,如果某个样本label数值为4,那么第一个回归树预测3,第二个预测为1;另外一组回归树,一个预测2,一个预测2,那么倾向后一种,为什么呢?前一种情况,第一棵树学的太多,太接近4,也就意味着有较大的过拟合的风险)

ok,听起来很美好,可是怎么实现呢,上面这个目标函数跟实际的参数怎么联系起来,记得我们说过,回归树的参数:(1)选取哪个feature分裂节点呢;(2)节点的预测值

答案:贪心策略+最优化(二次最优化,恩你没看错)

通俗解释贪心策略:就是决策时刻按照当前目标最优化决定,说白了就是眼前利益最大化决定,“目光短浅”策略

这里是怎么用贪心策略的呢,刚开始你有一群样本,放在第一个节点,这时候T=1T=1,ww多少呢,不知道,是求出来的,这时候所有样本的预测值都是ww(这个地方自己好好理解,决策树的节点表示类别,回归树的节点表示预测值),带入样本的label数值,此时loss function变为 

                                                           L(ϕ)=∑il(w−yi)+γ+1/2λ||w||2
如果这里的l(w−yi)l(w−yi)误差表示用的是平方误差,那么上述函数就是一个关于ww的二次函数求最小值,取最小值的点就是这个节点的预测值,最小的函数值为最小损失函数。

暂停下,这里你发现了没,二次函数最优化! 
要是损失函数不是二次函数咋办,哦,泰勒展开式会否?,不是二次的想办法近似为二次。

接着来,接下来要选个feature分裂成两个节点,变成一棵弱小的树苗,那么需要:(1)确定分裂用的feature,how?最简单的是粗暴的枚举,选择loss function效果最好的那个;(2)如何确立节点的ww以及最小的loss function,大声告诉我怎么做?对,二次函数的求最值(细节的会注意到,计算二次最值是不是有固定套路,导数=0的点,ok)

那么节奏是,选择一个feature分裂,计算loss function最小值,然后再选一个feature分裂,又得到一个loss function最小值…你枚举完,找一个效果最好的,把树给分裂,就得到了小树苗。

按照上述的方式,形成一棵树,再形成一棵树每次在上一次的预测基础上取最优进一步分裂/建树,是不是贪心策略?!

凡是这种循环迭代的方式必定有停止条件,什么时候停止呢: 
1.当引入的分裂带来的增益小于一个阀值的时候,我们可以剪掉这个分裂,所以并不是每一次分裂loss function整体都会增加

2.当树达到最大深度时则停止建立决策树,设置一个超参数max_depth,这个好理解吧,树太深很容易出现的情况学习局部样本,过拟合

3.当样本权重和小于设定阈值时则停止建树,这个解释一下,涉及到一个超参数-最小的样本权重和min_child_weight,大意就是一个叶子节点样本太少了,也终止,同样是过拟合;

看下Xgboost的一些重点

  • w是最优化求出来的,不是啥平均值或规则指定的,这个算是一个思路上的新颖吧;

  • 正则化防止过拟合的技术,上述看到了,直接loss function里面就有;

  • 支持自定义loss function,哈哈,不用我多说,只要能泰勒展开(能求一阶导和二阶导)就行,你开心就好;

  • 支持并行化,这个地方有必要说明下,因为这是xgboost的闪光点,直接的效果是训练速度快,boosting技术中下一棵树依赖上述树的训练和预测,所以树与树之间应该是只能串行!那么大家想想,哪里可以并行?! 
    没错,在选择最佳分裂点,进行枚举的时候并行!(据说恰好这个也是树形成最耗时的阶段)

来个简单的例子呗:

一、导入必要的工具包

#导入必要的包
import xgboost as xgb
#计算分类正确率
from sklearn.metrics import accuracy_score

二、数据读取
XGBoost可以加载libsvm格式的文本数据,libsvm的文件格式(稀疏特征)如下:
1  101:1.2 102:0.03
 1:2.1 10001:300 10002:400
...
每一行表示一个样本,第一行的开头的“1”是样本的标签“101”和“102”为特征索引,'1.2'和'0.03' 为特征的值。

在两类分类中,用“1”表示正样本,用“0” 表示负样本。也支持[0,1]表示概率用来做标签,表示为正样本的概率。

下面的示例数据需要我们通过一些蘑菇的若干属性判断这个品种是否有毒。
UCI数据描述:http://archive.ics.uci.edu/ml/machine-learning-databases/mushroom/ ,
每个样本描述了蘑菇的22个属性,比如形状、气味等等(将22维原始特征用加工后变成了126维特征,

并存为libsvm格式),然后给出了这个蘑菇是否可食用。其中6513个样本做训练,1611个样本做测试。

XGBoost加载的数据存储在对象DMatrix中
XGBoost自定义了一个数据矩阵类DMatrix,优化了存储和运算速度

DMatrix文档:http://xgboost.readthedocs.io/en/latest/python/python_api.html

数据下载地址:http://download.csdn.net/download/u011630575/10266113

my_workpath = 'data/'
dtrain = xgb.DMatrix(my_workpath + 'agaricus.txt.train')
dtest = xgb.DMatrix(my_workpath + 'agaricus.txt.test')

查看数据情况

dtrain.num_col()
dtrain.num_row()
dtest.num_row()

三、训练参数设置

max_depth: 树的最大深度。缺省值为6,取值范围为:[1,∞]

eta:为了防止过拟合,更新过程中用到的收缩步长。在每次提升计算之后,算法会直接获得新特征的权重。 

eta通过缩减特征的权重使提升计算过程更加保守。缺省值为0.3,取值范围为:[0,1]

silent:取0时表示打印出运行时信息,取1时表示以缄默方式运行,不打印运行时信息。缺省值为0

objective: 定义学习任务及相应的学习目标,“binary:logistic” 表示二分类的逻辑回归问题,输出为概率。

其他参数取默认值。

param = {'max_depth':2, 'eta':1, 'silent':0, 'objective':'binary:logistic' }
print(param)

四、训练模型

# 设置boosting迭代计算次数
num_round = 2
 
import time
starttime = time.clock()
 
bst = xgb.train(param, dtrain, num_round) #  dtrain是训练数据集
 
endtime = time.clock()
print (endtime - starttime)

XGBoost预测的输出是概率。这里蘑菇分类是一个二类分类问题,输出值是样本为第一类的概率。

我们需要将概率值转换为0或1。

train_preds = bst.predict(dtrain)
train_predictions = [round(value) for value in train_preds]
y_train = dtrain.get_label() #值为输入数据的第一行
train_accuracy = accuracy_score(y_train, train_predictions)
print ("Train Accuary: %.2f%%" % (train_accuracy * 100.0))

五、测试

模型训练好后,可以用训练好的模型对测试数据进行预测

# make prediction
preds = bst.predict(dtest)

检查模型在测试集上的正确率

XGBoost预测的输出是概率,输出值是样本为第一类的概率。我们需要将概率值转换为0或1。

predictions = [round(value) for value in preds]

y_test = dtest.get_label()
test_accuracy = accuracy_score(y_test, predictions)
print("Test Accuracy: %.2f%%" % (test_accuracy * 100.0))

猜你喜欢

转载自blog.csdn.net/qq_18668137/article/details/81135432