【问题标题】:Plotting output of ForwardDiff in Julia在 Julia 中绘制 ForwardDiff 的输出
【发布时间】:2020-11-13 12:11:12
【问题描述】:

我只想使用ForwardDiff.jl 功能来定义一个函数并绘制它的梯度(使用ForwardDiff.gradient 评估)。它似乎不起作用,因为 ForwardDiff.gradient 的输出是这种奇怪的 Dual 类型的东西,而且它不容易转换为所需的类型(在我的例子中,是 Float32 的一维数组)。

using Plots
using ForwardDiff

my_func(x::Array{Float32,1}) = 1f0. / (1f0 .+ exp(3f0 .* x)) # doesn't matter what this is, just a sigmoid function here

grad_f(x::Array{Float32,1}) = ForwardDiff.gradient(my_func, x)

x_values = collect(Float32,0:0.01:10)

plot(x_values,my_func(x_values)); # this works fine

plot!(x_values,grad_f(x_values)); # this throws an error

这是我得到的错误:

ERROR: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{typeof(g),Float32},Float64,12})

当我检查grad_f(x_values) 的类型时,我得到了这个:

Array{Array{ForwardDiff.Dual{ForwardDiff.Tag{typeof(g),Float32},Float32,12},1},1}

例如,为什么在 ForwardDiff 文档的示例中没有发生这种情况? 见这里:https://github.com/JuliaDiff/ForwardDiff.jl

提前致谢。

编辑: 在 Kristoffer Carlsson 的 cmets 之后:我试过这个,但它仍然不起作用。我不明白我在这里的尝试与他的建议有何不同:

function g(x::Float32)
    return x / (1f0 + exp(10f0 * (x - 5f0)))
end

function ∂g∂x(x::Float32)
    return ForwardDiff.derivative(g, x)
end

x_vals = collect(Float32,0:0.01:10)
plot(x_vals,g.(x_vals))
plot!(x_vals,∂g∂x.(x_vals))

现在的错误是:

no method matching g(::ForwardDiff.Dual{ForwardDiff.Tag{typeof(g),Float32},Float32,1})

这个错误发生在我调用∂g∂x(x)时,不管我是否使用广播版本∂g∂x.(x)。我想这与函数定义有关,但我看不出我定义它的方式与 Kristoffer 的版本有何不同,除了它没有在一行中定义......这太令人困惑了。

这应该可行,因为根据ForwardDiff 的文档,您只需要输入的类型是Real 的子类型 - 而Float32 是Real 的子类型。

编辑:我现在意识到,在阅读了其他人的 cmets 后,您需要将函数限制为足够通用以接受抽象类型 Real任何 输入,而我没有这样做从文档中不太清楚。对造成的混乱表示歉意。

【问题讨论】:

  • “考虑到 ForwardDiff.jl 文档中关于您使用的 Real 子类型没有限制的说法,这对我来说是 0 意义”。不,juliadiff.org/ForwardDiff.jl/stable/user/limitations 的文档明确表示:“目标函数必须写得足够笼统,以接受 T<:Real 类型的数字作为输入”,如果您将其限制为 Float32,那么这是不正确的。
  • 啊,我没有意识到那是什么意思——我认为符号 T<:Real 意味着您可以限制任何类型 T 是 Real 的子类型,因此它们可以以这种方式受到限制。我的错误,感谢您的澄清。

标签: type-conversion julia automatic-differentiation autodiff


【解决方案1】:

您在数组而不是标量上定义函数,并且过多地限制了输入类型。此外,对于标量函数,您应该使用ForwardDiff.derivative。尝试类似:

using Plots
using ForwardDiff

my_func(x::Real) =  1f0 / (1f0 + exp(3f0 * x))
my_func_derivative(x::Real) = ForwardDiff.derivative(my_func, x)

plot(my_func, xlimits = (0, 10))
plot!(my_func_derivative)

给予:

【讨论】:

  • 感谢您的提示 - 但是,我以稍微不同的方式进行了尝试,但没有成功。你知道为什么吗?我将编辑原始帖子以显示我尝试过的内容。
  • 您可能需要阅读 ForwardDiff 的工作原理——它使用 dual numbers 调用您的函数来跟踪导数。如果您将函数限制为 Float32 类型的输入,这将不起作用。您需要将其放松为 Real,因为 ForwardDiff 的 Dual 类型是 Real 的子类型。
  • 我要补充一点,除非您打算为该函数定义另一种方法(与其他类型的行为不同),否则您可能不应该强制执行任何输入类型。代码将更易于阅读,性能同样出色,并且可能会以零努力与其他包结合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多