使用 cross_val_predict 与 cross_val_score 时 scikit-learn 分数不同
scikit-learn scores are different when using cross_val_predict vs cross_val_score
我预计这两种方法都会 return 相当相似的错误,有人可以指出我的错误吗?
正在计算 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
这里有两个问题,cross_val_predict
的文档中都有提到:
Results can differ from cross_validate
and cross_val_score
unless all tests sets have equal size and the metric decomposes over samples.
首先是使所有集合(训练和测试)在两种情况下都相同,但在您的示例中并非如此。为此,我们需要使用 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))
以上代码片段的结果(完全可重现,因为我们已经明确设置了所有必要的随机种子)是:
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 程序并模拟 RMSE 计算,如 cross_val_score
所做和如上所述,即:
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(即完全根据其定义计算)是由 [=16= 返回的]; cross_val_score
returns 它的近似值。但是在实践中,我们往往会发现差别不是那么大,所以如果方便的话也可以用cross_val_score
我预计这两种方法都会 return 相当相似的错误,有人可以指出我的错误吗?
正在计算 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
这里有两个问题,cross_val_predict
的文档中都有提到:
Results can differ from
cross_validate
andcross_val_score
unless all tests sets have equal size and the metric decomposes over samples.
首先是使所有集合(训练和测试)在两种情况下都相同,但在您的示例中并非如此。为此,我们需要使用 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))
以上代码片段的结果(完全可重现,因为我们已经明确设置了所有必要的随机种子)是:
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 程序并模拟 RMSE 计算,如 cross_val_score
所做和如上所述,即:
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(即完全根据其定义计算)是由 [=16= 返回的]; cross_val_score
returns 它的近似值。但是在实践中,我们往往会发现差别不是那么大,所以如果方便的话也可以用cross_val_score