翻了很多blog发现对xgboost大多直接扎入公式推导,xgboost是什么都没有讲清楚,一知半解的。写一篇blog总结一下嘻嘻
Xgboost
首先先说清楚 Xgboost是多个CART组成的回归树! 这个要搞清楚啊 Adaboost是分类提升,Xgboost是回归提升!
那么分类树(决策树)跟回归树的区别哩,通俗来说决策树根据特征信息得到的是标签,回归树得到的是一个值我们可以认为是这个特征组合的权重值,那么对于回归树肯定不能通过信息增益、Gini系数这些方法来判断是否分裂了,那么回归树如何判断分裂效果呢,当然是拟合的越接近越好,也就是说可以采用均方误差或者指数误差来作为评价标准。
这个时候大家就能感觉到一个回归树形成的关键点:(1)分裂点依据什么来划分(如前面说的均方误差最小,loss);(2)分类后的节点预测值是多少(如前面说,有一种是将叶子节点下各样本实际值得均值作为叶子节点预测误差,或者计算所得)
关于提升前面blog也有提到吧,当然Xgboost也是针对多个弱学习器组合相加得到一个强学习器的,那么关于Xgboost,首先明确我们的目标,我们希望建立K个回归树,使得树群的预测值尽量接近真实值并且有尽量大的泛化能力,考虑之前我们定义的提升的目标函数:
其中l即损失函数,我们希望集成第t个学习器学习到的数值之后得到的预测值与真实值之间的误差越小越好!其次后面的函数即正则化,用来表示第t棵树的复杂度,当然复杂度越小代表树的泛化能力越好。
直观上看,目标要求预测误差尽可能小,叶子结点尽量少,节点数值尽可能不极端(过拟合风险呢),那么又回到我们最初的问题,如何选择回归树的分裂点呢,这个节点的预测值又怎么表达呢,难不成用取平均这种捞b的方法么。嘻嘻,Xgboost的做法就是贪心策略+最优化(二次函数最优化),下面分别介绍这两个核心点。
输出预测值最优化推导
在这里我们首先对回归树的形式和树的复杂度进行定义,把树f(x)拆分成结构部分q与叶子回归值w。下图是一个具体的例子。结构函数q把输入映射到叶子的索引号上面去,而w给定了每个索引号对应的叶子分数是什么。
定义树的复杂度包含了叶子结点的个数以及对输出回归值的约束形式,下图有一个具体的例子
有了这两部分定义之后我们再看目标函数,我们说过通过最优化的方式求解,那么我们对目标函数通过泰勒公式二次展开,得到如下形式:
有了上述对f(x)和复杂度的定义之后,我们可以对目标函数进行改写,其中I被定义为每个叶子上面样本集合 :
(ps:怕小白不懂,可以这样改写的原因是不管是决策树还是回归树如果被划分到相同的特征空间之后,输出的值都是一样的啦,所以我们可以只考虑叶子节点的权值,有多少个样本属于这个叶子结点那么原始的值在叶子结点上相应加多少个就行了。)
粗略一看,这不就是二次函数最优化嘛!只需求的一阶导数为0点的值不就得到了loss的最小值啦!在这种情况下,我们定义Gj,Hj为如下格式:
最终目标函数可以化简为:
通过对求导等于0,可以得到
然后把最优解代入得到:
通过这种方式我们就求得了回归树叶子结点的输出值啦。这也是Xgboost的创新点之一,原因是我们可以自定义目标函数了!因为taylor展开式帮我们可以让任意形式的函数统统显出原形,最终都可以化为二次函数最优化的形式。
节点分裂策略(贪心策略)
这里是怎么用贪心策略的呢,刚开始你有一群样本,放在第一个节点,这时候T = 1 ,w多少呢,不知道,是求出来的,这时候所有样本的预测值都是w(这个地方自己好好理解,决策树的节点表示类别,回归树的节点表示预测值),带入样本的label数值,得到了T=1的loss function。接着来,接下来要选个feature分裂成两个节点,变成一棵弱小的树苗,那么需要:(1)确定分裂用的feature,how?最简单的是粗暴的枚举,选择loss function效果最好的那个(关于粗暴枚举,Xgboost的改良并行方式咱们后面看);(2)如何确立节点的ww 以及最小的loss function,这个我们上一步不就解决了。
那么节奏是,选择一个feature分裂,计算loss function最小值,然后再选一个feature分裂,又得到一个loss function最小值…你枚举完,找一个效果最好的,把树给分裂,就得到了小树苗。
在分裂的时候,你可以注意到,每次节点分裂,只有属于这个节点的样本才会影响loss function,因而每次分裂,计算分裂的增益(loss function的降低量)只需要关注打算分裂的那个节点所包含的样本。具体公式如下:
接下来,继续分裂,按照上述的方式,形成一棵树,再形成一棵树,每次在上一次的预测基础上取最优进一步分裂/建树,是不是贪心策略?!
凡是这种循环迭代的方式必定有停止条件,什么时候停止呢:
(1)当引入的分裂带来的增益小于一个阀值的时候,我们可以剪掉这个分裂,所以并不是每一次分裂loss function整体都会增加的,有点预剪枝的意思(其实我这里有点疑问的,一般后剪枝效果比预剪枝要好点吧,只不过复杂麻烦些,这里大神请指教,为啥这里使用的是预剪枝的思想,当然Xgboost支持后剪枝),阈值参数为γγ 正则项里叶子节点数T的系数(大神请确认下);
(2)当树达到最大深度时则停止建立决策树,设置一个超参数max_depth,这个好理解吧,树太深很容易出现的情况学习局部样本,过拟合;
(3)当样本权重和小于设定阈值时则停止建树,这个解释一下,涉及到一个超参数-最小的样本权重和min_child_weight,和GBM的 min_child_leaf 参数类似,但不完全一样,大意就是一个叶子节点样本太少了,也终止同样是过拟合;
总结
w是最优化求出来的,不是啥平均值或规则指定的,这个算是一个思路上的新颖吧;
正则化防止过拟合的技术,上述看到了,直接loss function里面就有;
支持自定义loss function,哈哈,不用我多说,只要能泰勒展开(能求一阶导和二阶导)就行,你开心就好;
支持并行化,这个地方有必要说明下,因为这是xgboost的闪光点,直接的效果是训练速度快,boosting技术中下一棵树依赖上述树的训练和预测,所以树与树之间应该是只能串行!那么大家想想,哪里可以并行?! 没错,在选择最佳分裂点,进行枚举的时候并行!(据说恰好这个也是树形成最耗时的阶段) Attention:同层级节点可并行。具体的对于某个节点,节点内选择最佳分裂点,候选分裂点计算增益用多线程并行。—– 较少的离散值作为分割点倒是很简单,比如“是否是单身”来分裂节点计算增益是很easy,但是“月收入”这种feature,取值很多,从5k~50k都有,总不可能每个分割点都来试一下计算分裂增益吧?(比如月收入feature有1000个取值,难道你把这1000个用作分割候选?缺点1:计算量,缺点2:出现叶子节点样本过少,过拟合)我们常用的习惯就是划分区间,那么问题来了,这个区间分割点如何确定(难道平均分割),作者是这么做的: 方法名字:Weighted Quantile Sketch 大家还记得每个样本在节点(将要分裂的节点)处的loss function一阶导数 gi g i 和二阶导数 hi h i ,衡量预测值变化带来的loss function变化,举例来说,将样本“月收入”进行升序排列,5k、5.2k、5.3k、…、52k,分割线为“收入1”、“收入2”、…、“收入j”,满足(每个间隔的样本的 hi h i 之和/总样本的 hi h i 之和)为某个百分比 ϵ ϵ (我这个是近似的说法),那么可以一共分成大约 1 / ϵ 个分裂点。
Xgboost第一感觉就是防止过拟合+各种支持分布式/并行,所以一般传言这种大杀器效果好(集成学习的高配)+训练效率高(分布式),与深度学习相比,对样本量和特征数据类型要求没那么苛刻,适用范围广。Xgboost和深度学习的关系,陈天奇在Quora上的解答如下:不同的机器学习模型适用于不同类型的任务。深度神经网络通过对时空位置建模,能够很好地捕获图像、语音、文本等高维数据。而基于树模型的XGBoost则能很好地处理表格数据,同时还拥有一些深度神经网络所没有的特性(如:模型的可解释性、输入数据的不变性、更易于调参等)。