AnyMath 文档

从优化开始。jl

该页面正在翻译中。

在本教程中,我们介绍了优化的基础知识。jl通过展示如何在Rosenbrock方程上轻松混合局部优化器和全局优化器。

Rosenbrock方程定义如下:

Misplaced &

这是一个参数化优化问题,我们要为矢量求解 u s.t. u 最小化 f. 使用准牛顿法(LBFGS)解决Rosenbrock问题的最简单的复制粘贴代码如下所示:

# Import the package and define the problem to optimize
using Optimization, OptimizationLBFGSB, Zygote
rosenbrock(u, p) = (p[1] - u[1])^2 + p[2] * (u[2] - u[1]^2)^2
u0 = zeros(2)
p = [1.0, 100.0]

optf = OptimizationFunction(rosenbrock, AutoZygote())
prob = OptimizationProblem(optf, u0, p)

sol = solve(prob, OptimizationLBFGSB.LBFGSB())
retcode: Success
u: 2-element Vector{Float64}:
 0.9999997057368228
 0.999999398151528
sol.u
2-element Vector{Float64}:
 0.9999997057368228
 0.999999398151528
sol.objective
1.0433892998247468e-13

塔达! 你就是这么做的。 现在让我们深入了解每个部分的含义以及如何根据您的需求自定义它。

了解解决方案对象

解决方案对象是一个 SciMLBase。[医]抽象时间进化,因此它遵循https://docs.sciml.ai/SciMLBase/stable/interfaces/Solutions/[非timeseries对象的SciMLBase解决方案接口],并在 解决方案类型页面。 但是,为了简单起见,让我们展示一下它的作用。

优化解决方案具有数组接口,因此它的作用类似于它求解的数组。 这个数组语法是简单抓取解决方案的简写 u. 例如:

sol[1] == sol.u[1]
true
Array(sol) == sol.u
true

索尔目标 返回优化的最终成本。 我们可以通过将其插入我们的函数来验证这一点:

rosenbrock(sol.u, p)
1.0433892998247468e-13
sol.objective
1.0433892998247468e-13

索尔重码 为我们提供有关解决方案流程的更多信息。

sol.retcode
ReturnCode.Success = 1

上面写着 返回码。成功 这意味着solutuion成功解决了。 我们可以在以下网址了解有关不同退货代码的更多信息https://docs.sciml.ai/SciMLBase/stable/interfaces/Solutions/#retcodes[SciMLBase文档的ReturnCode部分]。

如果我们对求解过程的一些统计数据感兴趣,例如帮助选择更好的求解器,我们可以调查 索尔统计数字

sol.stats
SciMLBase.OptimizationStats
Number of iterations:                              21
Time in seconds:                                   0.095007
Number of function evaluations:                    25
Number of gradient evaluations:                    25
Number of hessian evaluations:                     0

这只是其中的一些内容,请查看其他页面以获取更多信息,但现在让我们转向自定义。

导入不同的求解程序包并解决问题

OptimizationOptimJL是一个包装https://github.com/JuliaNLSolvers/Optim.jl[Optim.jl]和OptimizationBBO是一个包装https://github.com/robertfeldt/BlackBoxOptim.jl[BlackBoxOptim.jl]。

首先,让我们使用Neldermead一个来自Optim的衍生自由求解器。jl:

using OptimizationOptimJL
sol = solve(prob, Optim.NelderMead())
retcode: Success
u: 2-element Vector{Float64}:
 0.9999634355313174
 0.9999315506115275

BlackBoxOptim.jl提供无导数全局优化求解器,通过 磅/磅ub优化问题. 让我们使用BBO_adaptive_de_rand_1_bin_radiuslimited()求解器:

using OptimizationBBO
prob = OptimizationProblem(rosenbrock, u0, p, lb = [-1.0, -1.0], ub = [1.0, 1.0])
sol = solve(prob, BBO_adaptive_de_rand_1_bin_radiuslimited())
retcode: MaxIters
u: 2-element Vector{Float64}:
 0.9999999999999549
 0.9999999999998697

从原始求解器的解决方案总是可以通过 原版:

sol.original
BlackBoxOptim.OptimizationResults("adaptive_de_rand_1_bin_radiuslimited", "Max number of steps (10000) reached", 10001, 1.764506066048731e9, 0.024574995040893555, BlackBoxOptim.ParamsDictChain[BlackBoxOptim.ParamsDictChain[Dict{Symbol, Any}(:RngSeed => 267918, :SearchRange => [(-1.0, 1.0), (-1.0, 1.0)], :TraceMode => :silent, :Method => :adaptive_de_rand_1_bin_radiuslimited, :MaxSteps => 10000),Dict{Symbol, Any}()],Dict{Symbol, Any}(:CallbackInterval => -1.0, :TargetFitness => nothing, :TraceMode => :compact, :FitnessScheme => BlackBoxOptim.ScalarFitnessScheme{true}(), :MinDeltaFitnessTolerance => 1.0e-50, :NumDimensions => :NotSpecified, :FitnessTolerance => 1.0e-8, :TraceInterval => 0.5, :MaxStepsWithoutProgress => 10000, :MaxSteps => 10000…)], 10121, BlackBoxOptim.ScalarFitnessScheme{true}(), BlackBoxOptim.TopListArchiveOutput{Float64, Vector{Float64}}(2.0390205507216543e-27, [0.9999999999999839, 0.9999999999999636]), BlackBoxOptim.PopulationOptimizerOutput{BlackBoxOptim.FitPopulation{Float64}}(BlackBoxOptim.FitPopulation{Float64}([0.9999999999983733 0.9999999999939537 … 0.9999999999983745 0.9999999999998465; 0.9999999999967175 0.9999999999880562 … 0.9999999999968257 0.999999999999873], NaN, [2.7307591692669577e-24, 3.877068432038875e-23, 5.55809157801959e-25, 3.1995258881033734e-23, 6.5007566939315425e-25, 1.6765511539061802e-23, 2.8525546708012154e-24, 9.636317579830916e-23, 2.0273443739444453e-24, 3.838239268473506e-24  …  2.5299495228989685e-25, 9.75549911803261e-24, 9.747575318388456e-24, 6.547552033762818e-24, 9.13098609184269e-26, 7.477175886078931e-24, 9.386881771962746e-24, 2.9202145742257537e-23, 3.229015759459305e-24, 3.2663906086420924e-24], 0, BlackBoxOptim.Candidate{Float64}[BlackBoxOptim.Candidate{Float64}([0.9999999999997331, 0.999999999999155], 42, 9.75549911803261e-24, BlackBoxOptim.AdaptiveDiffEvoRandBin{3}(BlackBoxOptim.AdaptiveDiffEvoParameters(BlackBoxOptim.BimodalCauchy(Distributions.Cauchy{Float64}(μ=0.65, σ=0.1), Distributions.Cauchy{Float64}(μ=1.0, σ=0.1), 0.5, false, true), BlackBoxOptim.BimodalCauchy(Distributions.Cauchy{Float64}(μ=0.1, σ=0.1), Distributions.Cauchy{Float64}(μ=0.95, σ=0.1), 0.5, false, true), [0.7019095088157352, 0.6418493346222249, 1.0, 1.0, 0.9901689616048996, 0.6644681867665791, 0.671689017186811, 0.7209713153619176, 1.0, 0.9411173782283542  …  0.5751425454237956, 0.5712613240820539, 0.6707312867031002, 1.0, 0.9546162476102832, 1.0, 0.6769709843338126, 1.0, 0.4210451880058542, 0.7874325114289209], [0.8058877890924663, 0.9727182455838859, 0.021959396113298474, 1.0, 0.20910451920538256, 0.15076474541336427, 1.0, 1.0, 1.0, 0.9584929292630588  …  0.05242416096178464, 1.0, 0.10243499347916171, 1.0, 0.10073386356470802, 1.0, 0.9529893323394578, 0.17451700793559494, 1.0, 1.0])), 0), BlackBoxOptim.Candidate{Float64}([0.999999999998981, 0.999999999999155], 42, 1.433475904892751e-22, BlackBoxOptim.AdaptiveDiffEvoRandBin{3}(BlackBoxOptim.AdaptiveDiffEvoParameters(BlackBoxOptim.BimodalCauchy(Distributions.Cauchy{Float64}(μ=0.65, σ=0.1), Distributions.Cauchy{Float64}(μ=1.0, σ=0.1), 0.5, false, true), BlackBoxOptim.BimodalCauchy(Distributions.Cauchy{Float64}(μ=0.1, σ=0.1), Distributions.Cauchy{Float64}(μ=0.95, σ=0.1), 0.5, false, true), [0.7019095088157352, 0.6418493346222249, 1.0, 1.0, 0.9901689616048996, 0.6644681867665791, 0.671689017186811, 0.7209713153619176, 1.0, 0.9411173782283542  …  0.5751425454237956, 0.5712613240820539, 0.6707312867031002, 1.0, 0.9546162476102832, 1.0, 0.6769709843338126, 1.0, 0.4210451880058542, 0.7874325114289209], [0.8058877890924663, 0.9727182455838859, 0.021959396113298474, 1.0, 0.20910451920538256, 0.15076474541336427, 1.0, 1.0, 1.0, 0.9584929292630588  …  0.05242416096178464, 1.0, 0.10243499347916171, 1.0, 0.10073386356470802, 1.0, 0.9529893323394578, 0.17451700793559494, 1.0, 1.0])), 0)], Base.Threads.SpinLock(0))))

定义目标函数

优化。jl假设你的目标函数有两个参数 目标(x,p)

  1. 优化变量 x.

  2. 其他参数 p,如成本函数的超参数。 如果你没有"`其他参数`",你可以放心地忽略这个论点。 如果你的目标函数是由其他人定义的,你可以创建一个匿名函数,只是像这样丢弃额外的参数

obj = (x, p) -> objective(x) # Pass this function into OptimizationFunction

控制梯度计算(自动微分)

请注意,上述两种方法都是无导数方法,因此不需要梯度来进行优化。 然而,通常一阶优化(即使用梯度)效率要高得多。 定义渐变可以通过两种方式完成。 一种方法是在 优化功能 构造函数。 但是,获取渐变的更方便的方法是提供广告后端类型。

例如,我们现在使用OptimizationOptimJL BFGS公司 方法来解决同样的问题。 我们将导入前向模式自动分化库(使用ForwardDiff),然后在指定 优化功能 使用ForwardDiff自动构造导数函数。jl. 这看起来像:

using ForwardDiff
optf = OptimizationFunction(rosenbrock, Optimization.AutoForwardDiff())
prob = OptimizationProblem(optf, u0, p)
sol = solve(prob, OptimizationOptimJL.BFGS())
retcode: Success
u: 2-element Vector{Float64}:
 0.9999999999373603
 0.99999999986862

我们可以检查 原版 查看所需步数和计算梯度的统计信息:

sol.original
 * Status: success

 * Candidate solution
    Final objective value:     7.645553e-21

 * Found with
    Algorithm:     BFGS

 * Convergence measures
    |x - x'|               = 3.48e-07 ≰ 0.0e+00
    |x - x'|/|x'|          = 3.48e-07 ≰ 0.0e+00
    |f(x) - f(x')|         = 6.91e-14 ≰ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 9.03e+06 ≰ 0.0e+00
    |g(x)|                 = 2.31e-09 ≤ 1.0e-08

 * Work counters
    Seconds run:   0  (vs limit Inf)
    Iterations:    16
    f(x) calls:    53
    ∇f(x) calls:   53

果然比无导数的方法少了很多!

然而,前向模式自动微分的计算成本会随着输入数量的增加而增加,因此随着优化问题的增大,它会减慢速度。 为了抵消这种情况,对于较大的优化问题(>100个状态变量),通常需要使用反向模式自动微分。 反向模式自动分化的一个常见选择是合子。jl. 我们可以通过:

using Zygote
optf = OptimizationFunction(rosenbrock, Optimization.AutoZygote())
prob = OptimizationProblem(optf, u0, p)
sol = solve(prob, OptimizationOptimJL.BFGS())
retcode: Success
u: 2-element Vector{Float64}:
 0.9999999999373603
 0.99999999986862

设置框约束

在许多情况下,人们知道解值的潜在边界。 在优化。jl,这些可以作为 磅/磅ub 下界和上限的参数分别为每个状态变量提供一个值向量。 现在让我们通过重建OptimizationProblem来使用框约束进行基于梯度的优化:

prob = OptimizationProblem(optf, u0, p, lb = [-1.0, -1.0], ub = [1.0, 1.0])
sol = solve(prob, OptimizationOptimJL.BFGS())
retcode: Success
u: 2-element Vector{Float64}:
 0.9999999992272249
 0.9999999984496138

有关处理约束的更多信息,特别是平等和不等式约束,请查看 约束教程