Julia 将ForwardDiff.jl用于多变量和多参数函数

Julia 将ForwardDiff.jl用于多变量和多参数函数,julia,Julia,ForwardDiff.jl的github repo有一些例子。我试图扩展这个例子,除了一个变量向量,一个参数。我不能让它工作 这是一个示例(它很短,因此我将显示它,而不是链接) 我想修改它,因为我使用了带有多个参数和变量的函数。作为一个简单的修改,我尝试添加一个参数 f(x::Vector, y) = (sum(sin, x) .+ prod(tan, x) * sum(sqrt, x)) * y; 我尝试了以下方法,但没有成功: fp = x -> ForwardDiff.gradi

ForwardDiff.jl的github repo有一些例子。我试图扩展这个例子,除了一个变量向量,一个参数。我不能让它工作

这是一个示例(它很短,因此我将显示它,而不是链接)

我想修改它,因为我使用了带有多个参数和变量的函数。作为一个简单的修改,我尝试添加一个参数

f(x::Vector, y) = (sum(sin, x) .+ prod(tan, x) * sum(sqrt, x)) * y;
我尝试了以下方法,但没有成功:

fp = x -> ForwardDiff.gradient(f, x); 
fp = x -> ForwardDiff.gradient(f, x, y); 

y = 1
println("test grad: ", fp(x, y))
我收到以下错误消息:

ERROR: LoadError: MethodError: no method matching (::var"#73#74")(::Array{Float64,1}, ::Int64)
2017年,类似的问题没有得到回答。A让我想到,函数似乎只能接受一个输入

目标函数必须是一元函数(即只接受一个参数)。ForwardDiff.jacobian是该规则的一个例外

这种情况改变了吗?它似乎仅限于能够区分一元函数


一种可能的解决方法是将变量和参数列表连接起来,然后对返回的梯度进行切片,以不包括与参数相关的梯度,但这似乎很愚蠢。

我个人认为,对于ForwardDiff使用这种仅一元语法是有意义的。在您的情况下,您可以将
x
y
打包/解包成一个向量(下面的
x2
):

但我倾向于以不同的方式明确命名偏导数:

julia> ∂f∂x(x,y) = ForwardDiff.gradient(x -> f(x,y), x)
∂f∂x (generic function with 1 method)

julia> ∂f∂y(x,y) = ForwardDiff.derivative(y -> f(x,y), y)
∂f∂y (generic function with 1 method)

julia> ∂f∂x(x, y)
5-element Array{Float64,1}:
 2.6105844240785796
 2.741442601659502
 1.9913192377198885
 1.9382805843854594
 2.26202717745402

julia> ∂f∂y(x, y)
3.434350946190029

下面是一个函数的快速尝试,该函数具有多个参数,签名与
Zygote.gradient

julia> using ForwardDiff, Zygote

julia> multigrad(f, xs...) = ntuple(length(xs)) do i
         g(y) = f(ntuple(j -> j==i ? y : xs[j], length(xs))...)
         xs[i] isa AbstractArray ? ForwardDiff.gradient(g, xs[i]) : 
           xs[i] isa Number ? ForwardDiff.derivative(g, xs[i]) : nothing
         end;

julia> f1(x,y,z) = sum(x.^2)/y;

julia> multigrad(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)

julia> Zygote.gradient(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)

对于一个具有多个标量参数的函数,这将分别计算每个导数,并且使用一个带有一些
Dual(x,(dx,dy,dz))
的求值可能会更有效。有了足够大的数组参数,
ForwardDiff.gradient
将执行多个求值,每个求值都有一定数量的扰动(块大小,您可以控制)。

只是想知道:有没有理由不在这里使用
Zygote
ReverseDiff
,我正在玩autodiff,这就是我开始的地方。反向分裂或合子能解决我的问题吗?合子是彩虹尽头的一罐金子。回答这个问题,你就会得到对勾。谢谢第一种方法将参数连接到变量上,它的一个问题是参数的数量经常超过变量的数量,所以这是一项额外的工作。我不需要花时间计算参数的梯度。但是我确实喜欢第二种方法当然,如果你只需要关于
x
的导数,并且你有很多参数,那么你只需要区分闭包
x->f(x,params…
(而不是单个
y
变量),这将概括上面我的第二个代码块中的第一行。在我看来,Zygote相当简单,而且或多或少很好地脱离了限制?Zygote是反向模式,一种不同的算法,适用于许多输入一输出函数,但更复杂。两者都很好,但还远远不够完美。ForwardDiff更简单、更稳定,通常更易于调试。(Tracker和ReverseDiff是两个较老的反向模式库,它们也比受精卵简单。)
julia> ∂f∂x(x,y) = ForwardDiff.gradient(x -> f(x,y), x)
∂f∂x (generic function with 1 method)

julia> ∂f∂y(x,y) = ForwardDiff.derivative(y -> f(x,y), y)
∂f∂y (generic function with 1 method)

julia> ∂f∂x(x, y)
5-element Array{Float64,1}:
 2.6105844240785796
 2.741442601659502
 1.9913192377198885
 1.9382805843854594
 2.26202717745402

julia> ∂f∂y(x, y)
3.434350946190029
julia> using ForwardDiff, Zygote

julia> multigrad(f, xs...) = ntuple(length(xs)) do i
         g(y) = f(ntuple(j -> j==i ? y : xs[j], length(xs))...)
         xs[i] isa AbstractArray ? ForwardDiff.gradient(g, xs[i]) : 
           xs[i] isa Number ? ForwardDiff.derivative(g, xs[i]) : nothing
         end;

julia> f1(x,y,z) = sum(x.^2)/y;

julia> multigrad(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)

julia> Zygote.gradient(f1, [1,2,3], 4)
([0.5, 1.0, 1.5], -0.875)