XGBoost - 参数gamma(五)

1,660 阅读5分钟

根据菜菜的课程进行整理,方便记忆理解

代码位置如下:

让树停止生长:gamma

在之前所有的推导过程中,我们都没有提到γ\gamma这个变量。从目标函数和结构分数之差GainGain的式子中来看, γ\gamma是我们每增加一片叶子就会被剪去的惩罚项。增加的叶子越多,结构分数之差GainGain会被惩罚越重,所以γ\gamma又被称之为是“复杂性控制”(complexity control),所以γ\gamma是我们用来防止过拟合的重要参数。实践证明, γ\gamma是对梯度提升树影响最大的参数之一,其效果丝毫不逊色于n_estimators和防止过拟合的神器max_depth。同时, γ\gamma还是我们让树停止生长的重要参数。

在逻辑回归中,我们使用参数toltol来设定阈值,并规定如果梯度下降时损失函数减小量小于toltol下降就会停止。在XGB中,我们规定,只要结构分数之差GainGain是大于0的,即只要目标函数还能够继续减小,我们就允许树继续进行分枝。也就是说,我们对于目标函数减小量的要求是:

image.png

如此,我们可以直接通过设定γ\gamma的大小来让XGB中的树停止生长。γ\gamma因此被定义为,在树的叶节点上进行进一步分枝所需的最小目标函数减少量,在决策树和随机森林中也有类似的参数(min_split_loss,min_samples_split)。γ\gamma设定越大,算法就越保守,树的叶子数量就越少,模型的复杂度就越低。

参数含义xgb.train()xgb.XGBRegressor()
复杂度的惩罚项γ\gammagamma,默认0,取值范围[0, +∞]gamma,默认0,取值范围[0, +∞]

如果我们希望从代码中来观察γ\gamma的作用,使用sklearn中传统的学习曲线等工具就比较困难了。来看下面这段代码,这是一段让参数γ\gamma在0~5之间均匀取值的学习曲线。其运行速度较缓慢并且曲线的效果匪夷所思,大家若感兴趣可以自己运行一下。

#======【TIME WARNING: 1 min】=======#
axisx = np.arange(0,5,0.05)
rs = []
var = []
ge = []
for i in axisx:
reg = XGBR(n_estimators=180,random_state=420,gamma=i)
result = CVS(reg,Xtrain,Ytrain,cv=cv)
rs.append(result.mean())
var.append(result.var())
ge.append((1 - result.mean())**2+result.var())
print(axisx[rs.index(max(rs))],max(rs),var[rs.index(max(rs))])
print(axisx[var.index(min(var))],rs[var.index(min(var))],min(var))

print(axisx[ge.index(min(ge))],rs[ge.index(min(ge))],var[ge.index(min(ge))],min(ge))
rs = np.array(rs)
var = np.array(var)*0.1
plt.figure(figsize=(20,5))
plt.plot(axisx,rs,c="black",label="XGB")
plt.plot(axisx,rs+var,c="red",linestyle='-.')
plt.plot(axisx,rs-var,c="red",linestyle='-.')
plt.legend()
plt.show()

可以看到,我们完全无法从中看出什么趋势,偏差时高时低,方差时大时小,参数引起的波动远远超过其他参数(其他参数至少还有一个先上升再平稳的过程,而则是仿佛完全无规律)。在sklearn下XGBoost太不稳定,如果这样来调整参数的话,效果就很难保证。因此,为了调整,我们需要来引入新的工具,xgboost库中的类xgboost.cv。

xgboost.cv (params, dtrain, num_boost_round=10, nfold=3, stratified=False, folds=None, metrics=(), obj=None, feval=None, maximize=False, early_stopping_rounds=None, fpreproc=None, as_pandas=True, verbose_eval=None, show_stdv=True, seed=0, callbacks=None, shuffle=True)

import xgboost as xgb

#为了便捷,使用全数据
dfull = xgb.DMatrix(X,y)

#设定参数
param1 = {'silent':True,'obj':'reg:linear',"gamma":0}
num_round = 100
n_fold=5 #sklearn - KFold

#使用类xgb.cv
time0 = time()
cvresult1 = xgb.cv(param1, dfull, num_round,n_fold)
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
# 00:00:814002

#看看类xgb.cv生成了什么结果?
cvresult1 #随着树不断增加,我们的模型的效果如何变化

image.png

为了使用xgboost.cv,我们必须要熟悉xgboost自带的模型评估指标。xgboost在建库的时候本着大而全的目标,和sklearn类似,包括了大约20个模型评估指标,然而用于回归和分类的其实只有几个,大部分是用于一些更加高级的功能比如ranking。来看用于回归和分类的评估指标都有哪些:

指标含义
rmse回归用,调整后的均方误差
mae回归用,绝对平均误差
logloss二分类用,对数损失
mlogloss多分类用,对数损失
error分类用,分类误差,等于1-准确率
auc分类用,AUC面积
plt.figure(figsize=(20,5))
plt.grid()
plt.plot(range(1,101),cvresult1.iloc[:,0],c="red",label="train,gamma=0")
plt.plot(range(1,101),cvresult1.iloc[:,2],c="orange",label="test,gamma=0")
plt.legend()
plt.show()

#从这个图中,我们可以看出什么?
#怎样从图中观察模型的泛化能力?
#从这个图的角度来说,模型的调参目标是什么?

image.png

来看看如果我们调整γ\gamma,会发生怎样的变化:

param1 = {'silent':True,'obj':'reg:linear',"gamma":0}
param2 = {'silent':True,'obj':'reg:linear',"gamma":20}
num_round = 180
n_fold=5

time0 = time()
cvresult1 = xgb.cv(param1, dfull, num_round,n_fold)
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
# 00:01:360537

time0 = time()
cvresult2 = xgb.cv(param2, dfull, num_round,n_fold)
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
# 00:01:545619


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.plot(range(1,181),cvresult2.iloc[:,0],c="green",label="train,gamma=20")
plt.plot(range(1,181),cvresult2.iloc[:,2],c="blue",label="test,gamma=20")
plt.legend()
plt.show()

#从这里,你看出gamma是如何控制过拟合了吗?控制训练集上的训练 - 降低训练集上的表现

image.png

试一个分类的例子:

from sklearn.datasets import load_breast_cancer
data2 = load_breast_cancer()
x2 = data2.data
y2 = data2.target
dfull2 = xgb.DMatrix(x2,y2)
param1 = {'silent':True,'obj':'binary:logistic',"gamma":0,"nfold":5}
param2 = {'silent':True,'obj':'binary:logistic',"gamma":2,"nfold":5}
num_round = 100
time0 = time()
cvresult1 = xgb.cv(param1, dfull2, num_round,metrics=("error"))
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
# 00:00:451818

time0 = time()
cvresult2 = xgb.cv(param2, dfull2, num_round,metrics=("error"))
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
# 00:00:532060

plt.figure(figsize=(20,5))
plt.grid()
plt.plot(range(1,101),cvresult1.iloc[:,0],c="red",label="train,gamma=0")
plt.plot(range(1,101),cvresult1.iloc[:,2],c="orange",label="test,gamma=0")
plt.plot(range(1,101),cvresult2.iloc[:,0],c="green",label="train,gamma=2")
plt.plot(range(1,101),cvresult2.iloc[:,2],c="blue",label="test,gamma=2")
plt.legend()
plt.show()

image.png

有了xgboost.cv这个工具,我们的参数调整就容易多了。这个工具可以让我们直接看到参数如何影响了模型的泛化能力。接下来,我们将重点讲解如何使用xgboost.cv这个类进行参数调整。到这里,所有关于XGBoost目标函数的原理就讲解完毕了,这个目标函数及这个目标函数所衍生出来的各种数学过程是XGB原理的重中之重,大部分XGB中基于原理的参数都集中在这个模块之中,到这里大家应该已经基本掌握。