-
Notifications
You must be signed in to change notification settings - Fork 0
/
parameter_search.py
151 lines (118 loc) · 5.98 KB
/
parameter_search.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
"""
Perform parameter search for a certain model type by generating a set of config file, where hyperparameters of interest
have been replaced by potential candidates. These candidates and the ranges they are sampled from are specified in
an extra config file. Due to some conflicts with the cluster queueing system, these configs have to be run separately
and the best set of hyperparameters have to be selected manually from the results.
"""
# STD
from argparse import ArgumentParser
import json
from typing import List, Dict, Union
# EXT
import numpy as np
from diagnnose.config.setup import ConfigSetup
# PROJECT
from src.recoding.mc_dropout import MCDropoutMechanism
from src.recoding.perplexity import SurprisalRecoding
from src.recoding.variational import VariationalMechanism
from src.recoding.anchored_ensemble import AnchoredEnsembleMechanism
# GLOBALS
RECODING_TYPES = {
"ensemble": AnchoredEnsembleMechanism,
"perplexity": SurprisalRecoding,
"mc_dropout": MCDropoutMechanism,
"variational": VariationalMechanism
}
def main():
config_dict = manage_parameter_search_config()
out_dir = config_dict["general"]["out_dir"]
# Generate sets of hyperparameters
trials = generate_trials(config_dict, num_trials=config_dict["general"]["num_trials"])
# Dump in new config files
for n, trial in enumerate(trials):
dump_config(trial, f"{out_dir}/trial{n+1}.json")
def dump_config(config_dict: dict, config_path: str) -> None:
"""
Dump a config to a .json file.
"""
with open(config_path, "w") as config_file:
json.dump(config_dict, config_file, sort_keys=True, indent=4)
def generate_trials(config_dict: dict, num_trials: int) -> List[Dict]:
"""
Generate all the configurations of hyperparameters that are going to be tested out.
"""
default_config = config_dict["default_parameters"]
trials = [dict(default_config) for _ in range(num_trials)] # Create new copies of the same default hyperparams
sample_funcs = {
"uniform": sample_uniform,
"log": sample_log
}
# Generate config for run
for trial in trials:
# Replace the number of epochs
trial["num_epochs"] = config_dict["general"]["num_epochs"]
# Models will not be saved
trial["model_save_path"] = None
# Replace other hyperparameters with candidates
for hyperparameter, search_info in config_dict["general"]["parameter_search"].items():
sample_range, type_ = search_info["range"], search_info["type"]
sampled_value = sample_funcs[search_info["dist"]](*sample_range, type_=type_)
trial[hyperparameter] = sampled_value
return trials
def sample_uniform(lower_limit: Union[float, int], upper_limit: Union[float, int], type_="float") -> Union[float, int]:
"""
Sample hyperparameter value from a uniform distribution.
"""
value = np.random.uniform(lower_limit, upper_limit)
value = int(value) if type_ == "int" else value
return value
def sample_log(lower_limit: Union[float, int], upper_limit: Union[float, int], type_="float") -> Union[float, int]:
"""
Sample hyperparameter value from a uniform distribution and transform with a log scale.
"""
value = np.random.exponential((upper_limit - lower_limit)/4)
value = int(value) if type_ == "int" else value
return value
def manage_parameter_search_config() -> dict:
"""
Parse a config file (if given), overwrite with command line arguments and return everything as dictionary
of different config groups.
"""
required_args = {"num_epochs", "num_trials", "model_config", "out_dir"}
arg_groups = {
"general": {"parameter_search", "corpus_dir", "num_epochs", "num_trials", "model_config", "out_dir"},
}
argparser = init_argparser()
config_object = ConfigSetup(argparser, required_args, arg_groups)
config_dict = config_object.config_dict
with open(config_dict["general"]["model_config"], "r") as model_config:
config_dict["default_parameters"] = json.load(model_config)
return config_dict
def init_argparser() -> ArgumentParser:
"""
Init the parser that parses additional command line arguments. These overwrite the default options
that were defined in a config if -c / --config is given.
"""
parser = ArgumentParser()
from_config = parser.add_argument_group('From config file', 'Provide full experiment setup via config file')
from_config.add_argument('-c', '--config', help='Path to json file containing classification config.')
from_cmd = parser.add_argument_group('From commandline', 'Specify experiment setup via commandline arguments')
# Model options
from_cmd.add_argument("--model_config", type=str, default=None, help="Path to model config defining parameters "
"that are not being searched")
from_cmd.add_argument("--recoding_type", type=str, default=None,
choices=["mc_dropout", "surprisal", "ensemble"],
help="Recoding model type used for training. Choices include recoding based on MC Dropout,"
"perplexity and anchored ensembles. If not specified, a vanilla model without recoding"
"is used.")
from_cmd.add_argument("--step_type", type=str, default=None, choices=["fixed", "ppl", "mlp"],
help="Specifies the way the step size is determined when using a recoding model.")
# Training options
from_cmd.add_argument("--out_dir", type=str, help="Directory to put the generated config files.")
from_cmd.add_argument("--num_epochs", type=int, help="Number of training epochs.")
from_cmd.add_argument("--num_trials", type=int, help="Number of hyperparameter configurations that should be tested.")
# Model saving and logging options
from_cmd.add_argument("--device", type=str, default="cpu", help="Device used for training.")
return parser
if __name__ == "__main__":
main()