如何以正确的方式做 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}')
我正在使用数据集大小为 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}')