使用 cross_val_predict 与 cross_val_score 时,scikit-learn 的分数不同

Posted

技术标签:

【中文标题】使用 cross_val_predict 与 cross_val_score 时,scikit-learn 的分数不同【英文标题】:scikit-learn scores are different when using cross_val_predict vs cross_val_score 【发布时间】:2020-09-23 20:22:33 【问题描述】:

我预计这两种方法都会返回相当相似的错误,有人可以指出错误吗?

计算 RMSE...

rf = RandomForestRegressor(random_state=555, n_estimators=100, max_depth=8)
rf_preds = cross_val_predict(rf, train_, targets, cv=7, n_jobs=7) 
print("RMSE Score using cv preds: :0.5f".format(metrics.mean_squared_error(targets, rf_preds, squared=False)))

scores = cross_val_score(rf, train_, targets, cv=7, scoring='neg_root_mean_squared_error', n_jobs=7)
print("RMSE Score using cv_score: :0.5f".format(scores.mean() * -1))

RMSE Score using cv preds: 0.01658
RMSE Score using cv_score: 0.01073

【问题讨论】:

你看过这个答案了吗:***.com/a/43613924/5612363 @Anwarvic 感谢分享,我已经检查过了,虽然我了解这两个函数之间的区别,但我仍然不明白为什么相同的 K 折叠会有不同的 RMSE 根据cross_val_predict,它说" 结果可能与cross_validatecross_val_score 不同,除非所有测试集具有相同的大小并且度量分解样本。 【参考方案1】:

这里有两个问题,在cross_val_predict的文档中都有提到:

结果可能与cross_validatecross_val_score 不同,除非所有测试集的大小都相同并且指标分解为样本。

首先是在这两种情况下使所有集合(训练和测试)都相同,但在您的示例中并非如此。为此,我们需要使用kfold 方法来定义我们的CV 折叠,然后在这两种情况下使用这些相同的折叠。这是一个带有虚拟数据的示例:

from sklearn.datasets import make_regression
from sklearn.model_selection import KFold, cross_val_score, cross_val_predict
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error

X, y = make_regression(n_samples=2000, n_features=4, n_informative=2,
                      random_state=42, shuffle=False)

rf = RandomForestRegressor(max_depth=2, random_state=0)
kf = KFold(n_splits=5)

rf_preds = cross_val_predict(rf, X, y, cv=kf, n_jobs=5) 
print("RMSE Score using cv preds: :0.5f".format(mean_squared_error(y, rf_preds, squared=False)))

scores = cross_val_score(rf, X, y, cv=kf, scoring='neg_root_mean_squared_error', n_jobs=5)
print("RMSE Score using cv_score: :0.5f".format(scores.mean() * -1))

上述代码 sn-p 的结果(完全可重现,因为我们已经明确设置了所有必要的随机种子)是:

RMSE Score using cv preds: 15.16839
RMSE Score using cv_score: 15.16031

所以,我们可以看到这两个分数确实相似,但仍然不相同

这是为什么呢?答案就在上面引用的句子的相当神秘的第二部分,即 RMSE 分数不会分解样本(老实说,我不知道它有任何 ML 分数)。

简单来说,cross_val_predict 严格按照定义计算 RMSE,即(伪代码):

RMSE = square_root([(y[1] - y_pred[1])^2 + (y[2] - y_pred[2])^2 + ... + (y[n] - y_pred[n])^2]/n)

其中n 是样本数,cross_val_score 方法并不能完全做到这一点;相反,它会计算每个k CV 折叠的 RMSE,然后平均这些 k 值,即(再次伪代码):

RMSE = (RMSE[1] + RMSE[2] + ... + RMSE[k])/k

正是因为 RMSE 在样本上不可分解,这两个值虽然接近,但不相同

我们实际上可以通过手动执行 CV 过程并模拟 cross_val_score 完成的 RMSE 计算和上面描述的那样来证明确实如此,即:

import numpy as np
RMSE__cv_score = []

for train_index, val_index in kf.split(X):
    rf.fit(X[train_index], y[train_index])
    pred = rf.predict(X[val_index])
    err = mean_squared_error(y[val_index], pred, squared=False)
    RMSE__cv_score.append(err)

print("RMSE Score using manual cv_score: :0.5f".format(np.mean(RMSE__cv_score)))

结果是:

RMSE Score using manual cv_score: 15.16031

即与上面cross_val_score 返回的相同。

所以,如果我们想要非常精确,事实是正确的 RMSE(即根据其定义精确计算)是 cross_val_predict 返回的那个; cross_val_score 返回它的近似值。但在实践中,我们经常会发现差别并不大,所以如果更方便的话,我们也可以使用cross_val_score

【讨论】:

以上是关于使用 cross_val_predict 与 cross_val_score 时,scikit-learn 的分数不同的主要内容,如果未能解决你的问题,请参考以下文章

sklearn TimeSeriesSplit cross_val_predict 仅适用于分区

对测试数据集使用 cross_val_predict

cross_val_predict 未完成。没有错误信息

为啥 cross_val_predict 不适合测量泛化误差?

在 cross_val_predict (sklearn) 中使用 StratifiedShuffleSplit

使用 RepeatedStratifiedKFold 5*10 的 cross_val_predict 概率