竞赛练习—公共自行车使用量预测

前言

本次练习主要致力于xgboost回归模型(XGBRegressor)的简单的数据处理模型调参,数据与标杆模型来自公共自行车使用量预测的一个竞赛。
(http://sofasofa.io/competition.php?id=1#c1)
结果要求RMSE的最小,本文最终处理结果为RMSE:14.835,排名47。

1 xgboost模型概述

1.1 XGBoost的核心算法思想

①不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数f(x),去拟合上次预测的残差。
②当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数。
③最后只需要将每棵树对应的分数加起来就是该样本的预测值。

目标是要使得树群的预测值尽量接近真实值,而且有尽量大的泛化能力。

1.2 XGBoost的优点

①使用许多策略去防止过拟合,如:正则化项、Shrinkage and Column Subsampling等。
②目标函数优化利用了损失函数关于待求函数的二阶导数。
③支持并行化,这是XGBoost的闪光点,虽然树与树之间是串行关系,但是同层级节点可并行。具体的对于某个节点,节点内选择最佳分裂点,候选分裂点计算增益用多线程并行。训练速度快。
④添加了对稀疏数据的处理。
⑤交叉验证,early stop,当预测结果已经很好的时候可以提前停止建树,加快训练速度。
⑥支持设置样本权重,该权重体现在一阶导数g和二阶导数h,通过调整权重可以去更加关注一些样本。

1.3 XGBoost的参数

通用参数:宏观函数控制。

booster:我们有两种参数选择,gbtree和gblinear。gbtree是采用树的结构来运行数据,而gblinear是基于线性模型。默认使用gbtree就可以了,不需要调参。
silent:静默模式,为1时模型运行不输出。
nthread: 使用线程数,一般我们设置成-1,使用所有线程。如果有需要,我们设置成多少就是用多少线程。

Booster参数:控制每一步的booster(tree/regression)。booster参数一般可以调控模型的效果和计算代价。我们所说的调参,很这是大程度上都是在调整booster参数。

n_estimator: 也作num_boosting_rounds
这是生成的最大树的数目,也是最大的迭代次数。

learning_rate: 有时也叫作eta,系统默认值为0.3
每一步迭代的步长,很重要。太大了运行准确率不高,太小了运行速度慢。我们一般使用比默认值小一点,0.1左右就很好。

gamma:系统默认为0,我们也常用0。
在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。gamma指定了节点分裂所需的最小损失函数下降值。 这个参数的值越大,算法越保守。因为gamma值越大的时候,损失函数下降更多才可以分裂节点。所以树生成的时候更不容易分裂节点。范围: [0,∞]

subsample:系统默认为1。
这个参数控制对于每棵树,随机采样的比例。减小这个参数的值,算法会更加保守,避免过拟合。但是,如果这个值设置得过小,它可能会导致欠拟合。 典型值:0.5-1,0.5代表平均采样,防止过拟合. 范围: (0,1],注意不可取0

colsample_bytree:系统默认值为1。我们一般设置成0.8左右。
用来控制每棵随机采样的列数的占比(每一列是一个特征)。 典型值:0.5-1范围: (0,1]

colsample_bylevel:默认为1,我们也设置为1.
这个就相比于前一个更加细致了,它指的是每棵树每次节点分裂的时候列采样的比例

max_depth: 系统默认值为6
我们常用3-10之间的数字。这个值为树的最大深度。这个值是用来控制过拟合的。max_depth越大,模型学习的更加具体。设置为0代表没有限制,范围: [0,∞]

max_delta_step:默认0,我们常用0.
这个参数限制了每棵树权重改变的最大步长,如果这个参数的值为0,则意味着没有约束。如果他被赋予了某一个正值,则是这个算法更加保守。通常,这个参数我们不需要设置,但是当个类别的样本极不平衡的时候,这个参数对逻辑回归优化器是很有帮助的。

min_child_weight: 默认为0
孩子节点中最小的样本权重和。如果一个叶子节点的样本权重和小于min_child_weight则拆分过程结束。在现行回归模型中,这个参数是指建立每个模型所需要的最小样本数。该成熟越大算法越conservative。即调大这个参数能够控制过拟合。取值范围为: [0,∞]

lambda:也称reg_lambda,默认值为0。
权重的L2正则化项。(和Ridge regression类似)。这个参数是用来控制XGBoost的正则化部分的。这个参数在减少过拟合上很有帮助。

alpha:也称reg_alpha默认为0,
权重的L1正则化项。(和Lasso regression类似)。 可以应用在很高维度的情况下,使得算法的速度更快。

scale_pos_weight:默认为1
在各类别样本十分不平衡时,把这个参数设定为一个正值,可以使算法更快收敛。通常可以将其设置为负样本的数目与正样本数目的比值。

学习目标参数:控制训练目标的表现。我们对于问题的划分主要体现在学习目标参数上。比如我们要做分类还是回归,做二分类还是多分类,这都是目标参数所提供的。

**objective **:在回归问题objective一般使用reg:squarederror ,即MSE均方误差。
eval_metric :校验数据所需要的评价指标,不同的目标函数将会有缺省的评价指标。

2 数据处理

image

2.1 数据可视化

查看各个变量的相关性:

sns.heatmap(train.corr())

plt.show()
image

考虑到不同城市有不同情况,我们再来查看不同城市下,其变量与y的联系:

sns.barplot(data = train, x='hour', y= 'y', hue='city',ci=None)

plt.show()
image

同理,其他变量也表示出节假日,气温适宜,天气和煦,风俗适中时,出行借车的人较多,比较符合我们的现实经验。

目前判断——特征数量不多,且在不同城市下,都与y相关,暂时都保留。

2.2 特征处理

在xgboost标杆模型上,使用其默认参数,在三折交叉验证下查看不同特征处理下RMSE的变化。


RMSE数值为:19.26933449074207
使用的时间为: 1.4850318431854248

2.2.1 标准化处理(连续特征)

将数据按比例缩放,使之落入一个小的特定区间。

columns=['temp_1','temp_2']

for i in columns:

    train[i]=(train[i] - train[i].min()) / (train[i].max() - train[i].min())  # Min-max 标准化

    train[i] = (train[i] - train[i].mean()) / (train[i].std())  # z-score 标准化

Min-max 标准化:19.282205008176323
使用的时间为:1.4032714366912842

z-score 标准化:19.0416315493435
使用的时间为: 1.4032535552978516


可看出并未有什么改变,这是因为数值缩放不影响分裂点位置,对树模型的结构不造成影响,(树形结构为什么不用归一化?)

2.2.3 'one-hot'处理(离散特征)

首先要求将分类值映射到整数值。然后,每个整数值被表示为二进制向量,除了整数的索引之外,它都是零值,它被标记为1。

columns=['city','is_workday','weather','wind','hour']

for i in columns:

get_dummy_feature=pd.get_dummies(train[i], prefix= i)

train=pd.concat([train, get_dummy_feature],axis=1) #将user_id'和movie_id垂直拼接

train=train.drop(i, axis=1)

RMSE数值为:22.964568418562475
使用的时间为: 2.29085636138916


可看出one-hot处理后,得分变低且时间变多,这是因为对于决策树来说,one-hot 的本质是增加树的深度,tree-model 是在动态的过程中生成类似 One-Hot + Feature Crossing 的机制****(树形结构为什么不用one-hot?)

2.2.4 等距分桶(连续特征)

分桶是离散化的常用方法,将连续型特征离线化为一系列 0/1 的离散特征。

train['temp_1']=np.floor_divide(train['temp_1'], 1)

train['temp_2']=np.floor_divide(train['temp_2'], 1)

RMSE数值为:19.286424187165196
使用的时间为: 1.4750919342041016


可看出等距分桶后并未有什么变化,是因为XGBoost本身使用的是pre-sorted算法(对所有特征都按照特征的数值进行预排序,找到最好分割点将数据分裂成左右子节点)与分桶相似。(xgboost gbdt特征点分烈点)

2.2.5 特征交叉

通过将单独的特征进行组合(相乘或求笛卡尔积)而形成的合成特征。

columns=['hour','temp_1','temp_2','city','is_workday','weather','wind']

for iin columns:

for jin columns:

name ='{}*{}'.format(i,j)

train[name]=train[i].mul(train[j])

RMSE数值为:17.40209548555587
使用的时间为: 3.1934659481048584


可看出虽然时间变多,但数值有明显提升,是因为特征组合有助于表示非线性关系。(特征组合&特征交叉 (Feature Crosses))

3 调参

3.1 girdSearchCV(网格搜索)

我们特征处理先暂时不采取特征交叉(特征交叉调参会花费巨大时间)直接使用原始数据通过gridSearchCV(网格搜索)Booster参数调参。

from sklearn.model_selectionimport GridSearchCV

cv_params ={} # 输入所调参的范围

other_params ={} # 初始参数

model = XGBRegressor(**other_params)

reg = GridSearchCV(estimator=model,param_grid=cv_params,scoring='neg_mean_squared_error',verbose=1,n_jobs=4,cv=3)

reg.fit(train_x, train_y)

# 查看结果

evalute_result = reg.cv_results_

print('每轮迭代运行结果:{0}'.format(evalute_result))

print('RMSE数值为:{0}'.format(reg.best_params_))

print('RMSE数值为:{0}'.format(np.sqrt(abs(reg.best_score_))))

3.2 迭代次数:n_estimators

cv_params ={'n_estimators':[400,500,600,700,800]} 

other_params ={'objective':'reg:squarederror'}
 # 'objective':'reg:squarederror'是解决WARNING: src/objective/regression_obj.cu:152: reg:linear is now deprecated in favor of reg:squarederror.

参数的最佳取值:{'n_estimators': 800}
RMSE数值为:15.74321267404705
使用的时间为: 11.980980634689331

3.3 min_child_weight以及max_depth

cv_params ={'max_depth': [3, 4, 5, 6, 7, 8, 9, 10],'min_child_weight': [1, 2, 3, 4, 5, 6]}

other_params = {'n_estimators':800}

参数的最佳取值:{'max_depth': 4, 'min_child_weight': 6,'objective':'reg:squarederror'}
RMSE数值为:15.216376799573485
使用的时间为: 224.291574716568

3.4 gamma

cv_params ={'gamma': [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]}

other_params = {'n_estimators':800,'max_depth': 4, 'min_child_weight': 6,'objective':'reg:squarederror'}

参数的最佳取值:{'gamma': 0.2}
RMSE数值为:15.300038541244316
使用的时间为: 21.902464628219604

3.5 subsample以及colsample_bytree

cv_params = {'subsample': [0.6, 0.7, 0.8, 0.9],'colsample_bytree': [0.6, 0.7, 0.8, 0.9]}

other_params = {'n_estimators':800,'max_depth':4,'min_child_weight':6,'gamma':0.2,'objective':'reg:squarederror'}

参数的最佳取值:{'colsample_bytree': 0.9, 'subsample': 0.9}
RMSE数值为:15.075942336446541
使用的时间为: 51.692848443984985

3.6 reg_alpha以及reg_lambda

cv_params = {'reg_alpha': [0.05, 0.1, 1, 2, 3],'reg_lambda': [0.05, 0.1, 1, 2, 3]}

other_params = {'n_estimators':800,'max_depth':4,'min_child_weight':6,'gamma':0.2,'colsample_bytree': 0.9, 'subsample': 0.9,'objective':'reg:squarederror'}

参数的最佳取值:{'reg_alpha': 3, 'reg_lambda': 3}
RMSE数值为:15.052545276896213
使用的时间为: 84.78042197227478

3.7 learning_rate

cv_params = {'learning_rate': [0.01,0.02,0.03,0.04,0.05,0.06]}

other_params = {'n_estimators':800,'max_depth':4,'min_child_weight':6,'gamma':0.2,'colsample_bytree':0.9,

'subsample':0.9,'reg_alpha':3,'reg_lambda':3,'objective':'reg:squarederror'}

参数的最佳取值:{'learning_rate': 0.06}
RMSE数值为:14.838008021573847
使用的时间为: 21.34994149208069

3.8 最佳参数

回顾以上调参结果,我们不难发现n_estimators、max_depth、learning_rate的调参对数值改变最大,于是对n_estimators、max_depth、learning_rate再调整

cv_params = {'n_estimators': [400,500,600,700,800],'max_depth': [4,5,6,7],'learning_rate': [0.02,0.04,0.06,0.08]}

other_params = {'n_estimators':800,'max_depth':4,'min_child_weight':6,'gamma':0.2,'colsample_bytree':0.9,

'subsample':0.9,'reg_alpha':3,'reg_lambda':3,'learning_rate':0.06,'objective':'reg:squarederror'}

参数的最佳取值:{'learning_rate': 0.02, 'max_depth': 6, 'n_estimators': 600}
RMSE数值为:14.769918846838962
使用的时间为: 134.6865828037262

3.9 尝试特征处理

因为考虑到之前默认参数不一定为特征处理后的最佳参数,我们在以上最佳参数下特征处理,再对n_estimators、max_depth、learning_rate再调整

结果发现在'one-hot'处理特征交叉不仅花费巨大时间还降低了RMSE的数值,而等距分桶标准化处理所得最佳参数没变,数值和时间也没什么变化,所以决定不进行特征处理。

确定最终参数为:

other_params = {'n_estimators':600,'max_depth':6,'min_child_weight':6,'gamma':0.2,'colsample_bytree':0.9,

'subsample':0.9,'reg_alpha':3,'reg_lambda':3,'learning_rate':0.02,'objective':'reg:squarederror'}

以上所有调参可缩小参数范围,进行多次遍历。

3.10 提交结果

由于交叉验证的存在,使得相同参数下的预测会有所不同,我们需要多次提交。最终最佳结果为RMSE:14.835,排名47。

小结

①在简单的数据下,xgboost更适合直接使用原数据,因为其本身具有一定的处理数据能力
②将不同特征化处理的数据进行逐步调参会花费大量时间,可以先进行简单的调参再代入特征化处理。
③本次仅仅针对简单的特征处理和调参,要想再提高模型拟合能力还需要数据清洗,特征选择,特征融合,模型融合等手段来进行改进。
④调参顺序很重要,不同参数有不同的联系和影响程度,了解参数就能了解模型构造。

代码与数据在我的码云里

你可能感兴趣的:(竞赛练习—公共自行车使用量预测)