R多核超高维单变量分析
R multicore ultra-high dimensional univariate analysis
我有一个由 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))
}
我想知道如何改进它以使其更有效率?
这是一个很好的例子,说明将问题分解成多个部分的步骤如何真正影响并行计算的好处。我遗漏了一些你的代码(例如 log t运行sformation of covariates and elimination of missing values)这对问题来说不是必需的。我认为您想避免在每次调用时都使用 t运行sposing 整个矩阵——只需在脚本顶部执行一次即可。 AFAIK R 以列主要顺序存储数据,因此通过处理列来避免该步骤可能已经为您节省了一些时间。
在第一次试用中,我先 运行 一个连续的版本,看看有多少改进。这是在 2.5 GHz 的 AMD Phenom 9850 四核和 8 GB RAM(太旧了)上。
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() 的调用一起工作。
我有一个由 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))
}
我想知道如何改进它以使其更有效率?
这是一个很好的例子,说明将问题分解成多个部分的步骤如何真正影响并行计算的好处。我遗漏了一些你的代码(例如 log t运行sformation of covariates and elimination of missing values)这对问题来说不是必需的。我认为您想避免在每次调用时都使用 t运行sposing 整个矩阵——只需在脚本顶部执行一次即可。 AFAIK R 以列主要顺序存储数据,因此通过处理列来避免该步骤可能已经为您节省了一些时间。
在第一次试用中,我先 运行 一个连续的版本,看看有多少改进。这是在 2.5 GHz 的 AMD Phenom 9850 四核和 8 GB RAM(太旧了)上。
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() 的调用一起工作。