【问题标题】:Difference between cross_val_score and cross_val_predictcross_val_score 和 cross_val_predict 的区别
【发布时间】:2017-09-22 15:09:27
【问题描述】:

我想评估使用 scikitlearn 构建的回归模型,使用交叉验证并感到困惑,我应该使用 cross_val_scorecross_val_predict 这两个函数中的哪一个。 一种选择是:

cvs = DecisionTreeRegressor(max_depth = depth)
scores = cross_val_score(cvs, predictors, target, cv=cvfolds, scoring='r2')
print("R2-Score: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))

另一个,将 cv 预测与标准 r2_score 一起使用:

cvp = DecisionTreeRegressor(max_depth = depth)
predictions = cross_val_predict(cvp, predictors, target, cv=cvfolds)
print ("CV R^2-Score: {}".format(r2_score(df[target], predictions_cv)))

我会假设这两种方法都是有效的并给出相似的结果。但这仅适用于小 k 折叠的情况。虽然 10-fold-cv 的 r^2 大致相同,但在使用“cross_vall_score”的第一个版本的情况下,更高的 k 值会越来越低。第二个版本几乎不受折叠数变化的影响。

这种行为是意料之中的吗?我是否对 SKLearn 中的 CV 缺乏了解?

【问题讨论】:

    标签: python machine-learning scikit-learn regression cross-validation


    【解决方案1】:

    cross_val_score 返回测试折叠的分数,其中cross_val_predict 返回测试折叠的预测 y 值。

    对于cross_val_score(),您使用的是输出的平均值,这将受到折叠数的影响,因为它可能有一些折叠可能有很高的误差(不正确)。

    然而,cross_val_predict() 针对输入中的每个元素返回在该元素位于测试集中时获得的预测。 [请注意,只能使用将所有元素分配给测试集一次的交叉验证策略]。所以增加折叠数,只会增加测试元素的训练数据,因此其结果可能不会受到太大影响。

    编辑(评论后)

    请查看以下关于cross_val_predict 工作原理的答案:

    How is scikit-learn cross_val_predict accuracy score calculated?

    我认为cross_val_predict 会过拟合,因为随着倍数的增加,更多数据将用于训练,而更少数据用于测试。所以得到的标签更依赖于训练数据。同样如上所述,一个样本的预测只进行一次,因此它可能更容易受到数据分裂的影响。 这就是为什么大多数地方或教程都建议使用cross_val_score 进行分析。

    【讨论】:

    • 非常感谢,这很清楚 :) 。但我仍然想知道我应该使用哪个功能。第一个似乎是我在教程中最常找到的示例,但第二个似乎是更稳定且因此可用的方法......或者使用 cross_val_predict 的评估对过度拟合不太敏感?
    • @Bobipuegi 我已经编辑了我的答案来回答你的问题。
    【解决方案2】:

    所以这个问题也困扰着我,虽然其他人提出了很好的观点,但他们并没有回答 OP 问题的所有方面。

    真正的答案是:增加 k 的分数差异是由于选择的度量 R2(确定系数)。例如MSE、MSLE 或 MAE 使用 cross_val_scorecross_val_predict 不会有任何区别。

    definition of R2

    R^2 = 1 - (MSE(ground truth, prediction)/MSE(ground truth, mean(ground truth)))

    粗体部分解释了为什么随着 k 的增加得分开始不同:我们拥有的分割越多,测试折叠中的样本越少,测试折叠的均值方差就越大。 相反,对于较小的 k,测试折叠的均值不会与完整的地面实况均值相差太大,因为样本量仍然足够大,方差很小。

    证明:

    import numpy as np
    from sklearn.metrics import mean_absolute_error as mae
    from sklearn.metrics import mean_squared_log_error as msle, r2_score
    
    predictions = np.random.rand(1000)*100
    groundtruth = np.random.rand(1000)*20
    
    def scores_for_increasing_k(score_func):
        skewed_score = score_func(groundtruth, predictions)
        print(f'skewed score (from cross_val_predict): {skewed_score}')
        for k in (2,4,5,10,20,50,100,200,250):
            fold_preds = np.split(predictions, k)
            fold_gtruth = np.split(groundtruth, k)
            correct_score = np.mean([score_func(g, p) for g,p in zip(fold_gtruth, fold_preds)])
    
            print(f'correct CV for k={k}: {correct_score}')
    
    for name, score in [('MAE', mae), ('MSLE', msle), ('R2', r2_score)]:
        print(name)
        scores_for_increasing_k(score)
        print()
    

    输出将是:

    MAE
    skewed score (from cross_val_predict): 42.25333901481263
    correct CV for k=2: 42.25333901481264
    correct CV for k=4: 42.25333901481264
    correct CV for k=5: 42.25333901481264
    correct CV for k=10: 42.25333901481264
    correct CV for k=20: 42.25333901481264
    correct CV for k=50: 42.25333901481264
    correct CV for k=100: 42.25333901481264
    correct CV for k=200: 42.25333901481264
    correct CV for k=250: 42.25333901481264
    
    MSLE
    skewed score (from cross_val_predict): 3.5252449697327175
    correct CV for k=2: 3.525244969732718
    correct CV for k=4: 3.525244969732718
    correct CV for k=5: 3.525244969732718
    correct CV for k=10: 3.525244969732718
    correct CV for k=20: 3.525244969732718
    correct CV for k=50: 3.5252449697327175
    correct CV for k=100: 3.5252449697327175
    correct CV for k=200: 3.5252449697327175
    correct CV for k=250: 3.5252449697327175
    
    R2
    skewed score (from cross_val_predict): -74.5910282783694
    correct CV for k=2: -74.63582817089443
    correct CV for k=4: -74.73848598638291
    correct CV for k=5: -75.06145142821893
    correct CV for k=10: -75.38967601572112
    correct CV for k=20: -77.20560102267272
    correct CV for k=50: -81.28604960074824
    correct CV for k=100: -95.1061197684949
    correct CV for k=200: -144.90258384605787
    correct CV for k=250: -210.13375041871123
    

    当然,这里还有一个没有展示的效果,其他人都提到了。 随着 k 的增加,有更多的模型在更多的样本上训练并在更少的样本上进行验证,这会影响最终的分数,但这并不是由cross_val_scorecross_val_predict 之间的选择引起的。

    【讨论】:

      【解决方案3】:

      我认为可以通过检查他们的输出来明确差异。考虑一下这个 sn-p:

      # Last column is the label
      print(X.shape)  # (7040, 133)
      
      clf = MLPClassifier()
      
      scores = cross_val_score(clf, X[:,:-1], X[:,-1], cv=5)
      print(scores.shape)  # (5,)
      
      y_pred = cross_val_predict(clf, X[:,:-1], X[:,-1], cv=5)
      print(y_pred.shape)  # (7040,)
      

      注意形状:为什么会这样? scores.shape 的长度为 5,因为它是通过 5 折交叉验证计算得出的分数(参见参数 cv=5)。因此,为每个折叠计算一个实数值。该值是分类器的分数:

      给定真实标签和预测标签,预测器在特定折叠中正确的答案有多少?

      在这种情况下,输入中给出的 y 标签被使用了两次:从数据中学习和评估分类器的性能。

      另一方面,y_pred.shape 的长度为 7040,这是数据集的形状。那是输入数据集的长度。这意味着每个值不是对多个值计算的分数,而是单个值:分类器的预测:

      给定输入数据及其标签,分类器对特定折叠测试集中的特定示例的预测是什么?

      请注意,您不知道使用了哪个折叠:每个输出都是根据某个折叠的测试数据计算的,但您无法分辨出哪个(至少从这个输出)。

      在这种情况下,标签只使用一次:训练分类器。将这些输出与真实输出进行比较以计算分数是您的工作。如果你只是对它们进行平均,就像你所做的那样,输出不是一个分数,它只是平均预测。

      【讨论】:

        猜你喜欢
        • 2017-12-17
        • 2021-10-21
        • 2021-05-08
        • 2014-04-08
        • 1970-01-01
        • 2020-09-23
        • 2016-02-12
        • 2021-06-27
        • 2021-01-01
        相关资源
        最近更新 更多