添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

I stumbled upon this very nice example from @SebastianCallh in which Neural ODE are used to model and forecast the weather in the city of Delhi. I have a keen interest in learning how to apply differential equations using Julia, so I thought I could give it a try and adapt this code to work on my data.

Took some time to understand the basics of Julia (first timer coming from Python!), but in the end I managed to use @SebastianCallh example to put everything together as follows:

using Dates
using Plots
using Plotly
using DiffEqFlux
using Statistics
using DataFrames, CSV
using OrdinaryDiffEq, Flux, Random
function neural_ode(t, data_dim; saveat = t)
    f = FastChain(FastDense(data_dim, 64, swish),
		      FastDense(64, 32, swish),
		      FastDense(32, data_dim))
    node = NeuralODE(f, (minimum(t), maximum(t)), Tsit5(),
			 saveat = saveat, abstol = 1e-9,
			 reltol = 1e-9)
function train(node, θ, y, opt, maxiters, y0 = y[:, 1]; kwargs...)
	predict(θ) = Array(node(y0, θ))
	loss(θ) = begin
	    ŷ = predict(θ)
	    Flux.mse(ŷ, y)
    θ = θ == nothing ? node.p : θ
    res = DiffEqFlux.sciml_train(
	    loss, θ, opt,
	    maxiters = maxiters;
	    kwargs...
    return res.minimizer
log_results(θs, losses) =
    (θ, loss) -> begin
	    push!(θs, copy(θ))
	    push!(losses, loss)
	    false
################
# Main program #
################
# Loading data and setting up metadata
meta_cols = ["rowid", "SiteId", "datetime", "longitude", "latitude"]
data_cols = ["datetime", "DryBulbTemperature_Celsius"]
# Loading 168 records of hourly temperature for a given week in July 2018
# Note: Contains 'missing' values that are casted to NaN and then ignored with filter(!isnan, )
path_in = "PATH/week_of_hourly_temperature_records.csv"
df = DataFrame!(CSV.read(path_in))[data_cols]
df["DryBulbTemperature_Celsius"] = replace(df["DryBulbTemperature_Celsius"], missing=>NaN)
# Normalizing and splitting on train/test
T = 72 # first 3 days for training, remaining 4 days for testing
mean_temp = mean(filter(!isnan, df["DryBulbTemperature_Celsius"])) # taking care of NaNs
stdev_temp = std(filter(!isnan, df["DryBulbTemperature_Celsius"]))
y = Matrix(df[:, ["DryBulbTemperature_Celsius"]] |>
    y -> Matrix(y)' |>
    y -> (y .- mean_temp) ./ stdev_temp)
train_dates, test_dates = df["datetime"][1:T], df["datetime"][T:end]
vals_train_norm, vals_test_norm = y[1:T], y[T:end]
# Starting with the analysis
Random.seed!(1);
maxiters = 150
θs, losses = [], []
θ = nothing
num_obs = 4:4:length(vals_train_norm)
for k in num_obs
    chunk = filter(!isnan, vals_train_norm[1:k])
    node = neural_ode(chunk, size(y, 1))
    θ = train(
	    node, θ, chunk,
	    ADAMW(8e-3), maxiters;
	    cb = log_results(θs, losses)

However, I found this persistent error and I don’t know how to fix it:

ERROR: LoadError: **UndefVarError: θ not defined**
Stacktrace:
 [1] top-level scope at /PATH/JuliaProjects/basics_of/attempt.jl:87
in expression starting at /PATH/JuliaProjects/basics_of/attempt.jl:78

Because this θ is first set to “nothing” and it seems that will become iteratively calculated/updated. So it seems it makes sense to set up the variable with and empty value and let it change. I printed “node” and “f” and they contain values, so something starts happening, but then it crashes. I even changed the Greek symbol to “theta” just in case there was some problem there :sweat_smile:, but nothing.

Please, could you let me know what is missing in this code? How can I “define” theta properly? Perhaps an extra library import is required? Maybe the author can share the version creating these nice plots as well? :slight_smile:

Thanks for your help!

for k in num_obs
    chunk = filter(!isnan, vals_train_norm[1:k])
    node = neural_ode(chunk, size(y, 1))
    θ = train(
	    node, θ, chunk,
	    ADAMW(8e-3), maxiters;
	    cb = log_results(θs, losses)

You need a global θ declaration in this loop, since the loop is in global scope. (It wouldn’t show up if you were writing your loop in a function.)

See Scope of Variables · The Julia Language

This is the infamous global-scope rule (Global variable scope rules lead to unintuitive behavior at the REPL/notebook · Issue #28789 · JuliaLang/julia · GitHub); the error message should be clearer in Julia 1.5.

This is due to Julia’s scoping rules, which regularly trip up beginners. Variables defined in global scope (in this case, θ) aren’t visible within for loops unless they’re explicitly annotated as global variables. See here for further discussion.

A quick example:

julia>  θ = 0
julia> for i = 1:10
         θ += 1
ERROR: UndefVarError: θ not defined
Stacktrace:
 [1] top-level scope at .\REPL[8]:2
julia> for i = 1:10
         global θ += 1
julia> θ
              

@Irene I’m glad you found it useful, and welcome to Julia! :slight_smile: @stevengj and @stillyslalom are absolutely right. The code you see in my blog post is inside a function which has different scoping rules. So it is totally fine to initialize a variable to nothing, but in this case the variable itself is not “seen” from inside the loop.

That change is a good suggestion, but this has already been changed more significantly in the upcoming 1.5 release. This works heuristically in the REPL and errors more clearly in a file. In the REPL:

julia> θ = 0
julia> for i = 1:10
           θ += 1
julia> θ

In a file:

┌ Warning: Assignment to `θ` in soft scope is ambiguous because
| a global variable by the same name exists: `θ` will be treated
| as a new local. Disambiguate by using `local θ` to suppress this
| warning or `global θ` to assign to the existing global variable.
└ @ string:5
ERROR: LoadError: UndefVarError: θ not defined

Hopefully that’s clear enough that it would have helped you figure it out.