gpt4 book ai didi

r - 如何正确指定用于 optim() 或其他优化器的梯度函数

转载 作者:行者123 更新时间:2023-12-03 15:30:24 26 4
gpt4 key购买 nike

我有一个优化问题 Nelder-Mead方法会解决,但我也想用 BFGS 解决或者 Newton-Raphson,或者采用梯度函数的东西,以获得更快的速度,并希望获得更精确的估计。我在 optim 中的示例之后(我认为)编写了这样的梯度函数/optimx文档,但是当我将它与 BFGS 一起使用时我的起始值要么不动( optim() ),要么函数完全不运行( optimx() ,返回 Error: Gradient function might be wrong - check it! )。很抱歉,复制此内容涉及一些代码,但这里是:

这是我想要获得参数估计的函数(这是为了平滑老年死亡率,其中 x 是年龄,从 80 岁开始):

    KannistoMu <- function(pars, x = .5:30.5){
a <- pars["a"]
b <- pars["b"]
(a * exp(b * x)) / (1 + a * exp(b * x))
}

这是一个对数似然函数,用于根据观察到的比率(定义为死亡, .Dx 过度暴露, .Exp)来估计它:
    KannistoLik1 <- function(pars, .Dx, .Exp, .x. = .5:30.5){
mu <- KannistoMu(exp(pars), x = .x.)
# take negative and minimize it (default optimizer behavior)
-sum(.Dx * log(mu) - .Exp * mu, na.rm = TRUE)
}

你看 exp(pars)在那里因为我给 log(pars)优化过来,以约束最终 ab要积极。

示例数据(1962 年日本女性,如果有人好奇的话):
    .Dx <- structure(c(10036.12, 9629.12, 8810.11, 8556.1, 7593.1, 6975.08, 
6045.08, 4980.06, 4246.06, 3334.04, 2416.03, 1676.02, 1327.02,
980.02, 709, 432, 350, 217, 134, 56, 24, 21, 10, 8, 3, 1, 2,
1, 0, 0, 0), .Names = c("80", "81", "82", "83", "84", "85", "86",
"87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
"98", "99", "100", "101", "102", "103", "104", "105", "106",
"107", "108", "109", "110"))
.Exp <- structure(c(85476.0333333333, 74002.0866666667, 63027.5183333333,
53756.8983333333, 44270.9, 36749.85, 29024.9333333333, 21811.07,
16912.315, 11917.9583333333, 7899.33833333333, 5417.67, 3743.67833333333,
2722.435, 1758.95, 1043.985, 705.49, 443.818333333333, 223.828333333333,
93.8233333333333, 53.1566666666667, 27.3333333333333, 16.1666666666667,
10.5, 4.33333333333333, 3.16666666666667, 3, 2.16666666666667,
1.5, 0, 1), .Names = c("80", "81", "82", "83", "84", "85", "86",
"87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97",
"98", "99", "100", "101", "102", "103", "104", "105", "106",
"107", "108", "109", "110"))

以下适用于 Nelder-Mead方法:
    NMab <- optim(log(c(a = .1, b = .1)), 
fn = KannistoLik1, method = "Nelder-Mead",
.Dx = .Dx, .Exp = .Exp)
exp(NMab$par)
# these are reasonable estimates
a b
0.1243144 0.1163926

这是我想出的梯度函数:
    Kannisto.gr <- function(pars, .Dx, .Exp, x = .5:30.5){
a <- exp(pars["a"])
b <- exp(pars["b"])
d.a <- (a * exp(b * x) * .Exp + (-a * exp(b * x) - 1) * .Dx) /
(a ^ 3 * exp(2 * b * x) + 2 * a ^ 2 * exp(b * x) + a)
d.b <- (a * x * exp(b * x) * .Exp + (-a * x * exp(b * x) - x) * .Dx) /
(a ^ 2 * exp(2 * b * x) + 2 * a * exp(b * x) + 1)
-colSums(cbind(a = d.a, b = d.b), na.rm = TRUE)
}

输出是一个长度为 2 的向量,相对于参数 a 的变化和 b .我还通过利用 deriv() 的输出得到了一个更丑陋的版本。 ,它返回相同的答案,我没有发布(只是为了确认导数是正确的)。

如果我提供给 optim()如下,用 BFGS作为方法,估计值不会从起始值移动:
    BFGSab <- optim(log(c(a = .1, b = .1)), 
fn = KannistoLik1, gr = Kannisto.gr, method = "BFGS",
.Dx = .Dx, .Exp = .Exp)
# estimates do not change from starting values:
exp(BFGSab$par)
a b
0.1 0.1

当我看着 $counts输出的元素,它说 KannistoLik1()被调用了 31 次和 Kannisto.gr()仅 1 次。 $convergence0 ,所以我猜它认为它收敛了(如果我给出不太合理的开始,它们也会保持原状)。我降低了容忍度等,但没有任何变化。当我在 optimx() 中尝试相同的调用时(未显示),我收到了我上面提到的警告,并且没有返回任何对象。指定 gr = Kannisto.gr 时我得到相同的结果与 "CG" .与 "L-BFGS-B"方法我得到了与估计相同的起始值,但也报告了函数和梯度都被调用了21次,并且有一个错误信息:
"ERROR: BNORMAL_TERMINATION_IN_LNSRCH"
我希望在编写梯度函数的方式中有一些小细节可以解决这个问题,如后面的警告和 optimx行为直截了本地暗示该功能根本不正确(我认为)。我也试过 maxNR()来自 maxLik 的最大化器打包并观察到类似的行为(起始值不移动)。谁能给我一个指针?多谢

[编辑]
@Vincent 建议我与数值近似的输出进行比较:
    library(numDeriv)
grad( function(u) KannistoLik1( c(a=u[1], b=u[2]), .Dx, .Exp ), log(c(.1,.1)) )
[1] -14477.40 -7458.34
Kannisto.gr(log(c(a=.1,b=.1)), .Dx, .Exp)
a b
144774.0 74583.4

如此不同的符号,并且相差 10 倍?我更改梯度函数以效仿:
    Kannisto.gr2 <- function(pars, .Dx, .Exp, x = .5:30.5){
a <- exp(pars["a"])
b <- exp(pars["b"])
d.a <- (a * exp(b * x) * .Exp + (-a * exp(b * x) - 1) * .Dx) /
(a ^ 3 * exp(2 * b * x) + 2 * a ^ 2 * exp(b * x) + a)
d.b <- (a * x * exp(b * x) * .Exp + (-a * x * exp(b * x) - x) * .Dx) /
(a ^ 2 * exp(2 * b * x) + 2 * a * exp(b * x) + 1)
colSums(cbind(a=d.a,b=d.b), na.rm = TRUE) / 10
}
Kannisto.gr2(log(c(a=.1,b=.1)), .Dx, .Exp)
# same as numerical:
a b
-14477.40 -7458.34

在优化器中试试:
    BFGSab <- optim(log(c(a = .1, b = .1)), 
fn = KannistoLik1, gr = Kannisto.gr2, method = "BFGS",
.Dx = .Dx, .Exp = .Exp)
# not reasonable results:
exp(BFGSab$par)
a b
Inf Inf
# and in fact, when not exp()'d, they look oddly familiar:
BFGSab$par
a b
-14477.40 -7458.34

按照文森特的回答,我重新调整了梯度函数,并使用了 abs()而不是 exp()保持参数为正。最新的、性能更好的目标函数和梯度函数:
    KannistoLik2 <- function(pars, .Dx, .Exp, .x. = .5:30.5){
mu <- KannistoMu.c(abs(pars), x = .x.)
# take negative and minimize it (default optimizer behavior)
-sum(.Dx * log(mu) - .Exp * mu, na.rm = TRUE)
}

# gradient, to be down-scaled in `optim()` call
Kannisto.gr3 <- function(pars, .Dx, .Exp, x = .5:30.5){
a <- abs(pars["a"])
b <- abs(pars["b"])
d.a <- (a * exp(b * x) * .Exp + (-a * exp(b * x) - 1) * .Dx) /
(a ^ 3 * exp(2 * b * x) + 2 * a ^ 2 * exp(b * x) + a)
d.b <- (a * x * exp(b * x) * .Exp + (-a * x * exp(b * x) - x) * .Dx) /
(a ^ 2 * exp(2 * b * x) + 2 * a * exp(b * x) + 1)
colSums(cbind(a = d.a, b = d.b), na.rm = TRUE)
}

# try it out:
BFGSab2 <- optim(
c(a = .1, b = .1),
fn = KannistoLik2,
gr = function(...) Kannisto.gr3(...) * 1e-7,
method = "BFGS",
.Dx = .Dx, .Exp = .Exp
)
# reasonable:
BFGSab2$par
a b
0.1243249 0.1163924

# better:
KannistoLik2(exp(NMab1$par),.Dx = .Dx, .Exp = .Exp) > KannistoLik2(BFGSab2$par,.Dx = .Dx, .Exp = .Exp)
[1] TRUE

这个问题的解决速度比我预期的要快得多,而且我学到了很多技巧。谢谢文森特!

最佳答案

要检查梯度是否正确,
您可以将其与数值近似值进行比较:

library(numDeriv); 
grad( function(u) KannistoLik1( c(a=u[1], b=u[2]), .Dx, .Exp ), c(1,1) );
Kannisto.gr(c(a=1,b=1), .Dx, .Exp)

迹象是错误的:算法没有看到任何改进
当它朝这个方向移动时,因此不会移动。

你可以使用一些计算机代数系统(这里,Maxima)
为你做计算:
display2d: false;
f(a,b,x) := a * exp(b*x) / ( 1 + a * exp(b*x) );
l(a,b,d,e,x) := - d * log(f(a,b,x)) + e * f(a,b,x);
factor(diff(l(exp(a),exp(b),d,e,x),a));
factor(diff(l(exp(a),exp(b),d,e,x),b));

我只是将结果复制并粘贴到 R 中:
f_gradient <- function(u, .Dx, .Exp, .x.=.5:30.5) {
a <- u[1]
b <- u[1]
x <- .x.
d <- .Dx
e <- .Exp
c(
sum( (e*exp(exp(b)*x+a)-d*exp(exp(b)*x+a)-d)/(exp(exp(b)*x+a)+1)^2 ),
sum( exp(b)*x*(e*exp(exp(b)*x+a)-d*exp(exp(b)*x+a)-d)/(exp(exp(b)*x+a)+1)^2 )
)
}

library(numDeriv)
grad( function(u) KannistoLik1( c(a=u[1], b=u[2]), .Dx, .Exp ), c(1,1) )
f_gradient(c(a=1,b=1), .Dx, .Exp) # Identical

如果一味地把梯度放在优化中,
有一个数值不稳定问题:给出的解决方案是 (Inf,Inf) ...
为了防止它,您可以重新缩放渐变
(更好的解决方法是使用比指数更小的爆炸性转换,
以确保参数保持正值)。
BFGSab <- optim(
log(c(a = .1, b = .1)),
fn = KannistoLik1,
gr = function(...) f_gradient(...) * 1e-3,
method = "BFGS",
.Dx = .Dx, .Exp = .Exp
)
exp(BFGSab$par) # Less precise than Nelder-Mead

关于r - 如何正确指定用于 optim() 或其他优化器的梯度函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11622956/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com