选择弱评估器:参数booster
在XGB中,除了使用梯度提升树(默认gbtree)以外,还可以使用gbtlinear和dart。
在原库中,在xgb.train()中输入xgb_model进行选择(但是注意这个参数只能由params参数导入!);sklearn中在xgb.XGBregressor()输入booster进行选择。
如果数据是线性的,可以使用gbtlinear。
XGB目标函数:重要参数objective
XGB的损失函数不仅仅考虑了传统的误差函数,还考虑了模型的复杂度:
O
b
j
=
∑
i
=
1
m
l
(
y
i
,
y
^
i
)
+
∑
k
=
1
K
Ω
(
f
k
)
Obj=\sum_{i=1}^ml(y_i,\hat{y}_i)+\sum_{k=1}^K\Omega(f_k)
Obj=i=1∑ml(yi,y^i)+k=1∑KΩ(fk)
第一项是针对每一个样本进行计算误差函数,第二项针对每一棵树计算模型复杂度,可以有多种计算定义。
第一项中的预测值
y
^
i
(
k
)
\hat{y}_i^{(k)}
y^i(k)实际上是一些列树的预测结果之和:
y
^
i
(
k
)
=
∑
k
K
f
k
(
x
i
)
=
∑
k
K
f
k
−
1
(
x
i
)
+
f
k
(
x
i
)
,
K
=
k
−
1
\hat{y}_i^{(k)}=\sum_k^K f_k(x_i)=\sum_k^K f_{k-1}(x_i)+f_{k}(x_i),K=k-1
y^i(k)=k∑Kfk(xi)=k∑Kfk−1(xi)+fk(xi),K=k−1
第一项描述模型的偏差情况,第二项描述模型的复杂度,其实同时可以衡量方差的情况,方式过拟合,从而实现整体泛化误差最小,非常巧妙!
在XGB中,目标函数可以选择的是第一项。
xgb.train() | xgb.XGBregressor() | xgb.XGBClassifier() |
---|---|---|
obj:默认binary:logistic | objective:默认reg:linear | objective:默认binary:logistic |
注意xgb库本身默认的参数,要做回归的话需要修改改参数。
binary:logistic指的是二分类时逻辑回归损失函数,即交叉熵。
常见的输入有以下几种:
输入 | 选用的损失函数 |
---|---|
reg:linear | 使用线性回归的损失函数,均方误差,回归时使用 |
binary:logistic | 使用逻辑回归的损失函数,对数损失log_loss,二分类时使用 |
binary:hinge | 使用支持向量机的损失函数,Hinge Loss,二分类时使用 |
multi:softmax | 使用softmax损失函数,多分类时使用 |
直接调用xgb的库
import xgboost as xgb
在这个库中需要用**xgb.DMatrix()**读取数据,必须同时输入特征和标签。
#使用类DMatrix读取数据
dtrain = xgb.DMatrix(Xtrain,Ytrain) #特征矩阵和标签都进行一个传入
dtest = xgb.DMatrix(Xtest,Ytest)
参数需要用一个字典来存储
#写明参数
param = {'silent':True #默认为False,通常要手动把它关闭掉
,'objective':'reg:linear'
,"eta":0.1}
num_round = 180 #n_estimators
注意num_round参数可以直接传入XGB,其它参数还是需要字典传入。
#类train,可以直接导入的参数是训练数据,树的数量,其他参数都需要通过params来导入
bst = xgb.train(param, dtrain, num_round)
然后就可以预测了
#接口predict
preds = bst.predict(dtest)
参数化决策树 f k ( x ) f_k{(x)} fk(x):参数alpha,lambda
XGB的目标函数可以转换为只与当前次的迭代相关的形式:
O
b
j
=
∑
i
=
1
m
[
f
t
(
x
i
)
g
i
+
1
2
(
f
t
(
x
i
)
)
2
h
i
)
]
+
Ω
(
f
t
)
Obj=\sum_{i=1}^m [f_t(x_i)g_i+\frac{1}{2}(f_t(x_i))^2h_i)]+\Omega(f_t)
Obj=i=1∑m[ft(xi)gi+21(ft(xi))2hi)]+Ω(ft)
其中
g
i
g_i
gi和
h
i
h_i
hi只与传统损失函数相关。
对XGB来说,预测值是需要单独计算的分数,即叶子权重,用
f
k
(
x
i
)
f_k(x_i)
fk(xi)或
ω
\omega
ω表示。
f
k
(
x
i
)
f_k(x_i)
fk(xi)表示将样本
x
i
x_i
xi放入到树
f
t
f_t
ft中获得的叶子权重。
当有多棵树时,集成模型的回归结果是所有树的预测分数之和:
y
^
i
(
k
)
=
∑
k
K
f
k
(
x
i
)
\hat{y}_i^{(k)}=\sum_k^K f_k(x_i)
y^i(k)=k∑Kfk(xi)
在树结构中,使用
q
(
x
i
)
q(x_i)
q(xi)表示样本
x
i
x_i
xi所在的叶子结点,用
w
q
(
x
i
)
w_{q(x_i)}
wq(xi)表示样本落到第
k
k
k棵树上的第
q
(
x
i
)
q(x_i)
q(xi)个叶子结点中获得的分数,于是:
f
k
(
x
i
)
=
w
q
(
x
i
)
f_k(x_i)=w_{q(x_i)}
fk(xi)=wq(xi)
这是对于每一个样本而言的叶子权重,然而在一个叶子节点上所有的样本所对应的叶子权重是相同的。
设总共有T个叶子结点,可以表示
Ω
(
f
)
\Omega(f)
Ω(f):
Ω
(
f
)
=
γ
T
+
正
则
项
\Omega(f)=\gamma T+正则项
Ω(f)=γT+正则项
若使用
L
2
L2
L2正则项:
Ω
(
f
)
=
γ
T
+
1
2
λ
∣
∣
w
∣
∣
2
=
γ
T
+
1
2
λ
∑
j
=
1
T
w
j
2
\Omega(f)=\gamma T+\frac 1 2 \lambda||w||^2\\=\gamma T+\frac 1 2 \lambda\sum_{j=1}^Tw^2_j
Ω(f)=γT+21λ∣∣w∣∣2=γT+21λj=1∑Twj2
若使用
L
1
L1
L1正则项:
Ω
(
f
)
=
γ
T
+
1
2
α
∣
w
∣
=
γ
T
+
1
2
α
∑
j
=
1
T
∣
w
j
∣
\Omega(f)=\gamma T+\frac 1 2 \alpha|w|\\=\gamma T+\frac 1 2 \alpha\sum_{j=1}^T|w_j|
Ω(f)=γT+21α∣w∣=γT+21αj=1∑T∣wj∣
也可以两个一起用:
Ω
(
f
)
=
γ
T
+
1
2
α
∣
w
∣
+
1
2
λ
∣
∣
w
∣
∣
2
\Omega(f)=\gamma T+\frac 1 2 \alpha|w|+\frac 1 2 \lambda||w||^2
Ω(f)=γT+21α∣w∣+21λ∣∣w∣∣2
由于在梯度提升树中都是二叉树,所以知道叶子结点的数目
T
T
T的话就可以知道树的深度,也知道树的复杂程度。
在剪枝前,XGB就有正则项参数可以调节以防止过拟合。
参数含义 | xgb.train() | xgb.XGBRegressor() |
---|---|---|
L1正则项参数 α \alpha α | alpha,默认0,取值范围 [ 0 , + ∞ ] [0,+\infty] [0,+∞] | reg_alpha,默认0,取值范围 [ 0 , + ∞ ] [0,+\infty] [0,+∞] |
L2正则项参数 λ \lambda λ | lambda,默认1,取值范围 [ 0 , + ∞ ] [0,+\infty] [0,+∞] | reg_lambda,默认1,取值范围 [ 0 , + ∞ ] [0,+\infty] [0,+∞] |
通常在回归中才用得到。
可以使用网格搜索来找正则化参数
寻找最佳树结构:求解 w w w与 T T T
在XGB中,一个核心的思想是把目标函数变为叶子节点数的函数,所以目标函数又可以称为“结构分数”。
前文已经得到XGB的目标函数可以表示为:
∑
i
=
1
m
[
f
t
(
x
i
)
g
i
+
1
2
(
f
t
(
x
i
)
)
2
h
i
]
+
Ω
(
f
t
)
\sum_{i=1}^m[f_t(x_i)g_i+\frac 1 2 (f_t(x_i))^2h_i]+\Omega (f_t)
i=1∑m[ft(xi)gi+21(ft(xi))2hi]+Ω(ft)
前半部分仍然为关于样本的求和,可以通过数学转换为
∑
j
=
1
T
[
w
j
∑
i
∈
I
j
g
i
+
1
2
w
j
2
(
∑
i
∈
I
j
h
i
+
λ
)
]
+
γ
T
\sum_{j=1}^T[w_j\sum_{i \in I_j}g_i+\frac 1 2 w_j^2(\sum_{i \in I_j}h_i+\lambda)]+\gamma T
j=1∑T[wji∈Ij∑gi+21wj2(i∈Ij∑hi+λ)]+γT
令
G
j
=
∑
i
∈
I
j
g
i
,
H
i
=
∑
i
∈
I
j
h
i
G_j=\sum_{i \in I_j}g_i,H_i=\sum_{i \in I_j}h_i
Gj=∑i∈Ijgi,Hi=∑i∈Ijhi,可以表示最终的目标函数
O
b
j
(
t
)
=
∑
j
=
1
T
[
w
j
G
j
+
1
2
w
j
2
(
H
j
+
λ
)
]
+
γ
T
F
∗
(
w
j
)
=
w
j
G
j
+
1
2
w
j
2
(
H
j
+
λ
)
Obj^{(t)}=\sum_{j=1}^T[w_jG_j+\frac 1 2 w_j^2(H_j+\lambda)]+\gamma T\\ F^*(w_j)=w_jG_j+\frac 1 2 w_j^2(H_j+\lambda)
Obj(t)=j=1∑T[wjGj+21wj2(Hj+λ)]+γTF∗(wj)=wjGj+21wj2(Hj+λ)
可以通过对每一个
j
j
j对应下的
F
∗
(
w
j
)
F^*(w_j)
F∗(wj)求导并带入解,最终可以得到
O
b
j
(
t
)
=
−
1
2
∑
j
=
1
T
G
j
2
H
j
+
λ
+
γ
T
Obj^{(t)}=-\frac 1 2 \sum_{j=1}^T \frac {G_j^2} {H_j+\lambda}+\gamma T
Obj(t)=−21j=1∑THj+λGj2+γT
即转化为只与叶子节点有关的函数了。
寻找最佳分枝:结构分数之差
在XGB中,是通过使每一次分枝前后的结构分数之差最大的贪心算法来实现分支的。
分枝后的结构分数之差为
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
+
λ
]
−
γ
Gain=\frac 1 2[\frac {G^2_L} {H_L+\lambda}+\frac {G^2_R} {H_R+\lambda}-\frac {(G_L+G_R)^2} {H_L+H_R+\lambda}]-\gamma
Gain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
让树停止生长:重要参数gamma
从上面的
G
a
i
n
Gain
Gain的计算式可以看到,
γ
\gamma
γ可以看作每进行一次分枝都要计算的一个惩罚项(因为我们希望
G
a
i
n
Gain
Gain越大越好),也叫“复杂性控制(complexity control)”,是用来防止过拟合的重要参数。
在XGB中,只要
G
a
i
n
Gain
Gain是大于0的,就可以继续分枝,由上面的式子可以知道,
γ
\gamma
γ可以看作是在树的叶子结点上进行进一步分枝所需要的最小目标函数减少量,其值越小,模型越复杂。
参数含义 | xgb.train() | xgb.XGBRegressor() |
---|---|---|
复杂度的惩罚项 γ \gamma γ | gamma,默认0,取值范围 [ 0 , + ∞ ] [0,+\infty] [0,+∞] | gamma,默认0,取值范围 [ 0 , + ∞ ] [0,+\infty] [0,+∞] |
XGB的调参可以使用xgb.cv()工具进行
import xgboost as xgb
#为了便捷,使用全数据
dfull = xgb.DMatrix(X,y) #设定参数
param1 = {'silent':True,'obj':'reg:linear',"gamma":0}
num_round = 180
n_fold=5 #使用类xgb.cv
time0 = time()
cvresult1 = xgb.cv(param1, dfull, num_round,n_fold)
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
#看看类xgb.cv生成了什么结果?
cvresult1
plt.figure(figsize=(20,5))
plt.grid()
plt.plot(range(1,181),cvresult1.iloc[:,0],c="red",label="train,gamma=0")
plt.plot(range(1,181),cvresult1.iloc[:,2],c="orange",label="test,gamma=0")
plt.legend()
plt.show()
通过xgb.cv()绘制的曲线可以看到调整gamma之后训练集和测试集分数之间的差异,需要调整到一个使测试集分数最高且训练集和测试集差异较小的状态。