cross_val_score 和 cross_val_predict 之间的区别
Difference between cross_val_score and cross_val_predict
我想使用交叉验证评估使用 scikitlearn 构建的回归模型,我感到困惑,我应该使用 cross_val_score
和 cross_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))
另一个,使用标准 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 折的情况。虽然 r^2 对于 10-fold-cv 大致相同,但在使用 "cross_vall_score" 的第一个版本的情况下,随着 k 值的增加,它变得越来越低。第二个版本基本上不受折叠次数的影响。
这种行为是预料之中的吗?我对 SKLearn 中的 CV 缺乏了解吗?
cross_val_score
returns 测试折叠分数,其中 cross_val_predict
returns 预测测试折叠的 y 值。
对于 cross_val_score()
,您使用的是输出的平均值,这将受到折叠次数的影响,因为它可能会有一些折叠,这可能会有很高的错误(不正确拟合)。
然而,cross_val_predict()
returns,对于输入中的每个元素,当该元素在测试集中时获得的预测。 [请注意,只能使用将所有元素分配给测试集一次的交叉验证策略]。所以增加折叠的次数,只是增加了测试元素的训练数据,因此其结果可能不会受到太大影响。
编辑(评论后)
请查看以下有关 cross_val_predict
工作原理的答案:
我认为 cross_val_predict
会过拟合,因为随着折叠次数的增加,更多的数据将用于训练,而更少的数据将用于测试。所以得到的标签更依赖于训练数据。同样如上所述,对一个样本的预测只进行一次,因此它可能更容易受到数据拆分的影响。
这就是为什么大多数地方或教程都建议使用 cross_val_score
进行分析。
我认为可以通过检查他们的输出来弄清区别。考虑这个片段:
# 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
)。因此,每次折叠都会计算一个真实值。该值是分类器的分数:
given true labels and predicted labels, how many answers the predictor were right in a particular fold?
在这种情况下,输入中给出的 y 标签被使用了两次:从数据中学习和评估分类器的性能。
另一方面,y_pred.shape
的长度为 7040,这是数据集的形状。那是输入数据集的长度。这意味着每个值不是根据多个值计算的分数,而是单个值:分类器的预测:
given the input data and their labels, what is the prediction of the classifier on a specific example that was in a test set of a particular fold?
请注意,您不知道使用的是什么折叠:每个输出都是根据特定折叠的测试数据计算的,但您无法分辨是哪一个(至少从这个输出来看)。
在这种情况下,标签仅使用一次:训练分类器。您的工作是将这些输出与真实输出进行比较以计算分数。如果你只是对它们进行平均,就像你所做的那样,输出不是分数,它只是平均预测。
所以这个问题也困扰了我,虽然其他人提出了很好的观点,但他们并没有回答 OP 问题的所有方面。
真正的答案是:增加 k 的分数差异是由于选择的指标 R2(决定系数)。例如MSE、MSLE 或 MAE 使用 cross_val_score
或 cross_val_predict
没有任何区别。
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_score
和 cross_val_predict
之间的选择引起的。
我想使用交叉验证评估使用 scikitlearn 构建的回归模型,我感到困惑,我应该使用 cross_val_score
和 cross_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))
另一个,使用标准 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 折的情况。虽然 r^2 对于 10-fold-cv 大致相同,但在使用 "cross_vall_score" 的第一个版本的情况下,随着 k 值的增加,它变得越来越低。第二个版本基本上不受折叠次数的影响。
这种行为是预料之中的吗?我对 SKLearn 中的 CV 缺乏了解吗?
cross_val_score
returns 测试折叠分数,其中 cross_val_predict
returns 预测测试折叠的 y 值。
对于 cross_val_score()
,您使用的是输出的平均值,这将受到折叠次数的影响,因为它可能会有一些折叠,这可能会有很高的错误(不正确拟合)。
然而,cross_val_predict()
returns,对于输入中的每个元素,当该元素在测试集中时获得的预测。 [请注意,只能使用将所有元素分配给测试集一次的交叉验证策略]。所以增加折叠的次数,只是增加了测试元素的训练数据,因此其结果可能不会受到太大影响。
编辑(评论后)
请查看以下有关 cross_val_predict
工作原理的答案:
我认为 cross_val_predict
会过拟合,因为随着折叠次数的增加,更多的数据将用于训练,而更少的数据将用于测试。所以得到的标签更依赖于训练数据。同样如上所述,对一个样本的预测只进行一次,因此它可能更容易受到数据拆分的影响。
这就是为什么大多数地方或教程都建议使用 cross_val_score
进行分析。
我认为可以通过检查他们的输出来弄清区别。考虑这个片段:
# 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
)。因此,每次折叠都会计算一个真实值。该值是分类器的分数:
given true labels and predicted labels, how many answers the predictor were right in a particular fold?
在这种情况下,输入中给出的 y 标签被使用了两次:从数据中学习和评估分类器的性能。
另一方面,y_pred.shape
的长度为 7040,这是数据集的形状。那是输入数据集的长度。这意味着每个值不是根据多个值计算的分数,而是单个值:分类器的预测:
given the input data and their labels, what is the prediction of the classifier on a specific example that was in a test set of a particular fold?
请注意,您不知道使用的是什么折叠:每个输出都是根据特定折叠的测试数据计算的,但您无法分辨是哪一个(至少从这个输出来看)。
在这种情况下,标签仅使用一次:训练分类器。您的工作是将这些输出与真实输出进行比较以计算分数。如果你只是对它们进行平均,就像你所做的那样,输出不是分数,它只是平均预测。
所以这个问题也困扰了我,虽然其他人提出了很好的观点,但他们并没有回答 OP 问题的所有方面。
真正的答案是:增加 k 的分数差异是由于选择的指标 R2(决定系数)。例如MSE、MSLE 或 MAE 使用 cross_val_score
或 cross_val_predict
没有任何区别。
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_score
和 cross_val_predict
之间的选择引起的。