机器学习:xgboost原理及实践

文章目录

  • xgboost原理及实践
    • 1. 前言
    • 2. 什么是集成学习?
    • 3. Boosting和Bagging
      • 3.1 偏差-方差分解
      • 3.2 Bagging
      • 3.3 Boosting
    • 4. GBDT
    • 5. Xgboost
      • 5.1 xgboost实现
      • 5.1 xgboost特征选择
      • 5.2 xgboost超参数优化
    • 5. Xgboost和GBDT的比较

xgboost原理及实践

最近参加了数据分析的比赛,在这次比赛中,我主要负责了特征工程和学习模型的构建的工作,特征工程这里不多叙述,可以参考另一篇博文特征工程,介绍了常用的特征编码,当然不够完善,继续补充中。本文对这段时间学习到的一种机器学习算法进行整理和回顾——xgboost,具体包括了算法的原理和实现,以及参数的调整优化过程。
以下是我学习的主要资料:
机器学习西瓜书
Gradient Boosting梯度提升-GBDT与XGBoost解析及应用
Introduce to Boosted Trees
xgboost入门与实战
xgboost参数优化
xgboost特征重要性

1. 前言

xgboost是大规模并行boosted tree的工具,它是目前最快最好的开源boosted tree工具包,比常见的工具包快10倍以上。在数据科学方面,有大量kaggle选手选用它进行数据挖掘比赛,其中包括两个以上kaggle比赛的夺冠方案。在工业界规模方面,xgboost的分布式版本有广泛的可移植性,支持在YARN, MPI, Sungrid Engine等各个平台上面运行,并且保留了单机并行版本的各种优化,使得它可以很好地解决于工业界规模的问题。

理解xgboost,可能需要了解一下集成学习、bagging、 boosting和GBDT这些概念。

2. 什么是集成学习?

什么是集成学习呢?
在南京大学周志华教授的西瓜书中,对集成学习有比较清晰的定义:

集成学习(ensemble learning),通过构建并结合多个学习器来完成学习任务,有时也被称为多分类器系统(multi-classifier system)、基于委员会的学习(committee-based learning).
集成学习通过将多个学习器进行结合,常可获得比单一学习器显著优越的泛化性能。要获得好的集成, 个体学习器应“好而不同”,即个体学习器应该具有一定的“准确性”, 即学习器不能太坏, 并且要有“多样性”,即学习器之间应该具有差异。
事实上,如何生成并结合“好而不同”的个体学习器,是集成学习研究的核心。根据目前个体学习器的生成方式,目前的集成学习方法大致可以分成两大类:

  • 个体学习器间存在强依赖关系、必须串行生成的序列化方法:boosting、xgboost
  • 个体学习器间不存在强依赖关系,可同时生成的并行化方法:bagging、 Random Forest

集成学习是机器学习算法中地位非常重要的一类算法,其核心思想是,使用弱学习器(如线性模型、决策树等)进行加权求和,从而产生性能较为强大的学习器。具体的,加入我们有数据集 D = ( x i , y i ) , i = 1 , 2 , … , n D=(x_i,y_i),i=1,2,\dots,n D=(xi,yi),i=1,2,,n,集成学习希望使用如下的模型对数据集D进行拟合
在这里插入图片描述
其中,系数 α m \alpha_m αm为各个弱学习器的权重, h m ( x ) ∈ H ,   m = 1 , 2 , … , M h_m(x) \in H,\ m=1,2,\dots,M hm(x)H, m=1,2,,M为弱学习器。进行如下的拟合过程
机器学习:xgboost原理及实践_第1张图片
即可得到强学习器 F ∗ F^* F。由上可知,获取强学习器的关键在于如何获得弱学习器 h m ( x ) ∈ H ,   m = 1 , 2 , … , M h_m(x) \in H,\ m=1,2,\dots,M hm(x)H, m=1,2,,M,如果按照指导弱学习器生进行学习的理论基础进行分类,我们可以将其分为bagging和boosting。

3. Boosting和Bagging

3.1 偏差-方差分解

理解这一点之前,我们回顾一下西瓜书上对于偏差-方差分解的解释:
对于测试样本 x x x,令 y D y_D yD为x在数据集中的标注, y y y x x x的真实标记, f ( x ; D ) f(x;D) f(x;D)为训练集 D D D上学得的模型 f f f x x x上的预测输出。以回归任务为例,学习算法的期望预测为:
机器学习:xgboost原理及实践_第2张图片
可知, 模型的期望泛化误差可表示成模型 f(x;D) 关于同分布的多个数据集 D 的方差、模型 f(x) 的偏差和数据集的噪声之和. 其中, 偏差度量了学习算法的期望预测与真实结果的偏离程度,即刻画了学习算法的拟合能力方差度量了同样大小的训练集的变动所导致的学习性能的变化,即刻画了数据扰动所带来的影响噪声则表达了在当前任务上任何学习算法所能达到的期望泛化误差的下界,即刻画了学习问题本身的难度. 一个很自然的想法是, 如果能使用多个同样大小的同分布数据集分别进行模型的训练, 然后使用其平均的预测结果作为模型的最终预测结果, 那么我们就能有效地降低模型的方差, 从而降低模型的期望泛化误差. 这个就是bagging的基本思想

3.2 Bagging

Bagging [Breiman, 1996a]是并行集成学习方法最著名的代表.从名字即可看出,它直接基于自助采样法(bootstrap sampling),进行有放回抽样, 训练出若干个弱学习器进行集成, 其目的是通过有放回抽样构造出多个数据集并分别进行弱学习器训练再进行集成, 以期降低模型的期望泛化误差偏差方差分解中的方差部分, 从而增强模型的泛化能力. 给定包含m个样本的数据集,我们先随机取出一个样本放入采样集中,再把该样本放回初始数据集,使得下次采样时该样本仍有可能被选中,这样,经过m 次随机采样操作,我们得到含m个样本的采样集,初始训练集中有的样本在采样集里多次出现,有的则从未出现. 初始训练集中约有63.2%的样本出现在采样集中.

照这样,我们可采样出T个含m个训练样本的采样集,然后基于每个采样集训练出一个基学习器,再将这些基学习器进行结合.这就是 Bagging的基本流程. 在对预测输出进行结合时,Bagging通常对分类任务使用简单投票法,对 回归任务使用简单平均法.若分类预测时出现两个类收到同样票数的情形,则最简单的做法是随机选择一个,也可进一步考察学习器投票的置信度来确定最终胜者.

值得一提的是,自助采样过程还给Bagging带来了另一个优点:由千每个基学习器只使用了初始训练集中约63.2%的样本,剩下约36.8%的样本可用作验证集来对泛化性能进行“包外估计"(out-of-bag estimate).

事实上,包外样本还有许多其他用途.例如当基学习器是决策树时,可使用 包外样本来辅助剪枝,或用于估计决策树中各结点的后验概率以辅助对零训练样本结点的处理;当基学习器是神经网络时,可使用包外样本来辅助早期停止以减小过拟合风险.

从偏差-方差分解的角度看, Bagging主要关注降低方差,因此它在不剪枝决策树、神经网络等易受样本扰动的学习器上效用更为明显.
机器学习:xgboost原理及实践_第3张图片

3.3 Boosting

Boosting是一族可将弱学习器提升为强学习器的算法.这族算法的工作机制类似:先从初始训练集训练出一个基学习器,再根据 基学习器的表现对训练一样本分布进行调整,使得先前基学习器做错的训练样本在后续受到更多关注,然后基于调整后的样本分布来训练下个基学习器;如此重复进行,直至基学习器数目达到事先指定的值T, 最终将这T个基学习器进行加权结合.这类算法具有代表性的是AdaBoost,具体推导可以参见西瓜书。
偏差-方差分解的角度看,Boosting主要关注降低偏差,因此Boosting 能基于泛化性能相当弱的学习器构建出很强的集成.
机器学习:xgboost原理及实践_第4张图片
sigal有一篇文章对boosting进行了比较清晰的说明:
链接:http://www.tensorinfinity.com/paper_151.html
机器学习:xgboost原理及实践_第5张图片
机器学习:xgboost原理及实践_第6张图片
我们常说集成学习框架中的基模型是弱模型,通常来说弱模型是偏差高(在训练集上准确度低)方差小(防止过拟合能力强)的模型。但是,并不是所有集成学习框架中的基模型都是弱模型。bagging和stacking中的基模型为强模型(偏差低方差高),boosting中的基模型为弱模型。
  
这里可以进一步参看为什么说bagging是减少variance,而boosting是减少bias?

  1. 使用模型的偏差和方差来描述其在训练集上的准确度和防止过拟合的能力
  2. 对于bagging来说,整体模型的偏差和基模型近似,随着训练的进行,整体模型的方差降低
  3. 对于boosting来说,整体模型的初始偏差较高,方差较低,随着训练的进行,整体模型的偏差降低(虽然也不幸地伴随着方差增高),当训练过度时,因方差增高,整体模型的准确度反而降低
  4. 整体模型的偏差和方差与基模型的偏差和方差息息相关

4. GBDT

GBDT(Gradient Boosting Decision Tree) 又叫 MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的结论累加起来做最终答案。它在被提出之初就和SVM一起被认为是泛化能力(generalization)较强的算法。近些年更因为被用于搜索排序的机器学习模型而引起大家关注。

GBDT主要由三个概念组成:Regression Decistion Tree(即DT),Gradient Boosting(即GB),Shrinkage (缩减);GBDT的核心在于累加所有树的结果作为最终结果,而分类树的结果没有办法累加,所以GBDT中的树都是回归树,不是分类树

GBDT算法原理深入解析
GBDT(Gradient Boosting Decision Tree) 没有实现只有原理
使用sklearn进行集成学习——理论
GBDT(MART) 迭代决策树入门教程 | 简介

gradient boosting
在这里插入图片描述
首先确定初始提升树 f 0 ( x ) = 0 f_0(x)=0 f0(x)=0,第m步的模型是: f m ( x ) = f m − 1 + T ( x ; Θ m ) f_m(x)=f_{m-1}+T(x;\Theta_m) fm(x)=fm1+T(x;Θm)通过经验风险极小化确定下一棵决策树的参数:
ALT
下面是梯度提升树的一般框架,其可以拥有多种不同变体且细节相异的实现方式:
机器学习:xgboost原理及实践_第7张图片
当采用均方误差损失函数时,上图中的 r i m r_{im} rim为: r m = y − f m − 1 ( x ) r_{m}=y-f_{m-1}(x) rm=yfm1(x)即最小均方损失函数的关于预测值的反向梯度即为残差

实际上可以理解为,每一次迭代,构建一个新的决策树拟合上一轮迭代的真实值和预测值之差,即残差。

这里的拟合残差指根据回归树的生成算法步骤,用平方误差最小化准则求解每个单元上的最优输出值时,本来应该取输出变量Y与预测值的平方差,在提升树使用前向分步算法到下一个决策树学习的时候,可以把当前模型拟合数据的残差作为下一个模型学习的输出变量Y’,通过学习每一轮的学习误差(残差),到下一轮拟合的误差会越来越小,最后将模型输出累加起来,就可以得到最优的输出结果。

GBDT的核心就在于,每一棵树学的是之前所有树结论和的残差,这个残差就是一个加预测值后能得真实值的累加量。比如A的真实年龄是18岁,但第一棵树的预测年龄是12岁,差了6岁,即残差为6岁。那么在第二棵树里我们把A的年龄设为6岁去学习,如果第二棵树真的能把A分到6岁的叶子节点,那累加两棵树的结论就是A的真实年龄;如果第二棵树的结论是5岁,则A仍然存在1岁的残差,第三棵树里A的年龄就变成1岁,继续学。这就是Gradient Boosting在GBDT中的意义。

回归树问题的提升树算法步骤
机器学习:xgboost原理及实践_第8张图片
如果不采用平方损失函数呢?我们可以引入任意损失函数,然后利用最速下降法,求得损失函数的负梯度,对该值进行拟合。

常见的损失函数:
  
  ls:最小均方回归中用到的损失函数。在之前我们已经谈到,从拟合残差的角度来说,残差即是该损失函数的反向梯度值(所以又称反向梯度为伪残差)。不同的是,从拟合残差的角度来说,步长是无意义的。该损失函数是sklearn中Gradient Tree Boosting回归模型默认的损失函数。
  
  deviance:逻辑回归中用到的损失函数。熟悉逻辑回归的读者肯定还记得,逻辑回归本质是求极大似然解,其认为样本服从几何分布,样本属于某类别的概率可以logistic函数表达。所以,如果该损失函数可用在多类别的分类问题上,故其是sklearn中Gradient Tree Boosting分类模型默认的损失函数。
  exponential:指数损失函数,表达式为: L ( y , F ) = e x p ( − y F ) L(y, F)=exp(-yF) L(y,F)=exp(yF)
  
  当损失函数是指数损失时,Gradient Boosting相当于二分类的Adaboost算法。是的,指数损失仅能用于二分类的情况。
  
   不同的损失函数和极小化损失函数方法决定了boosting的最终效果,我们现在来说几个常见的boosting:
机器学习:xgboost原理及实践_第9张图片

5. Xgboost

不同于传统的gbdt,只利用到了一阶导数的信息,xgboost对损失函数进行了二阶泰勒展开,并且在目标函数之外加入正则项整体求最优解,用以权衡目标函数的下降和模型的复杂程度,进一步避免过拟合。
机器学习:xgboost原理及实践_第10张图片
可以看出,xgboost的优化目标包括两项,一项是训练误差,一项是模型复杂度
机器学习:xgboost原理及实践_第11张图片
与我们之前所讲的前向分布算法一致,每一次迭代,训练一个新的决策树以拟合残差,在xgboost中如何选择函数,即决策树f呢?
机器学习:xgboost原理及实践_第12张图片
这里开始与传统的gbdt不同:
1)首先对损失函数进行二阶泰勒展开:
机器学习:xgboost原理及实践_第13张图片
2)将优化目标改写成:
在这里插入图片描述
这里, g i g_i gi h i h_i hi分别是损失函数的一阶导数和二阶导数,如果损失函数为平方损失的话,可以理解: g i = ∂ ( y ^ t − 1 − y i ) 2 ∂ y ^ t − 1 = 2 ( y ^ t − 1 − y i ) g_i=\frac{\partial(\hat{y}_{t-1}-y_i)^2}{\partial\hat{y}_{t-1}}=2(\hat{y}_{t-1}-y_i) gi=y^t1(y^t1yi)2=2(y^t1yi) h i = ∂ 2 ( y ^ t − 1 − y i ) 2 ∂ y ^ t − 1 = 2 h_i=\frac{\partial^2(\hat{y}_{t-1}-y_i)^2}{\partial\hat{y}_{t-1}}= 2 hi=y^t12(y^t1yi)2=23)进一步简化,去除常数项(当前步 y i y_i yi y ^ t − 1 \hat{y}_{t-1} y^t1已知):
机器学习:xgboost原理及实践_第14张图片
也就是说,xgboost中保留了泰勒展开的二次项,通过这种近似,只要二阶函数可导,可以自定义一些损失函数。
机器学习:xgboost原理及实践_第15张图片
4)重新定义树:
机器学习:xgboost原理及实践_第16张图片
5)定义树的复杂度项
机器学习:xgboost原理及实践_第17张图片
从图中可以看出,xgboost算法中对树的复杂度项包含了两个部分,一个是叶子节点总数,一个是叶子节点得分L2正则化项,针对每个叶结点的得分增加L2平滑,目的也是为了避免过拟合。
6)更新优化目标
机器学习:xgboost原理及实践_第18张图片
7)定义分数度量
可以看出,目标函数其实是一个一元二次函数,在树的结构q已经确定的情况下,为了使损失函数在其二阶近似中取得最小值,可得叶子的最优取值
机器学习:xgboost原理及实践_第19张图片
计算例子:
机器学习:xgboost原理及实践_第20张图片
8)如何生成最优秀的树:贪婪算法
机器学习:xgboost原理及实践_第21张图片
机器学习:xgboost原理及实践_第22张图片
例如:
机器学习:xgboost原理及实践_第23张图片
两种分类的方式:

  1. Exact Greedy Algorithm
    对于每一维特征, 该算法针对此特征对样本集进行排序, 然后遍历每个样本在该特征上的取值并进行分裂, 计算出 Δ L o s s \Delta \mathcal{Loss} ΔLoss 以确定最佳分裂特征和分裂点.
    进行了一次完全的扫描, 从而得到最佳的分裂方案. 其优点是, 能确保找到最佳的分裂方案, 然而缺点也非常明显, 当特征的维数 d 和样本个数 n 非常大时, 算法的执行会非常消耗时间和资源. 为此, XGBoost提出了另一种近似的算法以寻找分裂特征和分裂点.
  2. Approximate Algorithm
    当训练集的样本量为千万甚至上亿级别时, 对于任一特征 k , 这意味着寻找特征 k 的分裂点需要执行同样数量级的尝试操作. 如果我们能通过减少特征 k 所需要尝试分裂点的个数, 那自然能减少大量的计算量, 这便是Approximate Algorithm所采取的思路.具体过程见Gradient Boosting梯度提升-GBDT与XGBoost解析及应用

9)总结
机器学习:xgboost原理及实践_第24张图片

5.1 xgboost实现

xgboost的具体实现可以参考官方API

import xgboost as xgb
from xgboost.sklearn import XGBRegressor

# modelfit函数
def modelfit(alg, dtrain, y_train, dtest, y_test, useTrainCV=True, cv_folds=5, early_stopping_rounds=50):

    if useTrainCV:
        xgb_parameters = alg.get_xgb_params()
        xgtrain = xgb.DMatrix(dtrain.values, label=y_train.values)
        cvresult = xgb.cv(xgb_parameters, xgtrain, num_boost_round=alg.get_params()['n_estimators'], nfold=cv_folds,
                          metrics='rmse', early_stopping_rounds=early_stopping_rounds, show_stdv=False)
        print("the cv number is: ", cvresult.shape[0])
        alg.set_params(n_estimators=cvresult.shape[0])

    alg.fit(dtrain, y_train, eval_metric='rmse')

    dtrain_prediction = alg.predict(dtrain)
    dtest_prediction = alg.predict(dtest)

    # print model report
    print("\nModel Report")
    print("feature numbers: %d" % dtrain.shape[1])
    print("Train RMSE : %.4g" % mean_squared_error(y_train.values, dtrain_prediction)**0.5)
    print("Test RMSE : %.4g" % mean_squared_error(y_test.values, dtest_prediction) ** 0.5)

    # feat_imp = pd.Series(alg.get_booster().get_fscore()).sort_values(ascending=False)
    # feat_imp.plot(kind='bar', title='Feature Importance')
    # plt.ylabel('Feature Importance Score')
    # plt.show()
    plot_importance(alg)
    plt.show()

    importances = alg.feature_importances_

    return dtrain_prediction, dtest_prediction

5.1 xgboost特征选择

这里是一些特征选择的方法,此次我主要利用sklearn中的SelectFromModel进行特征选择

# 初始化模型
xgb0 = XGBRegressor(random_state=10, importance_type='gain')
start = time()
dtrain_prediction, dtest_prediction = modelfit(xgb0, X_train, y_train, X_val, y_val)
end = time()
print("the model fit time: %.4f" % (end-start))

train_out = pd.DataFrame(list(zip(y_train.values.flatten(), pd.Series(dtrain_prediction))),
                         index=y_train.index, columns=['y_true', 'y_pred'])
test_out = pd.DataFrame(list(zip(y_val.values.flatten(), pd.Series(dtest_prediction))),
                        index=y_val.index, columns=['y_true', 'y_pred'])


# step4:特征选择
model = SelectFromModel(xgb0, prefit=True)
selection_X_train = model.transform(X_train)
selection_X_val = model.transform(X_val)
selection_X_test = model.transform(X_test)

start = time()
xgb0.fit(selection_X_train, y_train)
end = time()
y_train_pred = xgb0.predict(selection_X_train)
y_val_pred = xgb0.predict(selection_X_val)

train_rmse = mean_squared_error(y_train, y_train_pred) ** 0.5
val_rmse = mean_squared_error(y_val, y_val_pred) ** 0.5
print("After feature selection: n=%d, train rmse=%.4f, val rmse=%.4f, the model fit time: %.4f" %
      (selection_X_train.shape[1], train_rmse, val_rmse, (end-start)))

5.2 xgboost超参数优化

在这次的项目中,我主要调用了sklearn.model_selection的GridSearchCV进行参数的调节,xgboost的参数有许多,具体参数可见官方介绍,在这里,我按照Complete Guide to Parameter Tuning in XGBoost (with codes in Python)中的方法,对几个超参数进行了调整。

# step5: 参数调优

# 5.1:手动输入参数
xgb1 = XGBRegressor(
    learning_rate=0.1,
    n_estimators=1000,
    max_depth=5,
    min_child_weight=1,
    gamma=0,
    subsample=0.8,
    colsample_bytree=0.8,
    # objective='reg:squarederror',
    seed=10,
    # nthread=4,
    tree_method='exact',
    random_state=10,
    importance_type='gain'
)

start = time()
dtrain_prediction, dtest_prediction = modelfit(xgb1, pd.DataFrame(selection_X_train),
                                               y_train, pd.DataFrame(selection_X_val), y_val)
end = time()
print("the model fit time: %.4f" % (end-start))
train_out = pd.DataFrame(list(zip(y_train.values.flatten(), pd.Series(dtrain_prediction))),
                         index=y_train.index, columns=['y_true', 'y_pred'])
test_out = pd.DataFrame(list(zip(y_val.values.flatten(), pd.Series(dtest_prediction))),
                        index=y_val.index, columns=['y_true', 'y_pred'])


# # 5.2 TODO: Tune max_depth and min_child_weight
print("\n--------------------------------------")
print("Tune max_depth and min_child_weight...")
cv = KFold(n_splits=5, random_state=10, shuffle=True)
param_test1 = {'max_depth': range(3, 11, 1), 'min_child_weight': range(1, 6, 1)}
gsearch1 = GridSearchCV(estimator=xgb1, param_grid=param_test1,
                        scoring=make_scorer(mean_squared_error, greater_is_better=False), iid=False, cv=cv)
grid_obj = gsearch1.fit(selection_X_train, y_train)
print(grid_obj.best_params_)
print(grid_obj.best_score_)
for i in range(len(grid_obj.cv_results_['params'])):
    print("the mean test score of {} is {}.".format(grid_obj.cv_results_['params'][i], grid_obj.cv_results_['mean_test_score'][i]))


# 5.3:TODO: tune the gamma
print("\n--------------------------------------")
print("Tune the gamma...")
xgb2 = grid_obj.best_estimator_
param_test2 = {'gamma': [i/100.0 for i in range(11)]}
gsearch2 = GridSearchCV(estimator=xgb2, param_grid=param_test2,
                        scoring=make_scorer(mean_squared_error, greater_is_better=False), iid=False, cv=cv)
grid_obj2 = gsearch2.fit(selection_X_train, y_train)
print(grid_obj2.best_params_)
print(grid_obj2.best_score_)
for i in range(len(grid_obj2.cv_results_['params'])):
    print("the mean test score of {} is {}.".format(grid_obj2.cv_results_['params'][i], grid_obj2.cv_results_['mean_test_score'][i]))

# 5.4:TODO: tune the subsample and colsample_bytree
print("\n--------------------------------------")
print("Tune the subsample and colsample_bytree...")
xgb3 = grid_obj2.best_estimator_
param_test3 = {'subsample': [0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
               'colsample_bytree': [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]}
gsearch3 = GridSearchCV(estimator=xgb3, param_grid=param_test3,
                        scoring=make_scorer(mean_squared_error, greater_is_better=False), iid=False, cv=cv)
grid_obj3 = gsearch3.fit(selection_X_train, y_train)
print(grid_obj3.best_params_)
print(grid_obj3.best_score_)
for i in range(len(grid_obj3.cv_results_['params'])):
    print("the mean test score of {} is {}.".format(grid_obj3.cv_results_['params'][i], grid_obj3.cv_results_['mean_test_score'][i]))

# 5.5:TODO: tune the Regularization Parameters
print("\n--------------------------------------")
print("Tune the Regularization Parameters")
xgb4 = grid_obj3.best_estimator_
param_test4 = {'reg_lambda': [1.0, 1.1, 1.2, 1.3, 1.4, 1.5]}
gsearch4 = GridSearchCV(estimator=xgb4, param_grid=param_test4,
                        scoring=make_scorer(mean_squared_error, greater_is_better=False),
                        iid=False, cv=cv)
grid_obj4 = gsearch4.fit(selection_X_train, y_train)
print(grid_obj4.best_params_)
print(grid_obj4.best_score_)
for i in range(len(grid_obj4.cv_results_['params'])):
    print("the mean test score of {} is {}.".format(grid_obj4.cv_results_['params'][i], grid_obj4.cv_results_['mean_test_score'][i]))

# 5.6:predict the best model
print("\n--------------------------------------")
print("predicting the best model")
best_xgb = grid_obj4.best_estimator_
start = time()
best_xgb.fit(selection_X_train, y_train)
y_train_pred = best_xgb.predict(selection_X_train)
y_val_pred = best_xgb.predict(selection_X_val)
end = time()
print("the model fit time: %.4f" % (end-start))
train_rmse = mean_squared_error(y_train, y_train_pred) ** 0.5
val_rmse = mean_squared_error(y_val, y_val_pred) ** 0.5
print("the best model report: train rmse=%.4f, val rmse=%.4f, the model fit time: %.4f" %
      (train_rmse, val_rmse, (end-start)))

5. Xgboost和GBDT的比较

第一个区别:牛顿法

GBDT在函数空间中利用梯度下降法进行优化,只考虑了一阶导数的信息,拟合的 f t ( x ) = f_t(x)= ft(x)=残差
xgboost在函数空间中利用牛顿法进行优化,加入了二阶导数的信息,拟合的 f t ( x ) = − g t ( x ) h t ( x ) f_t(x)=-\frac{g_t(x)}{h_t(x)} ft(x)=ht(x)gt(x)

第二个区别:正则化项

xgboost在目标函数中加入的正则化项,正则项对每棵回归树的复杂度进行了惩罚,而复杂度可以用树的深度,内部节点个数,叶子节点个数(T),叶节点分数(w)等来衡量。
机器学习:xgboost原理及实践_第25张图片
我们再来看看xgboost的优点:
机器学习:xgboost原理及实践_第26张图片

  • 二阶泰勒展开:传统的gbdt在优化时,只用到了一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶导数和二阶导数,我的理解是为了更加容易的理解收敛过程,同时支持自定义损失函数(二阶可导的条件下),欢迎指正;
  • 正则化:xgboost在目标函数里加入了正则化项,用于控制模型的复杂度。正则项里包含了树的叶子节点个数、每个叶子节点上输出的score的L2模的平方和。从Bias-variance tradeoff角度来讲,正则项降低了模型variance,使学习出来的模型更加简单,防止过拟合,这也是xgboost优于传统GBDT的一个特性 。
  • shrinkage(缩减):shrinkage缩减类似于学习速率,在每一步tree boosting之后增加了一个参数n(权重),通过这种方式来减小每棵树的影响力,给后面的树提供空间去优化模型。防止过拟合。
  • 对每颗子树增加一个参数,使得每颗子树的权重降低,防止过拟合,增加这个参数叫shrinkage方法。对特征进行降采样,灵感来源于随机森林,除了能降低计算量外,还能防止过拟合。

  • 增加处理缺失值的方案(通过枚举所有缺失值在当前节点是进入左子树,还是进入右子树更优来决定一个处理缺失值默认的方向)。
  • 对每个特征进行分块(block)并排序,使得在寻找最佳分裂点的时候能够并行化计算.这个结构加速了split finding的过程,只需要在建树前排序一次,后面节点分裂时直接根据索引得到梯度信息。这是xgboost比一般GBDT更快的一个重要原因。

你可能感兴趣的:(机器学习算法,xgboost)