XGBoost和hyperopt的交叉验证和参数调优

使用XGB模型进行嵌套交叉验证的一种方法是:

from sklearn.model_selection import GridSearchCV, cross_val_scorefrom xgboost import XGBClassifier# 假设我们有用于二分类问题的某些数据:X (n_samples, n_features) 和 y (n_samples,)...gs = GridSearchCV(estimator=XGBClassifier(),                   param_grid={'max_depth': [3, 6, 9],                               'learning_rate': [0.001, 0.01, 0.05]},                   cv=2)scores = cross_val_score(gs, X, y, cv=2)

然而,关于XGB参数的调优,有几个教程(例如这个)利用了Python的hyperopt库。我希望能够使用hyperopt来调优XGB参数(如上所述)进行嵌套交叉验证。

为此,我编写了自己的Scikit-Learn估计器:

from hyperopt import fmin, tpe, hp, Trials, STATUS_OKfrom sklearn.base import BaseEstimator, ClassifierMixinfrom sklearn.model_selection import train_test_splitfrom sklearn.exceptions import NotFittedErrorfrom sklearn.metrics import roc_auc_scorefrom xgboost import XGBClassifierdef optimize_params(X, y, params_space, validation_split=0.2):     """估计一组'最佳'模型参数。"""     # 将X, y拆分为训练/验证集     X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=validation_split, stratify=y)    # 估计XGB参数    def objective(_params):        _clf = XGBClassifier(n_estimators=10000,                             max_depth=int(_params['max_depth']),                             learning_rate=_params['learning_rate'],                             min_child_weight=_params['min_child_weight'],                             subsample=_params['subsample'],                             colsample_bytree=_params['colsample_bytree'],                             gamma=_params['gamma'])        _clf.fit(X_train, y_train,                 eval_set=[(X_train, y_train), (X_val, y_val)],                 eval_metric='auc',                 early_stopping_rounds=30)        y_pred_proba = _clf.predict_proba(X_val)[:, 1]        roc_auc = roc_auc_score(y_true=y_val, y_score=y_pred_proba)        return {'loss': 1. - roc_auc, 'status': STATUS_OK}    trials = Trials()    return fmin(fn=objective,                space=params_space,                algo=tpe.suggest,                max_evals=100,                trials=trials,                verbose=0)class OptimizedXGB(BaseEstimator, ClassifierMixin):    """具有优化参数的XGB。    参数    ----------    custom_params_space : dict or None        如果不为None,则为字典,其键为要优化的XGB参数,相应的值为给定参数值的'先验'概率分布。如果为None,则使用默认参数空间。    """    def __init__(self, custom_params_space=None):        self.custom_params_space = custom_params_space    def fit(self, X, y, validation_split=0.3):        """训练一个XGB模型。        参数        ----------        X : ndarray, shape (n_samples, n_features)            数据。        y : ndarray, shape (n_samples,) or (n_samples, n_labels)            标签。        validation_split : float (default: 0.3)            0到1之间的浮点数。对应于X中将用于估计'最佳'模型参数的验证数据的样本百分比。        """        # 如果没有给出自定义参数空间,则使用默认的。        if self.custom_params_space is None:            _space = {                'learning_rate': hp.uniform('learning_rate', 0.0001, 0.05),                'max_depth': hp.quniform('max_depth', 8, 15, 1),                'min_child_weight': hp.quniform('min_child_weight', 1, 5, 1),                'subsample': hp.quniform('subsample', 0.7, 1, 0.05),                'gamma': hp.quniform('gamma', 0.9, 1, 0.05),                'colsample_bytree': hp.quniform('colsample_bytree', 0.5, 0.7, 0.05)            }        else:            _space = self.custom_params_space        # 使用X, y估计最佳参数        opt = optimize_params(X, y, _space, validation_split)        # 使用优化后的参数实例化`xgboost.XGBClassifier`        best = XGBClassifier(n_estimators=10000,                             max_depth=int(opt['max_depth']),                             learning_rate=opt['learning_rate'],                             min_child_weight=opt['min_child_weight'],                             subsample=opt['subsample'],                             gamma=opt['gamma'],                             colsample_bytree=opt['colsample_bytree'])        best.fit(X, y)        self.best_estimator_ = best        return self    def predict(self, X):        """使用训练的XGB模型预测标签。        参数        ----------        X : ndarray, shape (n_samples, n_features)        返回        -------        output : ndarray, shape (n_samples,) or (n_samples, n_labels)        """        if not hasattr(self, 'best_estimator_'):            raise NotFittedError('在`predict`之前调用`fit`。')        else:            return self.best_estimator_.predict(X)    def predict_proba(self, X):        """使用训练的XGB模型预测标签概率。        参数        ----------        X : ndarray, shape (n_samples, n_features)        返回        -------        output : ndarray, shape (n_samples,) or (n_samples, n_labels)        """        if not hasattr(self, 'best_estimator_'):            raise NotFittedError('在`predict_proba`之前调用`fit`。')        else:            return self.best_estimator_.predict_proba(X)

我的问题是:

  • 这是一种有效的方法吗?例如,在我的OptimizedXGBfit方法中,best.fit(X, y)将会在X, y上训练一个XGB模型。然而,由于没有指定eval_set来确保提前停止,这可能会导致过拟合。
  • 在一个玩具示例中(著名的iris数据集),这个OptimizedXGB的表现比一个基本的LogisticRegression分类器要差。这是为什么呢?是因为示例过于简单吗?请看下面的示例代码。

示例:

import numpy as npfrom sklearn.datasets import load_irisfrom sklearn.linear_model import LogisticRegressionfrom sklearn.model_selection import GridSearchCV, cross_val_score, StratifiedKFoldfrom sklearn.pipeline import Pipelinefrom sklearn.preprocessing import StandardScalerX, y = load_iris(return_X_y=True)X = X[:, :2]X = X[y < 2]y = y[y < 2]skf = StratifiedKFold(n_splits=2, random_state=42)# 使用LogisticRegression分类器pipe = Pipeline([('scaler', StandardScaler()), ('lr', LogisticRegression())])gs = GridSearchCV(estimator=pipe, param_grid={'lr__C': [1., 10.]})lr_scores = cross_val_score(gs, X, y, cv=skf)# 使用OptimizedXGBxgb_scores = cross_val_score(OptimizedXGB(), X, y, cv=skf)# 打印结果print('使用LogisticRegression的准确率 = %.4f (+/- %.4f)' % (np.mean(lr_scores), np.std(lr_scores)))print('使用OptimizedXGB的准确率 = %.4f (+/- %.4f)' % (np.mean(xgb_scores), np.std(xgb_scores)))

输出:

使用LogisticRegression的准确率 = 0.9900 (+/- 0.0100)使用OptimizedXGB的准确率 = 0.9100 (+/- 0.0300)

尽管得分接近,但我本以为XGB模型至少应该和LogisticRegression分类器表现一样好。

编辑:


回答:

首先,查看这个帖子 – 可能会有所帮助 – 嵌套CV

关于你的问题:

  1. 是的,这是正确的方法。一旦你选择了超参数,你应该在整个训练数据上拟合你的模型(选定的模型)。然而,由于这个模型内部包含了一个模型选择过程,你只能使用外部CV来“评分”它的泛化能力,就像你所做的那样。
  2. 由于你也在评分选择过程(而不仅仅是模型,比如XGB与线性回归),选择过程可能存在一些问题。也许你的超参数空间没有正确定义,你选择了不佳的参数?

Related Posts

Flatten and back keras

我正在尝试使用自编码器获取简单向量中的值 这是我的代码…

如何按索引访问PyTorch模型参数

如果我的网络有10层,包括偏置项,如何仅通过索引访问第…

Python中多元逻辑回归显示错误

我试图使用逻辑回归进行预测,并使用Python和skl…

在MACOS上安装NLTK

我在我的2015款Mac Pro上尝试安装NLTK,操…

如何在R中将通过RFE选择的变量插入到机器学习模型中?

我想使用递归特征消除方法来选择最重要的特征,然后将这些…

CountVectorizer 错误:ValueError: setting an array element with a sequence

我有一个包含144条学生反馈的数据集,其中有72条正面…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注