R多核超高维单变量分析

时间:2015-11-28 04:05:39

标签: r multithreading parallel-processing parallel-foreach

我有一个由1M协变量组成的数据框,我希望使用R在数据帧的特定列上独立地回归每个协变量,但是采用多核方式。通过单变量分析,我指的是二项式回归或wilcoxon检验。

我目前的代码就是这个

library(MASS)
library(doParallel)
tt=dat.at.fil
nm.cores = detectCores() - 1
cl=makeCluster(nm.cores)
registerDoParallel(cl)
x <- foreach(cnt=1:nrow(tt),.combine=cbind) %dopar% {
whol.dat = data.frame(log10(t(tt)[,cnt]), y=factor(my.y))
deviance(glm(y~., data = whol.dat[-which(whol.dat[,1] == -Inf),], family = "binomial"))
}

library(MASS)
library(doParallel)
tt=dat.at.fil
nm.cores = detectCores() - 1
cl=makeCluster(nm.cores)
registerDoParallel(cl)
x <- foreach(cnt=1:nrow(tt),.combine=cbind) %dopar% {
whol.dat = data.frame(t(tt)[,cnt], y=factor(my.y))
wilcoxon.test(y~., data = whol.dat))
     }

我想知道如何才能提高效率呢?

1 个答案:

答案 0 :(得分:2)

这是一个很好的例子,说明将问题分解为碎片的步骤如何真正影响并行计算的好处。我省略了一些你的代码(例如协变量的日志转换和消除缺失值),这对问题不是必不可少的。我想你想避免在每次调用时转换整个矩阵 - 只需在脚本顶部执行一次。 AFAIK R按列主要顺序存储数据,因此通过处理列来避免该步骤可能已经节省了一些时间。

在第一次试用中,我首先运行了一个串行版本,看看有多少改进。这是在2.5 Phen GHz的AMD Phenom 9850四核处理器上,内存为8 GB(旧版)。

library(doParallel)
library(iterators)
#make covariate data
N = 100
P = 100000 # number of predictors
tt = as.data.frame(matrix(rnorm(N*P),nrow=N,ncol=P))
my.y = rbinom(N,p=0.5,size=1)
y = factor(my.y)

# How fast to do it serially?
system.time(x1 <- foreach(cc = iter(tt, by='col'),.combine=c) %do% {
  deviance(glm(y~cc, family = "binomial"))
}) # elapsed 718 s

nm.cores = detectCores() - 1
cl=makeCluster(nm.cores)
registerDoParallel(cl)

# send entire dataframe to each worker, pull out the desired column
system.time(x2 <- foreach(cnt=1:ncol(tt),.combine=c) %dopar% {
  whol.dat = data.frame(tt[,cnt], y=factor(my.y))
  deviance(glm(y~., data = whol.dat, family = "binomial"))
}) # elapsed 276 s, so 3 x faster

all.equal(x1,x2) # TRUE, just checkin' ...

我的第一个想法是每次向每个工作人员发送整个矩阵可能会带来一些开销,所以我重新编写foreach()以使用iter()将每列发送给工作人员:

system.time(x3 <- foreach(cc = iter(tt, by='col'),.combine=c) %dopar% {
  deviance(glm(y~cc, family = "binomial"))
}) # not much faster, 248s

这确实加快了一些,但并不多。我之前没有使用过迭代器,所以在阅读foreach插图时,我遇到了一个自定义迭代器iblkcol(),它将data.frame分成块,并发送每个块以节省将数据分派到并获取它的开销。从工人那里回来。 is hidden away on Github的代码(见第199-218行)。

## from vignette on foreach:
## use iblkcol() instead of iter in loop to send blocks of columns instead of one at a time
system.time(x4 <- foreach(cc = iblkcol(tt, chunks = nm.cores),.combine=c,.packages='foreach') %dopar% {
  foreach(x = 1:col(cc),.combine=c) %do% {
    deviance(glm(y~cc[,x], family = "binomial"))
  }
}) # 193 s! 

与每次发送一列相比,这是一个重大改进。我认为通过调整glm()的调用可以有一些额外的加速,以利用大多数模型框架从一次调用到下一次调用的重用。同样的事情应该与wilcoxon()的调用一起工作。