计算向量上正态分布的 cdf 的最快方法 - R::pnorm vs erfc vs?
Fastest way to compute the cdf of the Normal distribution over vectors - R::pnorm vs erfc vs?
我希望我改写后的问题现在符合 Whosebug 的标准。请考虑以下示例。我正在编写一个对数似然函数,其中计算向量的 cdf 是最耗时的部分。示例 1 使用 R::pnorm
,示例 2 使用 erfc
近似正常的 cdf。如您所见,结果非常相似,ercf 版本更快一些。
在实践中(在 MLE 中)然而事实证明 ercf 并不那么精确,这使得算法 运行 进入 inf 区域,除非人们准确地设置约束。我的问题:
1) 我错过了什么吗?是否有必要实施一些错误处理(对于 erfc)?
2) 您有任何其他加速代码的建议或替代方案吗?研究并行化 for 循环是否值得?
require(Rcpp)
require(RcppArmadillo)
require(microbenchmark)
#Example 1 : standard R::pnorm
src1 <- '
NumericVector ppnorm(const arma::vec& x,const arma::vec& mu,const arma::vec& sigma, int lt, int lg) {
int n = x.size();
arma::vec res(n);
for (int i=0; i<n; i++) {
res(i) = R::pnorm(x(i),mu(i),sigma(i),lt,lg);
}
return wrap(res);
}
'
#Example 2: approximation with ercf
src2 <- '
NumericVector ppnorm(const arma::vec& x,const arma::vec& mu,const arma::vec& sigma, int lt, int lg) {
int n = x.size();
arma::vec res(n);
for (int i=0; i<n; i++) {
res(i) = 0.5 * erfc(-(x(i) - mu(i))/sigma(i) * M_SQRT1_2);
}
if (lt==0 & lg==0) {
return wrap(1 - res);
}
if (lt==1 & lg==0) {
return wrap(res);
}
if (lt==0 & lg==1) {
return wrap(log(1 - res));
}
if (lt==1 & lg==1) {
return wrap(log(res));
}
}
'
#some random numbers
xex = rnorm(100,5,4)
muex = rnorm(100,3,1)
siex = rnorm(100,0.8,0.3)
#compile c++ functions
func1 = cppFunction(depends = "RcppArmadillo",code=src1) #R::pnorm
func2 = cppFunction(depends = "RcppArmadillo",code=src2) #ercf
#run with exemplaric data
res1 = func1(xex,muex,siex,1,0)
res2 = func2(xex,muex,siex,1,0)
# sum of squared errors
sum((res1 - res2)^2,na.rm=T)
# 6.474419e-32 ... very small
#benchmarking
microbenchmark(func1(xex,muex,siex,1,0),func2(xex,muex,siex,1,0),times=10000)
#Unit: microseconds
#expr min lq mean median uq max neval
#func1(xex, muex, siex, 1, 0) 11.225 11.9725 13.72518 12.460 13.617 103.654 10000
#func2(xex, muex, siex, 1, 0) 8.360 9.1410 10.62114 9.669 10.769 205.784 10000
#my machine: Ubuntu 14.04 LTS, i7 2640M 2.8 Ghz x 4, 8GB memory, RRO 3.2.0 based on version R 3.2.0
1) 嗯,你真的应该使用 R 的 pnorm()
作为你的第 0 个例子。
你不这样做,你使用它的 Rcpp 接口。 R 的 pnorm()
已经在 R 内部(即在 C 级别)很好地矢量化,因此很可能与 Rcpp 相当甚至更快。此外,确实 具有覆盖 NA、NaN、Inf 等情况的优势。
2) 如果您正在谈论 MLE,并且您关心速度和准确性,您几乎肯定应该使用对数,而不是 pnorm()
,而是 dnorm()
?
我希望我改写后的问题现在符合 Whosebug 的标准。请考虑以下示例。我正在编写一个对数似然函数,其中计算向量的 cdf 是最耗时的部分。示例 1 使用 R::pnorm
,示例 2 使用 erfc
近似正常的 cdf。如您所见,结果非常相似,ercf 版本更快一些。
在实践中(在 MLE 中)然而事实证明 ercf 并不那么精确,这使得算法 运行 进入 inf 区域,除非人们准确地设置约束。我的问题:
1) 我错过了什么吗?是否有必要实施一些错误处理(对于 erfc)?
2) 您有任何其他加速代码的建议或替代方案吗?研究并行化 for 循环是否值得?
require(Rcpp)
require(RcppArmadillo)
require(microbenchmark)
#Example 1 : standard R::pnorm
src1 <- '
NumericVector ppnorm(const arma::vec& x,const arma::vec& mu,const arma::vec& sigma, int lt, int lg) {
int n = x.size();
arma::vec res(n);
for (int i=0; i<n; i++) {
res(i) = R::pnorm(x(i),mu(i),sigma(i),lt,lg);
}
return wrap(res);
}
'
#Example 2: approximation with ercf
src2 <- '
NumericVector ppnorm(const arma::vec& x,const arma::vec& mu,const arma::vec& sigma, int lt, int lg) {
int n = x.size();
arma::vec res(n);
for (int i=0; i<n; i++) {
res(i) = 0.5 * erfc(-(x(i) - mu(i))/sigma(i) * M_SQRT1_2);
}
if (lt==0 & lg==0) {
return wrap(1 - res);
}
if (lt==1 & lg==0) {
return wrap(res);
}
if (lt==0 & lg==1) {
return wrap(log(1 - res));
}
if (lt==1 & lg==1) {
return wrap(log(res));
}
}
'
#some random numbers
xex = rnorm(100,5,4)
muex = rnorm(100,3,1)
siex = rnorm(100,0.8,0.3)
#compile c++ functions
func1 = cppFunction(depends = "RcppArmadillo",code=src1) #R::pnorm
func2 = cppFunction(depends = "RcppArmadillo",code=src2) #ercf
#run with exemplaric data
res1 = func1(xex,muex,siex,1,0)
res2 = func2(xex,muex,siex,1,0)
# sum of squared errors
sum((res1 - res2)^2,na.rm=T)
# 6.474419e-32 ... very small
#benchmarking
microbenchmark(func1(xex,muex,siex,1,0),func2(xex,muex,siex,1,0),times=10000)
#Unit: microseconds
#expr min lq mean median uq max neval
#func1(xex, muex, siex, 1, 0) 11.225 11.9725 13.72518 12.460 13.617 103.654 10000
#func2(xex, muex, siex, 1, 0) 8.360 9.1410 10.62114 9.669 10.769 205.784 10000
#my machine: Ubuntu 14.04 LTS, i7 2640M 2.8 Ghz x 4, 8GB memory, RRO 3.2.0 based on version R 3.2.0
1) 嗯,你真的应该使用 R 的 pnorm()
作为你的第 0 个例子。
你不这样做,你使用它的 Rcpp 接口。 R 的 pnorm()
已经在 R 内部(即在 C 级别)很好地矢量化,因此很可能与 Rcpp 相当甚至更快。此外,确实 具有覆盖 NA、NaN、Inf 等情况的优势。
2) 如果您正在谈论 MLE,并且您关心速度和准确性,您几乎肯定应该使用对数,而不是 pnorm()
,而是 dnorm()
?