大纲:
1、回顾集成学习的思想及典型算法。
2、GBDT整体流程回顾
3、XGBoost算法逻辑原理剖析
4、XGBoost算法实现代码剖析
5、XGBoost优缺点分析
集成学习的回顾
集成学习的目的和出发点是集弱成强,将若干个学习器组合之后产生一个新的学习器。
常见的集成学习思想有三种:bagging,boosting和stacking。
bagging的核心是数据的有放回抽取。
根据N批的数据训练处N个学习器,通过多数投票法或求平均法来获得最终结果。
典型算法如RF,extra tree,TRTE,IForest。其中TRTE用于数据由低维进行映射到高维。而IForest则是一种异常点检测算法。但本质上,后三者都是属于RF的变种形式。
boosting的核心是逐步预测,并加权累加到总模型中。
每个新的学习器都依赖于前面的模型,并按照某种确定性的策略将他们组合起来。
基于当前的集成学习结果,更新训练集,即权重或者是值。
典型算法如Adaboost、GBDT。
以及我们要提的四大神器。
其中Adaboost是根据每步预测的结果去适配数据,分析错误,并对错误的数据赋予更高的权重,继而再用后面的学习器去进一步修复,故而会不断地修正每个特征对应的权重,使之最终模型对应的所有权重都尽可能适配所有的数据。
而gbdt则是将损失函数的负梯度,作为本轮的真实值进行进一步的预测。它会将所有子树上对应预测值进行累加作为最终整体的预测值。
另外,gbdt主要通过衰减系数来进行防过拟合,即每一轮要预测的值会先乘上一个衰减系数,然后再作为本轮的真实目标值去预测,从而防止一家独大,导致模型过拟合的情况,也为后续学习器提供更多的可学习空间。
简单对比分析下bagging与boosting思想:
1、结构上,bagging思想对应的基学习器可以并行处理,而boosting思想对应的基学习器只能串行处理。
2、训练集上,bagging思想对应的基学习器各自的训练都是独立的。而boosting思想对应的基学习器其训练集依赖于前面的基学习器效果。
3、bagging侧重点在于降低方差,而boosting侧重点在于降低偏差。
方差描述的是测试集的表现。而偏差则描述的样本预测结果与真实值间的差距。
bagging因为是多个模型多数投票或者取平均,故而泛化能力强,整体方差小,但是模型独立,且一致,故而效果相仿,虽有提升,但无法很好地降低偏差。
boosting会不断修正权重或预测值,故而能降低偏差,但对数据依赖性大,每个模型相加权重不一,容易复杂,导致过拟合,故而并不能显著降低方差。
而stacking思想则和bagging、boosting有着本质区别。
bagging和boosting的所有弱学习器都是相同的学习器。而stacking通常则是不同的模型,并且每个模型都是使用全部训练数据,得到各自的预测结果,然后再训练一个分类器 MetaClassifier,将这些预测结果作为输入,注入到MetaClassifer中进行训练,从而得到最终的预测结果。
虽然看起来有集大家之所长的感觉,但得根据实际情况来,只能说,它可能更稳定,但不一定效果就会变得更好。
GBDT整体流程回顾
对于GBDT,前面回顾我们知道了一些基本理论。
其一,每一轮拟合的都是上一轮损失函数的负梯度。
特殊的,以回归算法为例,此时若GBDT的损失函数是最小二乘,均方差损失时,其负梯度则正好等于残差。
故而此时第二个基分类器是基于第一个基分类器的预测值与真实值的残差进行训练。
而第三个基分类器又是基于第二个基分类器的预测值与前面残差值的残差进行进一步训练。
其二,它是通过衰减系数来减缓差值,给予后面基学习器保留学习的空间,防止过拟合。
其三,GBDT本身的基学习器是CART树,CART树音译过来即分类回归树,既有分类决策树来做分类使用,也有相应的回归决策树来做回归预测。
回归树和分类树分别是以均方差和基尼系数来作为树节点的分割标准。同时,CART树和ID3、C4.5相比,还有一个最大的特点,即CART构建的,一定是严格的二叉树。
简单知道以上特点后,我们引入一个问题背景:
现在有男女老少,五个样本,我们要预测他们各自对游戏的偏好程度。
特征的维度有很多,每棵树若以不同的划分原则进行划分,最后得到的分值会不一样。
比如按如下图方式
从年龄的角度,老年人更不偏爱游戏,而年轻人更热衷游戏。
从性别角度,男生对游戏的偏爱程度,普遍上会比女生要强烈一些。
从日常是否使用电脑的角度上,如果越容易接触,那么就越容易偏爱游戏一些。
那么我们可以得到两棵树的各个样本对应的分值。
以男孩为例,那么男孩最终的得分则是其在不同树中对应的分值相加。
其他人也是如此,即对应样本的预测值等于所有树预测的分值之和。
实际上,这就可以简单地理解为GBDT算法对样本的真实预测值计算过程。
那么我们先简单整体回顾一下GBDT的构建过程
首先,GBDT是由多棵CART决策树来组成的一个强学习器。
也就是说在构建每一棵树时,主要是通过CART算法进行构建的。
而CART算法针对分类问题和回归问题则分别是以均方差和基尼系数为划分指标进行树节点的分割。
故而只要设定好叶子结点最小样本数,树的深度等迭代终止条件后,通过CART算法则能逐步构建好一棵完整的树模型来。
其次,GBDT模型是属于Boosting思想的一种典型算法,
故而其总模型等于各个树模型的累加得到。
也就是
因此对于第t棵树而言,
某个样本的预测值即等于前t-1棵树的总预测值加上当前第t棵树该样本的预测值。
预测值有了,接下来就是与真实值之间的目标损失函数。
假定当前GBDT模型是由t棵树集成得到的。
那么对GBDT的目标函数通过一阶泰勒进行展开可以获得:
这里的y0则代表的是对应样本在第0棵树时的真实值,也就是该样本最初始时的真实值。
目标损失函数也有了,但是损失中的预测值,我们实际上是通过t棵树的预测值的累加得到的。
那么我们还需要知道这t棵树都是如何进行构建的。
在GBDT中,每一棵树的构建,则是通过损失函数的负梯度来作为目标值来进行拟合的。
即通过样本及负梯度来进行下一棵树的拟合过程。
那么对于第零棵树而言,我们通过CART算法构建决策树,得到预测值,根据损失函数,进行求负梯度,从而得到第一棵树的真实目标值。
以回归问题的均方差损失为例,此时负梯度即为残差。故而也就能计算出初始真实值与第零棵树预测值的残差,来作为第一棵树的目标值来进行下一轮的预测。
故而可以不断地往后迭代下去。
所以GBDT整个求解可分为两部分:
一是求解真实值与前t-1颗树差异,这里即负梯度过程。
二是利用负梯度拟合CART树,然后针对回归问题使用均方差,针对分类问题使用Gini系数。
补充说明:在GBDT中,关于残差,损失函数以及负梯度的关系。
因为GBDT既可以用来解决分类问题,也可以用来解决回归问题,针对不同的问题,它用的损失函数也不一样,即便同样是解决某一类问题,也可以有多种损失函数可供选择,比如二元分类中使用对数似然损失,多元分类中虽然也是对数似然损失,但形式会有较大差别。
再比如回归问题中可能会使用均方差损失,亦或者绝对值损失,再或者分位数损失以及Huber损失。
但无论哪一种损失函数,都是以目标损失函数的负梯度来作为下一轮迭代的目标值进行更新的。
究其本质原因则是负梯度方向,总会是目标函数下降最快的方向。
而残差,则只是在解决回归问题选用均方差损失时,进行求负梯度得到的一个特殊产物。
也就是说残差是其负梯度的一个特殊实例。
根本方法是负梯度,残差只是其中的一个表现。
另外,GBDT也有自己的正则项,准确的说是它的防过拟合方式:
1、衰减系数,即GBDT的学习率,它对每棵树的训练效果进行一个一定比例的缩减,从而降低单棵决策树的决策权重,为其他基学习器预留一定空间,防止一家独大的现象。
2、Early Stopping,即早停策略。比如损失在多少轮后不再发生变化则停止学习。这也是一般通用的一种防过拟合的策略。
XGBoost算法逻辑原理剖析
GBDT的回顾,主要目的是为了说明XGBoost。
因为XGBoost和GBDT的出发点一样,属于GBDT算法的一种改进算法。
故而在很多地方,都是相同或者是相通的。
首先,还是以上面的那个问题背景。
即男女老少,五个样本,我们要预测他们各自对游戏的偏好程度。
首先,XGBoost也和GBDT一样,对应样本的预测值等于所有树预测的分值之和。
其次,GBDT在构建树的过程中,因为是使用的CART算法,所以构建树的过程中主要通过均方差或者是Gain值(基尼系数)来进行节点分割。
但是XGBoost,在这里,有做优化,其分割过程,不再是使用均方差或基尼系数来做分割,而是有自己的计算方式。具体咱们下面再阐述。
然后,XGBoost是属于GBDT算法的一种优化,其大背景也是属于Boosting思想的一种体现。
故而其总模型也等于各个树模型的累加得到。
即依然符合下列公式:
因此,和GBDT一样,对于第t棵树而言,
某个样本的预测值即等于前t-1棵树的总预测值加上当前第t棵树该样本的预测值。
预测值有了,接下来就是与真实值之间的目标损失函数。
这里也是XGBoost和GBDT比较重大不同的地方。
假定当前XGBoost模型是由t棵树集成得到的。
那么这里的目标损失函数则是通过二阶泰勒展开获得的。
和GBDT一样,这里的y0也代表的是对应样本在第0棵树时的真实值。
因为这里泰勒展开纯粹只是因为求解问题的方式不同,并不能说二阶就是一阶的优化。
具体咱们后面再说。
但是XGBoost在目标损失函数上确实也做了优化。
因为实际上,XGBoost的目标损失函数有两块内容:
第一块,也就是上面我们描述的,预测值和真实值之间的损失。
第二块,则是XGBoost自己的正则项,正则项首要目的即防止过拟合。
那么展开来看看具体形态:
其中gama是代表假如新叶子结点引入的复杂度代价。
T代表的是叶子数量。
而后面的则是L2正则,omega代表的是每棵树对应样本的取值,lambda对应惩罚系数。
那么这里通过正则项的内容可以看出,它不单单能有有一定的防过拟合能力,同时也能控制树的复杂度,让模型变得简化,也让最终的模型预测结果更加稳定。
因此,实际上XGBoost的目标损失函数即:
这里的constant即代表是常量。
然后为了方便表示,将一阶导和二阶导进行简化书写,即令
将式子中累加求和项外面的正则项往里面挪,然后,令
那么最终化简后,可以得到:
化简后,我们求偏导,令导数为0,从而求出我们每棵树对应的目标值。
可以得到:
此时,再代入回原式,我们则可以得到每一棵树其对应的最优解情况:
这里G(j)为对应第j个叶子结点上所有样本一阶求导后和的平方。
H(j)为对应第j个叶子结点上所有样本二阶求导后的和。
lambda为L2正则中的惩罚系数。
gama为加入新叶子结点引入的复杂度代价。
T为总叶子数量。
所以对每棵树而言,即要构造出Obj分数最小的树结构。
那么此时我们分割树节点实际上也有了相应的标准。
前两项是分割后的左子树对应分数与右子树对应分数。
而第三项则是分割前的树的分数。
因为是严格的二叉树,所以分割前后叶子只会相差一个,故而尾巴上会减去一个gama。
目标损失函数要越小越好,带上负号以后用于描述一棵树,令其为Gain值,即越大越好。
故而,分割时,我们会寻找Gain值最大的分裂方式进行子树的分割。
如果最大的Gain值仍小于零,那么此时我们就不必再进行进一步分割了。
那么算法原理剖析部分基本咱们就陈述清楚了。
那么问题来了!!!
问题一:如果树分割节点的规则是一致的,那么每一棵树的最后形态都会一样?
不会的,首先每一轮预测的目标值不一致。所以每次分割节点的时候形态则不是固定的。
其次XGBoost采用了column subsampling(列采样)的方式,即每棵树的建立过程中对于特征并不是一次用完,其出发点是为了更加具备泛化能力,降低过拟合的情况,但也会导致树的构建后不会得到相同的形态。
问题二,一阶泰勒展开和二阶泰勒展开的本质区别是什么?
或者说为什么GBDT用一阶泰勒展开,而XGBoost用二阶泰勒展开?
实际上这不是一阶泰勒展开和二阶泰勒展开的区别。
而是同一个问题的两种不同解决思路。
也就是对最优化问题采取数值解还是解析解的问题。
那么什么是数值解,什么是解析解呢?
简单来说,就是给定一个方程,你无法直接去计算它的精确解,那么这个时候我们会采用数值逼近的方式去不断地逼近真实解,最终获得的也是对应数值,这个即为数值解。
而解析解则是,我们通过一系列的求解技巧,获得了方程的精确解,给定一个自变量,就能通过精确解知道因变量对应的值。
举一个例子,给定一个方程y=x^2,那么此时
x=sqtr(y)则是该方程的精确解,也就是其解析解,对于任意给定的y,我们都能根据解析解得到结果。
而x=2.236则是该方程在y=5时的一个数值解,也就是通过数值逼近的方式获得具体的一个解。
所以这里GBDT是通过求数值解,进行不断逼近的过程。
首先,泰勒展开的出发点是为了寻找一个方便计算的式子来模仿逼近原函数,而一阶泰勒展开是为了让计算更加方便,便于快速求解。而其具体的求解方法也是先进行一阶泰勒展开,然后通过梯度下降法求解目标损失函数的最优解,不断地迭代寻找到最逼近的真实解。
故而它也就需要每一步的目标值来进行预测,即利用负梯度的方式来获取到每一轮迭代的目标值,然后再通过CART树来不断拟合每一轮的目标值。
而XGBoost则不是通过数值解的方式来进行数值逼近求解。
而是通过解析解的方式进行求解,先对损失函数进行二阶泰勒展开,然后利用对二元函数求极值的方式来求出对应的参数。
首先我们知道,泰勒展开的出发点是使用多项式的方式来近似表达原函数。
而这里我们所要近似表达的主体是损失函数,利用二阶泰勒展开的目的则是为了能够近似表达更多种类的损失函数。
XGBoost算法实现代码剖析
通过前面我们已经知道,XGBoost对目标损失函数进行二阶泰勒展开,并增加正则项来防止过拟合,降低树构建的复杂程度。
然后通过偏导求极值的方式得出目标损失函数极小值情况下每棵树对应的最优解。
并根据每棵树最优解的情况,进一步得到树节点分割前后对应的Gain值。
也就是只需要找到Gain最大的分割情况,也就能满足当前树的最优解情况。
这也是贪心算法的一种体现。所谓贪心算法,也就是要每一步都求出最优解,或者说只关心当前所走的这一步都是最优解,也就是其更多关注的是局部,而不是全局。
所以在建立一棵树时,我们可以根据Gain值进行树节点划分。
而Gain值,又可以直接通过样本计算获得。那么看起来,这个算法基本完整了。
在算法之前可能还会有人有疑问。
特征在哪里体现?决策树的节点分割不是以特征作为阈值进行样本划分的吗?
这个问题比较好答。因为特征是跟样本绑定的,而咱们讨论Gain值的时候,一定是讨论的某个特征对应某种分隔条件情况下对应的Gain值,比如问题背景中tree1的第一层分割条件是是否年龄小于十五岁。因为只有这样,我们才能对样本进行左右归类,然后去计算当前分割情况下的Gain值。
基本逻辑咱们已经剖析清楚了,来看看XGBoost的具体算法实现伪代码:
首先,针对不同的问题,我们可以使用不同的损失函数。
给定一个损失函数,比如还是以回归问题的均方差损失为例,一阶导为残差,二阶导为常数值,也就是说我们可以得到其一阶导和二阶导对应的式子。
假定当前正在构造第t棵树,也就是前面第t-1棵树已经确定了,并且第t-1棵树对应的预测值为某数a。
那么此时构造第t棵树时,首先我们先根据所有的样本以及上一轮的预测值进行所有g和h的计算,从而进一步得到所有样本对应的g之和G,以及h之和H。
那么如果有10w个样本的话,我们则需要进行10w次的g和h的计算,由于每个样本对于g和h的计算彼此之间是相互独立的。
所以这个地方,可以通过并行计算的方式来进行提速。也就是每个样本各自的g和h是可以进行并行计算的。
但由于这个计算过程都是CPU计算过程,所以实际上可以通过多进程的方式进行并行计算。
关于多进程与多线程的区别,咱们在python进程、线程与协程之理论篇中有提及。有需要的可以再看看。
霖淼书生,公众号:数据分析师成长之路python进程、线程与协程之理论篇
这里之所以先计算出G和H,是因为无论按照什么特征进行拆分。
计算Gain值时,都满足G=G(L)+G(R),同时,H=H(L)+H(R)。
故而先获得一个总体的值,后面无论哪种拆分方式,我们实际上也只需要计算一侧的G和H,就可以通过求差的方式知道另一侧的G和H,不必再对另一侧的全部样本重新遍历计算一遍g和h,再累加获得G和H了。
然后先进入第一层循环,这里的k从一遍历到m的意思是,逐个遍历每一个特征。
因为咱们要计算的是每一个特征分割时对应的Gain值,而Gain值的又涉及到样本的分配问题,即如何将样本划分为两堆,能够获取到最大的Gain值。
显然,随意地将两拨人进行分隔,然后计算Gain值是不合理的,因为即便我们穷举了所有划分方式,得到了两边样本各是啥的时候其Gain值最大,也依然没法找出一个划分标准,比如说大于该值的归到一边,小于等于该值的归到另一边。
而实际决策树的划分中,我们往往就是以一个阈值来进行划分的,即大于该值归属到右节点,小于等于该值归属到左节点。
所以这里在遍历到每一个特征时,会先对所有样本的对应当前特征维度的值进行一个排序(当然,这里的排序可以事先做好,每一次需要的时候直接使用即可),也就是这里的第二层循环。
然后分别去计算对应的G(L),H(L),G(R),H(R)以及对应的score,并且每一个score对应的特征划分阈值,我们也正好可以记录保存下来。
那么这里我们细致分析一下可以发现,第二层for循环是对所有样本当前的目标特征进行排序,然后逐个计算过去,并且下一次的计算可以在上一次计算的基础上直接做累加。
所以第二层for循环每一步之间是存在依赖关系的,故而无法做并行。
但是,第一层for循环遍历的是所有维度的特征,然后根据当前维度的特征计算当前维度特征下最大的Gain值,也就是说,选定一个特征维度后,实际上就可以独立地往下进行计算了。
故而实际上每个特征维度其最大的Gain值是相互独立的,也就是此处也可以进行并行计算。即不同特征下计算其各自的Gain值时是可以并行的。实际上,XGBoost就是这么做的。
故而通过上述两层的循环,我们可以寻找到所有特征下对应的Gain值,然后我们就可以根据当前最大Gain值对应的特征,以及其划分阈值,进行一次树的拆分。也就是说,上面的算法逻辑,实际上是一次节点分割的流程。
那么,给定一个迭代终止条件,比如最大树深,早停法迭代次数等。
就可以完成一棵树的完整建立过程。
贪心法代码实现逻辑小结:
所以再整体回过来看:
1、这里的两层for循环。
第一层是遍历所有的特征。第二层是计算当前特征下最大Gain值对应的划分阈值。
2、这里的G和H都先计算好总值,然后在每一次计算左右G、H时则只需要做一边的增量计算,另外一边则可以通过总值来减去已计算好那边的值来获得。不需要对另一边再重新遍历计算一遍所有g、h再累加求和。
3、由于每个样本的g和h是独立的,故而计算出G和H之前,对于每个样本的g和h的计算可以并行执行。其次,对于每个特征,其各自计算Gain值最大情况下样本的划分阈值的计算,也是独立的。故而此过程也可以进行并行执行。
4、算法的整体过程我们可以发现,这里实际上是对每一次的节点分割都是找的最优解,也就是贪心算法的一个体现,即它只关心局部最优,而不关心全局最优。
注意:
在计算单个特征时,其计算各阈值下对应的Gain值时,因为迭代期间下一轮会用到上一轮的结果,故而此处并不能并行。
另外,因为XGBoost和GBDT性质一样,总模型等于各个子模型累加获得。故而其树的构建具备一定的先后关系,此处也不能并行。
到这里,贪心法代码实现逻辑就基本理清了,但是问题也来了。
虽然代码实现过程中不同的特征计算Score时可以采用并行计算的方式,但是同一个特征下,划分最佳阈值的过程时没法进行并行计算的。
所以当某个特征时连续型特征变量时,样本数量很大,不同样本的取值情况又很多时。
那么遍历所有的取值情况,并计算各个取值情况下对应的Score时,会花费很多时间,并且可能会由于划分的太过精细而容易过拟合。
再夸张一点,当连续型特征变量特别多,并且都是取值情况也很多时,在计算过程中,会导致内存的急剧增加。甚至会可能产生内存塞不下,运行程序崩溃了的情况。
所以这里就需要进行计算过程在内存和速度上的优化。
故而作者提出了算法二,即对算法一的计算和内存优化改进。称之为近似分割算法。
具体算法实现伪代码:
实际上,这个算法的思路不是对第一个算法的摒弃,而是对其的一个补充。
它在寻找某一个特征下不同阈值分割的时候,不会枚举所有的特征值,而会先对特征值进行聚合统计,然后形成若干个bucket(桶),然后只将bucket边界上的特征值作为split节点的候选。
因为桶的数目会远小于样本的数目,故而在性能上会得到提升以及内存使用会得到优化。
实际上这里优化点有两个:
一个是计算总的G和H的过程。
另外一个则是针对每一个特征时,计算其排序后的阈值划分时对应的Score的过程。
第一个是减少计算次数,也就是减少累加的数目。
第二个则是减少阈值个数,也就是减少for循环的迭代次数。
再然后,由于训练集样本和测试集样本在某个特征上可能会存在缺失值的情况。
那么上述计算过程要做阈值划分时,就无法得知应该将当前特征维度特征值有缺失的样本如何归类了。
所以,为了解决特征值缺失这种稀疏特征的情况,作者又提出了算法三:稀疏特征处理
故而实际上,XGBoost在处理样本的时候,先将所有没有缺失值的样本进行汇总,用桶的方式来提速,计算出各自特征维度下总的G和H。
然后遍历迭代每个特征。
对未存在缺失值排序后的所有特征进行逐个遍历,当然,这里已经桶化了。
然后计算出各个样本(不存在缺失值)计算出g和h,累加上一步结果得到总的H(L)和G(L)。
再用总的对左边的相减来获得右边的。
此时,因为左边的是逐个取的并累加计算的,
右节点则是通过总的减去左边取过的,
那么在逻辑上,也就意味着,不在左边累加过程中计算过的,就都是属于右侧的。
也就默认将带有缺失值的样本是全部归属到右节点上。
然后,再调换左右计算的方式,即累加上一步结果计算得出总的H(R)和G(R)。
而对于左边的则使用总的对右边的相减获得。
此时,也就默认带有缺失值的样本全部归属到了左节点上。
故而总的来说,
就是使用总的H和G减去左边来得到右边H和G时,
此时就默认有缺失属性的样本归属在右边。
而使用总的H和G来减去右边来得到左边H和G时,
此时就默认有缺失属性的样本归属在左边。
因为被减的一方,都是逐个样本实打实累加上去的,你没参与计算,自然没法归属到这一堆里。
当然,这个是针对训练集训练的过程中使用的策略。
而在预测中,则是直接自动地将当前特征维度有缺失的样本归属到右节点上。
因为预测时,无法再计算所谓的g和h。是直接拿的特征阈值来进行划分了。
那么问题又来了!!!
计算总的G和H的时候,当前维度特征有缺失值的样本并未参与计算。
并且在阈值遍历过程中,计算G(L)和H(L) 或者 计算G(R)和H(R)的时候也是用的未存在缺失值的排序后的取值。
看起来,带有缺失值的特征对总G,总H以及局部G,局部H的计算并没有任何影响啊?
那为什么说有缺失值的样本在左边或在右边呢?
因为上述算法只是整棵树构建过程其中一次的分割过程。
分割完了以后,还要根据剩余的特征维度进行下一轮的分割过程。
而上一轮样本在分割属性的特征上有缺失值,并不意味着其他特征维度上也一定有缺失值啊。
而下一轮的分割就是要用到其他特征维度的特征值来进行走一遍完整算法流程啊。
所以这个放左放右的作用,实际上,并不是针对的本轮算法执行过程讲的。
而是为下一轮算法流程做铺垫,将样本按照某种策略安排下去。
XGBoost优缺点分析
首先,我们知道XGBoost是boosting思想的一种体现。
进一步说,它是GBDT算法的一种改进。但又不是完全基于GBDT做的改进。
而是针对同一问题,采用不同的方法进行解决的。
前者采用数值解的方式,对目标损失函数进行一阶泰勒展开,然后利用梯度下降法(负梯度),来进行逐步逼近真实解,并通过衰减系数来防止过拟合。
而后者则是采用解析解的方式,对目标损失函数进行二阶泰勒展开,然后通过偏导求极值,来获得当前步子中的局部最优,从而获得了每一棵树建立时的最佳模型。并通过正则项来防止过拟合,以及一定程度上降低树的复杂程度。
另外,XGBoost每棵树的建立过程,实际上并不是取的所有特征,而是借鉴了随机森林的算法,使用了列采样和行采样的方式来进行计算,在一定程度上也使得它具备更强的泛化能力,防止过拟合。
其次,根据XGBoost的流程梳理,我们知道,
它在寻找最佳分割点时,采用的是贪心算法的策略,但利用一些操作,以及其它补充算法,做了很多优化和弥补,包括并行计算,以及样本按特征排序后对G和H的简便计算,使用桶的边界值来替代一群样本的特征值等等都加速了计算过程,并优化了内存。
故而,简单梳理后我们可以知道,
XGBoost有以下一些优点:
1、贪心算法,局部最优精确解。通过最优化,求解析解的方式求得的最佳模型,适应性强。
2、防过拟合能力强:损失函数中即自带正则项,并且使用列采样和行采样的方式,使其具备较强的防过拟合能力。另外,XGBoost也和GBDT一样,采用了衰减系数,来防止一家独大,增强泛化能力。
3、速度快:算法运算过程中多处可以使用并行化计算,最典型的是每个特征下求其对应的Gain值。利用总值对逐步累加的单侧叶子H、G做差的方式来求得另一侧结果,加快运算速度。并且使用桶原理简化特征筛选维度,再次加速运算。
4、数据处理能力强:可以自动处理特征属性有缺失值的情况。
缺点:
1、算法参数太多,即意味着调参复杂。具体有哪些参数,这里可以参考官方链接。
2、不适合处理超高维特征数据。因为计算量还是会太大。
结束语:
到这里,基本篇幅已经足够了。
思量了一下,在神器之后,可以考虑单开一个实践篇幅。
来简单总结一下几个神器的使用,视情况而定吧。
觉得不错的小伙伴请转发关注一下。
你的关注,我的动力~
更多文章请关注