使用 RandomizedSearchCV 调整 XGBoost 超参数

Tuning XGBoost Hyperparameters with RandomizedSearchCV

我正在尝试将 XGBoost 用于包含大约 500,000 个观察值和 10 个特征的特定数据集。我正在尝试使用 RandomizedSeachCV 进行一些超参数调整,但具有最佳参数的模型的性能比具有默认参数的模型差。

具有默认参数的模型:

model = XGBRegressor()
model.fit(X_train,y_train["speed"])
y_predict_speed = model.predict(X_test)

from sklearn.metrics import r2_score
print("R2 score:", r2_score(y_test["speed"],y_predict_speed, multioutput='variance_weighted'))
R2 score: 0.3540656307310167

随机搜索的最佳模型:

booster=['gbtree','gblinear']
base_score=[0.25,0.5,0.75,1]

## Hyper Parameter Optimization
n_estimators = [100, 500, 900, 1100, 1500]
max_depth = [2, 3, 5, 10, 15]
booster=['gbtree','gblinear']
learning_rate=[0.05,0.1,0.15,0.20]
min_child_weight=[1,2,3,4]

# Define the grid of hyperparameters to search
hyperparameter_grid = {
    'n_estimators': n_estimators,
    'max_depth':max_depth,
    'learning_rate':learning_rate,
    'min_child_weight':min_child_weight,
    'booster':booster,
    'base_score':base_score
    }

# Set up the random search with 4-fold cross validation
random_cv = RandomizedSearchCV(estimator=regressor,
            param_distributions=hyperparameter_grid,
            cv=5, n_iter=50,
            scoring = 'neg_mean_absolute_error',n_jobs = 4,
            verbose = 5, 
            return_train_score = True,
            random_state=42)

random_cv.fit(X_train,y_train["speed"])

random_cv.best_estimator_

XGBRegressor(base_score=0.5, booster='gblinear', colsample_bylevel=None,
             colsample_bynode=None, colsample_bytree=None, gamma=None,
             gpu_id=-1, importance_type='gain', interaction_constraints=None,
             learning_rate=0.15, max_delta_step=None, max_depth=15,
             min_child_weight=3, missing=nan, monotone_constraints=None,
             n_estimators=500, n_jobs=16, num_parallel_tree=None,
             random_state=0, reg_alpha=0, reg_lambda=0, scale_pos_weight=1,
             subsample=None, tree_method=None, validate_parameters=1,
             verbosity=None)

使用最佳模型:

regressor = XGBRegressor(base_score=0.5, booster='gblinear', colsample_bylevel=None,
             colsample_bynode=None, colsample_bytree=None, gamma=None,
             gpu_id=-1, importance_type='gain', interaction_constraints=None,
             learning_rate=0.15, max_delta_step=None, max_depth=15,
             min_child_weight=3, monotone_constraints=None,
             n_estimators=500, n_jobs=16, num_parallel_tree=None,
             random_state=0, reg_alpha=0, reg_lambda=0, scale_pos_weight=1,
             subsample=None, tree_method=None, validate_parameters=1,
             verbosity=None)

regressor.fit(X_train,y_train["speed"])
y_pred = regressor.predict(X_test)

from sklearn.metrics import r2_score
print("R2 score:", r2_score(y_test["speed"],y_pred, multioutput='variance_weighted'))

R2 score: 0.14258774171629718

正如您所见,经过 运行 3 小时的随机搜索后,准确性实际上下降了。如果我将线性更改为树,该值会上升到 0.65,那么为什么随机搜索不起作用?

我还收到以下警告:

This may not be accurate due to some parameters are only used in language bindings but passed down to XGBoost core. Or some parameters are not used but slip through this verification. Please open an issue if you find above cases.

有人对这种超参数调整方法有什么建议吗?

XGBoost Docs

中所述

Parameter tuning is a dark art in machine learning, the optimal parameters of a model can depend on many scenarios.

您要求针对您的特定场景提出建议,所以这是我的一些建议。

  1. 从超参数搜索 space 中删除维度 booster。您可能想使用默认的助推器 'gbtree'。 如果您对线性模型的性能感兴趣,您可以尝试 linear or ridge 回归,但在 XGBoost 参数调整期间不要理会它。
  2. 从超参数搜索 space 中删除维度 base_score。 这应该不会对足够多的提升迭代产生太大影响(参见 XGB parameter docs)。
  3. 目前您的网格中有 3200 个超参数组合。期望通过随机查看 50 个来找到一个好的可能有点过于乐观。删除 boosterbase_score 维度后,您将下降到
hyperparameter_grid = {
    'n_estimators': [100, 500, 900, 1100, 1500],
    'max_depth': [2, 3, 5, 10, 15],
    'learning_rate': [0.05, 0.1, 0.15, 0.20],
    'min_child_weight': [1, 2, 3, 4]
    }

其中有 400 种可能的组合。 对于第一枪,我会进一步简化它。例如,您可以尝试

hyperparameter_grid = {
    'n_estimators': [100, 400, 800],
    'max_depth': [3, 6, 9],
    'learning_rate': [0.05, 0.1, 0.20],
    'min_child_weight': [1, 10, 100]
    }

只剩下 81 种组合,并且删除了一些非常昂贵的组合(例如深度为 15 的 1500 棵树)。 当然我不知道你的数据,所以也许有必要考虑这么大/复杂的模型。 对于具有平方损失的回归任务,min_child_weight 只是 child 中的实例数(再次参见 XGB parameter docs)。 由于您有 500000 个观察值,因此 1、2、3 或 4 个观察值最终出现在一片叶子中可能不会产生(很大)差异。因此,我在这里建议[1, 10, 100]。 也许随机搜索会在这个网格中找到比默认参数更好的东西?

  1. 另一种策略可以是: 运行 每个组合的交叉验证
hyperparameter_grid = {
    'max_depth': [3, 6, 9],
    'min_child_weight': [1, 10, 100]
    }

将学习率固定在某个恒定值(不要太低,例如 0.15)。对于每个设置,使用提前停止来确定适当数量的树。这可以使用 xgboost.cv 方法的 early_stopping_rounds 参数。 之后你知道 max_depthmin_child_weight 的良好组合(例如,对于给定的问题,基础学习器需要多复杂?)以及这种组合的大量树和固定的学习率. 然后,微调可能涉及“接近”当前(max_depth、min_child_weight)解决方案的另一个超参数搜索and/or 降低学习率,同时增加树的数量。

  1. 最后,由于答案有点长,如果详尽的网格搜索过于昂贵,还有其他方法可以替代随机搜索。例如。你可以看看 halving grid search and sequential model based optimization.