A configuration manager for Julia.
Suppose you have a configuration file called "tdlambda.json", specifying a set of parameter settings for an experiment
{
"save_path": "RandomWalk19/tdlambda",
"experiment": {
"class": "MarkovRewardProcess",
"episodes": 10
},
"environment": {
"class": "RandomWalk",
"nstates": 19
},
"agent": {
"class": "TDLambda",
"gamma": 1.0,
"metastep": [0.025, 0.05, 0.075, 0.1],
"lambda": [0.0, 0.4, 0.8, 0.9]
}
}Note: any config file must have the "save_path" parameter. This
specifies the directory in data/output which the data will be saved to. In this
example, data will be saved to data/output/RandomWalk19/tdlambda
Initialize a manager to manage all the details of this config file
cfg = ConfigManager("tdlambda.json", @__DIR__)The second argument specifies where the data directory should be
setup. In this case, a directory data/ will be setup in the same directory
as the experiment which ConfigManager was instantiated in.
Any lists of parameters in the lowest-level of the config can be swept over
(in this case, cfg["agent"]["metastep"] and cfg["agent"]["lambda"]).
The different parameter settings are linearized. In order to sweep all the
parameters of this config, we can first check how many different parameters
there are:
N = total_combinations(cfg)Then, we need to parse each of the individual settings into a concrete parameterization:
for idx=1:N
parse!(cfg, idx)
run_some_experiment(cfg)
endparse sets up the settings of a particular parameterization. After parsing the
config, individual parameters can be accessed by indexing. For example, to this
parameterization's "metastep" parameter, we can call cfg["agent"]["metastep"].
If you will be referencing certain nested parameters quite often, you can
get the subconfiguration instead: subcfg = get_subconfig(cfg, "agent").
Then access parameters of the subconfig in the same way: subcfg["metastep"].
Note that parse! has a third argument -- the run number -- which defaults to 1.
To do multiple runs of an experiment, you can therefore do:
for run=1:100
for idx=1:N
parse!(cfg, idx, run)
run_some_experiment(cfg)
end
endThe ConfigManager also takes care of saving data to the right place.
Just collect whatever data you want during your experiment in a Dict()
and pass it to the ConfigManager.
function experiment(cfg::ConfigManager)
data = Dict()
data["ValueError"] = Float64[]
for i=1:1000
push!(data["ValueError"], rand())
end
save(cfg, data)
endThen load the data later using load(cfg) (where cfg is a parse!'d config).