R optim():使用父环境时的意外行为
Posted
技术标签:
【中文标题】R optim():使用父环境时的意外行为【英文标题】:R optim(): unexpected behavior when working with parent environments 【发布时间】:2019-05-18 11:41:05 【问题描述】:考虑函数fn()
,它将最近的输入x
及其返回值ret <- x^2
存储在父环境中。
makeFn <- function()
xx <- ret <- NA
fn <- function(x)
if(!is.na(xx) && x==xx)
cat("x=", xx, ", ret=", ret, " (memory)", fill=TRUE, sep="")
return(ret)
xx <<- x; ret <<- sum(x^2)
cat("x=", xx, ", ret=", ret, " (calculate)", fill=TRUE, sep="")
ret
fn
fn <- makeFn()
fn()
仅在提供不同输入值时进行计算。否则,它会从父环境中读取ret
。
fn(2)
# x=2, ret=4 (calculate)
# [1] 4
fn(3)
# x=3, ret=9 (calculate)
# [1] 9
fn(3)
# x=3, ret=9 (memory)
# [1] 9
当插件fn()
到optim()
中找到其最小值时,会出现以下意外行为结果:
optim(par=10, f=fn, method="L-BFGS-B")
# x=10, ret=100 (calculate)
# x=10.001, ret=100.02 (calculate)
# x=9.999, ret=100.02 (memory)
# $par
# [1] 10
#
# $value
# [1] 100
#
# (...)
这是一个错误吗?怎么会这样?
即使使用 R 的 C-API,我也很难想象这种行为是如何实现的。有什么想法吗?
注意:
作品:
library("optimParallel") # (parallel) wrapper to optim(method="L-BFGS-B")
cl <- makeCluster(2); setDefaultCluster(cl)
optimParallel(par=10, f=fn)
作品:
optimize(f=fn, interval=c(-10, 10))
作品:
optim(par=10, fn=fn)
失败:
optim(par=10, fn=fn, method="BFGS")
作品:
library("lbfgs"); library("numDeriv")
lbfgs(call_eval=fn, call_grad=function(x) grad(func=fn, x=x), vars=10)
作品:
library("memoise")
fn_mem <- memoise(function(x) x^2)
optim(par=10, f=fn_mem, method="L-BFGS-B")
使用 R 版本 3.5.0 测试。
【问题讨论】:
我会将示例发送到 R-devel 列表 (stat.ethz.ch/mailman/listinfo/r-devel) 【参考方案1】:出现问题是因为x
的内存地址在“BFGS”或“L-BFGS-B”下的优化算法的第三次迭代中被修改时没有更新方法,应该的。
相反,x
的内存地址在第三次迭代时保持与xx
的内存地址相同,这使得xx
在fn
函数之前更新为x
的值第三次运行,从而使函数返回ret
的“内存”值。
如果您运行以下代码,使用envnames 或data.table 包的address()
函数在fn()
中检索x
和xx
的内存地址,您可以自己验证这一点:
library(envnames)
makeFn <- function()
xx <- ret <- NA
fn <- function(x)
cat("\nAddress of x and xx at start of fn:\n")
cat("address(x):", address(x), "\n")
cat("address(xx):", address(xx), "\n")
if(!is.na(xx) && x==xx)
cat("x=", xx, ", ret=", ret, " (memory)", fill=TRUE, sep="")
return(ret)
xx <<- x; ret <<- sum(x^2)
cat("x=", xx, ", ret=", ret, " (calculate)", fill=TRUE, sep="")
ret
fn
fn <- makeFn()
# Run the optimization process
optim(par=0.1, fn=fn, method="L-BFGS-B")
其部分输出(假设在运行此代码 sn-p 之前未进行任何优化运行)将类似于以下内容:
Address of x and xx at start of fn:
address(x): 0000000013C89DA8
address(xx): 00000000192182D0
x=0.1, ret=0.010201 (calculate)
Address of x and xx at start of fn:
address(x): 0000000013C8A160
address(xx): 00000000192182D0
x=0.101, ret=0.010201 (calculate)
Address of x and xx at start of fn:
address(x): 0000000013C8A160
address(xx): 0000000013C8A160
x=0.099, ret=0.010201 (memory)
这个问题不会发生在optim()
中可用的其他优化方法中,例如默认的。
注意:如前所述,data.table
包也可用于检索对象的内存地址,但在这里我借此机会宣传我最近发布的包envnames(除了检索对象的内存地址,它还从他们的内存地址中检索用户定义的环境名称——等等)
【讨论】:
跟踪内存地址的好主意。 - 我认为“BFGS”和“L-BFGS-B”方法都会出现问题。我可能很快会向 CRAN 报告错误。 是的,“BFGS”和“L-BFGS-B”两种方法都会出现问题。以上是关于R optim():使用父环境时的意外行为的主要内容,如果未能解决你的问题,请参考以下文章
使用 Comparator.comparing(HashMap::get) 作为比较器时的意外行为
尝试使用 outerWidth() 和 $(window).resize() 水平居中动态宽度元素时的意外行为