XGBoost的调用
直接调用XGBoost的库
使用
import xgboost as xgb
即可导入XGBoost的库。
在直接调用XGBoost库的情况下,需要用到xgb.DMatrix()来读取数据,需要在使用模型前用param={}写好参数,然后调用训练模型的类bst = xgb.train(param),再用**bst.predict()**进行预测,在xgboost不能直接调用接口score,必须先用predict得出预测的y,再用评估指标。
params {eta, gamma, max_depth, min_child_weight, max_delta_step, subsample, colsample_bytree,
colsample_bylevel, colsample_bynode, lambda, alpha, tree_method string, sketch_eps, scale_pos_weight, updater,
refresh_leaf, process_type, grow_policy, max_leaves, max_bin, predictor, num_parallel_tree}
xgboost.train (params, dtrain, num_boost_round=10, evals=(), obj=None, feval=None, maximize=False,
early_stopping_rounds=None, evals_result=None, verbose_eval=True, xgb_model=None, callbacks=None,
learning_rates=None)
使用sklearn的API
如sklearn中的API
class xgboost.XGBRegressor (max_depth=3, learning_rate=0.1, n_estimators=100, silent=True,
objective=‘reg:linear’, booster=‘gbtree’, n_jobs=1, nthread=None, gamma=0, min_child_weight=1, max_delta_step=0,
subsample=1, colsample_bytree=1, colsample_bylevel=1, reg_alpha=0, reg_lambda=1, scale_pos_weight=1,
base_score=0.5, random_state=0, seed=None, missing=None, importance_type=‘gain’, **kwargs)
提升集成算法
XGBoost的基础是梯度提升树,其中所有的树都是二叉的。
对于分类树来说,叶子上的类别由少数服从多数决定;对于回归树来说,叶子上的值为该叶子上样本的平均值。
对于梯度提升回归树(GBDT),每个样本的预测结果为所有树的预测结果的加权求和:
y
^
i
(
k
)
=
∑
k
=
k
K
γ
i
h
k
(
x
i
)
\hat{y}^{(k)}_i=\sum_{k=k}^K \gamma_i h_k(x_i)
y^i(k)=k=k∑Kγihk(xi)
其中K是树的总数,
γ
i
\gamma_i
γi是树的权重,
h
k
h_k
hk是树上的预测结果。
但XGBoost在做回归的时候,
y
^
\hat{y}
y^的计算有所不同,是一个预测分数predicttion_score,也叫做叶子权重,就是所有在这个叶子结点上的样本在这颗树上的回归取值,用
f
k
(
x
i
)
f^k{(x_i)}
fk(xi)或者
w
w
w表示;注意与上边
h
k
(
x
i
)
h^k{(x_i)}
hk(xi)的区别,是求均值。
所以,对XGBoost来说,
y
^
i
(
k
)
=
∑
k
=
k
K
γ
i
f
k
(
x
i
)
\hat{y}^{(k)}_i=\sum_{k=k}^K \gamma_i f_k(x_i)
y^i(k)=k=k∑Kγifk(xi)
而
K
K
K就对应着超参数n_estimators(sklearn API),而对应在XGBoost库则为num_round;silent参数表示是都打印每次建立的结果。
尝试建模:
导入相关的库
from xgboost import XGBRegressor as XGBR
from sklearn.ensemble import RandomForestRegressor as RFR
from sklearn.linear_model import LinearRegression as LinearR
from sklearn.datasets import load_boston
from sklearn.model_selection import KFold, cross_val_score as CVS, train_test_split as TTS
from sklearn.metrics import mean_squared_error as MSE
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from time import time
import datetime
data = load_boston()
#波士顿数据集非常简单,但它所涉及到的问题却很多
X = data.data
y = data.target
建模,查看其它接口和属性
Xtrain,Xtest,Ytrain,Ytest = TTS(X,y,test_size=0.3,random_state=420)
reg = XGBR(n_estimators=100).fit(Xtrain,Ytrain)
reg.predict(Xtest) #传统接口predict
reg.score(Xtest,Ytest) #你能想出这里应该返回什么模型评估指标么?
MSE(Ytest,reg.predict(Xtest))
reg.feature_importances_
#树模型的优势之一:能够查看模型的重要性分数,可以使用嵌入法进行特征选择
注意这里reg.score() 默认输出的是
R
2
R^2
R2
这里使用**reg.feature_importances_ **可以查看特征的重要性,可以使用嵌入法(SelectFromModel)进行特征选择。
交叉验证,与线性回归&随机森林回归进行对比
reg = XGBR(n_estimators=100)
CVS(reg,Xtrain,Ytrain,cv=5).mean()
#这里应该返回什么模型评估指标,还记得么?
#严谨的交叉验证与不严谨的交叉验证之间的讨论:训练集or全数据?
CVS(reg,Xtrain,Ytrain,cv=5,scoring='neg_mean_squared_error').mean()
#来查看一下sklearn中所有的模型评估指标
import sklearn
sorted(sklearn.metrics.SCORERS.keys())
#使用随机森林和线性回归进行一个对比
rfr = RFR(n_estimators=100)
CVS(rfr,Xtrain,Ytrain,cv=5).mean()
CVS(rfr,Xtrain,Ytrain,cv=5,scoring='neg_mean_squared_error').mean()
lr = LinearR()
CVS(lr,Xtrain,Ytrain,cv=5).mean()
CVS(lr,Xtrain,Ytrain,cv=5,scoring='neg_mean_squared_error').mean()
#开启参数slient:在数据巨大,预料到算法运行会非常缓慢的时候可以使用这个参数来监控模型的训练进度
reg = XGBR(n_estimators=10,silent=False)
CVS(reg,Xtrain,Ytrain,cv=5,scoring='neg_mean_squared_error').mean()
因为要使用交叉验证,所以最开始实例化的XGBR()不需要拟合数据。
注意这里的交叉验证默认返回的是
R
2
R^2
R2;而如果是分类任务,默认返回准确率。
在交叉验证CVS()中输入**‘neg_mean_squared_error’**后可以使用均方误差来作评估指标。
使用
import sklearn
sorted(sklearn.scores.SCORES.keys())
可以查看所有的评估指标。
绘制学习曲线的函数
def plot_learning_curve(estimator,title, X, y,
ax=None, #选择子图
ylim=None, #设置纵坐标的取值范围
cv=None, #交叉验证
n_jobs=None #设定索要使用的线程
):
from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt
import numpy as np
train_sizes, train_scores, test_scores = learning_curve(estimator, X, y
,shuffle=True
,cv=cv
,random_state=420
,n_jobs=n_jobs)
if ax == None:
ax = plt.gca()
else:
ax = plt.figure()
ax.set_title(title)
if ylim is not None:
ax.set_ylim(*ylim)
ax.set_xlabel("Training examples")
ax.set_ylabel("Score")
ax.grid() #绘制网格,不是必须
ax.plot(train_sizes, np.mean(train_scores, axis=1), 'o-'
, color="r",label="Training score")
ax.plot(train_sizes, np.mean(test_scores, axis=1), 'o-'
, color="g",label="Test score")
ax.legend(loc="best")
return ax
使用学习曲线探索
cv = KFold(n_splits=5, shuffle = True, random_state=42) #交叉验证模式
plot_learning_curve(XGBR(n_estimators=100,random_state=420)
,"XGB",Xtrain,Ytrain,ax=None,cv=cv)
plt.show()
通过测试集和训练集的学习曲线的对比以看是否存在过拟合的现象。
使用参数学习曲线观察n_estimators对模型的影响
#=====【TIME WARNING:25 seconds】=====#
axisx = range(10,1010,50)
rs = []
for i in axisx:
reg = XGBR(n_estimators=i,random_state=420,silent=True)
rs.append(CVS(reg,Xtrain,Ytrain,cv=cv).mean())
print(axisx[rs.index(max(rs))],max(rs))
plt.figure(figsize=(20,5))
plt.plot(axisx,rs,c="red",label="XGB")
plt.legend()
plt.show()
在这个学习曲线中,可以发现需要660棵树才能达到最高的得分,而总共样本只有五百多个,显然不合理。
改进学习曲线:方差和泛化误差
衡量一个模型在位置数据集上预测能力的指标,叫做泛化误差(Generalization error)。泛化误差由方差(var),偏差(bias)与造成共同决定。
E
(
f
;
D
)
=
b
i
a
s
2
+
v
a
r
+
ϵ
2
E(f;D)=bias^2+var+\epsilon^2
E(f;D)=bias2+var+ϵ2
改进的学习曲线应该考虑泛化误差而非仅仅考虑偏差。
#======【TIME WARNING: 20s】=======#
axisx = range(50,1050,50)
rs = []
var = []
ge = []
for i in axisx:
reg = XGBR(n_estimators=i,random_state=420,silent=True)
cvresult = CVS(reg,Xtrain,Ytrain,cv=cv)
#记录1-偏差
rs.append(cvresult.mean())
#记录方差
var.append(cvresult.var())
#计算泛化误差的可控部分
ge.append((1 - cvresult.mean())**2+cvresult.var())
#打印R2最高所对应的参数取值,并打印这个参数下的方差
print(axisx[rs.index(max(rs))],max(rs),var[rs.index(max(rs))])
#打印方差最低时对应的参数取值,并打印这个参数下的R^2
print(axisx[var.index(min(var))],rs[var.index(min(var))],min(var))
#打印泛化误差可控部分的参数取值,并打印这个参数下的R2,方差以及泛化误差的可控部分
print(axisx[ge.index(min(ge))],rs[ge.index(min(ge))],var[ge.index(min(ge))],min(ge))
plt.figure(figsize=(20,5))
plt.plot(axisx,rs,c="red",label="XGB")
plt.legend()
plt.show()
细化学习曲线,寻找最佳的n_estimators
axisx = range(100,300,10)
rs = []
var = []
ge = []
for i in axisx:
reg = XGBR(n_estimators=i,random_state=420,silent=True)
cvresult = CVS(reg,Xtrain,Ytrain,cv=cv)
rs.append(cvresult.mean())
var.append(cvresult.var())
ge.append((1 - cvresult.mean())**2+cvresult.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.01
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()
可以验证学习效果是否提升
#验证模型效果是否提高了?
time0 = time()
print(XGBR(n_estimators=100,random_state=420).fit(Xtrain,Ytrain).score(Xtest,Ytest))
print(time()-time0)
time0 = time()
print(XGBR(n_estimators=660,random_state=420).fit(Xtrain,Ytrain).score(Xtest,Ytest))
print(time()-time0)
time0 = time()
print(XGBR(n_estimators=180,random_state=420).fit(Xtrain,Ytrain).score(Xtest,Ytest))
print(time()-time0)
学习曲线的使用还是有一定的局限性,因为训练的时长需要的很久,如果数据集过大时训练时间很长,并且每个参数不一定是独立起作用,可能通过网格搜索还是更好的。
关于n_estimators,可以知道:
1 XGB中树的数量越多,模型的学习越强,树的量太大容易过拟合,建议300一下。
2 当树数目一开始较小时,增大的话提升的效果明显;到一定的数目之后,再增加树的数目效果就不明显了。
控制有放回随机抽样:参数subsample
在XGB中,使用subsample参数来决定随机抽样的时候抽取样本的比例,范围(0,1],默认值为1.
一般来说样本量越大,模型越不容易过拟合,当样本量较小时还抽样可能不太合适;可以通过学习曲线看一看取什么值合适,使用前可以根据理论进行分析;数据量较大的时候还是应该尝试使用该参数。
迭代决策树:参数eta
在提升树的模型当中,我们希望所建立的每一颗新的树都能在此前树预测错误的样本上表现更好,但是事实却很难实现。
在梯度提升树中,如何保证每次新添加的树能够提升集成学习的效果?
在此使用的策略是利用梯度下降来迭代算法:
y
^
i
(
k
+
1
)
=
y
i
^
(
k
)
+
f
k
+
1
(
x
i
)
\hat{y}_i^{(k+1)}=\hat{y_i}^{(k)}+f_{k+1}(x_i)
y^i(k+1)=yi^(k)+fk+1(xi)
每一次的预测结果都是前面一次预测结果加上第
k
+
1
k+1
k+1棵树的叶子权重。
在此算法中迭代的是预测结果,将其纳入损失函数。
在XGB的迭代中,也加入了类似步长的参数
y
^
i
(
k
+
1
)
=
y
i
^
(
k
)
+
η
f
k
+
1
(
x
i
)
\hat{y}_i^{(k+1)}=\hat{y_i}^{(k)}+\eta f_{k+1}(x_i)
y^i(k+1)=yi^(k)+ηfk+1(xi)
η
\eta
η是迭代决策树中的步长(shrinkage),也叫做学习率(learning rate)。
η
\eta
η较大时,迭代的速度快,算法的极限可能很快达到,但也可能无法收敛到最优;
η
\eta
η较小时,可能寻找到比较精确的最优点,但是迭代速度可能比较慢。
这个
η
\eta
η在xgb.XGBRegressor()中是learning_rate,默认0.1,在xgb.train()中为eta,默认0.3。
η
\eta
η的取值范围为[0,1]。取0意味着不会迭代。
通常使用网格搜索来确定
η
\eta
η和n_estimators
参数
η
\eta
η的探索
#首先我们先来定义一个评分函数,这个评分函数能够帮助我们直接打印Xtrain上的交叉验证结果
def regassess(reg,Xtrain,Ytrain,cv,scoring = ["r2"],show=True):
score = []
for i in range(len(scoring)):
if show:
print("{}:{:.2f}".format(scoring[i] #模型评估指标的名字
,CVS(reg
,Xtrain,Ytrain
,cv=cv,scoring=scoring[i]).mean()))
score.append(CVS(reg,Xtrain,Ytrain,cv=cv,scoring=scoring[i]).mean())
return score
from time import time
import datetime
for i in [0,0.2,0.5,1]:
time0=time()
reg = XGBR(n_estimators=180,random_state=420,learning_rate=i,silent=True)
print("learning_rate = {}".format(i))
regassess(reg,Xtrain,Ytrain,cv,scoring = ["r2","neg_mean_squared_error"])
print(datetime.datetime.fromtimestamp(time()-time0).strftime("%M:%S:%f"))
print("\t")
XGB中,参数n_estimators,learning_rate,silent和subsample都是与梯度提升树原理相关的参数,主要目的不是提升模型的表现,而是平衡运行时间。