3
\$\begingroup\$

Often when running exprimental code to empirically investigate an algorithm, you tweak a parameter, then rerun the code, and generate a bunch of saved models for various parameters.

To make sure I don't loose what the parameters were set to I defined a macro that saves there values.

module Util
using JLD

export @param_save #...

function names_candidates(blk::Expr)
    names_in_block = Vector{Symbol}()
    for a in blk.args
        typeof(a) <: Expr || continue
        if a.head == :(=)
            push!(names_in_block, a.args[1])
        else #Recurse, so we captured things in blocks or behind `const`
            append!(names_in_block, names_candidates(a))
        end
    end
    names_in_block
end

macro param_save(filename, blk::Expr)
    names_in_block =  names_candidates(blk)
    quote
        $(esc(blk))
        names_defined = Set($(names_in_block)) ∩ Set(names(current_module()))
        names_and_vals =[(string(name), eval(name)) for name in names_defined]
        println("Paramaters -- saving to $($filename)")
        println("----------")
        println(join(("$n = $v" for (n,v) in names_and_vals),"\n"))
        JLD.save($filename, Base.flatten(names_and_vals)...)
        println("----------")
    end
end
#...
end #module

Usage:

using Util

base_name = "paper_v1"
param_save_fn =  "../models/adagram/$(base_name).params.jld"
output_fn = "../models/adagram/$(base_name).adagram_model"#"file to save the model (in Julia format)"
@assert !isfile(output_fn)
@param_save param_save_fn begin
        nprocessors = nprocs()
        train_fn  =  "../data/corpora/WikiCorp/tokenised_lowercase_WestburyLab.wikicorp.201004.txt" #"training text data"
        output_fn = output_fn #file to save the model (in Julia format)"
        dict_fn = "../data/corpora/WikiCorp/tokenised_lowercase_WestburyLab.wikicorp.201004.1gram" #"dictionary file with word frequencies"

        window = 10 #"(max) window size" C in the paper
        min_freq  = 20 #"min. frequency of the word"
        remove_top_k = 0 #"remove top K most frequent words"
        dim  = 300 #"dimensionality of representations"
        prototypes = 5 #"number of word prototypes" T in the paper
        alpha = 0.15 #"prior probability of allocating a new prototype"
        d  = 0.0 #"parameter of Pitman-Yor process" D in paper
        subsample = 1e-5 #"subsampling treshold. useful value is 1e-5"
        context_cut  = true #"randomly reduce size of the context"
        epochs = 1 #"number of epochs to train"
        initcount = 1. #"initial weight (count) on first sense for each word"
        stopwords = Set{AbstractString}() #"list of stop words"
        sense_treshold = 1e-10 #"minimal probability of a meaning to contribute into gradients"
        save_treshold = 0.0 #"minimal probability of a meaning to save after training"
end

Output:

Paramaters -- saving to ../models/adagram/paper_v1btempexample.params.jld
----------
train_fn = ../data/corpora/WikiCorp/tokenised_lowercase_WestburyLab.wikicorp.201004.txt
alpha = 0.15
subsample = 1.0e-5
sense_treshold = 1.0e-10
initcount = 1.0
nprocessors = 1
epochs = 1
window = 10
output_fn = ../models/adagram/paper_v1btempexample.adagram_model
min_freq = 20
prototypes = 5
context_cut = true
stopwords = Set{AbstractString}()
remove_top_k = 0
save_treshold = 0.0
dict_fn = ../data/corpora/WikiCorp/tokenised_lowercase_WestburyLab.wikicorp.201004.1gram
dim = 300
d = 0.0
----------

Writing code with macros is hard. It took me a few shots to get the escaping all right.

Is this reasonable code? AFAICT julia doesn't have a method like pythons locals() that lists all variables in a given scope. So I have to work around that by finding possible variables in the AST then cross referencing that against all variables in the module.

\$\endgroup\$
1
\$\begingroup\$

Since this will be used at toplevel anyway, I would just wrap all the parameters in a module.

macro param_save(file, blk)
    modname = gensym()
    Expr(:toplevel,
        Expr(:using, :JLD),
        Expr(:module, false, modname, esc(blk)),
        quote
            JLD.save($file, Dict(
                string(n) => eval($modname, n)
                for n in names($modname, true)
                if n ≠ $(Meta.quot(modname))))
        end)
end
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.