如果优化函数的输出不是标量我该怎么办?

问题描述 投票:0回答:1

我正在使用这个优化脚本:

using DifferentialEquations, Optimization, OptimizationPolyalgorithms,       OptimizationOptimJL, Plots, SciMLSensitivity

parameters = \[\]

function lotka_volterra!(du, u, p, t)
x, y = u
α, β, δ, γ = p
du\[1\] = dx = α*x - β*x*y
du\[2\] = dy = -δ*y + γ*x*y
end

# Initial condition

u0 = \[1.0, 1.0\]

# Simulation interval and intermediary points

tspan = (0.0, 40.0)
tsteps = 0.0:0.1:40.0

# LV equation parameter. p = \[α, β, δ, γ\]

p = \[1.5, 1.0, 3.0, 1.0\]

# Setup the ODE problem, then solve

prob = ODEProblem(lotka_volterra!, u0, tspan, p)
sol = solve(prob, Tsit5())

# Plot the solution

using Plots
plot(sol)
savefig("LV_ode.png")

function loss(p)  # This function calculates how far the solution is from the     value we are aiming for
sol = solve(prob, Tsit5(), p=p, saveat = tsteps)
if any(isnan, sol) || any(isinf, sol)
println("Solution contains NaN or Inf: ", sol)
end

loss = sum(abs2, sol.-1) # Calculates the squared error; Sol for both x and y     values has 1 taken away. This way, it will converge on "1" (the target) because when     it reaches 1, 1-1 = 0 difference between desired and predicted points, so that is what     we want.

return loss, sol
end

callback = function (p, l, pred) # p = parameters, l = loss, pred = predicted
global parameters
push!(parameters, p.u)
display(l)
plt = plot(pred, ylim = (0, 6))
display(plt)
if l \<= 0.0135
return true
else
return false
end
end

adtype = Optimization.AutoZygote()
optf = Optimization.OptimizationFunction((x,p)-\>loss(x), adtype)
optprob = Optimization.OptimizationProblem(optf, p)

result_ode = Optimization.solve(optprob, PolyOpt(),
callback = callback,
maxiters = 600)

它一直有效,直到略低于 l = 0.013。如果您运行它,您可以看到它一直在工作。我在回调函数中放入了一个“if”语句,这样当它达到 0.0135 时就会停止,但我仍然收到此错误消息:

ERROR: Output should be scalar; gradients are not defined for output

我该如何解决这个问题?

我尝试从损失函数中删除“sol”,但这给了我难以理解的错误(需要绘制 sol 并在最后几行中使用)。

我尝试了“if”语句,它应该告诉迭代在某个点之后停止,但即使它们确实停止(我更改了停止值并且它停止在任何值),之后错误仍然出现。我该如何解决这个问题?

optimization julia iteration ode
1个回答
0
投票

你的损失函数,
loss(p)
应该返回一个标量。

您得到的错误与损失值低于

0.0135
无关。 发生错误是因为您的
loss
callback
函数具有不正确的签名
。使用
PolyOpt()
在掩盖真实错误方面也发挥着有趣的作用。

修复
loss
callback

如果您查看

OptimizationFunction
的文档,您会发现构造函数带有一个参数
f
,这是您想要最小化的函数。摘自文档:

f(u,p):优化 [...] 的函数 这应该返回一个标量, 损失值,作为返回输出。

因此,您应该更改函数

loss(p)
以返回标量损失值,而不是
Tuple
。例如

function loss(p)
    sol = solve(prob, Tsit5(), p=p, saveat = tsteps)
    return sum(abs2, sol .- 1)
end

现在,如果您查看

Optimization
求解器选项的文档,您将阅读以下有关回调的内容:

回调函数

callback
是一个在之后调用的函数 每个优化器步骤。它的签名是:

callback = (state, loss_val) -> false

因此,您应该在自己的回调函数中遵循此签名。当然,这意味着您无法传入在损失函数中计算的 ODE 解

sol
。但没关系,您可以使用
state.u
访问当前迭代参数,然后再次求解 ODE。例如

function callback(state, l)
    display(l)
    push!(parameters, state.u)
    sol = solve(prob, Tsit5(), p=state.u, saveat = tsteps)
    display(plot(sol, ylim=(0, 6)))
    return false
end

如果您在优化中使用这些新函数,那就没问题了。

为什么它有效?

为什么你的代码似乎可以运行几次迭代,然后突然出错?

因为

PolyOpt()
正在做的事情。来自文档

PolyOpt:运行 Adam,然后运行 BFGS 进行相同次数的迭代。

发生的情况是,您的

loss
callback
函数适用于
Adam
,但不适用于
BFGS
因此,当
PolyOpt()
Adam
切换到
BFGS
时,错误会“突然”出现出现。

您可以通过将

PolyOpt()
更改为
Optimisers.Adam
(这是
OptimizationPolyalgorithms
使用的 ADAM 版本),然后使用
BFGS
尝试相同的操作来亲自查看这一点。例如

using Optimisers
adtype = Optimization.AutoZygote()
optf = Optimization.OptimizationFunction((x, p) -> loss(x), adtype)
optprob = Optimization.OptimizationProblem(optf, p)
result_ode = Optimization.solve(optprob, Optimisers.Adam(), callback = callback, maxiters = 600)

可与您原来的

loss
callback
配合使用,但是

adtype = Optimization.AutoZygote()
optf = Optimization.OptimizationFunction((x, p) -> loss(x), adtype)
optprob = Optimization.OptimizationProblem(optf, p)
result_ode = Optimization.solve(optprob, BFGS(), callback = callback, maxiters = 600)

立即向您抛出错误。

现在,为什么

Optimisers.Adam
具有与
BFGS
和其他优化算法不同的界面,我不能肯定地说。我对所涉及的包及其设计了解不够。但是,我猜测
Optimisers.Adam
在某个时候将其添加为一个新接口,以很好地适应
Optimization
生态系统的其余部分,并且可能保留旧接口,以免破坏依赖它的旧代码。 无论如何,我都会坚持使用
Optimization.jl
文档中的界面。

© www.soinside.com 2019 - 2024. All rights reserved.