带有 GridSearchCV 但不是 train_test_split 的随机森林的大负 R2 或准确度得分

数据挖掘 Python scikit-学习 随机森林 网格搜索 网格搜索
2022-03-08 06:31:27

我正在尝试使用scikit-learn 中的GridSearchCV并查看训练/测试指标之间的差异。

当我使用 RandomForestRegressor 进行正常的测试/训练拆分时,指标具有可比性。类似于:
训练 R2:0.97
测试 R2:0.85

但是,当我尝试在 GridSearchCV 中使用相同的数据时,测试和训练指标似乎完全不同,测试准确度是一个很大的负数,而不是介于 0 和 1 之间。


from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import GridSearchCV

...

rf_reg = RandomForestRegressor(max_depth=10,
                              random_state=RANDOM,
                              n_estimators=100,
                              n_jobs=N_JOBS)

param_grid = {
              "min_samples_leaf": [1, 2, 4, 10]
             }
grid_cv = GridSearchCV(rf_reg, param_grid, cv=5, return_train_score=True)
grid_cv.fit(X, y)

我很惊讶当我检查测试和训练的分数时,它们似乎是两个不同的指标。RandomForestRegressor 的默认分数是 R2,但测试集的结果看起来完全是另一个指标。

results = pd.DataFrame(grid_cv.cv_results_)
print('Train scores:\n', results['mean_train_score'])
print('Test scores:\n', results['mean_test_score'])

Train scores: 
0    0.974572
1    0.963771
2    0.936328
3    0.877382
Name: mean_train_score, dtype: float64

Test scores: 
0   -5.948434
1   -5.798446
2   -6.034835
3   -6.655515
Name: mean_test_score, dtype: float64

训练分数对我来说很有意义,它们应该在 0-1 之间,因为我期待 R2 错误指标,这是 RandomForestRegressor 的默认值。但是为什么测试分数是不同的指标呢?它们也应该在 0 和 1 之间,怎么可能得到负数?这对我来说没有意义。

cross_val_score 也会发生同样的事情,我期待一个 R2 指标,但它返回负数。即使将评分方法显式设置为 'r2' 也会返回负数。

from sklearn.model_selection import cross_val_score

scores = cross_val_score(rf_reg, X, y, cv=5, scoring='r2')
print(scores)
[ -5.15970579  -4.67536964 -13.17643335  -2.11630272  -4.51688508]
2个回答

在模型验证的上下文中使用它时可以观察到负 R2 值(我们有从模型中保留的数据),因为在这种情况下,SSTSSE + SSR。也就是说,由于数据拆分,该约束不存在。这是因为在模型验证的上下文中,SST 的值仅使用仅在测试集中保存的观察值计算(它只是仅在测试集中观察到的 y 方差,通常乘以 n-1 的因子),而 SSE 是使用您训练的模型预测(模型当然是在单独的数据集上训练的)和测试集中 y 的实际值计算的。因此,上证所完全有可能>SST 如果您的模型在预测测试集方面非常差,则强制 R2 = 1 -小号小号小号小号为负。

您基本上可以将负 R2 解释为您的模型通常具有非常低的 R2。从随机森林中看到这一点并不奇怪,特别是由于算法非常详尽,它非常适合训练集(通常,随机森林倾向于完美地拟合训练集,如您所见),但在提供数据(尽管通常仍然足够好,具体取决于上下文。在您的情况下,显然不够好)。

根据sklearn 文档,如果模型任意变差,则 R2 可能为负数

因此,非常负的火车分数表明表现极差

为什么在简单的测试/训练拆分中表现不错的 GridSearchCV 的测试性能却如此糟糕?

主要问题是train_test_split随机选择观察结果而GridSearchCV不是随机选择!

我的问题是数据框是按目标变量排序的!

GridSearchCV 和 cross_val_score不会随机折叠他们实际上将数据框中的前 20% 的观察值作为折叠 1,接下来的 20% 作为折叠 2,等等。

假设我的目标是 1-50 之间的范围。如果我按目标对数据帧进行排序,那么所有观察结果的顺序都是从 1 到 50。交叉验证的第一折将(例如)仅采用目标在 1-10 之间的观察结果,将其保存以进行测试,然后仅针对 20-50 的目标训练模型这就是它表现如此糟糕的原因!模型在一定范围内训练,测试集只包含模型从未见过的目标范围!

解决方案很简单。在拆分为 X、y 以进行交叉验证之前,将原始数据帧打乱。

df = df.sample(frac=1, random_state=0)

这解决了我的问题,现在 GridSearchCV 的测试和训练分数都在 0-1 之间,与简单的 train_test_split 相当。

经验教训:在交叉验证之前始终对数据框进行洗牌 - 否则折叠将受到数据收集顺序的任何偏差的影响。