1.无论是分类问题还是回归问题,GBDT建立的决策树都是回归树
2.当损失函数是指数损失时,GBDT退化为AdaBoost分类算法
3.GBDT拟合最优解利用了泰勒展开二阶信息的近似解
4.GBDT以负梯度为目标先建树,再执行直线搜索求解最优值,而XGB一步到位
5.GBDT与XGBoost决策树的分裂标准评价指标不同
6.XGBoost加入了正则项,考虑树结构和树预测值的复杂度
本篇对集成学习算法AdaBoost、GBDT、XGBoost做一个总结和补充,并介绍sklearn实现细节。我们通过一个小样例手推结果并与sklearn比较,彻底对集成学习算法是如何实现有一个感性的理解。便于在以后工作中不仅仅只会调包实现,还能针对性发现问题
以及提出改进方案。
注:关于集成学习算法推导见集成学习(三)、集成学习(四)、集成学习(五)
算法总结
我们从均方误差为损失函数的GBDT回归算法出发来梳理三者的关系。
这里关键要理解(b)和(c)步,(b)是以整体损失函数对前K-1轮学习器F的导数的负梯度为拟合目标构建决策树,其分裂标准跟决策树一样,接下来对叶子节点的赋值与常规决策树赋值方式不同(常规决策树赋值标准是多数法或均值法),这里的赋值方式就是(c)步,执行一次直线搜索,找到使得全局损失函数最小的拟合值(并非当前第K轮弱学习器决策树损失最小的拟合值)。
通常来说,直接求解(c)步的最优解r是很复杂的,我们利用泰勒展开来求解近似值:
所以,第K轮弱学习器每个叶子节点的最优解r就是一元二次方程的驻点,它刚好等于-g/h(g是一阶导,h是二阶导),要是叶子节点有m个样本,就是m个样本一阶导之和除以二阶导之和。
因此,GBDT回归算法拟合的是残差,叶子节点最优解就是拟合目标均值,这个跟回归决策树叶子节点赋值方式一样。
对于不同的损失函数,得到的负梯度不同,进而最优解的表达式不同,仅此而已,我们给出分类问题不同损失函数拟合的目标值以及最优解:
交叉熵损失和负对数似然损失都是逻辑回归的损失函数,它们是对目标y值不同的表达形式,它们是等价的。
我们可以看出,无论是分类问题还是回归问题,GBDT建立的决策树都是回归树,所谓
的分类回归,是针对目标y值不同建立不同的损失函数而对应。
同时,仔细观察指数损失的分类问题,就是跟AdaBoost分类问题等价的,因此,当损失函数是指数损失时,GBDT退化为AdaBoost分类算法,损失函数一致,因为中间变形的不同,GBDT针对目标y建模,AdaBoost侧重样本权重。
我们也可以看出,GBDT在拟合最优值时,利用了泰勒展开的二阶导信息,那么我们常说的XGBoost展开到二阶导又是怎么回事呢。
实际上,XGBoost是把GBDT的(b)、(c)步合起来一步完成,就是建立决策树和直线搜索最优解一次完成。同时,为了防止过拟合,还加入了正则项,最后其叶子节点最优解是:
如果正则系数为0,就跟GBDT直线搜索的结果完全相同。
由于是一步到位,所以决策树的分裂规则不再是常规决策树的分裂规则,它的分裂评价标准是:
寻找最佳分裂有Exact和Approx算法,更多细节见集成学习(五),这里不再赘述。
由此,我们可以总结:
无论是分类问题还是回归问题,GBDT建立的决策树都是回归树
当损失函数是指数损失时,GBDT退化为AdaBoost分类算法
GBDT拟合最优解利用了泰勒展开二阶信息的近似解
GBDT以负梯度为目标先建树,再执行直线搜索求解最优值,而XGB一步到位
GBDT与XGBoost决策树的分裂标准评价指标不同
XGBoost加入了正则项,考虑树结构和树预测值的复杂度
从手动到sklearn实现
三种算法的sklearn包如下:
from sklearn.ensemble import AdaBoostRegressor
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble import GradientBoostingClassifier
from xgboost import XGBClassifier
from xgboost import XGBRegressor
下一节我们再介绍其参数,这节我们先感受其算法实现过程。
我们通过一个小样例手动计算一下这三个算法是怎么实现的,然后对比sklearn调包的结果是否一致,以此也可以验证小编的理解是否有偏差。我们先介绍样例:
X1和X2是两个维度,y是目标取值
假设分裂标准都是mse,为了方便演示,决策树深度限制为1,学习率设置为1,迭代3次,即建立3次弱学习器。
我们先来看GBDT回归算法,利用excel表手动计算三棵树的结果:
呈现一下每棵树的指标:
利用sklearn包GradientBoostingRegressor来看看结果是否一致。
model = GradientBoostingRegressor(
loss='ls',criterion='mse',
n_estimators=3,learning_rate = 1,
max_depth=1,random_state=0)
model.fit(X,y)
clf = model.estimators_[0][0]
dot_data = tree.export_graphviz(clf,
out_file=None,feature_names=f_name,
filled=True,rounded=True,
special_characters=True,proportion=False,
rotate=False,precision=2)
graph = pydotplus.graph_from_dot_data(dot_data)
Image(graph.create_png())
sklearn包跑的结果与手动计算的完全一致,可见,GBDT内置代码树建立与最优解实现过程与上一节分析过程吻合。
我们再来看分类问题的实现过程。
负对数似然损失:
交叉熵损失:
这两个损失是等价的。我们再来看看sklearn实现结果
model = GradientBoostingClassifier(
loss='deviance',
criterion='mse',
n_estimators=3,
learning_rate = 1,
max_depth=1,
random_state=0)
model.fit(X,y)
结果一致。
最后来看看指数损失:
同样看看sklearn的实现效果:
model = GradientBoostingClassifier(
loss='exponential',
criterion='mse',
n_estimators=3,
learning_rate = 1,
max_depth=1,
random_state=0)
model.fit(X,y)
结果一致。
上一节介绍过,当损失函数为指数损失时,GBDT退化为AdaBoost分类,我们在集成学习(三)已经手推了一个案例,数据也是本文的数据,只不过第二个和第三个弱学习器调换了,我们就换回来,跟本文的一样,直接给出手推的过程:
我们用sklearn来检验一下小编手推结果是否一致。
model = AdaBoostClassifier(n_estimators=3,random_state=0,algorithm='SAMME')
model.fit(X,y)
三次迭代的错误率和弱学习器的权重与excel手推结果一致。
但这里要注意,AdaBoostClassifier包的参数algorithm要设置为SAMME才是手推,但其还有另外一个实现更快的优化算法,就是参数设置为SAMME.R,此时弱学习器的权重均为1.这两者不同的地方是:前者用对样本集分类效果作为弱学习器权重,后者使用了对样本集分类的预测概率大小来作为弱学习器权重。但结果是一致的,这里不用过多纠结。
sklearn包参数
这节介绍集成算法sklearn包的参数。主要介绍最重要的参数,其他参数见官方文档说明
AdaBoostClassifier
base_estimator:初始学习器,默认是决策树
n_estimators:子学习器的数量
learning_rate:学习率,步长。加在弱学习器前面的参数
algorithm:就是上节提到的实现方式
AdaBoostRegressor比分类的多了一个参数loss,就是损失函数定义,详情见集成学习(三),具体有
线性误差:‘linear’
平方误差:‘square’,
指数误差:‘exponential’
GradientBoostingClassifier
loss:损失函数,‘deviance’(负对数似然损失),‘exponential’(指数损失)
init:初始学习器
n_estimators:弱学习器个数
learning_rate:学习率,步长。取值范围(0,1]
subsample:正则化项,子采样比例
ccp_alpha:决策树剪枝惩罚系数
criterion:决策树划分标准,'mse','mae','friedman_mse'
min_samples_split:决策树非叶子节点最小分裂样本数
min_samples_leaf:决策树叶子节点最小样本数
max_depth:决策树的最大深度
GradientBoostingRegressor大同小异,就是损失函数的定义有差别,再有采取huber损失或分位数损失时的参数alpha,其他无大差别,这里不再赘述。
XGBoost
booster:弱学习器类型,默认是gbtree
n_estimators:弱学习器的个数
objective:定义分类或回归以及损失函数,均方误差reg:squarederror ,二分类binary:logistic,多分类multi:softmax
learning_rate:学习率
subsample: 子采样参数,与GBDT一致
max_depth:树的最大深度
min_child_weight:最小的子节点权重阈值,如果某个树节点的权重小于这个阈值,则不会再分裂。原理是某个节点所有样本二阶导(h)之和
reg_alpha:决策树结构复杂度参数,就是叶子节点个数|T|对应的γ参数
reg_lambda:决策树预测值复杂度参数,就是λ值
gamma:决策树分裂所带来的损失减小(ΔL)阈值,也就是分裂标准要达到的最小阈值
更多内容见官方文档,链接在最后参考链接里。
XGBoost可视化
另外,我们还要介绍一下XGBoost可视化的问题。我们沿用本文的样例。代码如下
import xgboost as xgb
from xgboost import plot_tree
from xgboost import XGBClassifier
from xgboost import XGBRegressor
model = XGBRegressor(
n_estimators=3,
max_depth=1,
learning_rate=1,
reg_alpha=0,
reg_lambda=0,
objective='reg:squarederror',
random_state=0)
model.fit(X,y)
xgb.to_graphviz(
model,num_trees=0,yes_color='#32CD32',no_color='#DAA520',
leaf_node_params={'shape': 'box',
'style': 'filled',
'fillcolor': '#e48038'}
)
参考资料:
https://scikit-learn.org/stable/user_guide.html
https://www.cnblogs.com/pinard/p/6143927.html
https://www.cnblogs.com/pinard/p/6136914.html
https://www.cnblogs.com/pinard/p/11114748.html
https://xgboost.readthedocs.io/en/latest/parameter.html