pls R 的交叉验证中如何计算 R2 和 RMSE

Posted

技术标签:

【中文标题】pls R 的交叉验证中如何计算 R2 和 RMSE【英文标题】:How R2 and RMSE are calculated in cross-validation of pls R 【发布时间】:2018-10-02 22:18:26 【问题描述】:

我正在使用 Mevik (2007) 的 pls R 包进行偏最小二乘回归。 10折交叉验证模型如下:

pls.fa <- plsr(FA ~ ., ncomp = xcomp,scale = TRUE, validation = "CV", segments = 10,jackknife =TRUE, data=train)

然后,我可以打印出精度,例如 R2 或 RMSE 使用:

R2(pls.fa,ncomp=1:xcomp)

其中 xcomp 是组件的最佳数量。 例如,R2 的结果如下所示:

Intercept)      1 comps      2 comps      3 comps      4 comps      5 comps      6 comps      7 comps      8 comps      9 comps  
  -0.009828     0.551053     0.570584     0.574790     0.580414     0.583354     0.585812     0.580690     0.581536     0.595441  
   10 comps  
   0.596096  

我的问题是:这个交叉验证产生的 R2 是多少,是 10 倍的平均值吗?

谢谢

【问题讨论】:

【参考方案1】:

我进行了一些测试,似乎 pls::R2pls::RMSEP 返回的 R2RMSE 不是 10 折的平均统计数据。它们是通过从所有 10 个 CV 折叠中提取预测并将它们与观察到的结果进行比较,同时使用所有保留样本来计算的:

这是一个例子:

library(pls)

用内置的纱线数据集拟合模型:

data(yarn)
pls.fa <- plsr(density ~ NIR,
               data = yarn,
               ncomp = 6,
               scale = TRUE,
               validation = "CV",
               segments = 10,
               jackknife = TRUE)

我将使用等效的caret 函数进行比较

以下代码返回使用前 1:6 分量获得的 RMSE:

pls::RMSEP(pls.fa, ncomp = 1:6, estimate = "CV", intercept = FALSE) 
#output
1 comps  2 comps  3 comps  4 comps  5 comps  6 comps  
 8.4692   2.5553   1.9430   1.0151   0.7399   0.5801  

以数值向量的形式提取 RMSE:

unlist(lapply(1:6, function(x) pls::RMSEP(pls.fa,
                                          ncomp = 1:6,
                                          estimate = "CV",
                                          intercept = FALSE)$val[,,x]))

让我们使用所有数据将输出与caret::RMSE 进行比较:

all.equal(
  unlist(lapply(1:6, function(x) caret::RMSE(pls.fa$validation$pred[,,x],
                                             yarn$density))),
  unlist(lapply(1:6, function(x) pls::RMSEP(pls.fa,
                                            ncomp = 1:6,
                                            estimate = "CV",
                                            intercept = FALSE)$val[,,x])))
#output  
TRUE

所以RMSEP 是通过使用所有保留预测来计算的。

相当于R2:

all.equal(
  unlist(lapply(1:6, function(x) caret::R2(pls.fa$validation$pred[,,x],
                                           yarn$density,
                                           form = "traditional"))),
  unlist(lapply(1:6, function(x) pls::R2(pls.fa,
                                         ncomp = 1:6,
                                         estimate = "CV",
                                         intercept = FALSE)$val[,,x])))
#output  
TRUE

编辑:回答评论中的问题:

哪种方法更好地在折叠上平均 RMSE,或者从折叠中提取所有预测并计算一个 RMSE:

在我看来,任何一种方式都很好,只是在比较模型时需要在计算中保持一致。考虑以下示例:

set.seed(1)
true <- rnorm(100)
fold <- sample(1:10, size = 100, replace = T)
pred <- rnorm(100)

z <- data.frame(true, pred, fold)

library(tidyverse)

z %>%
  group_by(fold) %>%
  summarise(rmse = caret::RMSE(true, pred)) %>%
  pull(rmse) %>%
  mean
#ouput
 1.479923
    
z %>%
  summarise(rmse = caret::RMSE(true, pred)) %>%
  pull(rmse) 
#ouput
1.441471

与提取所有预测并计算 RMSE 相比,此处对折叠进行平均给出了更悲观的结果。

使用与 set.seed(2) 相同的代码:

平均折叠次数:1.442483 拉所有:1.500432

现在平均折叠次数更加乐观

因此,一种方法并不总是更乐观。

【讨论】:

这对我来说是非常有用的信息,非常感谢@missuse。这是否意味着每次模型进行交叉验证时,它都会保存预测值,并且在所有 10 次之后将它们拉到一起并将它们与观察值相关联? @Phuong Ho 很高兴为您提供帮助。是的,这是正确的,所有来自 CV 的预测都被提取并与观察值进行比较。编辑答案更容易理解。但是,由于输出 pls 对象包含折叠中的观察索引,您可以使用自定义函数计算您想要的任何指标。 @missue:您是否认为像上面的方法一样计算 R2(即所有保留的 cv 预测及其实际值比计算每个 CV 折叠的 R 并平均它们更好。我找到了所有保留的产生比所有 10 倍的平均 R2 更高的 R2。 @hn.phuong 我通常计算每个折叠的 R2 并对它们进行平均,因为这种方式提供了一些方差估计。但我认为这并不重要,更重要的是在比较模型时保持一致。无论哪种方式都可以正常工作。 @missuse:我问这个问题是因为我正在使用 h2o.deeplearning 做相同的模型,我平均了每个折叠的 R2,当然我们也可以查看方差。但是,正如我在之前的评论中提到的,我发现这个均值 R2 比保持 R2 小,这可能会导致如此乐观的结果

以上是关于pls R 的交叉验证中如何计算 R2 和 RMSE的主要内容,如果未能解决你的问题,请参考以下文章

Sci-kit 学习 PLS SVD 和交叉验证

如何进行交叉验证 SVM 分类器

交叉验证给出负 R2?

交叉验证概述

如何在 R 中执行随机森林/交叉验证

如何在 R 中有效地使用 big.matrix 进行交叉验证?