Scikit learn 中的交叉验证与网格搜索

Posted

技术标签:

【中文标题】Scikit learn 中的交叉验证与网格搜索【英文标题】:Cross validation vs. grid search in Scikit learn 【发布时间】:2017-12-29 19:59:06 【问题描述】:

我正在使用sklearn.model_selection.GridSearchCVsklearn.model_selection.cross_val_score,但在这样做的过程中我遇到了意想不到的结果。

在我的示例中,我使用以下导入:

from sklearn.datasets import make_classification
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
from sklearn.model_selection import cross_val_score
from sklearn.metrics import make_scorer
from sklearn.metrics import recall_score
from sklearn.model_selection import GridSearchCV
import numpy as np

首先,我创建一个随机数据集:

X, y = make_classification(n_samples=1000, n_features=20, random_state=42)

接下来,我定义管道“生成器”:

def my_pipeline(C=None):
    if C is None:
        return Pipeline(
            [
                ('step1', StandardScaler()),
                ('clf', LinearSVC(random_state=42))
            ])
    else:
        return Pipeline(
            [
                ('step1', StandardScaler()),
                ('clf', LinearSVC(C=C, random_state=42))
            ])        

接下来,我设置了几个C 进行测试:

Cs = [0.01, 0.1, 1, 2, 5, 10, 50, 100]

最后,我想检查一下可以得到的最大recall_score 是多少。一次,我使用cross_val_score 进行,一次直接使用GridSearchCV

np.max(
    [
        np.mean(
            cross_val_score(my_pipeline(C=c), X, y,
                            cv=3, 
                            scoring=make_scorer(recall_score)
    )) for c in Cs])

和:

GridSearchCV(
    my_pipeline(),
    
        'clf__C': Cs
    ,
    scoring=make_scorer(recall_score),
    cv=3
).fit(X, y).best_score_)

在我的示例中,前者产生0.85997883750571147,后者产生0.85999999999999999。我期望值是相同的。我错过了什么?

我也把它全部放在gist 中。

编辑:修复cv。我用StratifiedKFold(n_splits=3, random_state=42) 替换了cv=3,结果没有改变。事实上,cv 似乎不会影响结果。

【问题讨论】:

这里的一个非常快速的第一个猜测是,它与随机数生成器的状态有关,即数据被分成折叠以进行交叉验证。如果同时修复GridSearchCVcross_val_score 中的random_state 会发生什么? 猜对了,但是……错了。 @AngusWilliams 查看更新。 【参考方案1】:

在我看来,这似乎是一个精度问题。如果您查看完整的分数列表,那么对于cross_val_score,您会得到以下信息:

[0.85193468484717316,
 0.85394271697568724,
 0.85995478921674717,
 0.85995478921674717,
 0.8579467570882332,
 0.86195079720077905,
 0.81404660558401265,
 0.82201861337565829]

对于GridSearchCV,您会得到以下信息

[mean: 0.85200, std: 0.02736, params: 'clf__C': 0.01,
 mean: 0.85400, std: 0.02249, params: 'clf__C': 0.1,
 mean: 0.86000, std: 0.01759, params: 'clf__C': 1,
 mean: 0.86000, std: 0.01759, params: 'clf__C': 2,
 mean: 0.85800, std: 0.02020, params: 'clf__C': 5,
 mean: 0.86200, std: 0.02275, params: 'clf__C': 10,
 mean: 0.81400, std: 0.01916, params: 'clf__C': 50,
 mean: 0.82200, std: 0.02296, params: 'clf__C': 100]

因此,每对对应的分数基本上几乎相同,只是精度差异很小(似乎GridSearchCV 中的分数是四舍五入的)。

【讨论】:

确实,引擎盖下似乎有一些舍入。我在 the code 中找不到它的记录。

以上是关于Scikit learn 中的交叉验证与网格搜索的主要内容,如果未能解决你的问题,请参考以下文章

在 SciKit-Learn 中使用 XGBoost 交叉验证进行网格搜索和提前停止

使用 Scikit-Learn GridSearchCV 与 PredefinedSplit 进行交叉验证 - 可疑的交叉验证结果

带有 SVM 回归的 Scikit-learn 网格搜索

scikit-learn 中自定义内核 SVM 的交叉验证

使用 scikit-learn 进行递归特征消除和网格搜索

使用 scikit-learn 进行递归特征消除和网格搜索:DeprecationWarning