-
Notifications
You must be signed in to change notification settings - Fork 11
/
pittybook_utils.py
291 lines (252 loc) · 9.53 KB
/
pittybook_utils.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
from copy import deepcopy
from itertools import (
product,
combinations,
)
from pathlib import Path
from typing import List
from hydra import initialize, compose
from loguru import logger
import matplotlib.pyplot as plt
import numpy as np
from pytti.workhorse import _main as render_frames
from torchvision.io import read_image
import torchvision.transforms.functional as F
from torchvision.utils import make_grid
# this is useful enough that maybe I should just ship it with pytti
class ExperimentMatrix:
"""
Class for facilitating running experiments over varying sets of parameters
...which I should probably just be doing with hydra's multirun anyway, now that I think about it.
you know what, I'm not sure that's actually easier for what I'm doing.
"""
def __init__(
self,
variant: dict=None,
invariant:dict=None,
mapped:dict=None,
conditional:dict=None, # cutpow = 2 if cutouts>80 else 1 # {param0: f(kw)}
CONFIG_BASE_PATH:str = "config",
CONFIG_DEFAULTS:str = "default.yaml",
):
"""
:param: variant: Parameters to be varied and the values they can take
:param: invariant: Parameters that will stay fixed each experiment
:param: mapped: Settings whose values should be copied from other settings
:param: conditional: Settings whose values are conditoinal on the values of variants, in form: `{conditional_param: f(kw)}`
"""
self.variant = variant
self.invariant = invariant
self.mapped = mapped
self.conditional = conditional
self.CONFIG_BASE_PATH = CONFIG_BASE_PATH
self.CONFIG_DEFAULTS = CONFIG_DEFAULTS
def variant_combinations(self, n:int=None):
"""
Generates combinations of variant parameters, where n is the number of parameters
per combination. Defaults to pairs
"""
if not n:
n = len(self.variant)
return combinations(self.variant.items(), n)
def populate_mapped_settings(self, kw:dict) -> dict:
"""
Adds mapped settings to experiment kwargs
"""
for k0, krest in self.mapped.items():
for k1 in krest:
kw[k1] = kw[k0]
return kw
def populate_conditional_settings(self, kw:dict) -> dict:
"""
Adds conditional settings to experiment kwargs
"""
if self.conditional is None:
return kw
for p, f in self.conditional.items():
kw[p] = f(kw)
return kw
def populate_invariants(self, kw:dict)->dict:
"""
Seeds experiment with invariant settings
"""
return kw.update(deepcopy(self.invariant))
def dict2hydra(self, kw:dict)->List[str]:
"""
Converts dict of settings to hydra.compose format
"""
return [f"{k}={v}" for k,v in kw.items()]
def build_parameterizations(self, n:int=None):
"""
Builds settings for each respective experiment
"""
#if n != 2:
# raise NotImplementedError
if not n:
n = len(self.variant)
kargs = []
#for param0, param1 in self.variant_combinations(n):
# (p0_name, p0_vals_all), (p1_name, p1_vals_all) = param0, param1
# for p0_val, p1_val in product(p0_vals_all, p1_vals_all):
# kw = {
# p0_name:p0_val,
# p1_name:p1_val,
# 'file_namespace':f"matrix_{p0_name}-{p0_val}_{p1_name}-{p1_val}",
# }
#for args in self.variant_combinations(n):
#for args in combinations(self.variant.values(), n):
for args in product(*self.variant.values()):
kw = {k:v for k,v in zip(self.variant.keys(), args)}
#kw = {k:v for k,v in args}
self.populate_invariants(kw)
self.populate_mapped_settings(kw)
self.populate_conditional_settings(kw)
kargs.append(kw)
#kws = [self.dict2hydra(kw) for kw in kargs]
#return kargs, kws
self.kargs= kargs
return deepcopy(kargs)
def run_all(self, kargs:dict=None, convert_to_hydra:bool=True):
"""
Runs all experiments per given parameterizations
"""
if not kargs:
if not hasattr(self, 'kargs'):
self.build_parameterizations()
kargs = self.kargs
with initialize(config_path=self.CONFIG_BASE_PATH):
for kws in kargs:
#logger.debug(f"kws: {kws}")
print(f"kws: {kws}")
if convert_to_hydra:
kws = self.dict2hydra(kws)
self.run_experiment(kws)
def run_experiment(self, kws:dict):
"""
Runs a single experiment. Factored at to an isolated function
to facilitate overriding if hydra isn't needed.
"""
logger.debug(kws)
cfg = compose(
config_name=self.CONFIG_DEFAULTS,
overrides=kws
)
render_frames(cfg)
def display_results(self, kargs=None, variant=None):
"""
Displays a matrix of generated outputs
"""
if not kargs:
kargs = self.kargs
if not variant:
variant = self.variant
images = []
for k in kargs:
fpath = Path("images_out") / k['file_namespace'] / f"{k['file_namespace']}_1.png"
images.append(read_image(str(fpath)))
nr = len(list(variant.values())[0])
grid = make_grid(images, nrow=nr)
fix, axs = show(grid)
ax0_name, ax1_name = list(self.variant.keys())
fix.savefig(f"TestMatrix_{ax0_name}_{ax1_name}.png")
return fix, axs
#########################################
def run_experiment_matrix(
kws,
):
# https://github.com/facebookresearch/hydra/blob/main/examples/jupyter_notebooks/compose_configs_in_notebook.ipynb
# https://omegaconf.readthedocs.io/
# https://hydra.cc/docs/intro/
with initialize(config_path=CONFIG_BASE_PATH):
for k in kws:
logger.debug(k)
cfg = compose(config_name=CONFIG_DEFAULTS,
overrides=k)
render_frames(cfg)
def build_experiment_parameterizations(
cross_product,
invariants,
map_kv,
):
kargs = []
NAME, VALUE = 0, 1
for param0, param1 in combinations(cross_product, 2):
p0_name, p1_name = param0[NAME], param1[NAME]
for p0_val, p1_val in product(param0[VALUE], param1[VALUE]):
kw = deepcopy(invariants)
kw.update({
p0_name:p0_val,
p1_name:p1_val,
'file_namespace':f"matrix_{p0_name}-{p0_val}_{p1_name}-{p1_val}",
})
# map in "variable imputations"
for k0, krest in map_kv:
for k1 in krest:
kw[k1] = kw[k0]
kargs.append(kw)
kws = [[f"{k}={v}" for k,v in kw.items()] for kw in kargs]
return kargs, kws
def build_experiment_parameterizations_from_dicts(
cross_product: dict,
invariants: dict,
map_kv: dict,
conditional: dict = None,
):
kargs = []
for param0, param1 in combinations(cross_product.items(), 2):
(p0_name, p0_vals_all), (p1_name, p1_vals_all) = param0, param1
for p0_val, p1_val in product(p0_vals_all, p1_vals_all):
kw = deepcopy(invariants)
kw.update({
p0_name:p0_val,
p1_name:p1_val,
'file_namespace':f"matrix_{p0_name}-{p0_val}_{p1_name}-{p1_val}",
})
# map in "variable imputations"
for k0, krest in map_kv:
for k1 in krest:
kw[k1] = kw[k0]
#if (conditional is not None):
# for p in conditional:
# if p
kargs.append(kw)
kws = [[f"{k}={v}" for k,v in kw.items()] for kw in kargs]
return kargs, kws
def run_experiment_matrix(
kws,
CONFIG_BASE_PATH = "config",
CONFIG_DEFAULTS = "default.yaml",
):
# https://github.com/facebookresearch/hydra/blob/main/examples/jupyter_notebooks/compose_configs_in_notebook.ipynb
# https://omegaconf.readthedocs.io/
# https://hydra.cc/docs/intro/
with initialize(config_path=CONFIG_BASE_PATH):
for k in kws:
logger.debug(k)
cfg = compose(config_name=CONFIG_DEFAULTS,
overrides=k)
render_frames(cfg)
# https://pytorch.org/vision/master/auto_examples/plot_visualization_utils.html#visualizing-a-grid-of-images
# sphinx_gallery_thumbnail_path = "../../gallery/assets/visualization_utils_thumbnail2.png"
def show(imgs):
plt.rcParams["savefig.bbox"] = 'tight'
plt.rcParams['figure.figsize'] = 20,20
if not isinstance(imgs, list):
imgs = [imgs]
fix, axs = plt.subplots(ncols=len(imgs), squeeze=False)
for i, img in enumerate(imgs):
img = img.detach()
img = F.to_pil_image(img)
axs[0, i].imshow(np.asarray(img))
axs[0, i].set(xticklabels=[], yticklabels=[], xticks=[], yticks=[])
return fix, axs
def display_study_results(kargs, cross_product):
images = []
for k in kargs:
fpath = Path("images_out") / k['file_namespace'] / f"{k['file_namespace']}_1.png"
images.append(read_image(str(fpath)))
nr = len(cross_product[0][-1])
grid = make_grid(images, nrow=nr)
fix, axs = show(grid)
ax0_name, ax1_name = cross_product[0][0], cross_product[1][0]
fix.savefig(f"TestMatrix_{ax0_name}_{ax1_name}.png")