Использование автоматического дифференцирования в функции, которая использует предварительно выделенный массив в Julia - PullRequest
0 голосов
/ 04 сентября 2018

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

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

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, и автоматическое дифференцирование, которое она предлагает, и эта функция использует большую матрицу, которую я хотел бы предварительно распределить, чтобы ускорить до моего кода. Большое спасибо.

1 Ответ

0 голосов
/ 04 сентября 2018

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

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

с примером здесь 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])

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

EDIT

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

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
...