极端提升树XGBoost及其实践

XGBoost stands for eXtreme Gradient Boosting.
XGBoost is an implementation of gradient boosted decision trees designed for speed and performance.

Speed: 并行计算每个样本的一、二次梯度,CART分裂时,也可并行计算不同分裂点,从中选择最优分裂点。

Performance: 修剪技术、正则项、考虑二次梯度。


目标函数:训练损失 + 正则项

目标函数由两部分组成:训练损失 L ( θ ) L(\theta) L(θ)正则项 Ω ( θ ) \Omega(\theta) Ω(θ)
obj ( θ ) = L ( θ ) + Ω ( θ ) \color{darkcyan} \text{obj}(\theta) = L(\theta) + \Omega(\theta) obj(θ)=L(θ)+Ω(θ)
训练损失用于测量模型在训练集的预测性能,训练损失可以是 mean squared error:
L ( θ ) = ∑ i ( y i − y ^ i ) 2 \color{darkcyan} L(\theta)=\sum_i(y_i-\hat y_i)^2 L(θ)=i(yiy^i)2
或者是 logistic loss:
L ( θ ) = ∑ i [ y i ln ⁡ ( 1 + e − y ^ i ) + ( 1 − y i ) ln ⁡ ( 1 + e y ^ i ) ] \color{darkcyan} L(\theta) = \sum_i[ y_i\ln (1+e^{-\hat{y}_i}) + (1-y_i)\ln (1+e^{\hat{y}_i})] L(θ)=i[yiln(1+ey^i)+(1yi)ln(1+ey^i)]
正则项控制模型复杂度,以避免overfitting,考虑下列图片中对同一数据的不同拟合:

The model marked in red visually seems a reasonable fit to you (bias-variance tradeoff).
Understanding the process in a formalized way also helps us to understand the objective that we are learning and the reason behind the heuristics such as pruning and smoothing


集成决策树

以下是使用CART决策树预测是否喜欢电脑游戏的例子:

家族成员被分配到不同叶子节点,CART中不同叶节点对应不同分数,使用分数可以使用统一的优化方法(后续章节介绍)。通常情况下,单棵树在实践中效果不好,可利用集成思想,即建立多棵树,并把这些树的结果组合起来作为最终输出:

以上集成两棵树,同一样本在两棵树中对应叶节点的分数值相加作为最终输出。从例中可看到,两棵树互补,从数学角度可解释为
y ^ i = ∑ k = 1 K f k ( x i ) , f k ∈ F \color{darkcyan} \hat{y}_i = \sum_{k=1}^K f_k(x_i), f_k \in \mathcal{F} y^i=k=1Kfk(xi),fkF

其中 K \color{darkcyan}K K是树的数量, f \color{darkcyan}f f是函数空间 F \color{darkcyan}\mathcal F F中的函数, F \color{darkcyan}\mathcal F F是所有可能CARTs的集合。

目标函数的优化由下列公式给出:
obj ( θ ) = ∑ i n ℓ ( y i , y ^ i ) + ∑ k = 1 K Ω ( f k ) \color{darkcyan} \text{obj} (\theta) = \sum_i^n \ell(y_i, \hat{y}_i) + \sum_{k=1}^K \Omega(f_k) obj(θ)=in(yi,y^i)+k=1KΩ(fk)


XGBoost 训练

模型参数是什么? 从目标函数可以看出,模型参数是多棵叶节点输出分数值的决策树

树优化不能使用传统的欧式空间中的优化方法,因此我们采用累加策略:迭代学习每一棵子树用于修正当前损失(残差学习),并把所学子树组合到总体。若样本 ( x i , y i ) \color{darkcyan}(x_i,y_i) (xi,yi)在时间步 t \color{darkcyan}t t的预测为 y ^ i \color{darkcyan}\hat y_i y^i,则模型学习过程(逐步逼近)为
y ^ i ( 0 ) = 0 y ^ i ( 1 ) = f 1 ( x i ) = y ^ i ( 0 ) + f 1 ( x i ) y ^ i ( 2 ) = f 1 ( x i ) + f 2 ( x i ) = y ^ i ( 1 ) + f 2 ( x i ) … y ^ i ( t ) = ∑ k = 1 t f k ( x i ) = y ^ i ( t − 1 ) + f t ( x i ) \color{darkcyan} \begin{aligned}\hat{y}_i^{(0)} &= 0\\ \hat{y}_i^{(1)} &= f_1(x_i) = \hat{y}_i^{(0)} + f_1(x_i)\\ \hat{y}_i^{(2)} &= f_1(x_i) + f_2(x_i)= \hat{y}_i^{(1)} + f_2(x_i)\\ &\dots\\ \hat{y}_i^{(t)} &= \sum_{k=1}^t f_k(x_i)= \hat{y}_i^{(t-1)} + f_t(x_i) \end{aligned} y^i(0)y^i(1)y^i(2)y^i(t)=0=f1(xi)=y^i(0)+f1(xi)=f1(xi)+f2(xi)=y^i(1)+f2(xi)=k=1tfk(xi)=y^i(t1)+ft(xi)
子树如何学习? 新的子树可降低总体损失, t \color{darkcyan}t t时刻,我们定义目标函数为:
obj ( t ) = ∑ i = 1 n ℓ ( y i , y ^ i ( t ) ) + ∑ i = 1 t Ω ( f i ) = ∑ i = 1 n ℓ ( y i , y ^ i ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) + c o n s t a n t \color{darkcyan} \begin{aligned} \text{obj}^{(t)} &= \sum_{i=1}^n \ell(y_i, \hat{y}_i^{(t)}) + \sum_{i=1}^t\Omega(f_i)\\ &= \sum_{i=1}^n \ell(y_i, \hat{y}_i^{(t-1)} + f_t(x_i)) + \Omega(f_t) + \mathrm{constant} \end{aligned} obj(t)=i=1n(yi,y^i(t))+i=1tΩ(fi)=i=1n(yi,y^i(t1)+ft(xi))+Ω(ft)+constant

我们将 f t ( x ) \color{darkcyan}f_t(x) ft(x)作为增量,使用泰勒公式将损失函数 ℓ \color{darkcyan}\ell y ^ ( t − 1 ) \color{darkcyan}\hat y^{(t-1)} y^(t1)处二次展开得
ℓ ( y , y ^ ( t − 1 ) + f t ( x ) ) = ℓ ( y , y ^ ( t − 1 ) ) + g t f t ( x ) + 1 2 h t f t 2 ( x ) \color{darkcyan} \ell(y,\hat y^{(t-1)}+f_t(x))=\ell(y,\hat y^{(t-1)})+g_tf_t(x)+\frac{1}{2}h_tf_t^2(x) (y,y^(t1)+ft(x))=(y,y^(t1))+gtft(x)+21htft2(x)
式中一阶导 g t = ∂ ℓ / ∂ y ^ ( t − 1 ) \color{darkcyan}g_t=\partial \ell/\partial \hat y^{(t-1)} gt=/y^(t1),二阶导 h t = ∂ ℓ 2 / ∂ 2 y ^ ( t − 1 ) \color{darkcyan}h_t=\partial \ell^2/\partial^2 \hat y^{(t-1)} ht=2/2y^(t1) ℓ ( y , y ^ ( t − 1 ) ) \color{darkcyan}\ell(y,\hat y^{(t-1)}) (y,y^(t1))为常数。

此时可以得到近似目标函数
obj ( t ) ≈ ∑ i = 1 n [ g i f t ( x i ) + 1 2 h i f t 2 ( x i ) ] + Ω ( f t ) + c o n s t a n t \color{darkcyan} \text{obj}^{(t)}\approx \sum_{i=1}^n [g_i f_t(x_i) + \frac{1}{2} h_i f_t^2(x_i)] + \Omega(f_t) + \mathrm{constant} obj(t)i=1n[gift(xi)+21hift2(xi)]+Ω(ft)+constant
最小化目标函数求解第 t \color{darkcyan}t t步子树,第 t \color{darkcyan}t t步损失函数仅包含之前损失对样本上的一、二阶导,XGBoost支持自定义损失函数。

Taylor Expression
f ( x + Δ x ) = f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) ( Δ x ) 2 f(x+\Delta x)=f(x)+f'(x)\Delta x+\frac{1}{2}f''(x)(\Delta x)^2 f(x+Δx)=f(x)+f(x)Δx+21f(x)(Δx)2


q ( x ) \color{darkcyan}q(x) q(x)表示样本 x \color{darkcyan}x x所属叶节点, w \color{darkcyan}w w表示所有叶节点对应的分数向量, T \color{darkcyan}T T表示叶节点数量,则决策树 f t ( x ) \color{darkcyan}f_t(x) ft(x)表示为
f t ( x ) = w q ( x ) w ∈ R T , q : R d → { 1 , 2 , ⋯ , T } . \color{darkcyan} f_t(x)=w_{q(x)} \quad w∈R^T,q:R_d→\{1,2,⋯,T\}. ft(x)=wq(x)wRT,q:Rd{1,2,,T}.
XGBoost的复杂度定义为
Ω ( f ) = γ T + 1 2 λ ∑ j = 1 T w j 2 \color{darkcyan} \Omega(f) = \gamma T + \frac{1}{2}\lambda \sum_{j=1}^T w_j^2 Ω(f)=γT+21λj=1Twj2
定义树复杂度的方式有很多,但是这种方式的定义在实践中表现很好。


现在我们将损失函数中对样本损失求和转化为对叶节点损失求和,我们这样做的原因是因为同一叶节点中的样本分数相同,且样本数一定小于节点数,这样可以简化计算量或支持并行计算,因此,目标函数在时间 t \color{darkcyan}t t的损失为
obj ( t ) ≈ ∑ i = 1 n [ g i w q ( x i ) + 1 2 h i w q ( x i ) 2 ] + γ T + 1 2 λ ∑ j = 1 T w j 2 = ∑ j = 1 T [ w j ∑ i ∈ I j g i + 1 2 w j 2 ( λ + ∑ i ∈ I j h i ) ] + γ T \color{darkcyan} \begin{aligned} \text{obj}^{(t)} &\approx \sum_{i=1}^n [g_i w_{q(x_i)} + \frac{1}{2} h_i w_{q(x_i)}^2] + \gamma T + \frac{1}{2}\lambda \sum_{j=1}^T w_j^2\\ &= \sum^T_{j=1}\left[w_j \sum\nolimits_{i\in I_j}g_i+ \frac{1}{2} w_j^2\left(\lambda+\sum\nolimits_{i\in I_j}h_i\right)\right] + \gamma T \end{aligned} obj(t)i=1n[giwq(xi)+21hiwq(xi)2]+γT+21λj=1Twj2=j=1T[wjiIjgi+21wj2(λ+iIjhi)]+γT
式中 I j = { i ∣ q ( x i ) = j } \color{darkcyan}I_j = \{i|q(x_i)=j\} Ij={iq(xi)=j}是节点 j \color{darkcyan}j j中的样本集,令 G j = ∑ i ∈ I j g i ,   H j = ∑ i ∈ I j h i \color{darkcyan}G_j = \sum_{i\in I_j} g_i,\ H_j = \sum_{i\in I_j} h_i Gj=iIjgi, Hj=iIjhi,目标函数简化为
obj ( t ) = ∑ j = 1 T [ G j w j + 1 2 ( H j + λ ) w j 2 ] + γ T \color{darkcyan} \text{obj}^{(t)} = \sum^T_{j=1} [G_jw_j + \frac{1}{2} (H_j+\lambda) w_j^2] +\gamma T obj(t)=j=1T[Gjwj+21(Hj+λ)wj2]+γT
其中 w j \color{darkcyan}w_j wj相互独立,在特定树结构 q ( x ) \color{darkcyan}q(x) q(x)中,最优叶节点输出值和目标函数
w j ∗ = − G j H j + λ obj ∗ = − 1 2 ∑ j = 1 T G j 2 H j + λ + γ T \color{darkcyan} \begin{aligned} w_j^\ast &= -\frac{G_j}{H_j+\lambda}\\ \text{obj}^\ast &= -\frac{1}{2} \sum_{j=1}^T \frac{G_j^2}{H_j+\lambda} + \gamma T \end{aligned} wjobj=Hj+λGj=21j=1THj+λGj2+γT
剩下最后一个问题:怎么学习子树的结构 q ( x ) \color{darkcyan}\mathbb q(x) q(x)

学习到的子树应能极小化目标函数,可枚举所有可能的子树,并选择使损失最小的子树,但计算量太大。因此,我们通过贪婪方式学习子树:每个节点的分裂均能极小化目标函数,直到达到分类终止条件,如达最大深度、或分裂之后损失不在下降。

我们首先可获得每个样本的 g i \color{darkcyan}g_i gi h i \color{darkcyan}h_i hi,在特定的树结构 q ( x ) \color{darkcyan}q(x) q(x)下,可以很容易计算出每个节点的 G j \color{darkcyan}G_j Gj H j \color{darkcyan}H_j Hj。根据单节点二分裂前后对应的最优目标函数 obj \text{obj} obj,可以计算出分裂后降低的损失(增益)
G a i n = 1 2 [ G L 2 H L + λ + G R 2 H R + λ − ( G L + G R ) 2 H L + H R + λ ] − γ \color{darkcyan} Gain = \frac{1}{2} \left[\frac{G_L^2}{H_L+\lambda}+\frac{G_R^2}{H_R+\lambda}-\frac{(G_L+G_R)^2}{H_L+H_R+\lambda}\right] - \gamma Gain=21[HL+λGL2+HR+λGR2HL+HR+λ(GL+GR)2]γ
公式中四项的解释:划分后左子节点的损失、划分后右子节点的损失、原始节点的损失、叶节点上的正则项系数。
重要特性:额外增加的 γ \color{darkcyan}\gamma γ用于控制复杂度,若增益小于 γ \color{darkcyan}\gamma γ,则停止分裂,也称为决策树 “pruning (修剪)技术”


如何选择分裂特征和分裂点?对于实值特征,将所有样本按此特征值顺序排列,从左到右扫描,可有效地找到最优分裂点:

对于N个样本M个实值特征(特征取值不重复)的训练集,需遍历M*(N-1)次,此外当数据无法全载入内存时,也不能使用这种方法。

由于模型整体是在不断迭代极小化损失,我们在学习子树时,也不一定需要找到最优子树,因此分裂点可采用等分位点方法,对于大数据集可显著降低计算量,如果子树较多,效果并不一定差。


缩减和随机特征采样

这里介绍两种额外的避免过拟合的技术。

第一种技术是Friedman提出的 “shrinkage (缩减)”,shrinkage是将所学新树乘以一个参数 η \color{darkcyan}\eta η之后再加到总体模型,shrinkage降低了单棵树对总体的影响,给出来子树学习提供了更多的空间。

第二种技术是 “column (feature) subsampling (随机特征)”,这种技术常用于随机森林(子树是在随机样本、随机特征中学习),column sub-sampling 比 “row sub-sampling (随机样本)” 更能防止过拟合。


XGBoost 糖尿病预测

# First XGBoost model for Pima Indians dataset
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.model_selection import KFold, cross_val_score, train_test_split
from xgboost import XGBClassifier

# 数据集下载地址:https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv
df = pd.read_csv('pima-indians-diabetes.data.txt', header=None)
df.columns = ['preg_times', 'glucose', 'blood_pressure', 'skin_thick', 'insulin', 'BMI',
              'DPF', 'age', 'outcome']

print(df.describe())
print(df.shape)
print(df.head())

# glucose、blood_pressure、skin_thick、insulin、BMI 0值为缺失值,填充为均值
missing_columns = ['glucose', 'blood_pressure', 'skin_thick', 'insulin', 'BMI']
df[missing_columns] = df[missing_columns].replace(0, np.nan)
for column in missing_columns:
    df.loc[df[column].isna(), column] = df[column].mean()

print('missing filled.')
print(df.describe())

# load data
# dataset = loadtxt('pima-indians-diabetes.data.txt', delimiter=",")
dataset = df.values

# split data into X and y
X = dataset[:, 0:8]
y = dataset[:, 8]

# split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=7, shuffle=True)

model = XGBClassifier(
    eta=0.08,  # 别名learning_rate,子树shrinkage
    gamma=0.001,  # 别名min_split_loss,分割损失小于改值时停止分割
    max_depth=3,  # 最大深度
    min_child_weight=2,  # 子节点样本权重和低于该值时停止分割
    max_delta_step=0,
    subsample=0.8,  # 每次训练时样本采样率
    sampling_method='uniform',  # 训练样本采样方法
    colsample_bytree=0.7,  # 每棵树的特征采样比
    colsample_bylevel=0.8,  # 每层的特征采样比
    colsample_bynode=0.7,  # 每个节点的特征采样比,对防止过拟合非常重要
    reg_alpha=0,  # L1正则化系数
    reg_lambda=1,  # L2正则化系数
    tree_method='auto',  # 树构造算法
    base_score=0.5,
    booster='gbtree',
    missing=None,
    n_estimators=80,
    n_jobs=4,
    objective='binary:logistic',
    random_state=2,
    seed=6,
    scale_pos_weight=1.0,
    silent=True,
    verbosity=2
)
model.fit(X_train, y_train)

# cv_result = cross_val_score(model, X, y, cv=KFold(
#     n_splits=10, random_state=22, shuffle=True))
# print('Cross validation accuracy: %.2f%%' % (cv_result.mean() * 100))

# train predictions
acc_train = accuracy_score(y_train, model.predict(X_train))
print("Train accuracy: %.2f%%" % (acc_train * 100.0))

# evaluate predictions
acc_test = accuracy_score(y_test, model.predict(X_test))
print("Test accuracy: %.2f%%" % (acc_test * 100.0))

预测结果:

Train accuracy: 84.20%
Test accuracy: 83.12%

Reference

1. XGBoost Tutorials
2. XGBoost: A Scalable Tree Boosting System

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值