XGBoost 与 GridSearchCV、缩放、PCA 和 sklearn 管道中的 Early-Stopping

Posted

技术标签:

【中文标题】XGBoost 与 GridSearchCV、缩放、PCA 和 sklearn 管道中的 Early-Stopping【英文标题】:XGBoost with GridSearchCV, Scaling, PCA, and Early-Stopping in sklearn Pipeline 【发布时间】:2018-11-22 06:34:30 【问题描述】:

我想将 XGBoost 模型与 PCA 的输入缩放和特征空间缩减相结合。此外,模型的超参数以及 PCA 中使用的组件数量应使用交叉验证进行调整。并且为了防止模型过度拟合,应该添加提前停止。

为了结合各个步骤,我决定使用 sklearn 的 Pipeline 功能。

一开始,我在确保 PCA 也应用于验证集时遇到了一些问题。但我认为使用XGB__eval_set 可以达成交易。

代码实际上运行没有任何错误,但似乎永远运行(在某些时候所有内核的 CPU 使用率下降到零,但进程继续运行数小时;不得不在某个时候终止会话)。

from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from xgboost import XGBRegressor   

# Train / Test split
X_train, X_test, y_train, y_test = train_test_split(X_with_features, y, test_size=0.2, random_state=123)

# Train / Validation split
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=123)

# Pipeline
pipe = Pipeline(steps=[("Scale", StandardScaler()),
                       ("PCA", PCA()),
                       ("XGB", XGBRegressor())])

# Hyper-parameter grid (Test only)
grid_param_pipe = 'PCA__n_components': [5],
                   'XGB__n_estimators': [1000],
                   'XGB__max_depth': [3],
                   'XGB__reg_alpha': [0.1],
                   'XGB__reg_lambda': [0.1]

# Grid object
grid_search_pipe = GridSearchCV(estimator=pipe,
                                param_grid=grid_param_pipe,
                                scoring="neg_mean_squared_error",
                                cv=5,
                                n_jobs=5,
                                verbose=3)

# Run CV
grid_search_pipe.fit(X_train, y_train, XGB__early_stopping_rounds=10, XGB__eval_metric="rmse", XGB__eval_set=[[X_val, y_val]])

【问题讨论】:

将管道转换应用于验证集以提前停止似乎并非易事,我怀疑仅XGB__eval_set 就足够了。请参阅此 sklearn 问题 github.com/scikit-learn/scikit-learn/issues/8414,了解管道步骤子集的建议应用 pop 关闭最后一个管道步骤(分类器)并不难,在您的数据上调用 transform,然后重新附加分类器。挑战在于使用 CV 来完成,而您的早期停止集不是您的验证集。这可能需要自定义GridSearchCV 【参考方案1】:

问题是fit方法需要一个外部创建的评估集,但是我们不能在管道转换之前创建一个。

这有点老套,但想法是为 xgboost 回归器/分类器创建一个瘦包装器,为内部的评估集做准备。

from sklearn.base import BaseEstimator
from sklearn.model_selection import train_test_split
from xgboost import XGBRegressor, XGBClassifier

class XGBoostWithEarlyStop(BaseEstimator):
    def __init__(self, early_stopping_rounds=5, test_size=0.1, 
                 eval_metric='mae', **estimator_params):
        self.early_stopping_rounds = early_stopping_rounds
        self.test_size = test_size
        self.eval_metric=eval_metric='mae'        
        if self.estimator is not None:
            self.set_params(**estimator_params)

    def set_params(self, **params):
        return self.estimator.set_params(**params)

    def get_params(self, **params):
        return self.estimator.get_params()

    def fit(self, X, y):
        x_train, x_val, y_train, y_val = train_test_split(X, y, test_size=self.test_size)
        self.estimator.fit(x_train, y_train, 
                           early_stopping_rounds=self.early_stopping_rounds, 
                           eval_metric=self.eval_metric, eval_set=[(x_val, y_val)])
        return self

    def predict(self, X):
        return self.estimator.predict(X)

class XGBoostRegressorWithEarlyStop(XGBoostWithEarlyStop):
    def __init__(self, *args, **kwargs):
        self.estimator = XGBRegressor()
        super(XGBoostRegressorWithEarlyStop, self).__init__(*args, **kwargs)

class XGBoostClassifierWithEarlyStop(XGBoostWithEarlyStop):
    def __init__(self, *args, **kwargs):
        self.estimator = XGBClassifier()
        super(XGBoostClassifierWithEarlyStop, self).__init__(*args, **kwargs)

下面是一个测试。

from sklearn.datasets import load_diabetes
from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA
from sklearn.model_selection import GridSearchCV

x, y = load_diabetes(return_X_y=True)
print(x.shape, y.shape)
# (442, 10) (442,)

pipe = Pipeline([
    ('pca', PCA(5)),
    ('xgb', XGBoostRegressorWithEarlyStop())
])

param_grid = 
    'pca__n_components': [3, 5, 7],
    'xgb__n_estimators': [10, 20, 30, 50]


grid = GridSearchCV(pipe, param_grid, scoring='neg_mean_absolute_error')
grid.fit(x, y)
print(grid.best_params_)

如果向开发人员请求功能请求,最简单的扩展是允许XGBRegressor 在未提供时在内部创建评估集。这样,就不需要对 scikit-learn 进行扩展(我猜)。

【讨论】:

如果想在同一管道中使用套索或随机森林,或者一般情况下的另一种预测模型,该怎么办? @Kota Mori,我不太确定您介绍的这个类是否适用于早期停止的交叉验证。假设你想做 5 Fold CV。在第一轮中,我们将前 4 折作为训练,我们将模型拟合到它们上,并评估/验证最后一折的模型以获得分数。在您的班级中,您进行了另一个训练测试拆分(您班级的 fit 函数中的 test_size=0.1),基本上您在前 4 折的 10% 内评估模型,而不是使用最后一折来验证模型。正确的?如果我在这里误解了某事,请纠正我。

以上是关于XGBoost 与 GridSearchCV、缩放、PCA 和 sklearn 管道中的 Early-Stopping的主要内容,如果未能解决你的问题,请参考以下文章

与 xgboost.cv 相比,GridSearchCV 没有给出与预期相同的结果

GridSearchCV 中的 xgboost 轮数

如何从 GridSearchCV 输出可视化 XGBoost 树?

Pipeline 和 GridSearchCV,以及 XGBoost 和 RandomForest 的多类挑战

Python Xgboost GridSearchCV 被杀,如何修复?

XGBoost模型调参:GridSearchCV方法网格搜索优化参数