我如何在 Julia 中独立于 Optim.jl 使用 Linesearches.jl 模块?

How can I use the Linesearches.jl module in Julia independent of Optim.jl?

我有一个 L-BFGS 的实现,想从 LineSearches.jl 调用线搜索来比较。但是,文档非常稀疏,仅关注 Linesearches.jl 在 Optim.jl 上下文中的使用。我找不到任何有关如何独立使用 Linesearches.jl 的示例。

我创建了一个示例,说明如何将 LineSearches 算法与 custom-made optimizer in the latest documentation 一起使用。

请注意,该示例当前需要 LineSearches master,但应该很快会在 v6.0.0 中可用。

这是完整的示例,以防链接中断:(编辑:更新了简化过程的新示例代码。)

在没有 Optim/NLsolve

的情况下使用 LineSearches

假设我们已经编写了梯度下降优化算法,但想要 尝试不同的线搜索算法。 算法实现如下

function gdoptimize(f, g!, fg!, x0::AbstractArray{T}, linesearch,
                    maxiter::Int = 10000,
                    g_rtol::T = sqrt(eps(T)), g_atol::T = eps(T)) where T <: Number
    x = copy(x0)
    gvec = similar(x)
    g!(gvec, x)
    fx = f(x)

    gnorm = norm(gvec)
    gtol = max(g_rtol*gnorm, g_atol)

    # Univariate line search functions
    ϕ(α) = f(x .+ α.*s)
    function dϕ(α)
        g!(gvec, x .+ α.*s)
        return vecdot(gvec, s)
    end
    function ϕdϕ(α)
        phi = fg!(gvec, x .+ α.*s)
        dphi = vecdot(gvec, s)
        return (phi, dphi)
    end

    s = similar(gvec) # Step direction

    iter = 0
    while iter < maxiter && gnorm > gtol
        iter += 1
        s .= -gvec

        dϕ_0 = dot(s, gvec)
        α, fx = linesearch(ϕ, dϕ, ϕdϕ, 1.0, fx, dϕ_0)

        @. x = x + α*s
        g!(gvec, x)
        gnorm = norm(gvec)
    end

    return (fx, x, iter)
end

请注意,有许多优化和线搜索算法允许 用户同时评估 objective 和梯度,因为 计算效率的原因。 我们已将此功能包含在算法中作为输入函数 fg!, 即使梯度下降算法没有明确使用它,许多 LineSearches 算法也会使用它。

梯度下降gdoptimize方法选择下降方向并调用 线搜索算法 linesearch 其中 returns 步长 α 和 objective 值 fx = f(x + α*s).

函数 ϕ 和 dϕ 表示单变量 objective 及其导数,用于线搜索算法。 要在优化器中使用 fg! 函数调用,一些行搜索 需要一个函数 ϕdϕ 其中 returns 单变量 objective 和 同时导数

优化罗森布罗克

这里有一个例子来展示我们如何结合 gdoptimizeLineSearches 最小化由

定义的 Rosenbrock 函数
f(x) = (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2

function g!(gvec, x)
    gvec[1] = -2.0 * (1.0 - x[1]) - 400.0 * (x[2] - x[1]^2) * x[1]
    gvec[2] = 200.0 * (x[2] - x[1]^2)
    gvec
end

function fg!(gvec, x)
    g!(gvec, x)
    f(x)
end

我们现在可以使用 gdoptimizeBackTracking 来优化 Rosenbrock 函数 从给定的初始条件 x0.

x0 = [-1., 1.0]

using LineSearches
ls = BackTracking(order=3)
fx_bt3, x_bt3, iter_bt3 = gdoptimize(f, g!, fg!, x0, ls)

有趣的是,StrongWolfe 线搜索在一次迭代中收敛,而 所有其他算法都需要数千次迭代。 由于初始条件的特殊选择,这只是运气

ls = StrongWolfe()
fx_sw, x_sw, iter_sw = gdoptimize(f, g!, fg!, x0, ls)