Использование автоматического дифференцирования для функции, которая использует предварительно выделенный массив в Джулии

Мое длинное название темы в значительной степени охватывает это.

Мне удалось изолировать мою гораздо более серьезную проблему в следующем надуманном примере ниже. Я не могу понять, в чем именно проблема, хотя я думаю, что это как-то связано с типом предварительно выделенного массива?

using ForwardDiff

function test()

    A = zeros(1_000_000)

    function objective(A, value)
        for i=1:1_000_000
            A[i] = value[1]
        end

        return sum(A)
    end

    helper_objective = v -> objective(A, v)

    ForwardDiff.gradient(helper_objective, [1.0])

end

Ошибка читается следующим образом:

ERROR: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{getfield(Main, Symbol("##69#71")){Array{Float64,1},getfield(Main, Symbol("#objective#70")){Array{Float64,1}}},Float64},Float64,1})

В моей собственной проблеме (не описанной здесь) у меня есть функция, которую мне нужно оптимизировать с помощью Optim и предлагаемого ею автоматического дифференцирования, и эта функция использует большую матрицу, которую я хотел бы предварительно выделить, чтобы ускорить мой код . Большое спасибо.


person user1438310    schedule 03.09.2018    source источник


Ответы (1)


Если вы посмотрите на http://www.juliadiff.org/ForwardDiff.jl/latest/user/limitations.html вы найдете:

Целевая функция должна быть написана достаточно обобщенно, чтобы принимать в качестве входных данных числа типа T‹:Real (или массивы этих чисел) (...) Это также означает, что любое назначенное хранилище, используемое внутри функции, также должно быть обобщенным.

с примером здесь https://github.com/JuliaDiff/ForwardDiff.jl/issues/136#issuecomment-237941790.

Это означает, что вы можете сделать что-то вроде этого:

function test()
    function objective(value)
        for i=1:1_000_000
            A[i] = value[1]
        end
        return sum(A)
    end
    A = zeros(ForwardDiff.Dual{ForwardDiff.Tag{typeof(objective), Float64},Float64,1}, 1_000_000)
    ForwardDiff.gradient(objective, [1.0])
end

Но я бы не стал предполагать, что это сэкономит вам много ресурсов, поскольку это нестабильный тип.

Что вы можете сделать, так это обернуть objective и A в такой модуль:

using ForwardDiff

module Obj

using ForwardDiff

function objective(value)
    for i=1:1_000_000
        A[i] = value[1]
    end
    return sum(A)
end
const A = zeros(ForwardDiff.Dual{ForwardDiff.Tag{typeof(objective), Float64},Float64,1}, 1_000_000)

end

А теперь это:

ForwardDiff.gradient(Obj.objective, [1.0])

должен быть быстрым.

ИЗМЕНИТЬ

Также это работает (хотя это нестабильный тип, но в менее проблемном месте):

function test()::Vector{Float64}
    function objective(A, value)
        for i=1:1_000_000
            A[i] = value[1]
        end

        return sum(A)
    end
    helper_objective = v -> objective(A, v)
    A = Vector{ForwardDiff.Dual{ForwardDiff.Tag{typeof(helper_objective), Float64},Float64,1}}(undef, 1_000_000)
    ForwardDiff.gradient(helper_objective, [1.0])
end
person Bogumił Kamiński    schedule 04.09.2018