在 Julia 中使用预分配数组的函数使用自动微分
Using automatic differentiation on a function that makes use of a preallocated array in Julia
我的长标题几乎涵盖了它。
在下面的人为示例中,我设法找出了更大的问题。我无法弄清楚问题到底出在哪里,尽管我想它与预分配数组的类型有关?
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 优化,它提供了自动微分,这个函数使用了一个大矩阵,我想按顺序预分配加快我的代码。非常感谢。
如果您查看 http://www.juliadiff.org/ForwardDiff.jl/latest/user/limitations.html,您会发现:
The target function must be written generically enough to accept numbers of type T<:Real as input (or arrays of these numbers) (...) This also means that any storage assigned used within the function must be generic as well.
以此处为例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
我的长标题几乎涵盖了它。
在下面的人为示例中,我设法找出了更大的问题。我无法弄清楚问题到底出在哪里,尽管我想它与预分配数组的类型有关?
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 优化,它提供了自动微分,这个函数使用了一个大矩阵,我想按顺序预分配加快我的代码。非常感谢。
如果您查看 http://www.juliadiff.org/ForwardDiff.jl/latest/user/limitations.html,您会发现:
The target function must be written generically enough to accept numbers of type T<:Real as input (or arrays of these numbers) (...) This also means that any storage assigned used within the function must be generic as well.
以此处为例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