如果运行randomForest
模型,则可以基于该模型进行预测。有没有一种方法可以获取每个预测的预测间隔,以使我知道模型如何“确定”其答案。如果可能的话,是仅仅基于整个模型的因变量的可变性,还是根据特定预测所遵循的特定决策树,其间隔会变宽还是变窄?
如果运行randomForest
模型,则可以基于该模型进行预测。有没有一种方法可以获取每个预测的预测间隔,以使我知道模型如何“确定”其答案。如果可能的话,是仅仅基于整个模型的因变量的可变性,还是根据特定预测所遵循的特定决策树,其间隔会变宽还是变窄?
Answers:
这部分是对@Sashikanth Dareddy的回应(因为它不适合发表评论),部分是对原始帖子的回应。
记住什么是预测间隔,它是我们预测未来观测值所在的间隔或一组值。通常,预测区间有2个主要部分来确定其宽度,一个代表代表预测均值(或其他参数)的不确定性,这是置信区间部分,另一个代表围绕该均值的各个观测值的变异性。由于中心极限定理,置信区间具有一定的鲁棒性,并且在随机森林的情况下,自举也有帮助。但是,在给定预测变量的情况下,预测间隔完全取决于关于数据如何分布的假设,CLT和自举对该部分没有影响。
预测间隔应更宽,而相应的置信区间也应更宽。其他可能影响预测区间宽度的因素是是否存在均等方差的假设,这必须来自研究人员的知识,而不是随机森林模型的知识。
预测间隔对分类结果没有意义(您可以做一个预测集而不是间隔,但是在大多数情况下,它可能不会提供足够的信息)。
通过模拟我们知道确切真相的数据,我们可以看到预测间隔周围的一些问题。考虑以下数据:
set.seed(1)
x1 <- rep(0:1, each=500)
x2 <- rep(0:1, each=250, length=1000)
y <- 10 + 5*x1 + 10*x2 - 3*x1*x2 + rnorm(1000)
此特定数据遵循线性回归的假设,对于随机森林拟合而言相当简单。从“真实”模型中我们知道,当两个预测变量均为0时,均值均为10,我们还知道各个点遵循正态分布,标准偏差为1。这意味着基于完美知识的95%预测区间这些点将是8到12(实际上是8.04到11.96,但是四舍五入使其更简单)。任何估计的预测间隔都应大于此范围(没有完美的信息会增加宽度以进行补偿)并包括此范围。
让我们看一下回归的间隔:
fit1 <- lm(y ~ x1 * x2)
newdat <- expand.grid(x1=0:1, x2=0:1)
(pred.lm.ci <- predict(fit1, newdat, interval='confidence'))
# fit lwr upr
# 1 10.02217 9.893664 10.15067
# 2 14.90927 14.780765 15.03778
# 3 20.02312 19.894613 20.15162
# 4 21.99885 21.870343 22.12735
(pred.lm.pi <- predict(fit1, newdat, interval='prediction'))
# fit lwr upr
# 1 10.02217 7.98626 12.05808
# 2 14.90927 12.87336 16.94518
# 3 20.02312 17.98721 22.05903
# 4 21.99885 19.96294 24.03476
我们可以看到估计均值(置信区间)存在一些不确定性,这给我们提供了一个较宽的(但包括)8到12范围的预测间隔。
现在,让我们看一下基于单个树的单个预测的时间间隔(我们应该期望它们会更宽,因为随机森林无法从线性回归所做的假设(我们知道此数据是正确的)中受益):
library(randomForest)
fit2 <- randomForest(y ~ x1 + x2, ntree=1001)
pred.rf <- predict(fit2, newdat, predict.all=TRUE)
pred.rf.int <- apply(pred.rf$individual, 1, function(x) {
c(mean(x) + c(-1, 1) * sd(x),
quantile(x, c(0.025, 0.975)))
})
t(pred.rf.int)
# 2.5% 97.5%
# 1 9.785533 13.88629 9.920507 15.28662
# 2 13.017484 17.22297 12.330821 18.65796
# 3 16.764298 21.40525 14.749296 21.09071
# 4 19.494116 22.33632 18.245580 22.09904
该间隔比回归预测间隔宽,但是它们并不覆盖整个范围。它们的确包含真实值,因此可能作为置信区间是合法的,但它们仅预测平均值(预测值)在何处,而没有针对该平均值周围分布的增加项。对于x1和x2均为0的第一种情况,间隔不会低于9.7,这与下降到8的真实预测间隔有很大不同。如果我们生成新的数据点,则将有几个点(更多)大于5%),则不包含在真实和回归区间内,但不属于随机森林区间。
要生成预测间隔,您将需要对围绕预测均值的各个点的分布进行一些强有力的假设,然后您可以从各个树(自举置信区间)中进行预测,然后从假设中生成随机值该中心的分布。这些生成的片段的分位数可以形成预测间隔(但是我仍然会对其进行测试,您可能需要多次重复该过程并合并)。
这是通过将正常偏差(因为我们知道原始数据使用了正常偏差)添加到具有基于该树的估计MSE的标准偏差的预测来执行此操作的示例:
pred.rf.int2 <- sapply(1:4, function(i) {
tmp <- pred.rf$individual[i, ] + rnorm(1001, 0, sqrt(fit2$mse))
quantile(tmp, c(0.025, 0.975))
})
t(pred.rf.int2)
# 2.5% 97.5%
# [1,] 7.351609 17.31065
# [2,] 10.386273 20.23700
# [3,] 13.004428 23.55154
# [4,] 16.344504 24.35970
这些时间间隔包含基于完美知识的时间间隔,因此看起来很合理。但是,它们将在很大程度上取决于所做的假设(这些假设在这里是有效的,因为我们使用的是如何模拟数据的知识,在实际数据情况下它们可能不那么有效)。在完全信任此方法之前,我仍将对看起来更像您的真实数据的数据重复进行几次模拟(但模拟后才知道真相)。
我意识到这是一篇过时的文章,但是我已经对此进行了一些模拟,并认为我会分享我的发现。
@GregSnow对此发表了非常详细的文章,但是我相信,当使用来自单个树的预测来计算间隔时,他所查看的仅是70%的预测间隔。我们需要查看以获得95%的预测间隔。[ μ + 1.96 ∗ σ ,μ - 1.96 ∗ σ ]
对@GregSnow代码进行此更改,我们得到以下结果
set.seed(1)
x1 <- rep( 0:1, each=500 )
x2 <- rep( 0:1, each=250, length=1000 )
y <- 10 + 5*x1 + 10*x2 - 3*x1*x2 + rnorm(1000)
library(randomForest)
fit2 <- randomForest(y~x1+x2)
pred.rf <- predict(fit2, newdat, predict.all=TRUE)
pred.rf.int <- t(apply( pred.rf$individual, 1, function(x){
c( mean(x) + c(-1.96,1.96)*sd(x), quantile(x, c(0.025,0.975)) )}))
pred.rf.int
2.5% 97.5%
1 7.826896 16.05521 9.915482 15.31431
2 11.010662 19.35793 12.298995 18.64296
3 14.296697 23.61657 14.749248 21.11239
4 18.000229 23.73539 18.237448 22.10331
现在,将其与通过将标准偏差与标准偏差的预测值相加(如@GregSnow的MSE)所生成的间隔进行比较,我们可以得出,
pred.rf.int2 <- sapply(1:4, function(i) {
tmp <- pred.rf$individual[i,] + rnorm(1000, 0, sqrt(fit2$mse))
quantile(tmp, c(0.025, 0.975))
})
t(pred.rf.int2)
2.5% 97.5%
[1,] 7.486895 17.21144
[2,] 10.551811 20.50633
[3,] 12.959318 23.46027
[4,] 16.444967 24.57601
这两种方法的间隔现在看起来非常接近。在这种情况下,针对误差分布绘制三种方法的预测间隔如下图所示
现在,让我们重新运行仿真,但是这次增加了误差项的方差。如果我们的预测间隔计算结果很好,那么最终的间隔应该比上面得到的间隔宽。
set.seed(1)
x1 <- rep( 0:1, each=500 )
x2 <- rep( 0:1, each=250, length=1000 )
y <- 10 + 5*x1 + 10*x2 - 3*x1*x2 + rnorm(1000,mean=0,sd=5)
fit1 <- lm(y~x1+x2)
newdat <- expand.grid(x1=0:1,x2=0:1)
predict(fit1,newdata=newdat,interval = "prediction")
fit lwr upr
1 10.75006 0.503170 20.99695
2 13.90714 3.660248 24.15403
3 19.47638 9.229490 29.72327
4 22.63346 12.386568 32.88035
set.seed(1)
fit2 <- randomForest(y~x1+x2,localImp=T)
pred.rf.int <- t(apply( pred.rf$individual, 1, function(x){
c( mean(x) + c(-1.96,1.96)*sd(x), quantile(x, c(0.025,0.975)) )}))
pred.rf.int
2.5% 97.5%
1 7.889934 15.53642 9.564565 15.47893
2 10.616744 18.78837 11.965325 18.51922
3 15.024598 23.67563 14.724964 21.43195
4 17.967246 23.88760 17.858866 22.54337
pred.rf.int2 <- sapply(1:4, function(i) {
tmp <- pred.rf$individual[i,] + rnorm(1000, 0, sqrt(fit2$mse))
quantile(tmp, c(0.025, 0.975))
})
t(pred.rf.int2)
2.5% 97.5%
[1,] 1.291450 22.89231
[2,] 4.193414 25.93963
[3,] 7.428309 30.07291
[4,] 9.938158 31.63777
现在,这清楚地表明,通过第二种方法计算预测间隔的准确性要高得多,并且所产生的结果与线性回归的预测间隔非常接近。
以正常性为假设,还有另一种更容易的方法来从随机森林中计算预测间隔。从单独的树中,我们都有预测值()和均方误差()。因此,可以将来自每个单独树的预测视为。使用正态分布属性,我们对随机森林的预测将具有分布。将其应用到我们上面讨论的示例中,我们得到以下结果中号小号ë 我 Ñ (μ 我,- [R 中号小号ë 我)Ñ (Σ μ 我/ Ñ ,Σ [R 中号小号ë 我/ Ñ )
mean.rf <- pred.rf$aggregate
sd.rf <- mean(sqrt(fit2$mse))
pred.rf.int3 <- cbind(mean.rf - 1.96*sd.rf, mean.rf + 1.96*sd.rf)
pred.rf.int3
1 1.332711 22.09364
2 4.322090 25.08302
3 8.969650 29.73058
4 10.546957 31.30789
这些与线性模型间隔以及@GregSnow建议的方法非常吻合。但是请注意,在我们讨论的所有方法中,基本假设是误差遵循正态分布。
如果使用R,则可以轻松地为随机森林回归的预测生成预测间隔:只需使用该软件包quantregForest
(可在CRAN处获得),并阅读N. Meinshausen的论文,了解如何利用分位数回归森林推断条件分位数以及它们如何可用于建立预测间隔。即使您不使用R,也非常有用!
使用randomForest很容易解决。
首先让我处理回归任务(假设您的森林有1000棵树)。在predict
函数中,您可以选择从单个树返回结果。这意味着您将收到1000列的输出。我们可以取每一行1000列的平均值-这是常规RF输出会以任何方式产生的结果。现在,要获得预测间隔,可以说+/- 2 std。偏差您需要做的是,对于每一行,从1000个值中计算+/- 2 std。偏差,并将其作为预测的上限和下限。
其次,在分类的情况下,请记住,每棵树都输出1或0(默认情况下),并且所有1000棵树的总和除以1000得出类的概率(在二进制分类的情况下)。为了对概率设置预测间隔,您需要修改最小值。设置节点大小选项(有关该选项的确切名称,请参阅randomForest文档),一旦将其设置为值>> 1,则各个树将输出1到0之间的数字。现在,从此处开始,您可以重复上述步骤回归任务。
我希望这是有道理的。
在以下论文中已经解决了为随机森林预测构建预测间隔的问题:
张浩哲,约书亚·齐默尔曼,丹·内特尔顿和丹尼尔·诺德曼。“随机森林预测间隔。” 美国统计学家,2019年。
R包“ rfinterval”是其在CRAN可用的实现。
要安装R包rfinterval:
#install.packages("devtools")
#devtools::install_github(repo="haozhestat/rfinterval")
install.packages("rfinterval")
library(rfinterval)
?rfinterval
快速开始:
train_data <- sim_data(n = 1000, p = 10)
test_data <- sim_data(n = 1000, p = 10)
output <- rfinterval(y~., train_data = train_data, test_data = test_data,
method = c("oob", "split-conformal", "quantreg"),
symmetry = TRUE,alpha = 0.1)
### print the marginal coverage of OOB prediction interval
mean(output$oob_interval$lo < test_data$y & output$oob_interval$up > test_data$y)
### print the marginal coverage of Split-conformal prediction interval
mean(output$sc_interval$lo < test_data$y & output$sc_interval$up > test_data$y)
### print the marginal coverage of Quantile regression forest prediction interval
mean(output$quantreg_interval$lo < test_data$y & output$quantreg_interval$up > test_data$y)
数据示例:
oob_interval <- rfinterval(pm2.5 ~ .,
train_data = BeijingPM25[1:1000, ],
test_data = BeijingPM25[1001:2000, ],
method = "oob",
symmetry = TRUE,
alpha = 0.1)
str(oob_interval)
score
用于评估性能的功能。由于输出基于森林中树木的多数票,因此在分类的情况下,基于票数分配,您将有可能获得该结果为真。但是我不确定回归...。您使用哪个库?