簡體   English   中英

我可以使用Rcpp加速我的R代碼嗎?

[英]Can I speedup my R code with Rcpp?

我定義了一個R函數,其中包含一個矩陣,一個向量和一個參數a 我需要計算函數的結果的不同值a R編寫代碼很簡單,但是當矩陣為“大”且參數值的數量很大時,速度非常慢。

我可以在R定義函數並在Rcpp執行for循環嗎?

可以加快計算速度嗎?

R foo函數的一個最小示例是

f <- function(X,y,a){
  p = ncol(X)
  res = (crossprod(X) + a*diag(1,p))%*%crossprod(X,y)
  }

set.seed(0)
X <- matrix(rnorm(50*5),50,5)
y <- rnorm(50)
a <- seq(0,1,0.1)

result <- matrix(NA,ncol(X),length(a))

for(i in 1:length(a)){                  # Can I do this part in Rcpp?
  result[,i] <- f(X,y,a[i])
  }

result

我只是建議避免在循環中重新計算X'XX'y因為它們是不變的。

f <- function (XtX, Xty, a) (XtX + diag(a, ncol(XtX))) %*% Xty

set.seed(0)
X <- matrix(rnorm(50*5),50,5)
y <- rnorm(50)
a <- seq(0,1,0.1)

result1 <- matrix(NA, ncol(X), length(a))

XtX <- crossprod(X)
Xty <- crossprod(X, y)

for(i in 1:length(a)) {
  result1[,i] <- f(XtX, Xty, a[i])
  }

## compare with your `result`
all.equal(result, result1)
#[1] TRUE

幾小時后...

當我回來時,我看到了更多需要簡化的東西。

(XtX + diag(a, ncol(XtX))) %*% Xty = XtX %*% Xty + diag(a, ncol(XtX)) %*% Xty
                                   = XtX %*% Xty + a * Xty

因此,實際上XtX %*% Xty也是循環不變的。

f <- function (XtX.Xty, Xty, a) XtX.Xty + a * Xty

set.seed(0)
X <- matrix(rnorm(50*5),50,5)
y <- rnorm(50)
a <- seq(0,1,0.1)

result2 <- matrix(NA, ncol(X), length(a))

XtX <- crossprod(X)
Xty <- c(crossprod(X, y))  ## one-column matrix to vector
XtX.Xty <- c(XtX %*% Xty)  ## one-column matrix to vector

for(i in 1:length(a)) {
  result2[,i] <- f(XtX.Xty, Xty, a[i])
  }

## compare with your `result`
all.equal(result, result2)
#[1] TRUE

事實證明,我們可以擺脫循環:

## "inline" function `f`
for(i in 1:length(a)) {
  result2[,i] <- XtX.Xty + a[i] * Xty
  }

## compare with your `result`
all.equal(result, result2)
#[1] TRUE

## do it with recycling rule
for(i in 1:length(a)) {
  result2[,i] <- a[i] * Xty
  }
result2 <- XtX.Xty + result2

## compare with your `result`
all.equal(result, result2)
#[1] TRUE

## now use `tcrossprod`
result2 <- XtX.Xty + tcrossprod(Xty, a)

## compare with your `result`
all.equal(result, result2)
#[1] TRUE

我完全同意您的觀點,您在問題中的示例代碼只是一個"foo" 並且您在發布時可能沒有仔細考慮過。 但是,足以表明在編寫循環(R循環或C / C ++ / FORTRAN循環)時,我們應始終設法將那些循環不變式拉出循環以降低計算復雜性。

您關心的是要加快使用Rcpp(或任何編譯語言)的速度。 您想對不容易向量化的R代碼段進行向量化。 但是, "%*%"crossprodtcrossprod映射到BLAS(FORTRAN代碼),而不是R級計算。 您隨時可以將所有向量化。

不要總是將R循環的解釋開銷( 因為R是一種解釋語言 )歸咎於性能不佳。 如果每次迭代都在進行一些“繁重”的計算,例如大矩陣計算(使用BLAS)或擬合統計模型(例如lm ),那么這種開銷是微不足道的。 實際上,如果您確實想用編譯語言編寫這樣的循環,請使用lapply函數。 該函數在C級別實現循環,並為每次迭代調用R函數。 另外, 拉爾夫的答案是等價的Rcpp。 我認為,用R代碼編寫的循環嵌套更有可能效率低下。

李哲源的答案正確地確定了您的情況下應該采取的措施。 至於您最初的問題,答案有兩個:是的,您可以使用Rcpp將循環移至C ++。 不,您不會獲得性能:

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::NumericMatrix fillMatrix(Rcpp::NumericMatrix X,
                   Rcpp::NumericVector y,
                   Rcpp::NumericVector a,
                   Rcpp::Function f) {
  Rcpp::NumericMatrix result = Rcpp::no_init(X.cols(), a.length());
  for (int i = 0; i < a.length(); ++i) {
    result(Rcpp::_, i) = Rcpp::as<Rcpp::NumericVector>(f(X, y, a[i]));
  }
  return result;
}

/*** R
f <- function(X,y,a){
  p = ncol(X)
  res = (crossprod(X) + a*diag(1,p))%*%crossprod(X,y)
  }

X <- matrix(rnorm(500*50),500,50)
y <- rnorm(500)
a <- seq(0,1,0.01)

system.time(fillMatrix(X, y, a, f))
#   user  system elapsed 
#  0.052   0.077   0.075 
system.time({result <- matrix(NA,ncol(X),length(a))

for(i in 1:length(a)){
  result[,i] <- f(X,y,a[i])
  }
})
#   user  system elapsed 
#  0.060   0.037   0.049 
*/

因此,在這種情況下,Rcpp解決方案實際上比R解決方案要慢。 為什么? 因為實際工作是在函數f完成的。 這兩種解決方案都是相同的,但是Rcpp解決方案具有從C ++回調到R的額外開銷。 注意, R中的for循環不一定很慢 順便說一句,這里是一些基准數據:

          expr      min       lq     mean   median       uq      max neval
 fillMatrixR() 41.22305 41.86880 46.16806 45.20537 49.11250 65.03886   100
 fillMatrixC() 44.57131 44.90617 49.76092 50.99102 52.89444 66.82156   100

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM