cross_val_score和cross_val_predict的区别

37

我想使用交叉验证评估使用scikitlearn构建的回归模型,但却感到困惑,不知道应该使用哪个函数cross_val_score和cross_val_predict 。 其中一个选项是:

<code><code>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))
</code></code>

另一个方法是使用标准的r2_score函数来使用cv预测结果:

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-fold。虽然在10折交叉验证中r^2大致相同,但在使用"cross_vall_score"的第一种版本中,对于更高的k值,它将越来越低。第二个版本在改变fold数量时大多不受影响。

这种行为是否可以预期,我是否缺乏对SKLearn中CV的理解?

3个回答

41

cross_val_score返回测试折叠的得分,而cross_val_predict则返回测试折叠的预测y值。

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

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

编辑(评论后)

请查看以下关于cross_val_predict如何工作的答案:

如何计算scikit-learn交叉验证预测准确性得分?

我认为cross_val_predict会过度拟合,因为随着折叠数的增加,更多的数据将用于训练,较少的数据将用于测试。因此,结果标签更依赖于训练数据。正如上面已经提到的那样,一个样本的预测只进行一次,因此它可能对数据的分割更加敏感。

这就是为什么大多数地方或教程建议使用cross_val_score进行分析。


非常感谢,那部分很清楚 :) 但我仍然想知道应该使用哪个函数。第一个似乎是我在教程中最常见的例子,但第二个似乎是更稳定和可用的方法... 或者使用cross_val_predict进行评估是否不太容易过拟合? - Bobipuegi
@Bobipuegi,我已经编辑了我的答案来回答你的问题。 - Vivek Kumar

12

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

真正的答案是: 使用增加的k值得分数的差异是由于选择的度量R2(确定系数)所致。例如,对于MSE、MSLE或MAE,使用cross_val_score或cross_val_predict没有任何区别。

R2的定义:

R^2 = 1 - (MSE(实际值, 预测值)/ MSE(实际值, 实际值均值))

粗体部分解释了为什么得分开始在增加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 之间的选择所引起的。


5

我认为可以通过检查它们的输出来清楚地区分它们。考虑以下代码片段:

# 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,即数据集的形状。这是输入数据集的长度。这意味着每个值不是在多个值上计算得出的分数,而是一个单一的值:分类器的预测:

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

请注意,您不知道使用了哪个折叠:每个输出都是在某个折叠的测试数据上计算的,但您无法从这个输出中判断出哪个折叠。在这种情况下,标签只用于训练分类器。您的工作是将这些输出与真实输出进行比较,以计算得分。如果像您所做的那样仅对它们进行平均处理,则输出不是得分,而只是平均预测值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接