背景
在GBDT的基础上,有了XGB。XGB"(eXtreme Gradient Boosting极端梯度提升)本质还是 一个GBDT (Gradien tBoosting Decision Tree),XGB (Xtreme Gradient Boosting) 的本质还是一个GBDT, 但是在速度和效率上都发挥到了极致,XGB和GBDT比较大的不同就是目标函数的定义。
原理
XGB使用泰勒展开来近似目标函数。
二阶泰勒展开公式:
最小化下列损失函数,
γ
\gamma
γ和
λ
\lambda
λ是预先设定的超参数,
T
T
T是叶子节点的数量,
w
w
w是叶子结点的权重。
化简后:
g
i
=
∂
y
^
(
t
−
1
)
l
(
y
i
,
y
^
(
t
−
1
)
)
g_{i}=\partial_{\hat{y}^{(t-1)}} l\left(y_{i}, \hat{y}^{(t-1)}\right)
gi=∂y^(t−1)l(yi,y^(t−1)), 表示损失对
y
^
(
t
−
1
)
\hat{y}^{(t-1)}
y^(t−1)求导。
h
i
=
∂
y
^
(
t
−
1
)
2
l
(
y
i
,
y
^
(
t
−
1
)
)
h_{i}=\partial_{\hat{y}(t-1)}^{2} l\left(y_{i}, \hat{y}^{(t-1)}\right)
hi=∂y^(t−1)2l(yi,y^(t−1)), 表示损失对
y
^
(
t
−
1
)
\hat{y}^{(t-1)}
y^(t−1)求导。
可以得到:
g
i
=
∂
y
^
(
t
−
1
)
l
(
y
i
,
y
^
(
t
−
1
)
)
=
∂
y
^
(
t
−
1
)
(
y
i
−
y
^
(
t
−
1
)
)
2
=
−
2
y
i
+
2
y
^
(
t
−
1
)
\begin{matrix}{} {{g_i} = {\partial _{{{\hat y}^{(t - 1)}}}}l\left( {{y_i},{{\hat y}^{(t - 1)}}} \right)}\\ {\kern 10pt} = {\partial _{{{\hat y}^{(t - 1)}}}}{({y_i} - {{\hat y}^{(t - 1)}})^2}\\ {\kern -8pt} = {\rm{ - 2}}{y_i} + 2{{\hat y}^{(t - 1)}} \end{matrix}
gi=∂y^(t−1)l(yi,y^(t−1))=∂y^(t−1)(yi−y^(t−1))2=−2yi+2y^(t−1)
h
i
=
∂
y
^
(
t
−
1
)
2
l
(
y
i
,
y
^
(
t
−
1
)
)
=
2
{h_i} = \partial _{\hat y(t - 1)}^2l\left( {{y_i},{{\hat y}^{(t - 1)}}} \right) = 2
hi=∂y^(t−1)2l(yi,y^(t−1))=2
g
i
g_i
gi和
h
i
h_i
hi的结果带入公式展开,可以得到和原公式展开相同的结果。
为啥公式的第一行的
f
t
(
x
i
)
f_t(\bf{x_i})
ft(xi)消失了,因为这个得到的结果也是对应叶子节点的权重。注解:最后一步的转化思路是从在这个树中,每个样本落在哪个节点上转为了每个节点 上有哪些样本。
这个是对
w
j
w_j
wj做了求导,将最外层的求和符号去掉,然后就可以得到最后的结果。
这个是将优化后的
w
w
w带进去,然后得到的结果。
3. XGBoost在什么地方做的剪枝,怎么做的?
(1) 当引入的分割带来的增益小于一个阈值时,我们就可以减掉这个分割(参考决策树的分割)。
(2)在模型中加入正则
- XGBoost特征重要性实现原理:
每个特征对应一个模型参数 w i w_i wi,这个参数越大说明这个特征越重要。
gain :该特征在其出现过的所有树中产生的平均增益(我自己的理解就是目标函数减少值总和的平均值,这里也可以使用增益之和)。
每一个特征的分裂都会产生好多树,那么这些树是会带来增益的。
代码训练
#训练集和验证集数据划分
import xgboost as xgb
#train_x是DataFrame数据,选择对应的列就行
nums = int(train_x.shape[0] * 0.80)
# 训练集验证集划分
trn_x, trn_y, val_x, val_y = train_x[:nums], train_y[:nums], train_x[nums:], train_y[nums:]
#训练集
train_matrix = xgb.DMatrix(trn_x, label=trn_y.values, missing=np.nan)
valid_matrix = xgb.DMatrix(val_x, label=val_y.values, missing=np.nan)
# test_matrix = clf.DMatrix(test_x, label=val_y, missing=np.nan)
test_matrix = xgb.DMatrix(test_x, missing=np.nan)
params = {'booster': 'gbtree',
'eval_metric': 'mae',
'min_child_weight': 5,
'max_depth': 8,
'subsample': 0.5,
'colsample_bytree': 0.5,
'eta': 0.0015,
'seed': 2020,
'nthread': 4,
'silent': True,
}
watchlist = [(train_matrix, 'train'), (valid_matrix, 'eval')]
model = xgb.train(params, train_matrix, num_boost_round=50000, evals=watchlist, verbose_eval=500,
early_stopping_rounds=1000)
#划分的验证集数据
val_pred = model.predict(valid_matrix, ntree_limit=model.best_ntree_limit).reshape(-1, 1)
#需要测试的数据
test_pred = model.predict(test_matrix, ntree_limit=model.best_ntree_limit).reshape(-1, 1)
问题
XGBoost为什么要用二阶泰勒展开
- 二阶信息本身就能让梯度收敛更快更准确。这一点在优化算法里的牛顿法里已经证实了。可以简单认为一阶导指引梯度方向,二阶导指引梯度方向如何变化。
- 不同损失的求导是不一样的,为了可以统一损失函数求导的形式,所以用二阶泰勒展开。
参考资料
https://zhuanlan.zhihu.com/p/290964953?utm_source=ZHShareTargetIDMore (这个博客里面树模型比较详细)