这是马尔可夫链蒙特卡罗(MCMC)算法中出现的问题。我觉得这个问题很常见,特别是在分层高斯模型中。因此,如果有一个更有效的解决方案,那将是很好的。所以问题是这样的:
我有许多正整数向量xi
,{1}从1到n,p.s.d。矩阵i
和p.s.d.矩阵A
。对于每个B
,我想计算以下Cholesky分解:
chol(kron(diagmat(xi),A)+ B)
所以xi
是多元高斯的协方差矩阵,我想从这个高斯中进行采样,因此需要对其进行Cholesky分解。 kron( diagmat( xi ), A ) + B
和A
的维度不小,我有一个很大的B
,因此计算所有n
的上述Cholesky分解是非常耗时的。以下是我使用xi
编写的Rcpp
函数:
RcppArmadillo
我还使用以下代码测试计算效率:将其与R对应物进行比较:
#include <cmath>
#include <Rmath.h>
#include "RcppArmadillo.h"
// [[Rcpp::depends(RcppArmadillo)]]
using namespace arma;
using namespace Rcpp;
// [[Rcpp::export]]
mat test_C(mat A, mat B, mat X){
int n_x = X.n_cols;
int d_B = B.n_rows;
mat sample(d_B, n_x);
mat mS_chol_inv;
for (int i = 0; i < n_x; i++){
mS_chol_inv = inv(trimatu( chol(kron(diagmat( X.col(i) ),A) + B) ));
sample.col(i) = mS_chol_inv*randn(d_B);
}
return(sample);
}
结果如下
test_R <- function(A,B,X){
n_x <- ncol(X)
d_B <- ncol(B)
res <- sapply(1:n_x, function(x){
mS_chol <- chol( kronecker( diag(X[,x]),A ) + B )
return( mS_chol%*%as.matrix( rnorm(d_B) ) )
})
return(res)
}
# Simulate Data
R1 <- matrix(rnorm(24*2),24,2)
A <- R1%*%t(R1) + 0.1*diag(24)
R2 <- matrix(rnorm(264*2),264,2)
B <- R2%*%t(R2) + 0.1*diag(264)
X <- matrix(rpois(11*2178, 5),11,2178)
res <- benchmark(res_R <- test_R(A, B, X),
res_C <- test_C(A, B, X),
columns=c("test", "replications", "elapsed", "relative"),
order="relative",
replications = 2)
可以看出,单次运行大约是10秒,这在MCMC算法中根本不可行。此外,由于> print(res)
test replications elapsed relative
1 res_R <- test_R(A, B, X) 2 18.920 1.000
2 res_C <- test_C(A, B, X) 2 20.724 1.095
支配着计算复杂性,因此使用chol()
优于纯Rcpp
的改进是微不足道的。但也许我没有编写最有效的代码?那么有什么建议吗?
由于R
内的矩阵是非常结构化的,唯一变化的是chol()
,也许有一些我不知道可以有效解决这个问题的代数技巧?我在数学下发布了这个线性代数问题,这里是link。不幸的是到目前为止我还没有得到任何解决方案,人们确实指出这是令人尴尬的平行。
关于代码/代数的任何建议都会有所帮助!谢谢你的时间。
答案 0 :(得分:2)
您可以尝试使用以前称为RRO的艺术家MeasureString中的Microsoft R 。它集成了多线程英特尔MKL库(找到并安装它的相同位置),并且在英特尔硬件矩阵操作上非常快。
免责声明:YMMV
答案 1 :(得分:0)
改用 RcppEigen 会让您受益匪浅。
这是因为:
就您可以预期的性能提升而言,我只能谈谈我的用例,我使用 LLT 分解将矩阵分解到 100x100,但犰狳几乎慢了 50%!
查看 Eigen 和 Armadillo 的源代码! Armadillo 通过通用模板运行每个 cholesky 到 LAPACK/BLAS“辅助”库。 Eigen 在内部运行决策树来选择优化的实现,然后尽可能参考内存中的内部结构直接求解。
tl;dr:对于分解,你不会轻易击败 Eigen。