14

我想加快我的引导功能,它本身工作得很好。我读到自 R 2.14 以来有一个名为 的包parallel,但我发现它对某人来说非常困难。计算机科学知识很少,无法真正实现它。也许有人可以帮忙。

所以这里我们有一个引导程序:

n<-1000
boot<-1000
x<-rnorm(n,0,1)
y<-rnorm(n,1+2*x,2)
data<-data.frame(x,y)
boot_b<-numeric()
for(i in 1:boot){
  bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),]
  boot_b[i]<-lm(y~x,bootstrap_data)$coef[2]
  print(paste('Run',i,sep=" "))
}

目标是使用并行处理/利用我 PC 的多个内核。我在 Windows 下运行 R。谢谢!

编辑(诺亚回复后)

以下语法可用于测试:

library(foreach)
library(parallel)
library(doParallel)
registerDoParallel(cores=detectCores(all.tests=TRUE))
n<-1000
boot<-1000
x<-rnorm(n,0,1)
y<-rnorm(n,1+2*x,2)
data<-data.frame(x,y)
start1<-Sys.time()
boot_b <- foreach(i=1:boot, .combine=c) %dopar% {
  bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),]
  unname(lm(y~x,bootstrap_data)$coef[2])
}
end1<-Sys.time()
boot_b<-numeric()
start2<-Sys.time()
for(i in 1:boot){
  bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),]
  boot_b[i]<-lm(y~x,bootstrap_data)$coef[2]
}
end2<-Sys.time()
start1-end1
start2-end2
as.numeric(start1-end1)/as.numeric(start2-end2)

但是,在我的机器上,简单的 R 代码更快。这是并行处理的已知副作用之一,即它会导致分叉进程的开销,这会增加像这样的“简单任务”中的时间?

编辑:在我的机器上,parallel代码比“简单”代码长约 5 倍。boot当我增加任务的复杂性(例如增加或n)时,这个因素显然不会改变。所以也许代码或我的机器有问题(基于 Windows 的处理?)。

4

4 回答 4

11

试试这个boot包。它经过了很好的优化,并且包含一个parallel参数。这个包的棘手之处在于你必须编写新函数来计算你的统计数据,它接受你正在处理的数据和一个索引向量来重新采样数据。所以,从你定义的地方开始data,你可以做这样的事情:

# Define a function to resample the data set from a vector of indices
# and return the slope
slopeFun <- function(df, i) {
  #df must be a data frame.
  #i is the vector of row indices that boot will pass
  xResamp <- df[i, ]
  slope <- lm(y ~ x, data=xResamp)$coef[2] 
} 

# Then carry out the resampling
b <- boot(data, slopeFun, R=1000, parallel="multicore")

b$t是重采样统计量的向量,并且boot有很多很好的方法可以轻松地处理它 - 例如plot(b)

请注意,并行方法取决于您的平台。在您的 Windows 机器上,您需要使用parallel="snow".

于 2013-04-12T19:48:19.107 回答
8

我没有在 Windows 上测试foreach后端parallel,但我相信这对你有用:

library(foreach)
library(doSNOW)

cl <- makeCluster(c("localhost","localhost"), type = "SOCK")
registerDoSNOW(cl=cl)

n<-1000
boot<-1000
x<-rnorm(n,0,1)
y<-rnorm(n,1+2*x,2)
data<-data.frame(x,y)
boot_b <- foreach(i=1:boot, .combine=c) %dopar% {
  bootstrap_data<-data[sample(nrow(data),nrow(data),replace=T),]
  unname(lm(y~x,bootstrap_data)$coef[2])
}
于 2013-04-12T18:50:10.507 回答
3

我认为主要问题是你有很多小任务。在某些情况下,您可以通过使用任务分块来提高性能,这会导致 master 和 worker 之间的数据传输更少但更大,这通常更有效:

boot_b <- foreach(b=idiv(boot, chunks=getDoParWorkers()), .combine='c') %dopar% {
  sapply(1:b, function(i) {
    bdata <- data[sample(nrow(data), nrow(data), replace=T),]
    lm(y~x, bdata)$coef[[2]]
  })
}

我喜欢为此使用该idiv功能,但b=rep(boot/detectCores(),detectCores())如果您愿意,也可以。

于 2013-04-20T14:12:18.407 回答
0

这是一个老问题,但我认为使用data.table. 在使用更大的数据集之前,不会真正注意到这些好处。把这个答案放在这里是为了帮助其他可能需要引导更大数据集的人

library(data.table)
setDT(data) # convert data.frame to data.table by reference
system.time({
  b <- rbindlist(
    lapply(
      1:boot,
      function(i) {
        data.table(
          # store the statistic
          'statistic' = lm(y ~ x, data=data[sample(.N, .N, replace = T)])$coef[[2]],
          # store the iteration
          'iteration' = i
        )
      }
    )
  )
})
# 1.66 seconds on my system
ggplot(b) + geom_density(aes(x = statistic))

然后,您可以通过使用 package.json 进一步提高性能parallel

library(parallel)
cl <- makeCluster(detectCores())            # use all cores on machine, can change this

clusterExport(                              # give it the variables it needs #nolint
  cl,
  c(
    "data"
  ),
  envir = environment()
)
clusterEvalQ(                               # give it libraries needed #nolint
  cl,
  c(
    library(data.table)
  )
)

system.time({
  b <- rbindlist(
    parLapply(              # this is changed to be in parallel
      cl,                   # give it the cluster you created earlier
      1:boot,
      function(i) {
        data.table(
          'statistic' = lm(y ~ x, data=data[sample(.N, .N, replace = T)])$coef[[2]],
          'iteration' = i
        )
      }
    )
  )
})

stopCluster(cl)
# .47 seconds on my machine
于 2021-11-16T12:52:17.053 回答