1. XGBoost 基础理论
XGBoost (eXtreme Gradient Boosting) 本质是GBDT,但进行了算法和工程上的许多改进。
- XGB可做分类,亦可做回归。
- XGB是一个集成算法,弱评估器主要是CART tree (也可选线性模型)
1.1 建模方法
Boosted trees 一般方法
- y i ^ ( 0 ) = 0 \hat{y_i}^{(0)} = 0 yi^(0)=0 以0为初始预测值。
- y i ^ ( 1 ) = f 1 ( x i ) = y i ^ ( 0 ) + f 1 ( x i ) \hat{y_i}^{(1)} = f_1(x_i) = \hat{y_i}^{(0)}+ f_1(x_i) yi^(1)=f1(xi)=yi^(0)+f1(xi) 建立第一棵树。
- y i ^ ( 2 ) = f 1 ( x i ) + f 2 ( x i ) \hat{y_i}^{(2)} = f_1(x_i) + f_2(x_i) yi^(2)=f1(xi)+f2(xi) 建立第二棵树,学习一个新的函数,拟合第一棵树预测的残差。
- … 以此类推,不断建树,不断迭代。
- y i ^ ( t ) = ∑ k = 1 t f k ( x i ) = y i ^ ( t − 1 ) + f t ( x i ) \hat{y_i}^{(t)} = \sum_{k=1}^tf_k(x_i) =\hat{y_i}^{(t-1)}+f_t(x_i) yi^(t)=∑k=1tfk(xi)=yi^(t−1)+ft(xi) 最终预测结果为所有树对应分数的加和。
XGB 在创建一棵树时:
- 计算目标函数1阶导数 g i = ∂ l ( y i , f t − 1 ( x i ) ) ∂ f t − 1 ( x i ) g_{i} = \frac{\partial l(y_i,f_{t-1}(x_i))}{\partial f_{t-1}(x_i)} gi=∂ft−1(xi)∂l(yi,ft−1(xi)); 和二阶导数 h i = 1 2 ∂ 2 l ( y i , f t − 1 ( x i ) ) ∂ f t − 1 2 ( x i ) h_{i} = \frac{1}{2} \frac{\partial^2 l(y_i,f_{t-1}(x_i))}{\partial f_{t-1}^2(x_i)} hi=21∂ft−12(xi)∂2l(yi,ft−1(xi))
- 通过计算目标函数来选择拆分点,进行树的生长,长成完整树 f ( x ) f(x) f(x) 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
- 把 f ( x ) f(x) f(x)加入模型 y t = y t − 1 + ϵ f t ( x ) y^t =y^{t-1} + \epsilon f_t(x) yt=yt−1+ϵft(x),其中$ \epsilon$ 是缩减因子,一般在0.1左右。
1.2 XGB 与 GBDT的不同
- GBDT的目标函数对误差部分做负梯度(一阶泰勒)展开,XGB的目标函数对误差部分做二阶泰勒展开。
- XGB目标函数添加了正则项。在树生成时便考虑了树的复杂度。GBDT只在剪枝时考虑复杂度。
- 对于缺失值的特征,通过枚举所有缺失值在当前节点是进入左子树还是右子树来决定缺失值的处理方式。
- Shrinkage(缩减),相当于学习速率(xgboost中的eta)。xgboost在进行完一次迭代后,会将叶子节点的权重乘上该系数,主要是为了削弱每棵树的影响,让后面有更大的学习空间。
- 列抽样(Column Subsampling)。
- 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑回归(分类问题)或者线性回归(回归问题)。
- XGB支持并行。xgboost的并行不是tree粒度的并行,xgboost也是一次迭代完才能进行下一次迭代的(第t次迭代的代价函数里包含了前面t-1次迭代的预测值)。xgboost的并行是在特征粒度上的。
1.3 XGBoost 优缺点
优点:
- 可以灵活处理各种类型的数据,包括连续值和离散值。
- 高效可扩展。在处理大规模数据集时速度快效果好,对内存等硬件资源要求不高。
- 相对于深度学习模型不需要精细调参便能取得接近的效果。
- 使用一些健壮的损失函数,对异常值的鲁棒性非常强。比如 Huber损失函数和Quantile损失函数。
- XGBoost内部实现提升树模型,可以自动处理缺失值。
缺点:
- 相对于深度学习模型无法对时空位置建模,不能很好地捕获图像、语音、文本等高维数据。
- 在拥有海量训练数据,并能找到合适的深度学习模型时,深度学习的精度可以遥遥领先XGBoost。
1.3 XGB的目标函数
O b j ( t ) = ∑ i = 1 m l ( y i , y i ^ ( t − 1 ) + f t ( x i ) ) + Ω ( f t ) Obj^{(t)} = \sum_{i=1}^m l(y_i,\hat{y_i}^{(t-1)} + f_t({x_i})) + \Omega(f_t) Obj(t)=i=1∑ml(yi,yi^(t−1)+ft(xi))+Ω(ft)
其中:
∑ i = 1 m l ( y i , y i ^ ( t − 1 ) + f t ( x i ) ) \sum_{i=1}^m l(y_i,\hat{y_i}^{(t-1)} + f_t({x_i})) ∑i=1ml(yi,yi^(t−1)+ft(xi)) 是损失函数,常见的有MSE和logistic损失。
Ω ( f t ) \Omega(f_t) Ω(ft) 代表模型复杂度,跟树结构有关,因为前t-1棵树结构确定,所以对于第t棵树的目标函数,只考虑第t棵树的复杂度。
对于 l l l损失函数,用二阶泰勒近似展开:
O b j ( t ) ≈ ∑ i = 1 m ( l ( y i , f t − 1 ( x i ) ) + ∂ l ( y i , f t − 1 ( x i ) ) ∂ f t − 1 ( x i ) f t ( x i ) + 1 2 ∂ 2 l ( y i , f t − 1 ( x i ) ) ∂ f t − 1 2 ( x i ) f t 2 ( x i ) ) + Ω ( f t ) Obj^{(t)} \approx \sum_{i=1}^m(l(y_i,f_{t-1}(x_i)) + \frac{\partial l(y_i,f_{t-1}(x_i))}{\partial f_{t-1}(x_i)}f_t(x_i) +\frac{1}{2} \frac{\partial^2 l(y_i,f_{t-1}(x_i))}{\partial f_{t-1}^2(x_i)}f_t^2(x_i)) + \Omega(f_t) Obj(t)≈i=1∑m(l(yi,ft−1(xi))+∂ft−1(xi)∂l(yi,ft−1(xi))ft(xi)+21∂ft−12(xi)∂2l(yi,ft−1(xi))ft2(xi))+Ω(ft)
泰勒二阶展开公式为: f ( x + Δ x ) ≈ f ( x ) + f ′ ( x ) Δ x + 1 2 f ′ ′ ( x ) Δ x 2 f(x+\Delta x) \approx f(x) +f^{'}(x)\Delta x +\frac{1}{2}f^{''}(x)\Delta x^2 f(x+Δx)≈f(x)+f′(x)Δx+21f′′(x)Δx2
其中:
x 对应目标函数中的 f t − 1 ( x i ) f_{t-1}(x_i) ft−1(xi)
Δ x \Delta x Δx 对应目标函数中的 f t ( x i ) f_t(x_i) ft(xi)
f对x求导即目标函数对 f t − 1 ( x i ) f_{t-1}(x_i) ft−1(xi) 求导
定义第i个样本在第t个弱学习器的一阶和二阶导数分别为:
g t i = ∂ l ( y i , f t − 1 ( x i ) ) ∂ f t − 1 ( x i ) g_{ti} = \frac{\partial l(y_i,f_{t-1}(x_i))}{\partial f_{t-1}(x_i)} gti=∂ft−1(xi)∂l(yi,ft−1(xi))
h t i = 1 2 ∂ 2 l ( y i , f t − 1 ( x i ) ) ∂ f t − 1 2 ( x i ) h_{ti} = \frac{1}{2} \frac{\partial^2 l(y_i,f_{t-1}(x_i))}{\partial f_{t-1}^2(x_i)} hti=21∂ft−12(xi)∂2l(yi,ft−1(xi))
目标函数转化为:
O b j ( t ) ≈ ∑ i = 1 m ( l ( y i , f t − 1 ( x i ) ) + g t i f t ( x i ) + 1 2 h t i f t 2 ( x i ) ) + Ω ( f t ) Obj^{(t)} \approx \sum_{i=1}^m(l(y_i,f_{t-1}(x_i)) + g_{ti}f_t(x_i) +\frac{1}{2} h_{ti}f_t^2(x_i)) + \Omega(f_t) Obj(t)≈i=1∑m(l(yi,ft−1(xi))+gtift(xi)+21htift2(xi))+Ω(ft)
目标函数的变量是第t棵树,其余部分已知或可直接计算。
1.4 XGB 对树的定义
一棵树 f f f可以被拆分为2个部分:
- 树的分支结构q。q将实例映射到每个叶子节点。
- 叶子权重w。如果树有T个叶子,w即是长度为T的1维向量。
f
t
(
x
i
)
=
w
q
(
x
i
)
f_t(x_i) = w_{q(x_i)}
ft(xi)=wq(xi)
样本在这棵树的回归值 = 该样本所在叶子节点的权重值。(其中
q
(
x
i
)
q(x_i)
q(xi)代表
x
i
x_i
xi所在的叶子节点,是w的索引)
1.5 XGB 对复杂度的定义
一棵树 f f f的复杂度也可分为2部分:
- 叶子节点的数量
- 叶子结点权重向量的L2正则项(也可替换为L1正则或同时使用)
Ω ( f t ) = γ T + 1 2 λ ∑ j = 1 T w j 2 \Omega(f_t) = \gamma T + \frac{1}{2}\lambda \sum_{j=1}^{T}w_j^2 Ω(ft)=γT+21λj=1∑Twj2
1.6 更新目标函数
在一个叶子节点的所有样本均对应同一个叶子权重,将叶子节点
j
j
j上的所有样本归为一组:
I
j
=
{
i
∣
q
(
x
i
)
=
j
}
I_j = \{i|q(x_i)=j\}
Ij={i∣q(xi)=j}
将树与复杂度的定义代入目标函数, 并去掉对优化无影响的l(y_i,f_{t-1}(x_i)):
O b j ( t ) ≈ ∑ i = 1 m ( l ( y i , f t − 1 ( x i ) ) + g t i f t ( x i ) + 1 2 h t i f t 2 ( x i ) ) + Ω ( f t ) Obj^{(t)} \approx \sum_{i=1}^m(l(y_i,f_{t-1}(x_i)) + g_{ti}f_t(x_i) +\frac{1}{2} h_{ti}f_t^2(x_i)) + \Omega(f_t) Obj(t)≈∑i=1m(l(yi,ft−1(xi))+gtift(xi)+21htift2(xi))+Ω(ft)
= ∑ i = 1 m ( g t i w q ( x i ) + 1 2 h t i w q ( x i ) 2 ) + γ T + 1 2 ∑ j = 1 T w j 2 =\sum_{i=1}^{m}(g_{ti}w_{q(x_i)} + \frac{1}{2}h_{ti}w_{q(x_i)}^2) +\gamma T + \frac{1}{2}\sum_{j=1}{T}w_j^2 =∑i=1m(gtiwq(xi)+21htiwq(xi)2)+γT+21∑j=1Twj2
= ∑ j = 1 T ( ( ∑ i ∈ I j g i ) w j + 1 2 ( ∑ j = 1 T h i + λ ) w j 2 ) + γ T = \sum_{j=1}^{T}((\sum_{i\in I_j} g_i)w_j +\frac{1}{2}(\sum_{j=1}^{T}h_i +\lambda)w_j^2) + \gamma T =∑j=1T((∑i∈Ijgi)wj+21(∑j=1Thi+λ)wj2)+γT #这一步是将样本按叶子分组,目的是统一累加项目
如果定义:
G j = ∑ i ∈ I j g i G_j = \sum_{i\in I_j} g_i Gj=∑i∈Ijgi
H j = ∑ i ∈ I j h i H_j = \sum_{i\in I_j} h_i Hj=∑i∈Ijhi
则可将目标函数进一步简化为:
O
b
j
(
t
)
=
∑
j
=
1
T
(
G
j
w
j
+
1
2
(
H
j
+
λ
)
w
j
2
)
+
γ
T
Obj^{(t)} = \sum_{j=1}^{T}(G_jw_j +\frac{1}{2}(H_j +\lambda) w_j^2) + \gamma T
Obj(t)=j=1∑T(Gjwj+21(Hj+λ)wj2)+γT
1.7 树的 structure score
要求目标函数的最小值,可从其最小单元叶子入手,对于每一个叶子节点j,其在目标函数中表达式均为:
G j w j + 1 2 ( H j + λ ) w j 2 G_jw_j + \frac{1}{2}(H_j +\lambda) w_j^2 Gjwj+21(Hj+λ)wj2
因为 G j G_j Gj和 H j H_j Hj均为常量,所以上式为关于 w j w_j wj简单一元二次方程,其取最小值的点为:
w j ∗ = − G j H j + λ w_j^* = -\frac{G_j}{H_j+\lambda} wj∗=−Hj+λGj
又由于所有的叶子节点在目标函数中相互独立,所以每片叶子都取最小值,就得到整个目标函数最小值:
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
1.8 树的分裂
建立一棵树时,采用贪心算法进行树的分裂。
从深度为0开始,尝试对每个节点进行分裂。分裂依据为分裂前后的增益。
G
a
i
n
=
O
b
j
L
+
R
−
(
O
b
j
L
+
O
b
j
R
)
Gain = Obj_{L+R} - (Obj_L +Obj_R)
Gain=ObjL+R−(ObjL+ObjR)
带入公式得:
G
a
i
n
=
G
L
2
H
L
+
λ
+
G
R
2
H
R
+
λ
−
(
G
L
+
G
R
)
2
H
L
+
H
R
+
λ
−
γ
Gain = \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} - \gamma
Gain=HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2−γ
其中
γ
\gamma
γ为增加一个叶子的惩罚。
如果Gain>0, 表示分裂后目标函数下降,则可进行分裂。当有多个点满足分裂条件时,需寻求最优点,即Gain最大的点进行分裂。
相较于遍历所有特征的所有拆分点以找到最佳分裂点的贪心方法,XGB提出更高效的方法:
- 特征预排序+缓存:XGBoost在训练之前,预先对每个特征按照特征值大小进行排序,然后保存为block结构,后面的迭代中会重复地使用这个结构,使计算量大大减小。
- 分位点近似法:对每个特征按照特征值排序后,采用类似分位点选取的方式,仅仅选出常数个特征值作为该特征的候选分割点,在寻找该特征的最佳分割点时,从候选分割点中选出最优的一个。
- 并行查找:由于各个特性已预先存储为block结构,XGBoost支持利用多个线程并行地计算每个特征的最佳分割点,这不仅大大提升了结点的分裂速度,也极利于大规模训练集的适应性扩展。
2. XGBoost 的使用
要使用XGBoost需要先安装:
pip install xgboost # 安装
pip install --upgrade xgboost # 更新
2.1 使用xgboost库的建模流程
import xgboost as xgb
# 读取数据
dtrain = xgb.DMatrix(X_train,y_train)
dtest = xgb.DMatrix(X_test,y_test)
# 设置参数
params = {'max_depth':6, 'eta':0.1, 'verbosity':1, 'objective':'binary:logistic'}
# 训练模型
model = xgb.train(params, dtrain, num_boost_round=20)
# 预测
model.predict(dtest) # 预测的结果是概率
2.2 xgboost 的sklearn版
from xgboost.sklearn import XGBClassifier
# from xgboost import XGBClassifier
clf = XGBClassifier()
clf.fit(x_train, y_train)
clf.predict(x_test) # 预测
clf.score(x_test,y_test) #
2.3 xgboost 的调参
-
通用参数:宏观函数控制。
- booster 默认gbtree,是基于树的模型。可选gblinear(线性模型)或dart
- nthread 输入系统核数。如果希望使用CPU全部核,则不输入,算法会自动检测。 -
Booster参数:控制每一步的booster(tree/regression)。
- eta/learning rate 典型值为0.01-0.2
- min_child_weight 用于避免过拟合。值过高会导致欠拟合。
- max_depth 最大深度。 典型值:3-10
- max_leaf_nodes 可以替代max_depth的作用。
- gamma 这个参数的值越大,算法越保守。这个参数的值和损失函数息息相关。
- max_delta_step 默认0。这参数限制每棵树权重改变的最大步长。如果这个参数的值为0,那就意味着没有约束。如果它被赋予了某个正值,那么它会让这个算法更加保守。
- subsample 典型值:0.5-1
- colsample_bytree
- reg_lambda 默认1。 L2正则系数。
- reg_alpha 默认1。L1正则系数。可以应用在很高维度的情况下,使得算法的速度更快。
- scale_pos_weight -
学习目标参数:控制训练目标的表现。
- objective 需要被最小化的损失函数。最常用的值有:
说明 | |
---|---|
reg:squarederror | 回归损失函数,均方误差 |
binary:logistic | 二分类的逻辑回归,返回预测的概率 |
binary:hinge | 使用支持向量机的损失函数,Hinge Loss,二分类时使用 |
multi:softmax | 使用softmax的多分类器,返回预测的类别(不是概率)。在这种情况下,你还需要多设一个参数:num_class(类别数目) |
- eval_metric 默认值取决于objective
详细参数说明见官方文档
调参一般方法为:
- 选择较高的学习速率(learning rate)。一般情况下,学习速率的值为0.1。但是,对于不同的问题,理想的学习速率有时候会在0.05到0.3之间波动。选择对应于此学习速率的理想决策树数量。XGBoost有一个很有用的函数“cv”,这个函数可以在每一次迭代中使用交叉验证,并返回理想的决策树数量。
- 对于给定的学习速率和决策树数量,进行决策树特定参数调优(max_depth, min_child_weight, gamma,
subsample, colsample_bytree)。在确定一棵树的过程中,我们可以选择不同的参数,待会儿我会举例说明。 - xgboost的正则化参数的调优。(lambda, alpha)。这些参数可以降低模型的复杂度,从而提高模型的表现。
- 降低学习速率,确定理想参数。
import pandas as pd
import numpy as np
import xgboost as xgb
from xgboost.sklearn import XGBClassifier
from sklearn import cross_validation, metrics #Additional scklearn functions
from sklearn.grid_search import GridSearchCV #Perforing grid search
import matplotlib.pylab as plt
# 1. 先选择0.1的学习速率,找到该速率下的n_estimators
# 先定义一个函数,它可以帮助我们建立XGBoost models 并进行交叉验证
def modelfit(alg, dtrain, predictors,useTrainCV=True, cv_folds=5, early_stopping_rounds=50):
if useTrainCV:
xgb_param = alg.get_xgb_params()
xgtrain = xgb.DMatrix(dtrain[predictors].values, label=dtrain[target].values)
cvresult = xgb.cv(xgb_param, xgtrain, num_boost_round=alg.get_params()['n_estimators'], nfold=cv_folds,
metrics='auc', early_stopping_rounds=early_stopping_rounds, show_progress=False)
alg.set_params(n_estimators=cvresult.shape[0])
#Fit the algorithm on the data
alg.fit(dtrain[predictors], dtrain['Disbursed'],eval_metric='auc')
#Predict training set:
dtrain_predictions = alg.predict(dtrain[predictors])
dtrain_predprob = alg.predict_proba(dtrain[predictors])[:,1]
#Print model report:
print "\nModel Report"
print "Accuracy : %.4g" % metrics.accuracy_score(dtrain['Disbursed'].values, dtrain_predictions)
print "AUC Score (Train): %f" % metrics.roc_auc_score(dtrain['Disbursed'], dtrain_predprob)
feat_imp = pd.Series(alg.booster().get_fscore()).sort_values(ascending=False)
feat_imp.plot(kind='bar', title='Feature Importances')
plt.ylabel('Feature Importance Score')
# 为了确定boosting参数,我们要先给其它参数一个初始值。
xgb1 = XGBClassifier(
learning_rate =0.1,
n_estimators=1000,
max_depth=5,
min_child_weight=1,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb1, train, predictors) # xgboost中的cv函数来确定最佳的决策树数量。
# 从输出结果可以看出,在学习速率为0.1时,理想的决策树数目是140
#2. max_depth 和 min_weight 参数调优
param_test1 = {
'max_depth':range(3,10,2),
'min_child_weight':range(1,6,2)
}
gsearch1 = GridSearchCV(estimator = XGBClassifier(learning_rate =0.1, n_estimators=140, max_depth=5,min_child_weight=1, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1, seed=27),
param_grid = param_test1, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch1.fit(train[predictors],train[target])
gsearch1.grid_scores_, gsearch1.best_params_, gsearch1.best_score_
# 理想的max_depth值为5,理想的min_child_weight值为5在这个值附近我们可以再进一步调整,
#来找出理想值。我们把上下范围各拓展1,因为之前我们进行组合的时候,参数调整的步长是2。
param_test2 = {
'max_depth':[4,5,6],
'min_child_weight':[4,5,6]
}
gsearch2 = GridSearchCV(estimator = XGBClassifier(learning_rate=0.1, n_estimators=140, max_depth=5, min_child_weight=2, gamma=0, subsample=0.8, colsample_bytree=0.8,
objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27),
param_grid = param_test2, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch2.fit(train[predictors],train[target])
gsearch2.grid_scores_, gsearch2.best_params_, gsearch2.best_score_
# 得到max_depth的理想取值为4,min_child_weight的理想取值为6
# 3. gamma 调优
param_test3 = {
'gamma':[i/10.0 for i in range(0,5)]
}
gsearch3 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=140, max_depth=4, min_child_weight=6, gamma=0, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test3, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch3.fit(train[predictors],train[target])
gsearch3.grid_scores_, gsearch3.best_params_, gsearch3.best_score_
# 4. 重新调整n_estimators
xgb2 = XGBClassifier(
learning_rate =0.1,
n_estimators=1000,
max_depth=4,
min_child_weight=6,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb2, train, predictors)
# 5. 调整subsample 和 colsample_bytree 参数
param_test4 = {
'subsample':[i/10.0 for i in range(6,10)],
'colsample_bytree':[i/10.0 for i in range(6,10)]
}
gsearch4 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=177, max_depth=3, min_child_weight=4, gamma=0.1, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test4, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch4.fit(train[predictors],train[target])
gsearch4.grid_scores_, gsearch4.best_params_, gsearch4.best_score_
# 缩小刚刚范围,以更小精度继续寻找最优
param_test5 = {
'subsample':[i/100.0 for i in range(75,90,5)],
'colsample_bytree':[i/100.0 for i in range(75,90,5)]
}
gsearch5 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=177, max_depth=4, min_child_weight=6, gamma=0, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test5, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch5.fit(train[predictors],train[target])
gsearch5.grid_scores_, gsearch4.best_params_, gsearch4.best_score_
# 6. 正则化调优
param_test6 = {
'reg_alpha':[1e-5, 1e-2, 0.1, 1, 100]
}
gsearch6 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=177, max_depth=4, min_child_weight=6, gamma=0.1, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test6, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch6.fit(train[predictors],train[target])
gsearch6.grid_scores_, gsearch6.best_params_, gsearch6.best_score_
# 缩小刚刚范围,以更小精度继续寻找最优
param_test7 = {
'reg_alpha':[0, 0.001, 0.005, 0.01, 0.05]
}
gsearch7 = GridSearchCV(estimator = XGBClassifier( learning_rate =0.1, n_estimators=177, max_depth=4, min_child_weight=6, gamma=0.1, subsample=0.8, colsample_bytree=0.8, objective= 'binary:logistic', nthread=4, scale_pos_weight=1,seed=27), param_grid = param_test7, scoring='roc_auc',n_jobs=4,iid=False, cv=5)
gsearch7.fit(train[predictors],train[target])
gsearch7.grid_scores_, gsearch7.best_params_, gsearch7.best_score_
# 6.2 reg_lambda 同理
# 7. 重新调整n_estimators
xgb3 = XGBClassifier(
learning_rate =0.1,
n_estimators=1000,
max_depth=4,
min_child_weight=6,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.005,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb3, train, predictors)
# 8. 降低学习速率,同时使用更多决策树
xgb4 = XGBClassifier(
learning_rate =0.01,
n_estimators=5000,
max_depth=4,
min_child_weight=6,
gamma=0,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.005,
objective= 'binary:logistic',
nthread=4,
scale_pos_weight=1,
seed=27)
modelfit(xgb4, train, predictors)
2.4 自定义xgboost的损失函数(objective)和评估函数
XGB进行树分裂的时候,需要计算损失函数的一阶导数和二阶导数。XGB允许用户自定义这个损失函数,但必须提供相应的一阶导数和二阶导数。
# 以自定义logistic为例
def lgobj(ypred,dtrain):
labels = dtrain.get_label()
pred = 1/(1+np.exp(-ypred))
# loss = label*np.log(pred)+ (1-label)*np.log(1-pred)
grad1 = pred - labels # 一阶导数(注意是对ypred求导的结果)
grad2 = pred*(1-pred) # 二阶导数
# 自定义评估函数
def custom_eval(pred,dtrain):
labels = dtrain.get_label()
err = sum(labels!=(pred>0.0)/len(labels)
return 'errorrate', err
# 使用上述自定义
xgb1 =xgb.train(param,dtrain,num_round,watch_list, obj=lgobj,feval=custom_eval)
# 自定义逼近MAE的目标函数
def huber_approx_obj(preds, dtrain):
d = preds - dtrain.get_label()
h = 1 #h is delta in the graphic
scale = 1 + (d / h) ** 2
scale_sqrt = np.sqrt(scale)
grad = d / scale_sqrt
hess = 1 / scale / scale_sqrt
return grad, hess
def fair_obj(preds, dtrain):
"""y = c * abs(x) - c**2 * np.log(abs(x)/c + 1)"""
x = preds - dtrain.get_label()
c = 1
den = abs(x) + c
grad = c*x / den
hess = c*c / den ** 2
return grad, hess
def log_cosh_obj(preds, dtrain):
x = preds - dtrain.get_label()
grad = np.tanh(x)
hess = 1 / np.cosh(x)**2
return grad, hess
reference
陈天奇 《XGBoost:A Scalable Tree Boosting System》
https://blog.csdn.net/v_JULY_v/article/details/81410574
https://www.zhihu.com/question/41354392
https://www.cnblogs.com/pinard/p/10979808.html
https://blog.csdn.net/algorithmpro/article/details/101443415
https://www.cnblogs.com/xlingbai/p/8274250.html
https://blog.csdn.net/han_xiaoyang/article/details/52665396
https://www.analyticsvidhya.com/blog/2016/03/complete-guide-parameter-tuning-xgboost-with-codes-python/
https://www.cnblogs.com/Allen-rg/p/9542365.html
https://stackoverflow.com/questions/45006341/xgboost-how-to-use-mae-as-objective-function