Flo*_*ian 11 optimization r mathematical-optimization nonlinear-optimization
考虑在父环境中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()
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
当插件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
#
# (...)
Run Code Online (Sandbox Code Playgroud)
这是错误吗?怎么会这样
即使使用R的C-API,我也很难想象如何实现此行为。有任何想法吗?
注意:
作品:
library("optimParallel") # (parallel) wrapper to optim(method="L-BFGS-B")
cl <- makeCluster(2); setDefaultCluster(cl)
optimParallel(par=10, f=fn)
Run Code Online (Sandbox Code Playgroud)作品:
optimize(f=fn, interval=c(-10, 10))
Run Code Online (Sandbox Code Playgroud)作品:
optim(par=10, fn=fn)
Run Code Online (Sandbox Code Playgroud)失败:
optim(par=10, fn=fn, method="BFGS")
Run Code Online (Sandbox Code Playgroud)作品:
library("lbfgs"); library("numDeriv")
lbfgs(call_eval=fn, call_grad=function(x) grad(func=fn, x=x), vars=10)
Run Code Online (Sandbox Code Playgroud)作品:
library("memoise")
fn_mem <- memoise(function(x) x^2)
optim(par=10, f=fn_mem, method="L-BFGS-B")
Run Code Online (Sandbox Code Playgroud)使用R版本3.5.0测试。
发生问题的原因x是,当在“ BFGS”或“ L-BFGS-B”方法下的优化算法的第三次迭代中修改时,不更新的内存地址。
相反,的内存地址与第三次迭代时x的内存地址保持相同xx,并且在函数第三次运行之前将xx其更新为的值,从而使函数返回的“内存”值。xfnret
您可以通过自己,如果你运行下面的代码检索的内存地址进行验证x和xx内部fn()使用address()的功能envnames或data.table包:
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")
Run Code Online (Sandbox Code Playgroud)
其部分输出(假设在运行此代码段之前未执行优化运行)将类似于以下内容:
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)
Run Code Online (Sandbox Code Playgroud)
使用中提供的其他优化方法(例如默认方法)不会发生此问题optim()。
注意:如前所述,该data.table软件包还可以用于检索对象的内存地址,但是在这里,我借此机会宣传我最近发布的软件包envname(除了检索对象的内存地址之外,它还可以检索用户定义的内存地址中的环境名称-等等