# NeuralODE - Array has no field layer

**URL:** <https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586>\
**Category:** General Usage\
**Tags:** question\
**Created:** [January 9, 2024, 7:47pm UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586 "2024-01-09T19:47:33Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Nocix](https://avatars.discourse-cdn.com/v4/letter/n/b782af/32.png) [@Nocix](https://discourse.julialang.org/u/Nocix)\
**Post date:** [January 9, 2024, 7:47pm UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586/1 "2024-01-09T19:47:33Z")

</div>

Hi,

I’m new here and have a question about extrapolating data using neural ordinary differential equations. I am in the process of training a mathematical function in steps of four. Training the neural network seems to work. The parameters of the network are adjusted. However, I am not able to plot a prediction with the trained network.

Here is the code:

```julia
using Random, Dates, Optimization, ComponentArrays, Lux, OptimizationOptimisers, DiffEqFlux,
    OrdinaryDiffEq, CSV, DataFrames, Dates, Statistics, Plots, DataDeps
using Flux

#----------------------------------Data Extraction--------------------------------------------------
time = 0:1:15
t = collect(range(extrema(time)..., length=length(time)))
tspan = (Float32.(t[1]), Float32.(t[end]))
#tspan = (Float32.(t_scale[1]), Float32.(t_scale[end]));
data = sin.(t).*exp.(-0.05*t)
plot(t, data)

u0 = [Float32(data[1])];

#-------------------------------Training NN------------------------------------------
function neural_ode(t, data_dim)
    f = Lux.Chain(Lux.Dense(data_dim => 64, swish), Lux.Dense(64 => 32, swish), Lux.Dense(32 => data_dim))

    node = NeuralODE(f, extrema(t), Tsit5(); saveat = t,
        abstol = 1e-9, reltol = 1e-9)

    rng = Random.default_rng()
    p, state = Lux.setup(rng, f)

    return node, ComponentArray(p), state
end

function train_one_round(node, p, state, y, opt, maxiters, rng, y0 = y[:, 1]; kwargs...)
    predict(p) = Array(node(y0, p, state)[1])
    loss(p) = sum(abs2, predict(p) .- y)

    adtype = Optimization.AutoZygote()
    optf = OptimizationFunction((p, _) -> loss(p), adtype)
    optprob = OptimizationProblem(optf, p)
    res = solve(optprob, opt; maxiters = maxiters, kwargs...)
    res.minimizer, state
end

function train(t, y, obs_grid, maxiters, lr, rng, p = nothing, state = nothing; kwargs...)
    log_results(ps, losses) = (p, loss) -> begin
        push!(ps, copy(p))
        push!(losses, loss)
        false
    end

    ps, losses = ComponentArray[], Float32[]
    for k in obs_grid
        node, p_new, state_new = neural_ode(t, size(y, 1))
        p === nothing && (p = p_new)
        state === nothing && (state = state_new)

        p, state = train_one_round(node, p, state, y, ADAM(lr), maxiters, rng;
            callback = log_results(ps, losses), kwargs...)
    end
    ps, state, losses
end

rng = MersenneTwister(123)
obs_grid = 4:4:length(time) # we train on an increasing amount of the first k obs
maxiters = 150
lr = 5e-3
ps, state, losses = train(time, data, obs_grid, maxiters, lr, rng; progress = true)

#-------------------Prediction Part--------------------------------
t_grid = Float32.(collect(range(minimum(time), maximum(time); length = 500)))
predict(y0, t, p, state) = begin
    node, _, _ = neural_ode(t, length(y0))
    Array(node(y0, p, state)[1])
end

y_pred = predict(u0, t_grid, ps, state)

plot(time, data, label="Originaldaten", xlabel="Zeit", ylabel="Daten", linewidth=2)
plot!(time, y_pred, label="Vorhersagen", linestyle=:dash, linewidth=2)
legend()

```

I’m getting an error at this point:

```julia
predict(y0, t, p, state) = begin
    node, _, _ = neural_ode(t, length(y0))
    Array(node(y0, p, state)[1])
end

y_pred = predict(Float64(data[1]), t_grid, ps, state)

```

ERROR: type Array has no field layer\_1

Does anyone know how i can solve this problem?

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [January 9, 2024, 7:52pm UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586/2 "2024-01-09T19:52:45Z")

</div>

> [@Nocix](#):
>
> ERROR: type Array has no field layer\_1

It means the parameters you’re passing to the neural network are of type `Array` and not of type `ComponentArray`. Track down where the conversion was made or missing.

---

<div class="post-metadata">

**Author:** ![Nocix](https://avatars.discourse-cdn.com/v4/letter/n/b782af/32.png) [@Nocix](https://discourse.julialang.org/u/Nocix)\
**Post date:** [January 9, 2024, 8:50pm UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586/3 "2024-01-09T20:50:48Z")

</div>

> [@Nocix](#):
>
> `y_pred = predict(Float64(data[1]), t_grid, ps, state)`

Thank you for your answer! I forgot to use the last parameter set of the training for the prediction. Here ist the solution:

```julia
predict(y0, t, p, state) = begin
    node, _, _ = neural_ode(t, length(y0))
    Array(node(y0, p, state)[1])
end

y_pred = predict(Float32(data[1]), t_grid, ps[end], state)

```

Now I’m getting the next error at the same point:  
ERROR: DimensionMismatch: second dimension of A, 16, does not match length  
of x, 1

---

<div class="post-metadata">

**Author:** ![Nocix](https://avatars.discourse-cdn.com/v4/letter/n/b782af/32.png) [@Nocix](https://discourse.julialang.org/u/Nocix)\
**Post date:** [January 9, 2024, 9:45pm UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586/4 "2024-01-09T21:45:31Z")

</div>

Apparently the prediction has a problem with the dimension of the vector “time” but I don’t know why.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [January 10, 2024, 12:15am UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586/5 "2024-01-10T00:15:23Z")

</div>

I’m not 100% sure what your follow up question is.

---

<div class="post-metadata">

**Author:** ![Nocix](https://avatars.discourse-cdn.com/v4/letter/n/b782af/32.png) [@Nocix](https://discourse.julialang.org/u/Nocix)\
**Post date:** [January 10, 2024, 2:26pm UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586/6 "2024-01-10T14:26:22Z")

</div>

Is it possible that the NODE is not able to learn a simple mathematical function?

I tried one code which uses 4 different time series of a weather forecast which means that the network hast 4 inputs of time and 4 outputs (Temperature, Humidity,Velocity, Pressure) of the meteorological aspects. This code works verry well.

```julia
using Random, Dates, Optimization, ComponentArrays, Lux, OptimizationOptimisers, DiffEqFlux,
    OrdinaryDiffEq, CSV, DataFrames, Dates, Statistics, Plots, DataDeps

#----------------------------------Datenextraktion--------------------------------------------------
function download_data(data_url = "https://raw.githubusercontent.com/SebastianCallh/neural-ode-weather-forecast/master/data/",
        data_local_path = "./delhi")
    function load(file_name)
        data_dep = DataDep("delhi/train", "", "$data_url/$file_name")
        Base.download(data_dep, data_local_path; i_accept_the_terms_of_use = true)
        CSV.read(joinpath(data_local_path, file_name), DataFrame)
    end

    train_df = load("DailyDelhiClimateTrain.csv")
    test_df = load("DailyDelhiClimateTest.csv")
    return vcat(train_df, test_df)
end

df = download_data()

#----------------------------------Daten ordnen--------------------------------------------------------------
FEATURES = [:meantemp, :humidity, :wind_speed, :meanpressure]
UNITS = ["Celsius", "g/m³ of water", "km/h", "hPa"]
FEATURE_NAMES = ["Mean temperature", "Humidity", "Wind speed", "Mean pressure"]

function plot_data(df)
    plots = map(enumerate(zip(FEATURES, FEATURE_NAMES, UNITS))) do (i, (f, n, u))
        plot(df[:, :date], df[:, f]; title = n, label = nothing,
            ylabel = u, size = (800, 600), color = i)
    end

    n = length(plots)
    plot(plots...; layout = (Int(n / 2), Int(n / 2)))
end

plot_data(df)

#------------------------------Daten standardisieren----------------------------------------
function standardize(x)
    μ = mean(x; dims = 2)
    σ = std(x; dims = 2)
    z = (x .- μ) ./ σ
    return z, μ, σ
end

function featurize(raw_df, num_train = 20)
    raw_df.year = Float64.(year.(raw_df.date))
    raw_df.month = Float64.(month.(raw_df.date))
    df = combine(groupby(raw_df, [:year, :month]),
        :date => (d -> mean(year.(d)) .+ mean(month.(d)) ./ 12),
        :meantemp => mean,
        :humidity => mean,
        :wind_speed => mean,
        :meanpressure => mean;
        renamecols = false)
    t_and_y(df) = df.date', Matrix(select(df, FEATURES))'
    t_train, y_train = t_and_y(df[1:num_train, :])
    t_test, y_test = t_and_y(df[(num_train + 1):end, :])
    t_train, t_mean, t_scale = standardize(t_train)
    y_train, y_mean, y_scale = standardize(y_train)
    t_test = (t_test .- t_mean) ./ t_scale
    y_test = (y_test .- y_mean) ./ y_scale

    return (vec(t_train), y_train,
        vec(t_test), y_test,
        (t_mean, t_scale),
        (y_mean, y_scale))
end

function plot_features(t_train, y_train, t_test, y_test)
    plt_split = plot(reshape(t_train, :), y_train';
        linewidth = 3, colors = 1:4,
        xlabel = "Normalized time",
        ylabel = "Normalized values",
        label = nothing,
        title = "Features")
    plot!(plt_split, reshape(t_test, :), y_test';
        linewidth = 3, linestyle = :dash,
        color = [1 2 3 4], label = nothing)
    plot!(plt_split, [0], [0]; linewidth = 0,
        label = "Train", color = 1)
    plot!(plt_split, [0], [0]; linewidth = 0,
        linestyle = :dash, label = "Test",
        color = 1,
        ylims = (-5, 5))
end

t_train, y_train, t_test, y_test, (t_mean, t_scale), (y_mean, y_scale) = featurize(df)
plot_features(t_train, y_train, t_test, y_test)

#-------------------------------Training des NN------------------------------------------
function neural_ode(t, data_dim)
    f = Lux.Chain(Lux.Dense(data_dim => 64, swish), Lux.Dense(64 => 32, swish), Lux.Dense(32 => data_dim))

    node = NeuralODE(f, extrema(t), Tsit5(); saveat = t,
        abstol = 1e-9, reltol = 1e-9)

    rng = Random.default_rng()
    p, state = Lux.setup(rng, f)

    return node, ComponentArray(p), state
end

function train_one_round(node, p, state, y, opt, maxiters, rng, y0 = y[:, 1]; kwargs...)
    predict(p) = Array(node(y0, p, state)[1])
    loss(p) = sum(abs2, predict(p) .- y)

    adtype = Optimization.AutoZygote()
    optf = OptimizationFunction((p, _) -> loss(p), adtype)
    optprob = OptimizationProblem(optf, p)
    res = solve(optprob, opt; maxiters = maxiters, kwargs...)
    res.minimizer, state
end

function train(t, y, obs_grid, maxiters, lr, rng, p = nothing, state = nothing; kwargs...)
    log_results(ps, losses) = (p, loss) -> begin
        push!(ps, copy(p))
        push!(losses, loss)
        false
    end

    ps, losses = ComponentArray[], Float32[]
    for k in obs_grid
        node, p_new, state_new = neural_ode(t, size(y, 1))
        p === nothing && (p = p_new) #Löscht Eintrag von p, nimmt jedoch p für train_one_round
        state === nothing && (state = state_new)

        p, state = train_one_round(node, p, state, y, ADAM(lr), maxiters, rng;
            callback = log_results(ps, losses), kwargs...)
    end
    ps, state, losses
end

rng = MersenneTwister(123)
obs_grid = 4:4:length(t_train) # we train on an increasing amount of the first k obs
maxiters = 150
lr = 5e-3
ps, state, losses = train(t_train, y_train, obs_grid, maxiters, lr, rng; progress = true)

#------------------------------Prediction Part-------------------------------------------
predict(y0, t, p, state) = begin
    node, _, _ = neural_ode(t, length(y0))
    Array(node(y0, p, state)[1])
end

node, _, _ = neural_ode(t, length(y_train[:, 1]))
y_pred = Array(node(y_train[:, 1], ps[end], state)[1])

t_grid = collect(range(minimum(t_train), maximum(t_test); length = 500))
y_pred = predict(y_train[:, 1], t_grid, ps[end], state)

plot(t_grid, y_pred[1,:], label = "Prediction")
plot!(t_train, y_train[1,:], label = "Data")

```

![Prediction_4InputsOutputs](https://global.discourse-cdn.com/julialang/original/3X/5/a/5a1cf824460d5ebdcb5aadf3800f89f0a494b5ef.png)

Then I reduced the Inputs and Outputs to 1 (Only Temperature) and trained again. Now it does’nt work anymore.

```julia
using Random, Dates, Optimization, ComponentArrays, Lux, OptimizationOptimisers, DiffEqFlux,
    OrdinaryDiffEq, CSV, DataFrames, Dates, Statistics, Plots, DataDeps

#----------------------------------Datenextraktion--------------------------------------------------
function download_data(data_url = "https://raw.githubusercontent.com/SebastianCallh/neural-ode-weather-forecast/master/data/",
        data_local_path = "./delhi")
    function load(file_name)
        data_dep = DataDep("delhi/train", "", "$data_url/$file_name")
        Base.download(data_dep, data_local_path; i_accept_the_terms_of_use = true)
        CSV.read(joinpath(data_local_path, file_name), DataFrame)
    end

    train_df = load("DailyDelhiClimateTrain.csv")
    test_df = load("DailyDelhiClimateTest.csv")
    return vcat(train_df, test_df)
end

df = download_data()

#----------------------------------Daten ordnen--------------------------------------------------------------
FEATURES = [:meantemp, :humidity, :wind_speed, :meanpressure]
UNITS = ["Celsius", "g/m³ of water", "km/h", "hPa"]
FEATURE_NAMES = ["Mean temperature", "Humidity", "Wind speed", "Mean pressure"]

function plot_data(df)
    plots = map(enumerate(zip(FEATURES, FEATURE_NAMES, UNITS))) do (i, (f, n, u))
        plot(df[:, :date], df[:, f]; title = n, label = nothing,
            ylabel = u, size = (800, 600), color = i)
    end

    n = length(plots)
    plot(plots...; layout = (Int(n / 2), Int(n / 2)))
end

plot_data(df)

#------------------------------Daten standardisieren----------------------------------------
function standardize(x)
    μ = mean(x; dims = 2)
    σ = std(x; dims = 2)
    z = (x .- μ) ./ σ
    return z, μ, σ
end

function featurize(raw_df, num_train = 20)
    raw_df.year = Float64.(year.(raw_df.date))
    raw_df.month = Float64.(month.(raw_df.date))
    df = combine(groupby(raw_df, [:year, :month]),
        :date => (d -> mean(year.(d)) .+ mean(month.(d)) ./ 12),
        :meantemp => mean,
        :humidity => mean,
        :wind_speed => mean,
        :meanpressure => mean;
        renamecols = false)
    t_and_y(df) = df.date', Matrix(select(df, FEATURES))'
    t_train, y_train = t_and_y(df[1:num_train, :])
    t_test, y_test = t_and_y(df[(num_train + 1):end, :])
    t_train, t_mean, t_scale = standardize(t_train)
    y_train, y_mean, y_scale = standardize(y_train)
    t_test = (t_test .- t_mean) ./ t_scale
    y_test = (y_test .- y_mean) ./ y_scale

    return (vec(t_train), y_train,
        vec(t_test), y_test,
        (t_mean, t_scale),
        (y_mean, y_scale))
end

function plot_features(t_train, y_train, t_test, y_test)
    plt_split = plot(reshape(t_train, :), y_train';
        linewidth = 3, colors = 1:4,
        xlabel = "Normalized time",
        ylabel = "Normalized values",
        label = nothing,
        title = "Features")
    plot!(plt_split, reshape(t_test, :), y_test';
        linewidth = 3, linestyle = :dash,
        color = [1 2 3 4], label = nothing)
    plot!(plt_split, [0], [0]; linewidth = 0,
        label = "Train", color = 1)
    plot!(plt_split, [0], [0]; linewidth = 0,
        linestyle = :dash, label = "Test",
        color = 1,
        ylims = (-5, 5))
end

t_train, y_train, t_test, y_test, (t_mean, t_scale), (y_mean, y_scale) = featurize(df)
plot_features(t_train, y_train, t_test, y_test)

plot(t_train, y_train[1,:])

#-------------------------------Training des NN------------------------------------------
function neural_ode(t, data_dim)
    f = Lux.Chain(Lux.Dense(data_dim => 64, swish), Lux.Dense(64 => 32, swish), Lux.Dense(32 => data_dim))

    node = NeuralODE(f, extrema(t), Tsit5(); saveat = t,
        abstol = 1e-9, reltol = 1e-9)

    rng = Random.default_rng()
    p, state = Lux.setup(rng, f)

    return node, ComponentArray(p), state
end

function train_one_round(node, p, state, y, opt, maxiters, rng, y0 = [y[1, 1]]; kwargs...)
    predict(p) = Array(node(y0, p, state)[1])
    loss(p) = sum(abs2, predict(p) .- y)

    adtype = Optimization.AutoZygote()
    optf = OptimizationFunction((p, _) -> loss(p), adtype)
    optprob = OptimizationProblem(optf, p)
    res = solve(optprob, opt; maxiters = maxiters, kwargs...)
    res.minimizer, state
end

function train(t, y, obs_grid, maxiters, lr, rng, p = nothing, state = nothing; kwargs...)
    log_results(ps, losses) = (p, loss) -> begin
        push!(ps, copy(p))
        push!(losses, loss)
        false
    end

    ps, losses = ComponentArray[], Float32[]
    for k in obs_grid
        node, p_new, state_new = neural_ode(t, size(y[1,1], 1))
        p === nothing && (p = p_new) #Löscht Eintrag von p, nimmt jedoch p für train_one_round
        state === nothing && (state = state_new)

        p, state = train_one_round(node, p, state, y, ADAM(lr), maxiters, rng;
            callback = log_results(ps, losses), kwargs...)
    end
    ps, state, losses
end

rng = MersenneTwister(123)
obs_grid = 4:4:length(t_train) # we train on an increasing amount of the first k obs
maxiters = 150
lr = 5e-3
ps, state, losses = train(t_train, y_train[1,:], obs_grid, maxiters, lr, rng; progress = true)

#--------------------------------Prediction Part------------------------------------------
predict(y0, t, p, state) = begin
    node, _, _ = neural_ode(t, length(y0))
    Array(node(y0, p, state)[1])
end

t_grid = collect(range(minimum(t_train), maximum(t_train); length = 500))
y_pred = predict([y_train[1, 1]], t_grid, ps[end], state)

plot(t_grid, y_pred[1,:], label = "Prediction")
plot!(t_train, y_train[1,:], label = "Data")

```

![Prediction_1InputOutput](https://global.discourse-cdn.com/julialang/original/3X/b/d/bd4b7a94c5b91fc7f32c36fc0484cdc7e3ee85f4.png)

Does the network need the connections between the 4 meteorological aspects in order to learn the course of a single aspect? I know this is a difficult question but maybe someone knows an answer.

---

<div class="post-metadata">

**Author:** ![Nocix](https://avatars.discourse-cdn.com/v4/letter/n/b782af/32.png) [@Nocix](https://discourse.julialang.org/u/Nocix)\
**Post date:** [January 10, 2024, 2:46pm UTC](https://discourse.julialang.org/t/neuralode-array-has-no-field-layer/108586/7 "2024-01-10T14:46:11Z")

</div>

Ok sorry for the spam but now i got the solution. It seems to me that the network needs to be connectet with two inputs (time) and two outputs (mathematical function, time) to find a correlation. Now it works.

```julia
using Random, Dates, Optimization, ComponentArrays, Lux, OptimizationOptimisers, DiffEqFlux,
    OrdinaryDiffEq, CSV, DataFrames, Dates, Statistics, Plots, DataDeps
using Flux

#----------------------------------Datenextraktion--------------------------------------------------
time = 0:1:11
t = collect(range(extrema(time)..., length=length(time)))
tspan = (Float32.(t[1]), Float32.(t[end]))
y = exp.(0.05*t).*sin.(t)
data = hcat(y, t)'

u0 = [Float32(data[1])];

#-------------------------------Training des NN------------------------------------------
function neural_ode(t, data_dim)
    f = Lux.Chain(Lux.Dense(data_dim => 32, swish), Lux.Dense(32 => 20, swish), Lux.Dense(20 => data_dim))

    node = NeuralODE(f, extrema(t), Tsit5(); saveat = t,
        abstol = 1e-9, reltol = 1e-9)

    rng = Random.default_rng()
    p, state = Lux.setup(rng, f)

    return node, ComponentArray(p), state
end

function train_one_round(node, p, state, y, opt, maxiters, rng, y0 = y[:, 1]; kwargs...)
    predict(p) = Array(node(y0, p, state)[1])
    loss(p) = sum(abs2, predict(p) .- y)

    adtype = Optimization.AutoZygote()
    optf = OptimizationFunction((p, _) -> loss(p), adtype)
    optprob = OptimizationProblem(optf, p)
    res = solve(optprob, opt; maxiters = maxiters, kwargs...)
    res.minimizer, state
end

function train(t, y, obs_grid, maxiters, lr, rng, p = nothing, state = nothing; kwargs...)
    log_results(ps, losses) = (p, loss) -> begin
        push!(ps, copy(p))
        push!(losses, loss)
        false
    end

    ps, losses = ComponentArray[], Float32[]
    for k in obs_grid
        node, p_new, state_new = neural_ode(t, size(y, 1))
        p === nothing && (p = p_new)
        state === nothing && (state = state_new)

        p, state = train_one_round(node, p, state, y, ADAM(lr), maxiters, rng;
            callback = log_results(ps, losses), kwargs...)
    end
    ps, state, losses
end

rng = MersenneTwister(123)
obs_grid = 4:4:length(time) # we train on an increasing amount of the first k obs
maxiters = 500
lr = 5e-3
ps, state, losses = train(time, data, obs_grid, maxiters, lr, rng; progress = true)

plot(losses, yaxis = :log)

#-------------------Prediction Part--------------------------------

predict(y0, t, p, state) = begin
    node, _, _ = neural_ode(t, length(y0))
    Array(node(y0, p, state)[1])
end

t_grid = Float32.(collect(range(minimum(time), maximum(time); length = 500)))
y_pred = predict(data[:,1], t_grid, ps[end], state)

plot(time, data[1,:], label="Data", xlabel="time", ylabel="data", linewidth=2)
plot!(t_grid, y_pred[1,:], label="Prediction", linestyle=:dash, linewidth=2)

```

![Prediction_MathFunc](https://global.discourse-cdn.com/julialang/original/3X/b/f/bf6d7dbe3955b8d2521cdf4ad741d51bf86fb77f.png)
