当GridSearch赶上XGBoost 一段代码解决调参问题

写在最前

数据比赛,GBM(Gredient Boosting Machine)少不了,咱们最多见的就是XGBoost和LightGBM。html

模型是在数据比赛中尤其重要的,可是实际上,在比赛的过程当中,大部分朋友在模型上花的时间倒是相对较少的,你们都倾向于将宝贵的时间留在特征提取与模型融合这些方面。在实战中,咱们会先作一个baseline的demo,尽量快尽量多的挖掘出模型的潜力,以便后期将精力花在特征和模型融合上。这里就须要一些调参功底。git

本文从这两种模型的一共百余参数中选取重要的十余个进行探讨研究。并给你们展现快速轻量级的调参方式。固然,有更高一步要求的朋友,仍是得戳LightGBMXGBoost这两个官方文档连接。算法

为了更好的试验,我将预处理后的数据放在百度云上。你们戳连接下载。apache

XGBoost 的一些重要参数

XGBoost的参数一共分为三类:数组

  1. 通用参数:宏观函数控制。
  2. Booster参数:控制每一步的booster(tree/regression)。booster参数通常能够调控模型的效果和计算代价。咱们所说的调参,很这是大程度上都是在调整booster参数。
  3. 学习目标参数:控制训练目标的表现。咱们对于问题的划分主要体如今学习目标参数上。好比咱们要作分类仍是回归,作二分类仍是多分类,这都是目标参数所提供的。

Note: 我下面介绍的参数都是我以为比较重要的, 完整参数请戳官方文档bash

通用参数

  1. booster:咱们有两种参数选择,gbtreegblinear。gbtree是采用树的结构来运行数据,而gblinear是基于线性模型。
  2. silent:静默模式,为1时模型运行不输出。
  3. nthread: 使用线程数,通常咱们设置成-1,使用全部线程。若是有须要,咱们设置成多少就是用多少线程。

Booster参数

  1. n_estimator: 也做num_boosting_roundsdom

    这是生成的最大树的数目,也是最大的迭代次数。机器学习

  2. learning_rate: 有时也叫做eta,系统默认值为0.3,。函数

    每一步迭代的步长,很重要。太大了运行准确率不高,过小了运行速度慢。咱们通常使用比默认值小一点,0.1左右就很好。工具

  3. gamma:系统默认为0,咱们也经常使用0

    在节点分裂时,只有分裂后损失函数的值降低了,才会分裂这个节点。gamma指定了节点分裂所需的最小损失函数降低值。 这个参数的值越大,算法越保守。由于gamma值越大的时候,损失函数降低更多才能够分裂节点。因此树生成的时候更不容易分裂节点。范围: [0,∞]

  4. subsample:系统默认为1

    这个参数控制对于每棵树,随机采样的比例。减少这个参数的值,算法会更加保守,避免过拟合。可是,若是这个值设置得太小,它可能会致使欠拟合。 典型值:0.5-10.5表明平均采样,防止过拟合. 范围: (0,1]注意不可取0

  5. colsample_bytree:系统默认值为1。咱们通常设置成0.8左右。

    用来控制每棵随机采样的列数的占比(每一列是一个特征)。 典型值:0.5-1范围: (0,1]

  6. colsample_bylevel:默认为1,咱们也设置为1.

    这个就相比于前一个更加细致了,它指的是每棵树每次节点分裂的时候列采样的比例

  7. max_depth: 系统默认值为6

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

  8. max_delta_step:默认0,咱们经常使用0.

    这个参数限制了每棵树权重改变的最大步长,若是这个参数的值为0,则意味着没有约束。若是他被赋予了某一个正值,则是这个算法更加保守。一般,这个参数咱们不须要设置,可是当个类别的样本极不平衡的时候,这个参数对逻辑回归优化器是颇有帮助的。

  9. lambda:也称reg_lambda,默认值为0

    权重的L2正则化项。(和Ridge regression相似)。这个参数是用来控制XGBoost的正则化部分的。这个参数在减小过拟合上颇有帮助。

  10. alpha:也称reg_alpha默认为0,

    权重的L1正则化项。(和Lasso regression相似)。 能够应用在很高维度的状况下,使得算法的速度更快。

  11. scale_pos_weight:默认为1

    在各种别样本十分不平衡时,把这个参数设定为一个正值,可使算法更快收敛。一般能够将其设置为负样本的数目与正样本数目的比值。

学习目标参数

objective [缺省值=reg:linear]

  • reg:linear– 线性回归
  • reg:logistic – 逻辑回归
  • binary:logistic – 二分类逻辑回归,输出为几率
  • binary:logitraw – 二分类逻辑回归,输出的结果为wTx
  • count:poisson – 计数问题的poisson回归,输出结果为poisson分布。在poisson回归中,max_delta_step的缺省值为0.7 (used to safeguard optimization)
  • multi:softmax – 设置 XGBoost 使用softmax目标函数作多分类,须要设置参数num_class(类别个数)
  • multi:softprob – 如同softmax,可是输出结果为ndata*nclass的向量,其中的值是每一个数据分为每一个类的几率。

eval_metric [缺省值=经过目标函数选择]

  • rmse: 均方根偏差
  • mae: 平均绝对值偏差
  • logloss: negative log-likelihood
  • error: 二分类错误率。其值经过错误分类数目与所有分类数目比值获得。对于预测,预测值大于0.5被认为是正类,其它归为负类。 error@t: 不一样的划分阈值能够经过 ‘t’进行设置
  • merror: 多分类错误率,计算公式为(wrong cases)/(all cases)
  • mlogloss: 多分类log损失
  • auc: 曲线下的面积
  • ndcg: Normalized Discounted Cumulative Gain
  • map: 平均正确率

通常来讲,咱们都会使用xgboost.train(params, dtrain)函数来训练咱们的模型。这里的params指的是booster参数。

两种基本的实例

咱们要注意的是,在xgboost中想要进行二分类处理的时候,咱们仅仅在 objective中设置成 binary,会发现输出仍然是一堆连续的值。这是由于它输出的是模型预测的全部几率中最大的那个值。咱们能够后续对这些几率进行条件处理获得最终类别,或者直接调用xgboost中的XGBClassifier()类,但这两种函数的写法不太同样。你们看我下面的例子。

import xgboost as xgb
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

train_data = pd.read_csv('train.csv')   # 读取数据
y = train_data.pop('30').values   # 用pop方式将训练数据中的标签值y取出来,做为训练目标,这里的‘30’是标签的列名
col = train_data.columns   
x = train_data[col].values  # 剩下的列做为训练数据
train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.333, random_state=0)   # 分训练集和验证集
train = xgb.DMatrix(train_x, train_y)
valid = xgb.DMatrix(valid_x, valid_y)   # train函数下须要传入一个Dmatrix值,具体用法如代码所示

params = {
            'max_depth': 15,
            'learning_rate': 0.1,
            'n_estimators': 2000,
            'min_child_weight': 5,
            'max_delta_step': 0,
            'subsample': 0.8,
            'colsample_bytree': 0.7,
            'reg_alpha': 0,
            'reg_lambda': 0.4,
            'scale_pos_weight': 0.8,
            'silent': True,
            'objective': 'binary:logistic',
            'missing': None,
            'eval_metric': 'auc',
            'seed': 1440,
            'gamma': 0
}    # 这里的params特指booster参数,注意这个eva_metric是评估函数

xlf = xgb.train(params, train, evals=[(valid, 'eval')], 
                num_boost_round=2000, early_stopping_rounds=30, verbose_eval=True)   
                # 训练,注意验证集的写法, 还有early_stopping写法,这里指的是30轮迭代中效果未增加便中止训练
y_pred = xlf.predict(valid_x, ntree_limit=xlf.best_ntree_limit) 
# xgboost没有直接使用效果最好的树做为模型的机制,这里采用最大树深限制的方法,目的是获取刚刚early_stopping效果最好的,实测性能能够
auc_score = roc_auc_score(valid_y, y_pred)  # 算一下预测结果的roc值

复制代码

以上是xgboost.train()写法,这是xgboost最原始的封装函数。这样训练咱们预测输出的是一串连续值,是xgboost在这几个类别上几率最大的几率值。咱们若是想要获得咱们的分类结果,还须要进行其余操做。

幸运的是,xgboost为了贴合sklearn的使用,好比gridsearch这些实用工具,又开发了XGBoostClassifier()XGBoostRegression()两个函数。能够更加简单快捷的进行分类和回归处理。注意xgboost的sklearn包没有 feature_importance 这个量度,可是get_fscore()函数有相同的功能。固然,为了和sklearn保持一致,写法也发生变化,具体请看下面代码:

import xgboost as xgb
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

train_data = pd.read_csv('train.csv')   # 读取数据
y = train_data.pop('30').values   # 用pop方式将训练数据中的标签值y取出来,做为训练目标,这里的‘30’是标签的列名
col = train_data.columns   
x = train_data[col].values  # 剩下的列做为训练数据
train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.333, random_state=0)   # 分训练集和验证集
# 这里不须要Dmatrix

xlf = xgb.XGBClassifier(max_depth=10,
			learning_rate=0.01,
			n_estimators=2000,
			silent=True,
			objective='binary:logistic',
			nthread=-1,
			gamma=0,
			min_child_weight=1,
			max_delta_step=0,
			subsample=0.85,
			colsample_bytree=0.7,
			colsample_bylevel=1,
			reg_alpha=0,
			reg_lambda=1,
			scale_pos_weight=1,
			seed=1440,
			missing=None)
xlf.fit(train_x, train_y, eval_metric='error', verbose=True, eval_set=[(valid_x, valid_y)], early_stopping_rounds=30)
# 这个verbose主要是调节系统输出的,若是设置成10,即是每迭代10次就有输出。
# 注意咱们这里eval_metric=‘error’即是准确率。这里面并无accuracy命名的函数,网上大多例子为auc,我这里特地放了个error。
y_pred = xlf.predict(valid_x, ntree_limit=xlf.best_ntree_limit)
auc_score = roc_auc_score(valid_y, y_pred)
y_pred = xlf.predict(valid_x, ntree_limit=xlf.best_ntree_limit) 
# xgboost没有直接使用效果最好的树做为模型的机制,这里采用最大树深限制的方法,目的是获取刚刚early_stopping效果最好的,实测性能能够
auc_score = roc_auc_score(valid_y, y_pred)  # 算一下预测结果的roc值

复制代码

那么咱们介绍了这么多,重点就来了:如何又快又好的调参?首先咱们须要了解grid search是个什么原理。

GridSearch 简介

这是一种调参手段;穷举搜索:在全部候选的参数选择中,经过循环遍历,尝试每一种可能性,表现最好的参数就是最终的结果。其原理就像是在数组里找最大值。(为何叫网格搜索?以有两个参数的模型为例,参数a有3种可能,参数b有4种可能,把全部可能性列出来,能够表示成一个3*4的表格,其中每一个cell就是一个网格,循环过程就像是在每一个网格里遍历、搜索,因此叫grid search)

其实这个就跟咱们经常使用的遍历是同样的。建议你们使用sklearn里面的GridSearch函数,简洁速度快。

import xgboost as xgb
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score

train_data = pd.read_csv('train.csv')   # 读取数据
y = train_data.pop('30').values   # 用pop方式将训练数据中的标签值y取出来,做为训练目标,这里的‘30’是标签的列名
col = train_data.columns   
x = train_data[col].values  # 剩下的列做为训练数据
train_x, valid_x, train_y, valid_y = train_test_split(x, y, test_size=0.333, random_state=0)   # 分训练集和验证集
# 这里不须要Dmatrix

parameters = {
              'max_depth': [5, 10, 15, 20, 25],
              'learning_rate': [0.01, 0.02, 0.05, 0.1, 0.15],
              'n_estimators': [500, 1000, 2000, 3000, 5000],
              'min_child_weight': [0, 2, 5, 10, 20],
              'max_delta_step': [0, 0.2, 0.6, 1, 2],
              'subsample': [0.6, 0.7, 0.8, 0.85, 0.95],
              'colsample_bytree': [0.5, 0.6, 0.7, 0.8, 0.9],
              'reg_alpha': [0, 0.25, 0.5, 0.75, 1],
              'reg_lambda': [0.2, 0.4, 0.6, 0.8, 1],
              'scale_pos_weight': [0.2, 0.4, 0.6, 0.8, 1]

}

xlf = xgb.XGBClassifier(max_depth=10,
			learning_rate=0.01,
			n_estimators=2000,
			silent=True,
			objective='binary:logistic',
			nthread=-1,
			gamma=0,
			min_child_weight=1,
			max_delta_step=0,
			subsample=0.85,
			colsample_bytree=0.7,
			colsample_bylevel=1,
			reg_alpha=0,
			reg_lambda=1,
			scale_pos_weight=1,
			seed=1440,
			missing=None)
			
# 有了gridsearch咱们便不须要fit函数
gsearch = GridSearchCV(xlf, param_grid=parameters, scoring='accuracy', cv=3)
gsearch.fit(train_x, train_y)

print("Best score: %0.3f" % gsearch.best_score_)
print("Best parameters set:")
best_parameters = gsearch.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
    print("\t%s: %r" % (param_name, best_parameters[param_name]))

复制代码

咱们须要注意的是,Grid Search 须要交叉验证支持的。这里的cv=3,是个int数,就表明3-折验证。实际上cv能够是一个对象,也能够是其余类型。分别表明不一样的方式验证。具体的你们可看下面这段表述。

Possible inputs for cv are:
        - None, to use the default 3-fold cross-validation,
        - integer, to specify the number of folds.
        - An object to be used as a cross-validation generator.
        - An iterable yielding train/test splits.
复制代码

参考文献:

机器学习之-xgboost