I have been using Flux.jl
and have been confused by the differences when running code within a let
block and without. The following example runs without error:
using Flux
p = rand(2)
function f(x)
f, b = p
x*f + b
end
data = reduce(hcat, [[x, f(x)] for x in 0:0.1:1.0])
p = rand(2)
θ = params(p)
loss(y) = sum((y .- f.(data[1,:])).^2)
for n in 1:1000
grads = Flux.gradient(θ) do
loss(data[2,:])
end
Flux.Optimise.update!(ADAM(), θ, grads)
end
However, wrapping the same code in a let
block does not work as I expect:
using Flux
let
...
end
and produces the stacktrace:
MethodError: objects of type Float64 are not callable
Maybe you forgot to use an operator such as [36m*, ^, %, / etc. [39m?
Stacktrace:
[1] macro expansion
@ ~/.julia/packages/Zygote/bJn8I/src/compiler/interface2.jl:0 [inlined]
[2] _pullback(ctx::Zygote.Context, f::Float64, args::Float64)
@ Zygote ~/.julia/packages/Zygote/bJn8I/src/compiler/interface2.jl:9
[3] (::Zygote.var"#1100#1104"{Zygote.Context, Float64})(x::Float64)
@ Zygote ~/.julia/packages/Zygote/bJn8I/src/lib/broadcast.jl:186
[4] _broadcast_getindex_evalf
@ ./broadcast.jl:670 [inlined]
[5] _broadcast_getindex
@ ./broadcast.jl:643 [inlined]
[6] getindex
@ ./broadcast.jl:597 [inlined]
[7] copy
@ ./broadcast.jl:899 [inlined]
[8] materialize
@ ./broadcast.jl:860 [inlined]
[9] _broadcast
@ ~/.julia/packages/Zygote/bJn8I/src/lib/broadcast.jl:163 [inlined]
[10] adjoint
@ ~/.julia/packages/Zygote/bJn8I/src/lib/broadcast.jl:186 [inlined]
[11] _pullback
@ ~/.julia/packages/ZygoteRules/AIbCs/src/adjoint.jl:65 [inlined]
[12] _apply
@ ./boot.jl:814 [inlined]
[13] adjoint
@ ~/.julia/packages/Zygote/bJn8I/src/lib/lib.jl:200 [inlined]
[14] _pullback
@ ~/.julia/packages/ZygoteRules/AIbCs/src/adjoint.jl:65 [inlined]
[15] _pullback
@ ./broadcast.jl:1297 [inlined]
[16] _pullback(::Zygote.Context, ::typeof(Base.Broadcast.broadcasted), ::Float64, ::Vector{Float64})
@ Zygote ~/.julia/packages/Zygote/bJn8I/src/compiler/interface2.jl:0
[17] _pullback
@ ./In[198]:32 [inlined]
[18] _pullback(::Zygote.Context, ::var"#loss#155", ::Vector{Float64}, ::Vector{Float64})
@ Zygote ~/.julia/packages/Zygote/bJn8I/src/compiler/interface2.jl:0
[19] _pullback
@ ./In[198]:37 [inlined]
[20] _pullback(::Zygote.Context, ::var"#152#156"{var"#loss#155", Matrix{Float64}})
@ Zygote ~/.julia/packages/Zygote/bJn8I/src/compiler/interface2.jl:0
[21] pullback(f::Function, ps::Zygote.Params)
@ Zygote ~/.julia/packages/Zygote/bJn8I/src/compiler/interface.jl:351
[22] gradient(f::Function, args::Zygote.Params)
@ Zygote ~/.julia/packages/Zygote/bJn8I/src/compiler/interface.jl:75
[23] top-level scope
@ In[198]:36
[24] eval
@ ./boot.jl:373 [inlined]
[25] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1196
Whereas I had expected them to both behave identically (at least in isolation). I cannot tell much myself from the stacktrace, since I have no experience with the implementation of Flux.jl
or Zygote.jl
. But the problem seems to be something to do with the definition of the function f
, since changing the definition of f
to:
function f(x)
a, b = p
x*a + b
end
Allows both the let
and let
less versions to work. Of course, I could fix it like this and call it a day. But I am curious if anyone knows why the two versions work differently?
Note
(@v1.7) pkg> status Flux
Status `~/.julia/environments/v1.7/Project.toml`
[587475ba] Flux v0.12.8