ML-Estimation中的并行设计矩阵,参数向量乘法

时间:2014-07-10 11:55:52

标签: r matrix parallel-processing matrix-multiplication

我使用R的optim-程序(使用BFGS)进行最大似然优化。

每次评估目标函数时,都会发生一对矢量矩阵乘法,其中相同的设计矩阵将与变化参数的矢量进行后复制。

如果我只是使用parallel包在核心之间分配作业,那么每次迭代中的分发时间基本上会导致计算时间,如果与普通矩阵矢量产品相比,它甚至需要更长的并行版本。

我想要做的是在一次核心之间分配矩阵的片段,然后对片段进行乘法,因为矩阵在迭代之间不会改变。 基本上我不希望每次迭代都会分发相同的对象。

到目前为止我所做的是

nc <- detectCores()
cl <- makeCluster(rep("localhost", nc))
matprod.par <- function(cl, A, B){
    idx <- splitIndices(nrow(A), length(cl))
    Alist <- lapply(idx, function(ii) A[ii,,drop=FALSE])
    ans <- clusterApply(cl, Alist, get("%*%"), B)
    do.call(rbind, ans)
}

此处,clusterApply - 函数在核心之间分发A,即Alist。是否有可能在核心之间分配Alist,然后对分布式碎片进行乘法并通过clusterApply将它们重新组合在一起?

/修改

我将Steve Weston的clusterCall方法与简单的foreach-%dopar%-approch进行了比较:

matprod.par1 <-function(Alist,B,nc){
    par <- foreach(i=1:nc, .combine=rbind) %dopar%{
        Alist[[i]]%*%B
    }
}

以及clusterExport-approach,与clusterCall方法不同,它将所有内容复制到每个spawn。

matprod.par2 <-function(obj1,obj2,nc){
    return( do.call(rbind, sfClusterApplyLB(1:nc, function(i) eval(as.name(obj1))[[i]]%*%eval(as.name(obj2)))) )
}

我将运行两个1000x1000矩阵的矩阵乘法,在具有30GB RAM的8核群集上运行100次

设置为

nr <- 1000
A <- matrix(round(rnorm(nr^2),1),nr=nr)
B <- t(A) + 4
ordinary <- A %*% B

这就是我所看到的 enter image description here

在y轴上,我们以秒为单位看到时间。

显然,sfCluster-Approach表现最好但如果矩阵非常大(在我的情况下它们是),则不可行。因此,即使foreach更好,使用clusterCall方法也是明智的吗?我不确定机器人是不会将所有内容复制到正确的位置?

1 个答案:

答案 0 :(得分:1)

我会使用clusterApply将A的子矩阵分发给worker,然后使用clusterCall在这些子矩阵上重复执行操作。例如:

A <- matrix(rnorm(16), 4)
idx <- splitIndices(nrow(A), length(cl))
Alist <- lapply(idx, function(ii) A[ii,,drop=FALSE])
clusterApply(cl, Alist, function(a) { subA <<- a; NULL })

matprod.par <- function(cl, B) {
  do.call(rbind, clusterCall(cl, function(b) subA %*% b, B))
}

AB <- matprod.par(cl, matrix(1:16, 4))
AC <- matprod.par(cl, matrix(rnorm(16), 4))

与clusterExport不同,clusterApply可以导出变量的不同值&#34; subA&#34;对于每个集群工作者。

clusterCall函数在此上下文中非常有用,因为它允许您迭代已经分发给工作者的数据,并且您仍然可以传递&#34; B&#34;以及每项任务。

更新

首先,我应该注意到我的例子有三个假设:

  • 真正的问题涉及的计算比简单矩阵乘法更多,因为在主服务器上顺序完成更好;
  • &#34; matprod.par&#34;函数将被执行多次,因此值得预先分配矩阵&#34; A&#34;因为它会被重用;
  • &#34; B&#34;每次调用&#34; matprod.par&#34;时矩阵都不同,所以不能重复使用。

&#34; foreach&#34;您提供的示例并未预先分发&#34; A&#34;,因此它无法重复使用&#34; A&#34;如果它被多次调用。 &#34; foreach&#34;和&#34; sfClusterApplyLB&#34;示例复制了所有&#34; A&#34;对于所有使用更多记忆的工人,正如你所指出的那样。

如果你打算打电话给#34; matprod.par&#34;多次,您可以使用:

matprod.par <- function(Alist, B) {
    foreach(a=Alist, .combine=rbind) %dopar% {
        a %*% B
    }
}

避免复制所有&#34; A&#34;对所有的工人。这相当于:

matprod.par <- function(cl, Alist, B) {
  do.call(rbind, clusterApply(cl, Alist, get('%*%'), B))
}

它的运行速度比foreach版本快一些,因为它的开销较小。

如果 要多次调用matprod.par,预分发时间就变得无关紧要了,并且&#34; clusterCall&#34;例子会更快,因为没有&#34; A&#34;被再次发送给工人。交叉发生的位置取决于调用matprod.par的次数,&#34; A&#34;的大小,以及工人的数量。