部分手动应用Pipeline和嵌套交叉验证,使用RFECV和GridSearch

我正在尝试以一种比在cross_val_score中使用GridSearchCV更加手动的方式实现嵌套交叉验证,这样我可以更好地控制我的操作,考虑数据泄漏,观察我的变量和超参数如何变化,并记录它们,同时也为了更清楚地理解一切是如何工作的。

我想问一下下面的实现看起来是否合理。

处理流程:数据泄漏

分类变量

cat_pipe = Pipeline([("encoder",OneHotEncoder(drop="first"))])

可能为分类的数值特征

num_pipe = Pipeline([("encoder",OneHotEncoder(drop="first"))])

连续变量

num_cont_pipe = Pipeline([("scaler",StandardScaler())])preprocessor = ColumnTransformer(transformers=[('cat', cat_pipe, cat_var),                                               ('num', num_pipe, num_cat_vars), "num_cont",num_cont_pipe,num_cont)],n_jobs=-1)#,sparse_threshold=0)

嵌套交叉验证

cv_outer = StratifiedKFold(n_splits=5, random_state=0)cv_inner = StratifiedKFold(n_splits=10,random_state=0) # RepeatedStratifiedKFold(n_repeats=3,n_splits=10,random_state=0)

估计器和模型

estimator = RandomForestClassifier(class_weight="balanced")model = XGBClassifier(eval_metric="logloss")

特征选择和超参数优化

rfe = RFECV(estimator,cv=cv_inner, scoring="roc_auc",n_jobs=-1)

实现

for train_ix, test_ix in cv_outer.split(X,y):    time_start_outer = time.time()    X_train = X.iloc[train_ix,:]    X_test = X.iloc[test_ix,:]    y_train = y.iloc[train_ix]    y_test = y.iloc[test_ix]    # 数据准备    X_train_enc = preprocessor.fit_transform(X_train)    X_test_enc = preprocessor.transform(X_test)            # 特征选择    X_train_enc_var = rfe.fit(X_train_enc,y_train)    print(f"The number of features selected is {X_train_enc_var.n_features_}", sep="/n")    X_train_enc_var = rfe.transform(X_train_enc)    X_test_enc_var = rfe.transform(X_test_enc)    # 超参数调优     counter = Counter(y_train)    weight = counter[0] / counter[1]    hyper_params = {        # XGBClassifier        "scale_pos_weight":[1,2,3,4,5,6,weight,7,8,9,10,11,12]        }    grid_search = GridSearchCV(model,cv=cv_inner,param_grid=hyper_params,n_jobs=-1,scoring="roc_auc")    X_train_enc_var_hyp = grid_search.fit(X_train_enc_var,y_train)    best_model = X_train_enc_var_hyp.best_estimator_    yhat = best_model.predict(X_test_enc_var)    # 评估模型    score = roc_auc_score(y_test, yhat)    # 存储结果    outer_results.append(score)    outer_params.append(X_train_enc_var_hyp.best_params_)    # 报告进度    print(f' test_score= {score}, validation_score= {X_train_enc_var_hyp.best_score_}')# 总结模型的估计性能print(f'best_score: {np.mean(outer_results)}, {np.std(outer_results)}')

回答:

我注意到了三件事。

首先,不重要:

    # 特征选择    X_train_enc_var = rfe.fit(X_train_enc,y_train)    print(f"The number of features selected is {X_train_enc_var.n_features_}", sep="/n")    X_train_enc_var = rfe.transform(X_train_enc)    X_test_enc_var = rfe.transform(X_test_enc)

您将拟合的rfe保存到X_train_enc_var中,然后在两行后将其覆盖为转换后的数据集。这一切都按您希望的方式进行,但为了更忠实于变量名称:

    X_train_enc_var = rfe.fit_transform(X_train_enc,y_train)    X_test_enc_var = rfe.transform(X_test_enc)    print(f"The number of features selected is {rfe.n_features_}", sep="/n")

其次,您对递归特征消除和网格搜索分别使用了相同的cv_inner。这意味着选定的特征数量包含了(内部)测试折叠的信息,因此网格搜索得分可能存在乐观偏见。这可能不是什么大问题,因为您只关心搜索的相对得分。但是,可能某些超参数组合在不同数量的特征下表现得更好,从而被惩罚了。

最后,最严重但也是最容易修复的问题:您调用best_model.predict(...),但由于您使用的是roc_auc_score,您应该改为使用best_model.predict_proba(...)[:, 1]来获取正类别的概率得分。

另一个建议:由于您进行了大量计算,请保存更多信息:例如rfe的grid_scores_和网格搜索的cv_results_

Related Posts

如何对SVC进行超参数调优?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

如何在初始训练后向模型添加训练数据?

我想在我的scikit-learn模型已经训练完成后再…

使用Google Cloud Function并行运行带有不同用户参数的相同训练作业

我正在寻找一种方法来并行运行带有不同用户参数的相同训练…

加载Keras模型,TypeError: ‘module’ object is not callable

我已经在StackOverflow上搜索并阅读了文档,…

在计算KNN填补方法中特定列中NaN值的”距离平均值”时

当我从头开始实现KNN填补方法来处理缺失数据时,我遇到…

使用巨大的S3 CSV文件或直接从预处理的关系型或NoSQL数据库获取数据的机器学习训练/测试工作

已关闭。此问题需要更多细节或更清晰的说明。目前不接受回…

发表回复

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