Rai*_*Rai 10 linux windows debian r blas
我一直在 R 中运行一些代码,在测试时意识到 Windows 和 Linux 上的结果是不同的。我试图理解为什么会发生这种情况,但找不到答案。让我们用一个例子来说明它:
这些是一些用于再现性的硬编码值,始终从干净的环境开始。我已经检查过这些值的位表示在 Windows 和 Linux 计算机中完全相同:
data <- structure(list(x = c(0.1, 0.1, 0.1, 5, 5, 5, 10, 10, 10, 20, 20, 20),
y = c(0.013624804, 0.014023006, 0.013169554, 0.70540352,
0.68711807, 0.69233506, 1.4235181, 1.348244, 1.4141854, 2.779813,
2.7567347, 2.7436437)), class = c("data.frame"), row.names = c(NA, 12L))
val <- c(43.3065849160736, 0.00134925463859564, 1.03218302435548, 270.328323775978)
theta <- 1.60812569803848
init <- c(b0 = 2.76836653333333, b1 = 0.0134350095, b2 = 2.15105945932773,
b3 = 6.85922519794374)
Run Code Online (Sandbox Code Playgroud)
现在我定义了一个新变量W,它在 Windows 和 Linux 中的位表示形式再次完全相同:
f <- function(X, b0, b1, b2, b3) {
b0 + (b1 - b0) / (1 + exp(b2*(log(X) - log(b3))))
}
W <- 1 / f(data$x, val[1], val[2], val[3], val[4])^theta
Run Code Online (Sandbox Code Playgroud)
最后我应用一个optim函数:
SSw <- function(Y, X, b0, b1, b2, b3, w) {
sum(w * (Y - f(X, b0, b1, b2, b3))^2)
}
SSw.vec <- function(par) SSw(data$y, data$x, par[1], par[2], par[3], par[4], W)
mod <- optim(init, SSw.vec, method = "L-BFGS-B", lower = c(-Inf,-Inf,-Inf,0))
print(mod$par)
# In Windows it returns:
# b0 b1 b2 b3
# 3.097283e+01 1.831543e-03 1.047613e+00 1.842448e+02
# In Linux it returns:
# b0 b1 b2 b3
# 3.459241e+01 1.530134e-03 1.040363e+00 2.101996e+02
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,差异非常显着,但即使不是……为什么会有差异呢?
任何帮助将非常感激!
sessionInfo()这里我在Windows和Linux上都添加了。
在 Windows 上:
R version 3.6.3 (2020-02-29)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19045)
Matrix products: default
locale:
[1] LC_COLLATE=English_United Kingdom.1252 LC_CTYPE=English_United Kingdom.1252 LC_MONETARY=English_United Kingdom.1252
[4] LC_NUMERIC=C LC_TIME=English_United Kingdom.1252
attached base packages:
[1] stats graphics grDevices datasets utils methods base
loaded via a namespace (and not attached):
[1] Rcpp_1.0.8.3 plyr_1.8.6 cellranger_1.1.0 compiler_3.6.3 pillar_1.7.0 nloptr_1.2.2.2 tools_3.6.3
[8] bit_4.0.4 boot_1.3-24 lme4_1.1-29 lifecycle_1.0.0 tibble_3.1.7 nlme_3.1-144 gtable_0.3.0
[15] lattice_0.20-38 pkgconfig_2.0.3 rlang_1.0.2 Matrix_1.2-18 cli_3.4.1 rstudioapi_0.11 dplyr_1.0.6
[22] generics_0.1.0 vctrs_0.3.8 lmerTest_3.1-3 grid_3.6.3 tidyselect_1.1.1 glue_1.4.2 R6_2.4.1
[29] fansi_0.4.1 readxl_1.3.1 minqa_1.2.4 ggplot2_3.3.6 purrr_0.3.5 magrittr_1.5 scales_1.1.1
[36] ellipsis_0.3.2 MASS_7.3-51.5 splines_3.6.3 colorspace_1.4-1 numDeriv_2016.8-1.1 renv_0.13.2 utf8_1.1.4
[43] munsell_0.5.0 crayon_1.3.4
Run Code Online (Sandbox Code Playgroud)
在 Linux 上:
R version 3.6.3 (2020-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 10 (buster)
Matrix products: default
BLAS: /opt/r/lib/R/lib/libRblas.so
LAPACK: /opt/r/lib/R/lib/libRlapack.so
locale:
[1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8
[4] LC_COLLATE=C LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8
[7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
[10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices datasets utils methods base
loaded via a namespace (and not attached):
[1] compiler_3.6.3 tools_3.6.3 renv_0.13.2
Run Code Online (Sandbox Code Playgroud)
shs*_*shs 16
我在本地计算机上使用 Fedora 37 和 R 4.2.2 运行了您的代码。和其他评论者一样,我也得到了你在 Windows 上得到的结果。然后我拉取了R 版本 3.6.3 的摇杆图像:
\ndocker run -ti rocker/r-ver:3.6.3 R\nRun Code Online (Sandbox Code Playgroud)\n该镜像也是基于 Debian 的。在这里,我能够重新创建您在系统上得到的结果。
\n然后我转移到摇杆版本 4.0.0:
\ndocker run -ti rocker/r-ver:4.0.0 R\nRun Code Online (Sandbox Code Playgroud)\n这里的结果与你在 Windows 上得到的结果以及其他人在他们的机器上得到的结果相同。必须指出的是,在 R 4.0.0 中,rocker 项目从基于 Debian 的映像转移到了 Ubuntu LTS。
\nFedora 能够通过软件包轻松切换 BLAS/LAPACK 后端flexiblas。因此,我能够使用系统上可用的八个不同后端来测试您的代码。如下所示,它们确实会产生不同的结果。特别是,ATLAS后端与您得到的结果有些接近。相比之下,OPENBLAS-OPENMP(Fedora 上的默认设置)、其他OPENBLAS变体NETLIB都产生与 Windows 上相同的结果。第三个家庭BLIS又产生了另一组可能的结果。
其中一个结果是否比其他结果更好?是的!optim()寻找最小化所提供函数的结果。在返回的列表中,它不仅报告最小化参数,还报告它们的值。我已将其包含在下表中。所以ATLAS后端在这里获胜。必须指出的是,这optim()并没有在分析上最小化。所以它总是给出近似结果。这就是为什么初始值和方法对于我们得到的结果很重要。显然,对于你的功能来说,后端也很重要。如果您查看 Buster 上获得的参数,该函数将转到0.002800321。所以它实际上比我们在更现代的系统上得到的结果更好,除了我得到的结果ATLAS。这也恰好比其他后端慢得多。看来,较新的后端可能已经用速度换取了准确性。
如果您的目标是跨平台的一致性,您可以将系统升级到 Debian 11 Bullseye,因为这似乎有一个后端产生与其他现代平台相同的结果,正如@jay.sf 的回答所示。您还可以调查是否可以找到 Buster for Windows 上使用的相同 BLAS 后端版本。
\n此外,您可以尝试更改为当前系统上的另一个 blas 库。以下是如何执行此操作的指南。虽然它适用于 Ubuntu,但由于两者都使用apt,它也应该适用于您的系统。(编辑:我在 Buster 的 VM 中尝试过。可用的 BLAS 后端都没有产生与更现代的系统相同的结果)
最后,如果您觉得您的旧系统上必须有一个更新的 BLAS 库,那么您可以尝试自己向后移植它。我对此没有经验。我不知道这样做是否明智,也不知道成功的可能性有多大。我只是为了完整性而提及它。
\nlibrary(flexiblas)\nlibrary(tidyverse)\n\ntest_fun <- function(i) {\n flexiblas_switch(i)\n data <- structure(list(x = c(0.1, 0.1, 0.1, 5, 5, 5, 10, 10, 10, 20, 20, 20),\n y = c(0.013624804, 0.014023006, 0.013169554, 0.70540352,\n 0.68711807, 0.69233506, 1.4235181, 1.348244, 1.4141854, 2.779813,\n 2.7567347, 2.7436437)), class = c("data.frame"), row.names = c(NA, 12L))\n val <- c(43.3065849160736, 0.00134925463859564, 1.03218302435548, 270.328323775978)\n theta <- 1.60812569803848\n init <- c(b0 = 2.76836653333333, b1 = 0.0134350095, b2 = 2.15105945932773,\n b3 = 6.85922519794374)\n f <- function(X, b0, b1, b2, b3) {\n b0 + (b1 - b0) / (1 + exp(b2*(log(X) - log(b3))))\n }\n W <- 1 / f(data$x, val[1], val[2], val[3], val[4])^theta\n SSw <- function(Y, X, b0, b1, b2, b3, w) {\n sum(w * (Y - f(X, b0, b1, b2, b3))^2)\n }\n SSw.vec <- function(par) SSw(data$y, data$x, par[1], par[2], par[3], par[4], W)\n mod <- optim(init, SSw.vec, method = "L-BFGS-B", lower = c(-Inf,-Inf,-Inf,0))\n return(c(mod$par, value = mod$value))\n}\nRun Code Online (Sandbox Code Playgroud)\nflexiblas_list() |>\n setdiff("__FALLBACK__") |>\n tibble(backend = _) |>\n mutate(\n idx = flexiblas_load_backend(backend),\n res = map(idx, test_fun)\n ) |>\n unnest_wider(res)\n#> # A tibble: 8 \xc3\x97 7\n#> backend idx b0 b1 b2 b3 value\n#> <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl>\n#> 1 NETLIB 2 31.0 0.00183 1.05 184. 0.00282\n#> 2 OPENBLAS-OPENMP 1 31.0 0.00183 1.05 184. 0.00282\n#> 3 ATLAS 3 34.7 0.00168 1.04 209. 0.00280\n#> 4 BLIS-SERIAL 4 27.1 0.00225 1.06 158. 0.00285\n#> 5 BLIS-OPENMP 5 27.1 0.00225 1.06 158. 0.00285\n#> 6 BLIS-THREADS 6 27.1 0.00225 1.06 158. 0.00285\n#> 7 OPENBLAS-SERIAL 7 31.0 0.00183 1.05 184. 0.00282\n#> 8 OPENBLAS-THREADS 8 31.0 0.00183 1.05 184. 0.00282\nRun Code Online (Sandbox Code Playgroud)\n
我现在可以确认你的问题了。我在虚拟机上安装了 Debian Buster,apt install r-base并获得了 R3.5.2,运行了你的代码,它显示了与 OP 相同(可能)“有缺陷”的 Linux 结果。
然而,后来我更新到了 R.4.2.2,但“有缺陷”的结果并没有改变!它使用了libblas3.8. 在我的真实机器上,我运行 Ubuntu、R4.2.2、libblas3.10 并获得(可能)“正确”的 Windows 结果。
不幸的是,我无法libblas3.10在 Debian Buster 上安装,并且我不确定这是否可能(请参阅链接中的 Buster 下未列出它)。请注意,Debian Bullseye 现在是实际版本,而您的系统实际上已经过时了。
正如我的评论\xe2\x80\x94 中所述,过时的 BLAS/LAPACK 可能 \xe2 \x80\x95 仍然被认为会产生错误的结果,因为这些是实际的代数引擎。您也许可以在 Debian Buster 上安装更新的 BLAS/LAPACK,但我倾向于建议您升级到 Debian Bullseye。
\n这有点离题,但减轻平台间差异最简单的方法可能是使用
control = list(parscale = abs(init))
Run Code Online (Sandbox Code Playgroud)
在你的optim()通话中。
这样做的原因是,除非指定梯度函数,否则会自动使用具有固定步长( ,所有参数默认为 1e-3 )的L-BFGS-B有限差分来近似梯度。这通常足够好,但可能会导致困难/不稳定的优化问题,或者当参数的规模非常不同时。 如上所述,告诉我们如何在内部缩放参数,通常会改善结果。ndepsparscaleoptim
通过分析(或自动微分)梯度可能会更好,但这需要更多工作......