目标是在R中实现正交投影非负矩阵分解(opnmf)的快速版本。我正在翻译可用的here的matlab代码。
我实现了Vanilla R版本,但是它比20因子解决方案在我的数据上的Matlab实现(〜225000 x 150)要慢得多(慢5.5倍)。
因此,我认为使用c ++可能会加快速度,但其速度与R相似。我认为这可以进行优化,但不确定如何成为c ++的新手。 Here是讨论类似问题的主题。
这是我的RcppArmadillo实现。
// [[Rcpp::export]]
Rcpp::List arma_opnmf(const arma::mat & X, const arma::mat & W0, double tol=0.00001, int maxiter=10000, double eps=1e-16) {
arma::mat W = W0;
arma::mat Wold = W;
arma::mat XXW = X * (X.t()*W);
double diffW = 9999999999.9;
Rcout << "The value of maxiter : " << maxiter << "\n";
Rcout << "The value of tol : " << tol << "\n";
int i;
for (i = 0; i < maxiter; i++) {
XXW = X * (X.t()*W);
W = W % XXW / (W * (W.t() * XXW));
//W = W % (X*(X.t()*W)) / (W*((W.t()*X)*(X.t()*W)));
arma::uvec idx = find(W < eps);
W.elem(idx).fill(eps);
W = W / norm(W,2);
diffW = norm(Wold-W, "fro") / norm(Wold, "fro");
if(diffW < tol) {
break;
} else {
Wold = W;
}
if(i % 10 == 0) {
Rcpp::checkUserInterrupt();
}
}
return Rcpp::List::create(Rcpp::Named("W")=W,
Rcpp::Named("iter")=i,
Rcpp::Named("diffW")=diffW);
}
此suggested issue证实了matlab相当快,因此使用R / c ++时没有希望吗?
测试是在Windows 10和Ubuntu 16. R版本4.0.0上进行的。
编辑
在下面的答案中有趣的评论之后。我要发布其他详细信息。我在带有R 3.5.3的Windows 10计算机上进行了测试(这就是Microsoft提供的功能),比较结果表明,带有Microsoft R的RcppArmadillo最快。
R
user system elapsed
213.76 7.36 221.42
R与RcppArmadillo
user system elapsed
179.88 3.44 183.43
Microsoft的Open R
user system elapsed
167.33 9.96 45.94
微软与RcppArmadillo的公开赛
user system elapsed
85.47 4.66 23.56
答案 0 :(得分:3)
您是否知道这段代码是由一对称为LAPACK和BLAS的库“最终”执行的?
您是否知道Matlab附带高度优化的产品?您是否知道在R上运行的所有系统上,您都可以更改所使用的LAPACK / BLAS。
差异非常重要。就在今天早上,一个朋友发布了此tweet,对比了在同一Windows计算机上但在两种不同R环境中运行的相同R代码。 快6倍一个“简单”地使用并行LAPACK / BLAS实现。
在这里,您甚至没有告诉我们您所使用的操作系统。您可以为运行R的所有操作系统获取OpenBLAS(使用并行性)。您甚至可以在某些操作系统上相当容易地获得Intel MKL(MIRC也使用了IIRC)。对于Ubuntu / Debian,我发布了a script on GitHub,只需一步即可完成。
最后,很多年前,我“继承”了在一台(当时很大的)Windows计算机上在Matlab中运行的快速程序。我使用RcppArmadillo重写了C ++中的Matlab部分(仔细而缓慢地努力),这导致了一些改进因素-并且因为我们可以在同一台计算机上与R并行地运行该代码(现在为开源),还有其他一些因素。总的来说,数量级的变化将一天的模拟变成了几分钟的运行。所以“是的,你可以。”
编辑:您可以访问Ubuntu,可以通过一个命令从基本的LAPACK / BLAS切换到OpenBLAS,尽管我对Ubuntu 16.04不再那么熟悉(因为我自己运行20.04) )。
编辑2:从Josef's tweet进行比较,我也是维护者(作为r-base的一部分)的Docker Rocker Project容器可以使用OpenBLAS。 [1]因此,一旦我们通过 apt-get install libopenblas-dev
添加了它们,例如 ,一个简单的重复矩阵叉积的时间就从
root@0eb44b1fcc06:/# Rscript -e 'v <- matrix(1:1e6,1e3); system.time(replicate(10, crossprod(v,v)))'
user system elapsed
9.289 0.084 9.373
root@0eb44b1fcc06:/#
到
root@67bd334f53d4:/# Rscript -e 'v <- matrix(1:1e6,1e3); system.time(replicate(10, crossprod(v,v)))'
user system elapsed
2.259 2.370 0.447
root@67bd334f53d4:/#
这很重要。