R 优化:每次迭代时将值从函数传递到梯度

sky*_*mma 5 optimization r mathematical-optimization

我有一个正在使用optimxR 中的函数进行优化的函数(我也愿意使用optim,因为我不确定它会对我想做的事情产生影响)。optimx与不使用梯度相比,我传递一个梯度是为了(希望)更快地收敛。函数和梯度都使用许多根据每个新参数集计算的相同量。其中一个量的计算成本非常高,并且每次迭代都必须计算该量两次(一次针对函数,另一次针对梯度),这是多余的。我试图找到一种方法来计算这个数量一次,然后将其传递给函数和梯度。

这就是我正在做的事情。到目前为止,这是可行的,但效率很低:

optfunc<-function(paramvec){
    quant1<-costlyfunction(paramvec) 
    #costlyfunction is a separate function that takes a while to run

    loglikelihood<-sum(quant1)**2 
    #not really squared, but the log likelihood uses quant1 in its calculation

    return(loglikelihood)
}

optgr<-function(paramvec){
    quant1<-costlyfunction(paramvec)
    mygrad<-sum(quant1) #again not the real formula, just for illustration
    return(mygrad)
}

optimx(par=paramvec,fn=optfunc,gr=optgr,method="BFGS")
Run Code Online (Sandbox Code Playgroud)

我试图找到一种方法,quant1每次迭代时只计算一次optimx。看来第一步是将fn和组合gr成一个函数。我认为这个问题的答案可能对我有帮助,所以我将优化重新编码为:

optfngr<-function(){
    quant1<-costlyfunction(paramvec)
    optfunc<-function(paramvec){
        loglikelihood<-sum(quant1)**2
        return(loglikelihood)
    }
    optgr<-function(paramvec){
        mygrad<-sum(quant1)
        return(mygrad)
    }
    return(list(fn = optfunc, gr = optgr))
}

do.call(optimx, c(list(par=paramvec,method="BFGS",optfngr() )))
Run Code Online (Sandbox Code Playgroud)

在这里,我收到错误:“optimx.check(par, optcfg$ufn, optcfg$ugr, optcfg$uhess, lower, : 无法在初始参数处计算函数错误。”当然,我的代码存在明显的问题因此,我认为回答以下任何或所有问题可能会有所启发:

  1. paramvec作为唯一的参数传递给optfuncandoptgr所以它optimx知道这paramvec就是需要迭代的内容。但是,我不知道如何传递quant1optfuncoptgr。如果我尝试传递quant1,那么optimx将无法正确识别参数向量,这是真的吗?

  2. 我将optfunc和包装optgr到一个函数中,以便该数量quant1将与两个函数存在于同一函数空间中。如果我能找到一种方法quant1从返回optfunc,然后将其传递给 ,也许我可以避免这种情况optgr。这可能吗?我认为不是,因为 的文档optimx非常清楚该函数需要返回一个标量。

  3. 我知道我也许可以使用点参数作为optimx额外的参数参数,但我知道这些参数用于固定参数,而不是随着每次迭代而改变的参数。除非还有一种方法可以操纵它?

提前致谢!

use*_*330 6

您的方法接近您想要的,但不太正确。costlyfunction(paramvec)您想从optfn(paramvec)或内部调用optgr(paramvec),但仅限于paramvec发生更改时。然后,您希望将其值以及paramvec用于执行此操作的值保存在封闭框架中。也就是说,像这样:

optfngr<-function(){
    quant1 <- NULL
    prevparam <- NULL

    updatecostly <- function(paramvec) {
      if (!identical(paramvec, prevparam)) {
        quant1 <<- costlyfunction(paramvec)
        prevparam <<- paramvec
      }
    }
    optfunc<-function(paramvec){
        updatecostly(paramvec)
        loglikelihood<-sum(quant1)**2
        return(loglikelihood)
    }
    optgr<-function(paramvec){
        updatecostly(paramvec)
        mygrad<-sum(quant1)
        return(mygrad)
    }
    return(list(fn = optfunc, gr = optgr))
}

do.call(optimx, c(list(par=paramvec,method="BFGS"),optfngr() ))
Run Code Online (Sandbox Code Playgroud)

我曾经<<-对封闭框架进行分配,并修复了你的do.call第二个参数。

执行此操作称为“记忆化”(在某些语言环境中称为“记忆化”;请参阅http://en.wikipedia.org/wiki/Memoization),并且有一个名为的包memoise可以执行此操作。它会跟踪以前调用 的大量(或全部?)结果,因此如果只采用少量值,costlyfunction效果会特别好。paramvec但我认为这在你的情况下不会那么好,因为你可能只会进行少量的重复调用costlyfunction,然后再也不会使用相同的paramvec