【问题标题】:Why is my Rcpp code is much slower than glmnet's?为什么我的 Rcpp 代码比 glmnet 慢得多?
【发布时间】:2019-01-14 06:27:41
【问题描述】:

我从this 站点编辑了套索代码,以将其用于多个 lambda 值。 我将 lassoshooting 包用于一个 lambda 值(此包适用于一个 lambda 值),并使用 glmnet 用于多个 lambda 值进行比较。

系数估计值不同,这是预期的,因为标准化和缩减到原始规模。这超出了范围,在这里并不重要。

对于一种参数情况,lassoshooting 快 1.5 倍。

这两种方法都在我的代码中使用了所有 100 个 lambda 值来处理多个 lambda 情况。但是 glmnet 比我的 cpp 代码快 7.5 倍。当然,我预计 glmnet 会更快,但这个数量似乎太多了。这是正常的还是我的代码错误?



编辑

我还附加了 lshoot 函数,它计算 R 循环中的系数路径。这也优于我的 cpp 代码。

我可以改进我的 cpp 代码吗?

C++ 代码:

// [[Rcpp::depends(RcppArmadillo)]]

#include <RcppArmadillo.h>
using namespace Rcpp;
using namespace arma;


// [[Rcpp::export]]
vec softmax_cpp(const vec & x, const vec & y) {
  return sign(x) % max(abs(x) - y, zeros(x.n_elem));
}

// [[Rcpp::export]]
mat lasso(const mat & X, const vec & y, const vec & lambda,
           const double tol = 1e-7, const int max_iter = 10000){
  int p = X.n_cols; int lam = lambda.n_elem;
  mat XX = X.t() * X;
  vec Xy = X.t() * y;
  vec Xy2 = 2 * Xy;
  mat XX2 = 2 * XX;
  mat betas = zeros(p, lam); // to store the betas

  vec beta = zeros(p); // initial beta for each lambda

  bool converged = false;
  int iteration = 0;
  vec beta_prev, aj, cj;

  for(int l = 0; l < lam; l++){
    while (!converged && (iteration < max_iter)){

      beta_prev = beta;

      for (int j = 0; j < p; j++){
        aj = XX2(j,j);
        cj = Xy2(j) - dot(XX2.row(j), beta) + beta(j) * XX2(j,j);
        beta(j) = as_scalar(softmax_cpp(cj / aj, as_scalar(lambda(l)) / aj));
      }
      iteration = iteration + 1;
      converged =  norm(beta_prev - beta, 1) < tol;  
    }
    betas.col(l) = beta;
    iteration = 0;
    converged = false;
  }
  return betas;
}

R 代码:

library(Rcpp)
library(rbenchmark)
library(glmnet)
library(lassoshooting)

sourceCpp("LASSO.cpp")

library(ElemStatLearn)
X <- as.matrix(prostate[,-c(9,10)])
y <- as.matrix(prostate[,9])
lambda_one <- 0.1
benchmark(cpp=lasso(X,y,lambda_one),
          lassoshooting=lassoshooting(X,y,lambda_one)$coefficients,
          order="relative", replications=100)[,1:4]

################################################
lambda <- seq(0,10,len=100)

benchmark(cpp=lasso(X,y,lambda),
          glmn=coef(glmnet(X,y,lambda=lambda)),
          order="relative", replications=100)[,1:4]

    ####################################################

编辑

lambda <- seq(0,10,len=100)

lshoot <- function(lambda){
  betas <- matrix(NA,8,100)
  for(l in 1:100){
    betas[, l] <- lassoshooting(X,y,lambda[l])$coefficients
  }
  return(betas)
}

benchmark(cpp=lasso(X,y,lambda),
          lassoshooting_loop=lshoot(lambda),
          order="relative", replications=300)[,1:4]

一种参数情况的结果:

           test replications elapsed relative
2 lassoshooting          300    0.06      1.0
1           cpp          300    0.09      1.5

多参数情况的结果:

  test replications elapsed relative
2 glmn          300    0.70    1.000
1  cpp          300    5.24    7.486

套索循环和 cpp 的结果:

                test replications elapsed relative
2 lassoshooting_loop          300    4.06    1.000
1                cpp          300    6.38    1.571

【问题讨论】:

  • 您在构建代码时是否启用了优化?
  • 抱歉回复晚了。我在下面的答案下附上了评论。谢谢
  • 你是如何编译你的代码的?
  • 使用 Rcpp。 Rcpp 在 R 中生成一些函数。当我在 R 中运行这个函数时,它会在 C++ 后台编译代码。

标签: c++ r rcpp lasso-regression


【解决方案1】:

包 {glmnet} 使用热启动和特殊规则来丢弃大量预测变量,这使得整个“正则化路径”的拟合速度非常快。

their paper

【讨论】:

  • 这说明了情况。 (+1)我附加了一个新代码,用于比较多参数情况下的套索射击和 cpp(见编辑)。但是我的cpp代码仍然很糟糕。也正常吗?我可以进行编辑以改进我的 cpp 代码吗?
  • @mert 正如另一位评论者所提到的,您似乎在编译代码时没有进行优化。如果是这样,难怪它很慢。现代 C++ 编译器严重依赖于启用的优化。
  • 很抱歉,我对 C++ 很陌生(通过 Rcpp)。也许你写的很明显,但我不知道该怎么做。你能给我推荐一个学习它的参考吗?
猜你喜欢
  • 1970-01-01
  • 2021-09-09
  • 2019-08-28
  • 2016-10-23
  • 2021-01-24
  • 2013-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多