GridSearchCV未选择xgboost的最佳超参数

3

我目前正在使用xgboost开发回归模型。由于xgboost有多个超参数,我已经使用GridSearchCV()添加了交叉验证逻辑。作为一次尝试,我设置了max_depth:[2,3]。我的Python代码如下。

from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer
from sklearn.metrics import mean_squared_error
​
xgb_reg = xgb.XGBRegressor()
​
# Obtain the best hyper parameter
scorer=make_scorer(mean_squared_error, False)
params = {'max_depth': [2,3], 
          'eta': [0.1], 
          'colsample_bytree': [1.0],
          'colsample_bylevel': [0.3],
          'subsample': [0.9],
          'gamma': [0],
          'lambda': [1],
          'alpha':[0],
          'min_child_weight':[1]
         }
grid_xgb_reg=GridSearchCV(xgb_reg,
                          param_grid=params,
                          scoring=scorer,
                          cv=5,
                          n_jobs=-1)
​
grid_xgb_reg.fit(X_train, y_train)
y_pred = grid_xgb_reg.predict(X_test)
y_train_pred = grid_xgb_reg.predict(X_train)

## Evaluate model
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
​
print('RMSE  train: %.3f,  test: %.3f' %(np.sqrt(mean_squared_error(y_train, y_train_pred)),np.sqrt(mean_squared_error(y_test, y_pred))))
print('R^2   train: %.3f,  test: %.3f' %(r2_score(y_train, y_train_pred),r2_score(y_test, y_pred)))

问题在于GridSearchCV似乎无法选择最佳的超参数。在我的情况下,当我将max_depth设置为[2,3]时,结果如下所示。在以下情况中,GridSearchCV选择了max_depth:2作为最佳超参数。
#  The result when max_depth is 2
RMSE  train: 11.861,  test: 15.113
R^2   train: 0.817,  test: 0.601

然而,如果我更新max_depth[3](通过删除2),则测试分数如下更好。

#  The result when max_depth is 3
RMSE  train: 9.951,  test: 14.752
R^2   train: 0.871,  test: 0.620

问题

我的理解是,即使我将 max_depth 设置为 [2,3]GridSearchCV 方法仍应该选择 max_depth:3 作为最佳超参数,因为相对于 max_depth:2max_depth:3 可以返回更好的RSME或R^2得分。有人可以告诉我为什么当我将max_depth设置为[2,3]时,我的代码无法选择最佳的超参数吗?


网格搜索根据其内部交叉验证得分(请查看其属性'cv_results_')选择最佳超参数;然而,赢家并不能保证在新的测试集上表现最佳。 - Ben Reiniger
1个回答

4
如果您运行第二个实验,使用max_depth:2,则即使对于max_depth:[2,3]的第一个实验,结果也是不可比较的,因为您的代码存在一些随机性,您无法明确控制,即您的代码不具有可重复性。
第一个随机源是CV折叠;为了确保实验在相同的数据拆分上运行,您应该按以下方式定义GridSearchCV:
from sklearn.model_selection import KFold

seed_cv = 123 # any random value here

kf = KFold(n_splits=5, random_state=seed_cv)

grid_xgb_reg=GridSearchCV(xgb_reg,
                          param_grid=params,
                          scoring=scorer,
                          cv=kf,   # <- change here
                          n_jobs=-1)

第二个随机源是XGBRegressor本身,它还包括一个random_state参数(请参阅文档)。您应该将其更改为:
seed_xgb = 456 # any random value here (can even be the same with seed_cv)
xgb_reg = xgb.XGBRegressor(random_state=seed_xgb)

即使有这些安排,虽然您的数据拆分现在将是相同的,但构建的回归模型在一般情况下不一定如此;如果您像这样保持实验,即首先使用max_depth:[2,3],然后使用max_depth:2,结果确实会相同;但是,如果您将其更改为max_depth:[2,3],然后再使用max_depth:3,它们将不会,因为在第一个实验中,运行max_depth:3将以随机数生成器的不同状态开始(即在运行max_depth:2完成后)。
在这种情况下,您可以使不同运行变得多么相同存在限制;关于非常微妙的差异的例子,尽管它破坏了两个实验之间的精确可重复性,请参见我的答案Why does the importance parameter influence performance of Random Forest in R?

谢谢!你是在说如果我使用 GridSearchCV 运行 max_depth:[2,3],那么 max_depth:3 的结果会受到之前 max_depth:2 结果的影响,所以最佳超参数将变为 max_depth:2,对吗?如果是这样,我想要让每个结果都一致。在这种情况下,我应该像你在另一个主题中解释的那样运行 for 循环并进行手动 GridSearch 吗? - shumach5
@shumach5,我并没有说什么是最佳超参数;我只是说,与使用 max_depth:3 运行单个运行的结果不会完全相同,并解释了原因。不幸的是,不是所有的实验都可以在这个意义上完全可比,正如我所提到的 - 随机性从太多的角度进入。 - desertnaut
谢谢您的回答!所以我错过的关键因素是结果并不相同。 - shumach5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接