如何在 scikit-learn 中正确执行交叉验证?

Posted

技术标签:

【中文标题】如何在 scikit-learn 中正确执行交叉验证?【英文标题】:How to correctly perform cross validation in scikit-learn? 【发布时间】:2019-08-11 17:13:26 【问题描述】:

我正在尝试对 k-nn 分类器进行交叉验证,但我对以下两种方法中的哪一种正确进行交叉验证感到困惑。

training_scores = defaultdict(list)
validation_f1_scores = defaultdict(list)
validation_precision_scores = defaultdict(list)
validation_recall_scores = defaultdict(list)
validation_scores = defaultdict(list)

def model_1(seed, X, Y):
    np.random.seed(seed)
    scoring = ['accuracy', 'f1_macro', 'precision_macro', 'recall_macro']
    model = KNeighborsClassifier(n_neighbors=13)

    kfold = StratifiedKFold(n_splits=2, shuffle=True, random_state=seed)
    scores = model_selection.cross_validate(model, X, Y, cv=kfold, scoring=scoring, return_train_score=True)
    print(scores['train_accuracy'])
    training_scores['KNeighbour'].append(scores['train_accuracy'])
    print(scores['test_f1_macro'])
    validation_f1_scores['KNeighbour'].append(scores['test_f1_macro'])
    print(scores['test_precision_macro'])
    validation_precision_scores['KNeighbour'].append(scores['test_precision_macro'])
    print(scores['test_recall_macro'])
    validation_recall_scores['KNeighbour'].append(scores['test_recall_macro'])
    print(scores['test_accuracy'])
    validation_scores['KNeighbour'].append(scores['test_accuracy'])

    print(np.mean(training_scores['KNeighbour']))
    print(np.std(training_scores['KNeighbour']))
    #rest of print statments

看来第二个模型中的for循环是多余的。

def model_2(seed, X, Y):
    np.random.seed(seed)
    scoring = ['accuracy', 'f1_macro', 'precision_macro', 'recall_macro']
    model = KNeighborsClassifier(n_neighbors=13)

    kfold = StratifiedKFold(n_splits=2, shuffle=True, random_state=seed)
    for train, test in kfold.split(X, Y):
        scores = model_selection.cross_validate(model, X[train], Y[train], cv=kfold, scoring=scoring, return_train_score=True)
        print(scores['train_accuracy'])
        training_scores['KNeighbour'].append(scores['train_accuracy'])
        print(scores['test_f1_macro'])
        validation_f1_scores['KNeighbour'].append(scores['test_f1_macro'])
        print(scores['test_precision_macro'])
        validation_precision_scores['KNeighbour'].append(scores['test_precision_macro'])
        print(scores['test_recall_macro'])
        validation_recall_scores['KNeighbour'].append(scores['test_recall_macro'])
        print(scores['test_accuracy'])
        validation_scores['KNeighbour'].append(scores['test_accuracy'])

    print(np.mean(training_scores['KNeighbour']))
    print(np.std(training_scores['KNeighbour']))
    # rest of print statments

我正在使用 StratifiedKFold,但我不确定是否需要像 model_2 函数中那样的循环,或者 cross_validate 函数是否已经使用拆分,因为我们将 cv=kfold 作为参数传递。

我没有调用fit 方法,这样可以吗? cross_validate 会自动调用,还是我需要在调用cross_validate 之前调用fit

最后,如何创建混淆矩阵?我是否需要为每个折叠创建它,如果是,如何计算最终/平均混淆矩阵?

【问题讨论】:

【参考方案1】:

documentation 可以说是此类问题中你最好的朋友;从那里的简单示例可以看出,您既不应该使用for 循环也不应该调用fit。像你一样调整示例以使用KFold

from sklearn.model_selection import KFold, cross_validate
from sklearn.datasets import load_boston
from sklearn.tree import DecisionTreeRegressor

X, y = load_boston(return_X_y=True)
n_splits = 5
kf = KFold(n_splits=n_splits, shuffle=True)

model = DecisionTreeRegressor()
scoring=('r2', 'neg_mean_squared_error')

cv_results = cross_validate(model, X, y, cv=kf, scoring=scoring, return_train_score=False)
cv_results

结果:

'fit_time': array([0.00901461, 0.00563478, 0.00539804, 0.00529385, 0.00638533]),
 'score_time': array([0.00132656, 0.00214362, 0.00134897, 0.00134444, 0.00176597]),
 'test_neg_mean_squared_error': array([-11.15872549, -30.1549505 , -25.51841584, -16.39346535,
        -15.63425743]),
 'test_r2': array([0.7765484 , 0.68106786, 0.73327311, 0.83008371, 0.79572363])

如何创建混淆矩阵?我需要为每个折叠创建它吗

没有人可以告诉您是否需要为每个折叠创建混淆矩阵 - 这是您的选择。如果您选择这样做,最好跳过 cross_validate 并“手动”执行该过程 - 请参阅我在 How to display confusion matrix and report (recall, precision, fmeasure) for each cross validation fold 中的回答。

如果是,如何计算最终/平均混淆矩阵?

没有“最终/平均”混淆矩阵;如果您想计算比链接答案中描述的 k 更远的任何东西(每个 k 折一个),您需要有一个单独的验证集......

【讨论】:

如果我想为每个折叠显示混淆矩阵,你将如何做到这一点,因为在我的情况下我使用的是 cross_validate 所以我无权访问confusion_matrix(y[val_index], pred)),如你的链接所示。因为 cross_validate 正在创建折叠。 @learner 你的意思是你在使用cross_validate有界?因为链接的答案是最直接的方法,并且与您当前的方法相比,您不会“失去”任何东西(分数等) - 它实际上是您的 model_2 方法的(正确)修改;无论如何,你可以检查cross_val_predict【参考方案2】:

model_1 是正确的。

https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_validate.html

cross_validate(estimator, X, y=None, groups=None, scoring=None, cv=’warn’, n_jobs=None, verbose=0, fit_params=None, pre_dispatch=‘2*n_jobs’, return_train_score=’warn’, return_estimator=False, error_score=’raise-deprecating’)

在哪里

estimator 是一个实现“fit”的对象。它将被调用以将模型拟合到火车折叠上。

cv: 是一个交叉验证生成器,用于生成训练和测试分割。

如果您按照 sklearn 文档中的示例进行操作

cv_results = cross_validate(lasso, X, y, cv=3, return_train_score=False) cv_results['test_score'] array([0.33150734, 0.08022311, 0.03531764])

您可以看到模型 lasso 在火车拆分中的每个折叠拟合了 3 次,并且还在测试拆分中验证了 3 次。您可以看到报告了验证数据的测试分数。

Keras 模型的交叉验证

Keras 提供了包装器,使 keras 模型与 sklearn cross_validation 方法兼容。您必须使用 KerasClassifier 包装 keras 模型

from keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import KFold, cross_validate
from keras.models import Sequential
from keras.layers import Dense
import numpy as np

def get_model():
    model = Sequential()
    model.add(Dense(2, input_dim=2, activation='relu')) 
    model.add(Dense(1, activation='sigmoid'))
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

model = KerasClassifier(build_fn=get_model, epochs=10, batch_size=8, verbose=0)
kf = KFold(n_splits=3, shuffle=True)

X = np.random.rand(10,2)
y = np.random.rand(10,1)

cv_results = cross_validate(model, X, y, cv=kf, return_train_score=False)

print (cv_results)

【讨论】:

以上是关于如何在 scikit-learn 中正确执行交叉验证?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 scikit-learn 中执行随机森林模型的交叉验证?

如何在 scikit-learn 中使用交叉验证获得预测概率

如何在 Scikit-Learn 中绘制超过 10 倍交叉验证的 PR 曲线

如何在 scikit-learn 中使用 k 折交叉验证来获得每折的精确召回?

如何创建一个应用 z-score 和交叉验证的 scikit-learn 管道?

如何在交叉验证中获得 Keras scikit-learn 包装器的训练和验证损失?