如何以正确的方式做 repeatedKfold CV?

How to do repeatedKfold CV the right way?

我正在使用数据集大小为 977 条记录和 6 列的随机森林进行二进制 class化。 class 比率是 77:23(不平衡数据集)

由于我的数据集很小,我了解到不建议使用 70 和 30 的常规 train_test 拆分进行拆分。

所以,我想做repeatedKfold CV。请在下面找到我的代码

方法 1 - 完整数据 - X, y

rf_boruta = RandomForestClassifier(class_weight='balanced',max_depth=3,max_features='sqrt',n_estimators=300)
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=100)
scores = cross_val_score(rf_boruta,X,y, scoring='f1', cv=cv)
print('mean f1: %.3f' % mean(scores))

但我看到我们已将完整的输入数据 X 立即传递给模型。这不会导致数据泄露吗?意思是,如果我正在进行分类编码,我们必须根据完整数据集中遇到的所有类别进行编码。同样,考虑一个数据集的范围是否为 2017 年到 2022 年。模型有可能在其中一个折叠中使用 2021 年的数据,并在 2020 年的数据上对其进行验证。

那么,像下面这样使用repeatedKfold对吗?

方法 2 - 仅训练数据 - X_train、y_train

rf_boruta = RandomForestClassifier(class_weight='balanced',max_depth=3,max_features='sqrt',n_estimators=300)
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=100)
scores = cross_val_score(rf_boruta,X_train,y_train, scoring='f1', cv=cv)
print('mean f1: %.3f' % mean(scores))

可以帮助我了解哪种方法最好吗?

我会说有两种方法可以做到这一点。第一种方式是手动编写用于训练和验证的代码。这是它的代码示例:

scores = []
folds = RepeatedStratifiedKFold(n_splits=10, n_repeats=100)
for fold_n, (train_index, valid_index) in enumerate(folds.split(train, y, groups=train['breath_id'])):
    X_train, X_valid = X[columns].iloc[train_index], X[columns].iloc[valid_index]
    y_train, y_valid = y.iloc[train_index], y.iloc[valid_index]
    
    encoder = LabelEncoder()
    encoder.fit(X_train[:, 0])
    X_train[:, 0] = encoder.transform(X_train[:, 0])
    X_valid [:, 0] = encoder.transform(X_valid [:, 0])
    rf_boruta = RandomForestClassifier(class_weight='balanced',max_depth=3,max_features='sqrt',n_estimators=300)

    rf_boruta .fit(X_train, y_train)
    score = metrics.f1_score(y_valid, rf_boruta .predict(X_valid))
    
    scores.append(score)

第二种方法是使用 sklearn 中的 Pipeline:

import numpy as np
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score

# creating artificial data
X, y = make_classification(n_samples=1000, n_features=6, n_informative=4, n_redundant=2)
# making one of the column categorical
X[:, 0] = np.random.randint(0, 10, 1000)
# converting into DataFrame so that we can use column names
X = pd.DataFrame(X, columns = [str(i) for i in range(6)])

preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), ['1', '2']),
        ('cat', OneHotEncoder(), ['0']),
    ]
)                                  
                                  
rf = RandomForestClassifier(class_weight='balanced', max_depth=3, max_features='sqrt', n_estimators=300)
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=10)
pipe = Pipeline([('transformer', preprocessor), ('rf', rf)])
scores = cross_val_score(rf, X, y, scoring='f1', cv=cv)
print(f'Mean f1: {np.mean(scores):.3f}')