使用 foreach 函数和 doParallel 库在 R 中嵌套 for 循环
Nested for loops in R using foreach function and doParallel library
我正在尝试计算矩阵中各列之间的余弦相似度。我能够使用标准的 for 循环使其工作,但是当我尝试使其并行 运行 以使代码 运行 更快时,它没有给我相同的答案。问题是我无法使用 foreach 循环方法获得相同的答案。我怀疑我没有使用正确的语法,因为我已经使用了单个 foreach 循环。我试图使第二个循环成为常规的 for 循环,并且我在 foreach 循环中使用了 %:%
参数,但是函数甚至没有 运行。
请看下面我附上的代码。在此先感谢您的帮助。
## Function that calculates cosine similarity using paralel functions.
#for calculating parallel processing
library(doParallel)
## Set up cluster on 8 cores
cl = makeCluster(8)
registerDoParallel(cl)
#create an example data
x=array(data=sample(1000*100), dim=c(1000, 100))
## Cosine similarity function using sequential for loops
cosine_seq =function (x) {
co = array(0, c(ncol(x), ncol(x)))
for (i in 2:ncol(x)) {
for (j in 1:(i - 1)) {
co[i, j] = crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j]))
}
}
co = co + t(co)
diag(co) = 1
return(as.matrix(co))
}
## Cosine similarity function using parallel for loops
cosine_par =function (x) {
co = array(0, c(ncol(x), ncol(x)))
foreach (i=2:ncol(x)) %dopar% {
for (j in 1:(i - 1)) {
co[i, j] = crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j]))
}
}
co = co + t(co)
diag(co) = 1
return(as.matrix(co))
}
## Calculate cosine similarity
tm_seq=system.time(
{
x_cosine_seq=cosine_seq(x)
})
tm_par=system.time(
{
x_cosine_par=cosine_par(x)
})
## Test equality of cosine similarity functions
all.equal(x_cosine_seq, x_cosine_par)
#stop cluster
stopCluster(cl)
嵌套循环的正确并行化使用%:%
(阅读here)。
library(foreach)
library(doParallel)
registerDoParallel(detectCores())
cosine_par1 <- function (x) {
co <- foreach(i=1:ncol(x)) %:%
foreach (j=1:ncol(x)) %dopar% {
co = crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j]))
}
matrix(unlist(co), ncol=ncol(x))
}
我建议你用 Rcpp 编写,而不是 运行 并行编写,因为 foreach(i=2:n, .combine=cbind)
不会总是以正确的顺序绑定列。另外,在上面的代码中我只删除了下三角条件,但是 运行 时间比非并行代码时间慢得多。
set.seed(186)
x=array(data=sample(1000*100), dim=c(1000, 100))
cseq <- cosine_seq(x)
cpar <- cosine_par1(x)
all.equal(cpar, cseq)
#[1] TRUE
head(cpar[,1])
#[1] 1.0000000 0.7537411 0.7420011 0.7496145 0.7551984 0.7602620
head(cseq[,1])
#[1] 1.0000000 0.7537411 0.7420011 0.7496145 0.7551984 0.7602620
附录: 对于这个特定问题,cosine_seq
的(半)矢量化是可能的; cosine_vec
比 cosine_seq
快 40-50 倍。
cosine_vec <- function(x){
crossprod(x) / sqrt(tcrossprod(apply(x, 2, crossprod)))
}
all.equal(cosine_vec(x), cosine_seq(x))
#[1] TRUE
library(microbenchmark)
microbenchmark(cosine_vec(x), cosine_seq(x), times=20L, unit="relative")
#Unit: relative
# expr min lq mean median uq max neval
# cosine_vec(x) 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 20
# cosine_seq(x) 55.81694 52.80404 50.36549 52.17623 49.56412 42.94437 20
要在foreach
中做嵌套循环并使用并行实现,有两种方法。
%:%
+ %dopar%
%dopar%
+ %do%
请注意,对于(1),它实际上创建了一个foreach 对象,您不能在两者之间添加任何内容。否则,您将收到一条错误消息:"%:%" was passed an illegal right operand
。
对于 (2),您可以在两者之间插入任何内容。但是请记住在外循环中将 foreach
添加到 .package
参数,因为内部 foreach 使用 foreach
package.
以下是解决余弦矩阵问题的巧妙方法。注意为了说明(2),我多加了一行,余弦矩阵计算时记得去掉。
testfunc <- function (x) {
cl<-makeCluster(4)
registerDoParallel(cl)
co <- foreach(i=1:ncol(x), .combine = 'rbind', .packages = c('foreach', 'stats')) %dopar% {
k <- rnorm(3)
foreach (j=1:ncol(x), .combine = 'c') %do% {
crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j])) + k - k
}
}
stopCluster(cl)
co
}
x <- array(data=sample(20*10), dim=c(20, 10))
testfunc(x)
我正在尝试计算矩阵中各列之间的余弦相似度。我能够使用标准的 for 循环使其工作,但是当我尝试使其并行 运行 以使代码 运行 更快时,它没有给我相同的答案。问题是我无法使用 foreach 循环方法获得相同的答案。我怀疑我没有使用正确的语法,因为我已经使用了单个 foreach 循环。我试图使第二个循环成为常规的 for 循环,并且我在 foreach 循环中使用了 %:%
参数,但是函数甚至没有 运行。
请看下面我附上的代码。在此先感谢您的帮助。
## Function that calculates cosine similarity using paralel functions.
#for calculating parallel processing
library(doParallel)
## Set up cluster on 8 cores
cl = makeCluster(8)
registerDoParallel(cl)
#create an example data
x=array(data=sample(1000*100), dim=c(1000, 100))
## Cosine similarity function using sequential for loops
cosine_seq =function (x) {
co = array(0, c(ncol(x), ncol(x)))
for (i in 2:ncol(x)) {
for (j in 1:(i - 1)) {
co[i, j] = crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j]))
}
}
co = co + t(co)
diag(co) = 1
return(as.matrix(co))
}
## Cosine similarity function using parallel for loops
cosine_par =function (x) {
co = array(0, c(ncol(x), ncol(x)))
foreach (i=2:ncol(x)) %dopar% {
for (j in 1:(i - 1)) {
co[i, j] = crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j]))
}
}
co = co + t(co)
diag(co) = 1
return(as.matrix(co))
}
## Calculate cosine similarity
tm_seq=system.time(
{
x_cosine_seq=cosine_seq(x)
})
tm_par=system.time(
{
x_cosine_par=cosine_par(x)
})
## Test equality of cosine similarity functions
all.equal(x_cosine_seq, x_cosine_par)
#stop cluster
stopCluster(cl)
嵌套循环的正确并行化使用%:%
(阅读here)。
library(foreach)
library(doParallel)
registerDoParallel(detectCores())
cosine_par1 <- function (x) {
co <- foreach(i=1:ncol(x)) %:%
foreach (j=1:ncol(x)) %dopar% {
co = crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j]))
}
matrix(unlist(co), ncol=ncol(x))
}
我建议你用 Rcpp 编写,而不是 运行 并行编写,因为 foreach(i=2:n, .combine=cbind)
不会总是以正确的顺序绑定列。另外,在上面的代码中我只删除了下三角条件,但是 运行 时间比非并行代码时间慢得多。
set.seed(186)
x=array(data=sample(1000*100), dim=c(1000, 100))
cseq <- cosine_seq(x)
cpar <- cosine_par1(x)
all.equal(cpar, cseq)
#[1] TRUE
head(cpar[,1])
#[1] 1.0000000 0.7537411 0.7420011 0.7496145 0.7551984 0.7602620
head(cseq[,1])
#[1] 1.0000000 0.7537411 0.7420011 0.7496145 0.7551984 0.7602620
附录: 对于这个特定问题,cosine_seq
的(半)矢量化是可能的; cosine_vec
比 cosine_seq
快 40-50 倍。
cosine_vec <- function(x){
crossprod(x) / sqrt(tcrossprod(apply(x, 2, crossprod)))
}
all.equal(cosine_vec(x), cosine_seq(x))
#[1] TRUE
library(microbenchmark)
microbenchmark(cosine_vec(x), cosine_seq(x), times=20L, unit="relative")
#Unit: relative
# expr min lq mean median uq max neval
# cosine_vec(x) 1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 20
# cosine_seq(x) 55.81694 52.80404 50.36549 52.17623 49.56412 42.94437 20
要在foreach
中做嵌套循环并使用并行实现,有两种方法。
%:%
+%dopar%
%dopar%
+%do%
请注意,对于(1),它实际上创建了一个foreach 对象,您不能在两者之间添加任何内容。否则,您将收到一条错误消息:"%:%" was passed an illegal right operand
。
对于 (2),您可以在两者之间插入任何内容。但是请记住在外循环中将 foreach
添加到 .package
参数,因为内部 foreach 使用 foreach
package.
以下是解决余弦矩阵问题的巧妙方法。注意为了说明(2),我多加了一行,余弦矩阵计算时记得去掉。
testfunc <- function (x) {
cl<-makeCluster(4)
registerDoParallel(cl)
co <- foreach(i=1:ncol(x), .combine = 'rbind', .packages = c('foreach', 'stats')) %dopar% {
k <- rnorm(3)
foreach (j=1:ncol(x), .combine = 'c') %do% {
crossprod(x[, i], x[, j])/sqrt(crossprod(x[, i]) * crossprod(x[, j])) + k - k
}
}
stopCluster(cl)
co
}
x <- array(data=sample(20*10), dim=c(20, 10))
testfunc(x)