Skip to content

Commit d7071f7

Browse files
committed
Add grid search script and test.
Grid search is called like this: python -m bootstrap.grid -o <path> --gpu 0.5 --cpu 10 This will run training on all available gpus and cpus, with 10 cpus per training and 0.5 gpus assigned for each training (2 jobs per gpu). The option file must contain a new option: "gridsearch:" which contains a list of options to modify, and their associated values should be a list containing all the values to test. See the example file in tests/options-grid.yaml TODO: analysis at the end. This will need an api to get the best results for a given run, which exists only as a script for now (compare.py)
1 parent 10872d0 commit d7071f7

File tree

5 files changed

+196
-4
lines changed

5 files changed

+196
-4
lines changed

bootstrap/grid.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import ray
2+
from ray import tune
3+
import os
4+
import argparse
5+
import yaml
6+
7+
from bootstrap.run import run
8+
9+
10+
def train_func(config):
11+
# change exp dir
12+
13+
option_path = config.pop("option_file")
14+
os.chdir(config.pop("run_dir"))
15+
exp_dir = config.pop("exp_dir_prefix")
16+
17+
override_options = {
18+
"resume": "last",
19+
}
20+
21+
for name, value in config.items():
22+
override_options[name] = value
23+
if type(value) == list:
24+
value_str = ",".join(str(x) for x in value)
25+
else:
26+
value_str = str(value)
27+
exp_dir += f"--{name.split('.')[-1]}_{value_str}"
28+
29+
override_options["exp.dir"] = exp_dir
30+
run(path_opts=option_path, override_options=override_options, run_parser=False)
31+
32+
33+
def build_tune_config(option_path):
34+
with open(option_path, "r") as yaml_file:
35+
options = yaml.load(yaml_file)
36+
config = {}
37+
for key, values in options["gridsearch"].items():
38+
config[key] = tune.grid_search(values)
39+
config["exp_dir_prefix"] = options["exp"]["dir"]
40+
config["option_file"] = option_path
41+
config["run_dir"] = os.getcwd()
42+
return config, config["exp_dir_prefix"]
43+
44+
45+
def grid(path_opts, cpu_per_trial=2, gpu_per_trial=0.5):
46+
config, name = build_tune_config(path_opts)
47+
ray.init()
48+
tune.run(
49+
train_func,
50+
name=name,
51+
# stop={"avg_inc_acc": 100},
52+
config=config,
53+
resources_per_trial={"cpu": cpu_per_trial, "gpu": gpu_per_trial},
54+
local_dir="ray_results",
55+
)
56+
57+
# TODO: tune analysis to get best results.
58+
# For this, we need to extract the best score for each experiment.
59+
# analysis = tune.run(
60+
# train_mnist, config={"lr": tune.grid_search([0.001, 0.01, 0.1])})
61+
# print("Best config: ", analysis.get_best_config(metric="mean_accuracy"))
62+
63+
64+
def main():
65+
parser = argparse.ArgumentParser()
66+
parser.add_argument("-o", "--path_opts", required=True, help="Main file")
67+
parser.add_argument(
68+
"-g",
69+
"--gpu",
70+
type=float,
71+
default=0.5,
72+
help="Percentage of gpu needed for one training",
73+
)
74+
parser.add_argument(
75+
"-c",
76+
"--cpu",
77+
type=float,
78+
default=2,
79+
help="Percentage of gpu needed for one training",
80+
)
81+
args = parser.parse_args()
82+
grid(args.path_opts, args.cpu, args.gpu)
83+
84+
85+
if __name__ == "__main__":
86+
main()

bootstrap/lib/options.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def exit(self, status=0, message=None):
161161
raise Options.MissingOptionsException()
162162
super().exit(status, message)
163163

164-
def __new__(cls, source=None, arguments_callback=None, lock=False, run_parser=True):
164+
def __new__(cls, source=None, arguments_callback=None, lock=False, run_parser=True, override_options=None):
165165
# Options is a singleton, we will only build if it has not been built before
166166
if not Options.__instance:
167167
Options.__instance = object.__new__(Options)
@@ -178,7 +178,7 @@ def __new__(cls, source=None, arguments_callback=None, lock=False, run_parser=Tr
178178

179179
if run_parser:
180180
fullopt_parser = Options.HelpParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
181-
fullopt_parser.add_argument('-o', '--path_opts', type=str, required=True)
181+
fullopt_parser.add_argument('-o', '--path_opts', type=str)
182182
Options.__instance.add_options(fullopt_parser, options_dict)
183183

184184
arguments = fullopt_parser.parse_args()
@@ -201,6 +201,10 @@ def __new__(cls, source=None, arguments_callback=None, lock=False, run_parser=Tr
201201
else:
202202
Options.__instance.options = options_dict
203203

204+
if override_options is not None:
205+
for key, value in override_options.items():
206+
Options.__instance.options[key] = value
207+
204208
if lock:
205209
Options.__instance.lock()
206210
return Options.__instance

bootstrap/run.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ def init_logs_options_files(exp_dir, resume=None):
5353
Logger(exp_dir, name=logs_name)
5454

5555

56-
def run(path_opts=None):
56+
def run(path_opts=None, override_options=None, run_parser=True):
5757
# first call to Options() load the options yaml file from --path_opts command line argument if path_opts=None
58-
Options(path_opts)
58+
Options(path_opts, override_options=override_options, run_parser=run_parser)
5959

6060
# init options and exp dir for logging
6161
init_experiment_directory(Options()['exp']['dir'], Options()['exp']['resume'])

tests/options-grid.yaml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
exp:
2+
dir: logs/myproject/1_exp
3+
resume: # last, best_[...], or empty (from scratch)
4+
dataset:
5+
import: myproject.datasets.factory
6+
name: myproject
7+
dir: data/myproject
8+
train_split: train
9+
eval_split: val
10+
nb_threads: 4
11+
batch_size: 64
12+
nb_items: 100
13+
model:
14+
name: default
15+
network:
16+
import: myproject.models.networks.factory
17+
name: myproject
18+
dim_in: 10
19+
dim_out: 1
20+
criterion:
21+
import: myproject.models.criterions.factory
22+
name: myproject
23+
metric:
24+
import: myproject.models.metrics.factory
25+
name: myproject
26+
thresh: 0.5
27+
optimizer:
28+
name: adam
29+
lr: 0.0004
30+
engine:
31+
name: default
32+
debug: False
33+
nb_epochs: 10
34+
print_freq: 10
35+
saving_criteria:
36+
- loss:min # save when new_best < best
37+
- accuracy:max # save when new_best > best
38+
misc:
39+
cuda: False
40+
seed: 1337
41+
views:
42+
name: plotly
43+
items:
44+
- logs:train_epoch.loss+logs:eval_epoch.loss
45+
- logs:train_batch.loss
46+
- logs:train_epoch.accuracy+logs:eval_epoch.accuracy
47+
48+
49+
gridsearch:
50+
"optimizer.lr": [0.1, 1.e-3]
51+
"misc.seed": [1337, 42]

tests/test_grid.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from os import path as osp
2+
import os
3+
import shutil
4+
import sys
5+
from bootstrap.new import new_project
6+
from tests.test_options import reset_options_instance
7+
from bootstrap.grid import main as main_grid
8+
9+
10+
def test_grid(tmpdir):
11+
new_project("MyProject", tmpdir)
12+
code_dir = osp.join(tmpdir, "myproject.bootstrap.pytorch")
13+
path_opts = osp.join(code_dir, "myproject/options/options-grid.yaml")
14+
shutil.copy("tests/options-grid.yaml", path_opts)
15+
os.chdir(code_dir)
16+
17+
expected_exp_dirs = [
18+
"logs/myproject/1_exp--lr_0.1--seed_1337",
19+
"logs/myproject/1_exp--lr_0.1--seed_42",
20+
"logs/myproject/1_exp--lr_0.001--seed_1337",
21+
"logs/myproject/1_exp--lr_0.001--seed_42",
22+
]
23+
24+
# path needed to change import
25+
# https://stackoverflow.com/questions/23619595/pythons-os-chdir-function-isnt-working
26+
sys.path.insert(0, code_dir)
27+
reset_options_instance()
28+
sys.argv += ["--path_opts", path_opts]
29+
sys.argv += ["--gpu-per-trial", "0.0"]
30+
sys.argv += ["--cpu-per-trial", "0.5"]
31+
main_grid()
32+
33+
fnames = [
34+
"ckpt_best_accuracy_engine.pth.tar",
35+
"ckpt_best_loss_optimizer.pth.tar",
36+
"logs.txt",
37+
"ckpt_best_accuracy_model.pth.tar",
38+
"ckpt_last_engine.pth.tar",
39+
"options.yaml",
40+
"ckpt_best_accuracy_optimizer.pth.tar",
41+
"ckpt_last_model.pth.tar",
42+
"view.html",
43+
"ckpt_best_loss_engine.pth.tar",
44+
"ckpt_last_optimizer.pth.tar",
45+
"ckpt_best_loss_model.pth.tar",
46+
"logs.json",
47+
]
48+
for exp_dir in expected_exp_dirs:
49+
for fname in fnames:
50+
file_path = osp.join(code_dir, f"{exp_dir}/{fname}")
51+
assert osp.isfile(file_path)

0 commit comments

Comments
 (0)