R中的三维矩阵乘法

R中的三维矩阵乘法,r,matrix,tensor,R,Matrix,Tensor,我有一个简单的问题。我想用R中的另一个3D数组乘以一个3D数组,而不使用for循环 为了说明: 假设我有一个1x3矩阵a: [A1, A2, A3] 我有一个3x3矩阵B: [B1, B2, B3 \\ B4, B5, B6 \\ B7, B8, B9] 我的主要操作是A%*%B生成1x3矩阵 但现在我想重复这个过程10000次,每个过程都有一个不同的a和B,它们的维度与上面相同。我可以使用for循环 for (i in 1:10000) { A[i] %*% B[i] }

我有一个简单的问题。我想用R中的另一个3D数组乘以一个3D数组,而不使用for循环

为了说明

假设我有一个1x3矩阵a:

[A1, A2, A3] 
我有一个3x3矩阵B:

[B1, B2, B3 \\
 B4, B5, B6 \\
 B7, B8, B9]
我的主要操作是
A%*%B
生成1x3矩阵

但现在我想重复这个过程10000次,每个过程都有一个不同的a和B,它们的维度与上面相同。我可以使用for循环

for (i in 1:10000) {
     A[i] %*% B[i]
}
然后我可以存储10000个值

但是,有没有办法不使用for循环就实现同样的事情呢。我在考虑可能的3D数组乘法。但我不知道在R如何做到这一点

Matrix A: 1 x 3 x 10000

[A1, A2, A3] 

Matrix B: 3 x 3 x 10000

[B1, B2, B3
 B4, B5, B6
 B7, B8, B9]
另外,矢量化会有帮助吗


你们能帮忙吗?谢谢大家!

如果您的
A
B
列表
s,您可以使用
mapply()

nn set.seed(1) >A B头(映射层(“%*%”),A,B,SIMPLIFY=FALSE),3) [[1]] [,1] [,2] [,3] [1,] -1.193976 0.1275999 -0.6831007 [[2]] [,1] [,2] [,3] [1,] 1.371143 1.860379 -1.639078 [[3]] [,1] [,2] [,3] [1,] 0.8250047 -0.6967286 1.949236
有几种方法可以通过数组乘法实现这一点。您要付出的代价是将矩阵重新格式化为包含许多零的更大张量。根据定义,这些是稀疏的,因此主要成本是转换的间接费用。当你有10000个数组需要乘法时,它实际上比循环要好

n
乘以(A,B)对的数量,
k
=3表示尺寸

最圆滑的解决方案似乎是将
A
(A
n
by
k
矩阵)的
n
行重组为
n*k
by
k
块的
n*k
块对角矩阵。块
i
i
=1..
n
,在其顶行包含
A
的行
i
,否则为零。将其(右侧)乘以
B
(排列为
k*n
乘以
k
矩阵,该矩阵由
n
维度块
k
乘以
k
)组成,计算所有单个产品,将它们存放在结果的第1行,k+1,2k+1,…,在那里可以将它们挑选出来

f3 <- function(a, b) {
  require(RcppArmadillo) # sparseMatrix package
  n <- dim(b)[3]
  k <- dim(b)[2]
  i0 <- (1:n-1)*k+1
  i <- rep(i0, each=k)
  j <- 1:(k*n)
  aa <- sparseMatrix(i, j, x=c(t(a)), dims=c(n*k, n*k))
  bb <- matrix(aperm(b, c(1,3,2)), nrow=n*k)
  t((aa %*% bb)[i0, ])
}
我们可以将这些解决方案应用于相同的输入,并比较结果:

#
# Create random matrices for testing.
#
k <- 3
n <- 1e6  # Number of (a,B) pairs
a <- matrix(runif(k*n), ncol=k)
b <- array(runif(k^2*n), dim=c(k,k,n))

system.time(c1 <- f1(a,b)) # 4+ seconds
system.time(c3 <- f3(a,b)) # 2/3 second

mean((c1-c3)^2) # Want around 10^-32 or less
#
#创建用于测试的随机矩阵。
#

kfor循环比您想象的更有效

虽然whuber提出了一种非常巧妙的方法,通过将B作为块堆叠在稀疏矩阵中,将其转化为矩阵乘法,但将n(A,B)对相乘的问题并不等同于通常意义上的张量乘法

您已经说过要避免for循环,但是当有效编程时,for循环方法实际上非常有竞争力,我建议您重新考虑它

我将使用与whuber相同的符号,A的维数为nxk,B的维数为kxn,例如:

n <- 1e4
k <- 3
A <- array(rnorm(k*n),c(n,k))
B <- array(rnorm(k*k*n),c(k,k,n))
现在我在一个新的R会话中重新运行whuber的模拟:

> k <- 3
> n <- 1e6
> a <- matrix(runif(k*n), ncol=k)
> b <- array(runif(k^2*n), dim=c(k,k,n))
> 
> system.time(c1 <- f1(a,b))
   user  system elapsed 
   3.40    0.09    3.50 
> system.time(c3 <- f3(a,b))
Loading required package: Matrix
   user  system elapsed 
   1.06    0.24    1.30 
> system.time(c4 <- justAForLoop(a,b))
   user  system elapsed 
   1.27    0.00    1.26 
然而,
f3
比for循环使用更多的RAM。在我的电脑上,我可以使用
n=1e8
成功运行
justAForLoop
,而
f1
f3
都会耗尽内存并失败

摘要

直接for循环方法比sapply
更有效


对于
n
=10000矩阵乘法的问题,运行For循环是简单而有效的,RcppArmadillo包没有定义使用
sparseMatrix()
。您是否打算改为要求矩阵包?+1用于整洁稀疏矩阵解决方案。不过,循环方法可以更有效地实现——请看我的答案。做得很好——谢谢。您可能已经知道,我特别欣赏对计算资源权衡的分析。:-)我希望稀疏矩阵有大约100%到200%的存储开销,这取决于它们索引的数据类型。非常感谢你们的回答,非常感谢!当k=3时,稀疏矩阵解确实优于简单for循环方法,但当k>10时,它会比简单for循环方法更差。你们知道为什么吗?我记得用k测试这些程序,最高可达50。稀疏矩阵方法在我的机器上仍然保持较快的速度,但只有两倍左右。原因是这些技巧所能获得的收益较少,因为大部分工作都是由矩阵乘法组成的,而对于大k,无论您使用哪种解决方案,这项工作都已经非常有效地完成了。@whuber您使用的是哪种机器?在我的机器上,稀疏矩阵解在k=15时变慢。你知道为什么吗?我使用了你在这里发布的代码。对于k=3,我机器上的计算时间非常接近你在上面的评论。这是一个好问题——这就是我在回答中写“YMMV”的原因。对于这些基准测试,我在一台四物理内核的Xeon机器上使用了Windows中的MicrosoftR,24GB内存。对于某些线性代数运算,此设置大约比标准R分布快三倍。我还没有研究它如何与大数据进行扩展。
n <- 1e4
k <- 3
A <- array(rnorm(k*n),c(n,k))
B <- array(rnorm(k*k*n),c(k,k,n))
justAForLoop <- function(A,B) {
  n <- nrow(A)
  for (i in 1:n) A[i,] <- A[i,] %*% B[,,i]
  A
}
f3 <- function(a, b) {
  require(Matrix)
  n <- dim(b)[3]
  k <- dim(b)[2]
  i0 <- (1:n-1)*k+1
  i <- rep(i0, each=k)
  j <- 1:(k*n)
  aa <- sparseMatrix(i, j, x=c(t(a)), dims=c(n*k, n*k))
  bb <- matrix(aperm(b, c(1,3,2)), nrow=n*k)
  (aa %*% bb)[i0, ]
}
> k <- 3
> n <- 1e6
> a <- matrix(runif(k*n), ncol=k)
> b <- array(runif(k^2*n), dim=c(k,k,n))
> 
> system.time(c1 <- f1(a,b))
   user  system elapsed 
   3.40    0.09    3.50 
> system.time(c3 <- f3(a,b))
Loading required package: Matrix
   user  system elapsed 
   1.06    0.24    1.30 
> system.time(c4 <- justAForLoop(a,b))
   user  system elapsed 
   1.27    0.00    1.26 
> system.time(c1 <- f1(a,b))
   user  system elapsed 
   3.23    0.04    3.26 
> system.time(c3 <- f3(a,b))
   user  system elapsed 
   0.33    0.20    0.53 
> system.time(c4 <- justAForLoop(a,b))
   user  system elapsed 
   1.28    0.01    1.30