I want to change the output in a custom Flux layer, but when I try to do so, I get a “Mutating arrays not supported” error. I’ve tried playing with things a bit and while I can act on the array as a whole (element-wise), I can’t act individually on any elements. (Declaring the struct
as mutable
doesn’t help.)
The code is below, with the error message following. Look inside the custom_layer
function; I have a few examples of what works and what doesn’t. I need to be able to change individual elements of the array. I’m very much stuck, so I appreciate any suggestions.
using Flux.Data: DataLoader
using Flux: Params #Zygote
##### DATA #####################
num_samples = 50
x_noise_std = 0.01
y_noise_std = 0.1
function generate_data()
x = reshape(range(0, stop=1.5π, length=num_samples), num_samples, 1)
y_noise = rand(Normal(0,y_noise_std), 3, num_samples)
# 3 outputs
y = hcat(
sin.(x).^2 .-0.3 .+ y_noise[1,:],
cos.(x) .+ y_noise[2,:],
cos.(2x).*sin.(x) .+ y_noise[3,:]
)
x = x'
y = y'
return x, y
end
X, Y = generate_data() # Training data of shape (3,num_samples)
##### CALLBACK & PLOTS #################
LossLog = Array{Float64, 1}()
LossLog_T = Array{Int, 1}()
function evalcb()
global LossLog
global LossLog_T
loss_value = loss(X, Y)
push!(LossLog, loss_value)
push!(LossLog_T, length(LossLog))
if mod(length(LossLog_T),100)==1
@show LossLog[end]
end
end
#
##### CUSTOM LAYER #########
#
mutable struct custom_layer{F,S<:AbstractArray,T<:AbstractArray}
W::S
b::T
σ::F
end
#custom_layer(W, b) = custom_layer(W, b, identity)
function custom_layer(in::Integer, out::Integer, σ=softplus)
return custom_layer(randn(out, in), randn(out), σ)
end
Flux.@functor custom_layer # makes trainable
function (a::custom_layer)(x::AbstractArray)
#x_out = a.σ.(a.W * x .+ a.b)
x_out = a.W * x .+ a.b
# x_out = x_out.*2.0 # SUCCESS (trivial)
# x_out = tanh.(x_out) # SUCCESS (trivial)
x_out[1] = 1.0 # FAILS
# x_out = map!(tanh, x_out, x_out) # FAILS
# FAILS:
#= f = [sin, cos, tan]
for i in 1:size(x_out, 1)
x_out = view(x_out, i, :)
map!(f[i], x_out, x_out)
end =#
return x_out
end
# @treelike custom_layer # some say to use @treelike, but it's not used in the Flux definition of Dense
##### MODEL & TRAINING #####################
n = 10
act_f = tanh
last_layer = custom_layer(n, 3) #Dense(n, size(Y,1))
m = Chain(Dense(size(X, 1), n, act_f), Dense(n, n, act_f), Dense(n, n, act_f), last_layer)
opt = ADAM()
loss(x, y) = mse(m(x), y)
train_loader = DataLoader(X, Y, batchsize=10, shuffle=true)
NumEpochs = 500
if training_size_option==3 NumEpochs = convert(Int64, NumEpochs/2.0) end
for epoch in 1:NumEpochs
for (x, y) in train_loader
Flux.train!(loss, Flux.params(m), train_loader, opt; cb=evalcb)
end
end
And the error message:
Mutating arrays is not supported
Stacktrace:
[1] error(::String) at .\error.jl:33
[2] (::Zygote.var"#1048#1049")(::Nothing) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\lib\array.jl:61
[3] (::Zygote.var"#2775#back#1050"{Zygote.var"#1048#1049"})(::Nothing) at C:\Users\username\.julia\packages\ZygoteRules\6nssF\src\adjoint.jl:49
[4] custom_layer at .\In[184]:63 [inlined]
[5] (::typeof(∂(λ)))(::Array{Float64,2}) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[6] applychain at C:\Users\username\.julia\packages\Flux\Fj3bt\src\layers\basic.jl:36 [inlined]
[7] (::typeof(∂(applychain)))(::Array{Float64,2}) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[8] applychain at C:\Users\username\.julia\packages\Flux\Fj3bt\src\layers\basic.jl:36 [inlined]
[9] (::typeof(∂(applychain)))(::Array{Float64,2}) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[10] applychain at C:\Users\username\.julia\packages\Flux\Fj3bt\src\layers\basic.jl:36 [inlined]
[11] (::typeof(∂(applychain)))(::Array{Float64,2}) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[12] applychain at C:\Users\username\.julia\packages\Flux\Fj3bt\src\layers\basic.jl:36 [inlined]
[13] (::typeof(∂(applychain)))(::Array{Float64,2}) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[14] Chain at C:\Users\username\.julia\packages\Flux\Fj3bt\src\layers\basic.jl:38 [inlined]
[15] (::typeof(∂(λ)))(::Array{Float64,2}) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[16] loss at .\In[184]:85 [inlined]
[17] (::typeof(∂(loss)))(::Float64) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[18] (::Zygote.var"#174#175"{typeof(∂(loss)),Tuple{Tuple{Nothing,Nothing}}})(::Float64) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\lib\lib.jl:182
[19] (::Zygote.var"#347#back#176"{Zygote.var"#174#175"{typeof(∂(loss)),Tuple{Tuple{Nothing,Nothing}}}})(::Float64) at C:\Users\username\.julia\packages\ZygoteRules\6nssF\src\adjoint.jl:49
[20] #17 at C:\Users\username\.julia\packages\Flux\Fj3bt\src\optimise\train.jl:89 [inlined]
[21] (::typeof(∂(λ)))(::Float64) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface2.jl:0
[22] (::Zygote.var"#49#50"{Params,Zygote.Context,typeof(∂(λ))})(::Float64) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface.jl:179
[23] gradient(::Function, ::Params) at C:\Users\username\.julia\packages\Zygote\YeCEW\src\compiler\interface.jl:55
[24] macro expansion at C:\Users\username\.julia\packages\Flux\Fj3bt\src\optimise\train.jl:88 [inlined]
[25] macro expansion at C:\Users\username\.julia\packages\Juno\f8hj2\src\progress.jl:134 [inlined]
[26] train!(::typeof(loss), ::Params, ::DataLoader, ::ADAM; cb::typeof(evalcb)) at C:\Users\username\.julia\packages\Flux\Fj3bt\src\optimise\train.jl:81
[27] top-level scope at .\In[184]:92