XGBoost回归的sklearnAPI实现

不同于内嵌在sklearn框架中的其他算法,xgboost是独立的算法库,因此它有一套不同于sklearn代码的原生代码。大部分时候我们使用原生代码来运行xgboost,因为这套原生代码是完全为集成学习所设计的,不仅可以无缝使用交叉验证、默认输出指标为RMSE,还能够默认输出训练集上的结果帮我们监控模型。然而对于熟悉sklearn的我们来说,这一套代码略有难度,因此许多人也会倾向于使用xgboost自带的sklearn接口来实现算法。

XGBoost自带sklearn接口(sklearn API),通过这个接口,我们可以使用跟sklearn代码一样的方式来实现xgboost,即可以通过fit和predict等接口来执行训练预测过程,也可以调用属性比如coef_等。在XGBoost的sklearn API中,我们可以看到下面五个类:

说明
XGBRegressor() 实现xgboost回归
XGBClassifier() 实现xgboost分类
XGBRanker() 实现xgboost排序
XGBRFClassifier() 基于xgboost库实现随机森林分类
XGBRFRegressor() 基于xgboost库实现随机森林回归

其中XGBRF的两个类是以XGBoost方式建树、但以bagging方式构建森林的类,通常只有在我们使用普通随机森林效果不佳、但又不希望使用Boosting的时候使用。这种使用XGBoost方式建树的森林在sklearn中已经开始了实验,不过还没有正式上线。

另外两个类就很容易理解了,一个是XGBoost的回归,一个是XGBoost的分类。这两个类的参数高度相似,我们可以以XGBoost回归为例查看:

class xgboost.XGBRegressor(n_estimators, max_depth, learning_rate, verbosity, objective, booster, tree_method, n_jobs, gamma, min_child_weight, max_delta_step, subsample, colsample_bytree, colsample_bylevel, colsample_bynode, reg_alpha, reg_lambda, scale_pos_weight, base_score, random_state, missing, num_parallel_tree, monotone_constraints, interaction_constraints, importance_type, gpu_id, validate_parameters, predictor, enable_categorical, eval_metric, early_stopping_rounds, callbacks,**kwargs)

class xgboost.XGBClassifier(n_estimators, use_label_encoder, max_depth, learning_rate, verbosity, objective, booster, tree_method, n_jobs, gamma, min_child_weight, max_delta_step, subsample, colsample_bytree, colsample_bylevel, colsample_bynode, reg_alpha, reg_lambda, scale_pos_weight, base_score, random_state, missing, num_parallel_tree, monotone_constraints, interaction_constraints, importance_type, gpu_id, validate_parameters, predictor, enable_categorical, **kwargs)

可以看到,两个类的参数两都很多,其中不乏一些我们非常熟悉的参数,例如n_estimatorslearning_rate, max_depth等。但大部分参数还是需要我们重新学习和认识,这与xgboost复杂的原理有很大的关系,但由于是sklearn API,所以所有这些参数都有相应的默认值。我们可以在不认识参数的情况下调用这个类。以回归类为例我们来看:

1
2
3
4
5
from xgboost import XGBRegressor
from sklearn.model_selection import cross_validate, KFold
# 交叉验证的类cross_validate,定义交叉验证方式的KFold
from sklearn.model_selection import train_test_split
# train_test_split分训练集与测试集
1
data = pd.read_csv(r"D:\Pythonwork\2021ML\PART 2 Ensembles\datasets\House Price\train_encode.csv",index_col=0)
1
data.head()
Id 住宅类型 住宅区域 街道接触面积(英尺) 住宅面积 街道路面状况 巷子路面状况 住宅形状(大概) 住宅现状 水电气 ... 泳池面积 泳池质量 篱笆质量 其他配置 其他配置的价值 销售月份 销售年份 销售类型 销售状态 SalePrice
0 0.0 5.0 3.0 36.0 327.0 1.0 0.0 3.0 3.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 1.0 2.0 8.0 4.0 208500
1 1.0 0.0 3.0 51.0 498.0 1.0 0.0 3.0 3.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 4.0 1.0 8.0 4.0 181500
2 2.0 5.0 3.0 39.0 702.0 1.0 0.0 0.0 3.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 8.0 2.0 8.0 4.0 223500
3 3.0 6.0 3.0 31.0 489.0 1.0 0.0 0.0 3.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 1.0 0.0 8.0 0.0 140000
4 4.0 5.0 3.0 55.0 925.0 1.0 0.0 0.0 3.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 11.0 2.0 8.0 4.0 250000

5 rows × 81 columns

1
2
3
#回归数据
X = data.iloc[:,:-1]
y = data.iloc[:,-1]
1
X.shape
(1460, 80)
1
2
# 1000多条数据,80多项特征 
# RMSE:均方根误差(测试最优分数)
1
y.describe()
count      1460.000000
mean     180921.195890
std       79442.502883
min       34900.000000
25%      129975.000000
50%      163000.000000
75%      214000.000000
max      755000.000000
Name: SalePrice, dtype: float64

在这个数据集上我们曾经达到过如下的分数:(TPE代表经过调参,其它的代表未调参)

算法 RF AdaBoost GBDT RF
(TPE)
AdaBoost
(TPE)
GBDT
(TPE)
梯度提升树GBDT
贝叶斯优化TPE调参
5折验证
运行时间
1.29s 0.28s 0.49s 0.22s 0.27s 1.54s(↑)
测试最优分数
(RMSE)
30571.267 35345.931 28783.954 28346.673 35169.730 26415.835(↓)
1
2
3
4
5
6
7
8
9
10
11
12
13
# sklearn普通训练代码三步走:实例化,fit,score
# (不执行交叉验证的步骤)
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X,y,test_size=0.3,random_state=1412)
# 第一步将数据分为训练集和测试集

xgb_sk = XGBRegressor(random_state=1412)
# 实例化模型,里面的参数是随机数种子,是万能参数
# 其它的参数都有默认值,不填写也没有关系

xgb_sk.fit(Xtrain,Ytrain) # 这一步结束训练就完成了
xgb_sk.score(Xtest,Ytest) # 默认指标R2(得到评分)

# 由于它是回归类算法,所以得到的默认评估指标是R平方,下面的0.87就是R平方
0.8707175563742298
1
2
3
4
5
# 如果要进行交叉验证的话,步骤如下:
# sklearn交叉验证三步走:实例化,交叉验证,对结果求平均

xgb_sk = XGBRegressor(random_state=1412) #实例化模型
# 得到模型之后不需要训练,就可以直接放入我们交叉验证的类中去进行交叉验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#定义所需的交叉验证方式
cv = KFold(n_splits=5,shuffle=True,random_state=1412)
# n_splits=5代表五折交叉验证
# shuffle=True代表在交叉验证之前需要将训练集上的数据先打乱
# 先打乱再抽样可以帮助我们更好地衡量模型的泛化能力


result_xgb_sk = cross_validate(xgb_sk,X,y,cv=cv #前面的cv是交叉验证中的参数cv,表示交叉验证的模式;后面的cv是我们刚刚定义好的交叉验证的具体方式
,scoring="neg_root_mean_squared_error" #评估指标返回负根均方误差
,return_train_score=True # 是否返回训练分数
,verbose=True # 是否打印建树监控流程
,n_jobs=-1) # 代表请调用当前计算机上所有的资源来帮我们运行
# cross_validate交叉验证类可以输出具体的交叉验证的结果,包括训练集和测试集上的结果和运行时间
# 而corss_val_score类只会输出分数
# cross_validate中填写的是我们已经实例化了但是还未训练的模型
# 对于集成模型来说,标准是需要返回根均方误差;但是在sklearn中所有的误差都是负的,所以这里返回负根均方误差
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 16 concurrent workers.
[Parallel(n_jobs=-1)]: Done   5 out of   5 | elapsed:    1.5s finished
1
2
result_xgb_sk # 最终返回的交叉验证的结果
# 包括训练时间、测试时间、训练分数、测试分数
{'fit_time': array([0.23205304, 0.45810175, 0.46110272, 0.47810745, 0.47210622]),
 'score_time': array([0.00499988, 0.00500083, 0.00700212, 0.00600123, 0.0080018 ]),
 'test_score': array([-25398.06063039, -42892.11176772, -26426.91326917, -20676.41934632,
        -33375.56869975]),
 'train_score': array([ -903.74997856, -1106.45801425,  -997.3799282 ,  -818.69215194,
         -877.57892862])}
1
2
3
4
def RMSE(result,name):
return abs(result[name].mean())

# 这里用RMSE返回结果的平均值(RMSE是均方根误差的意思)
1
RMSE(result_xgb_sk,"train_score")
940.7718003131752
1
RMSE(result_xgb_sk,"test_score")
29753.814742669765

可以看到,在默认参数下,xgboost模型极度不稳定,并且过拟合的情况非常严重,在训练集上的RMSE达到了前所未有的低点940.77,这说明XGBoost的学习能力的确强劲,现有数据量对xgboost来说可能有点不足。在没有调整任何参数的情况下,XGBoost的表现没能胜过梯度提升树,这可能是因为在默认参数下梯度提升树的过拟合程度较轻。我们可以尝试使用之前学过的知识,对XGBoost的参数略微进行调整,例如将最可能影响模型的参数之一:max_depth设置为一个较小的值。

1
2
xgb_sk = XGBRegressor(max_depth=5,random_state=1412) #实例化
# 这里令最大深度为5,对于大部分的决策树模型来说,这已经相当于砍掉了大部分的树枝
1
2
3
4
5
result_xgb_sk = cross_validate(xgb_sk,X,y,cv=cv
,scoring="neg_root_mean_squared_error" #负根均方误差
,return_train_score=True
,verbose=True
,n_jobs=-1)
[Parallel(n_jobs=-1)]: Using backend LokyBackend with 16 concurrent workers.
[Parallel(n_jobs=-1)]: Done   5 out of   5 | elapsed:    0.3s finished
1
RMSE(result_xgb_sk,"train_score")
2362.6596931022264
1
RMSE(result_xgb_sk,"test_score")
28623.2199609373

过拟合程度立刻减轻了,这说明模型是有潜力的,经过精密的调参之后xgboost上应该能够获得不错的结果。

当sklearn API训练完毕之后,我们可以调用sklearn中常见的部分属性对训练后的模型进行查看,例如查看特征重要性的属性feature_importances_,以及查看XGB下每一棵树的get_booster()方法、查看总共有多少棵树的get_num_boosting_rounds()方法、以及查看当前所有参数的方法get_params

1
xgb_sk = XGBRegressor(max_depth=5,random_state=1412).fit(X,y) # 进行训练
1
2
#查看特征重要性
xgb_sk.feature_importances_
array([2.75380560e-04, 3.31971998e-04, 1.00358156e-02, 1.05883693e-03,
       3.16664134e-03, 0.00000000e+00, 1.98506648e-04, 2.89293937e-03,
       1.25273280e-02, 0.00000000e+00, 5.93377743e-04, 1.02778999e-02,
       2.30982271e-03, 1.63636741e-03, 2.90878420e-03, 2.35304120e-03,
       6.21278654e-04, 4.83804524e-01, 3.57081974e-03, 5.26868505e-03,
       5.39273489e-03, 1.19193934e-03, 6.73988950e-04, 8.58596177e-04,
       7.24220707e-04, 6.31435658e-04, 6.40772341e-04, 1.79228242e-02,
       1.88877675e-04, 5.03550633e-04, 1.56989340e-02, 2.03612563e-03,
       2.47432292e-03, 1.01661379e-03, 8.10322072e-03, 2.90135911e-04,
       5.88890282e-04, 7.41500990e-04, 1.55724427e-02, 4.79830429e-04,
       3.00964224e-04, 3.08048874e-02, 1.71841952e-04, 1.05094137e-02,
       7.64262909e-03, 5.08715457e-04, 3.02572548e-02, 2.92710634e-03,
       3.74272262e-04, 3.38621549e-02, 1.68845232e-03, 5.42165304e-04,
       3.92313190e-02, 2.88939234e-02, 3.41931777e-03, 3.12080747e-03,
       9.38584842e-03, 8.22769129e-04, 1.47235217e-02, 3.14127025e-03,
       1.07205287e-03, 1.11840509e-01, 3.14075779e-03, 2.42510848e-02,
       7.32478802e-05, 7.96243025e-04, 9.54234274e-04, 1.75007887e-03,
       5.87832765e-04, 3.06713278e-04, 1.96228363e-03, 1.95614668e-03,
       0.00000000e+00, 6.25234039e-04, 6.64470572e-05, 1.08636224e-04,
       8.74570746e-04, 8.18299886e-04, 3.22782877e-03, 3.69613548e-03],
      dtype=float32)
1
2
#调出其中一棵树,不过无法展示出树的细节,只能够调出建树的Booster对象
xgb_sk.get_booster()[2]
<xgboost.core.Booster at 0x1f905eec130>

一棵树都是一个单独的Booster提升树,Booster就相当于sklearn中DecisionTreeRegressor,只不过是使用xgboost独有的建树规则进行计算。

1
2
#查看一共建立了多少棵树,相当于是n_estimators的取值
xgb_sk.get_num_boosting_rounds()
100
1
2
#获取每一个参数的取值
xgb_sk.get_params()
{'objective': 'reg:squarederror',
 'base_score': 0.5,
 'booster': 'gbtree',
 'colsample_bylevel': 1,
 'colsample_bynode': 1,
 'colsample_bytree': 1,
 'enable_categorical': False,
 'gamma': 0,
 'gpu_id': -1,
 'importance_type': None,
 'interaction_constraints': '',
 'learning_rate': 0.300000012,
 'max_delta_step': 0,
 'max_depth': 5,
 'min_child_weight': 1,
 'missing': nan,
 'monotone_constraints': '()',
 'n_estimators': 100,
 'n_jobs': 16,
 'num_parallel_tree': 1,
 'predictor': 'auto',
 'random_state': 1412,
 'reg_alpha': 0,
 'reg_lambda': 1,
 'scale_pos_weight': 1,
 'subsample': 1,
 'tree_method': 'exact',
 'validate_parameters': 1,
 'verbosity': None}

查看参数对xgboost来说很有意义,因为XGBRegressor的说明中没有注明默认参数,因此通过查看参数,我们可以了解到xgboost在sklearn API中都设置了怎样的参数,作为未来调参的参考。对于xgboost分类器,我们还可以调用predict_proba这样的方法来输出概率值,除此之外我们一般不会再用到xgboost sklearn API中的其他功能。