-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript.py
189 lines (162 loc) · 5.63 KB
/
script.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
import logging
import warnings
import argparse
from argparse import Namespace
from functools import partial
import torch
import torchvision
import numpy as np
import pytorch_lightning as pl
import ConfigSpace.hyperparameters as CSH
from torch import Generator
from torchvision import transforms
from sklearn.gaussian_process.kernels import Matern
from matplotlib.backends.backend_pdf import PdfPages
from bayesopt.bayesopt import BayesOpt
from bayesopt.resnet import ResNetModel, ResNetFashionMnistModule
def optimize(
val_size: float = 0.3,
max_epochs: int = 10,
batch_size: int = 128,
patience: int = 0,
lr: float = 1e-3,
generator: Generator = None,
) -> float:
"""Trains and evaluates a ResNet on the FashionMNIST classification task.
Splits the training data into training and validation sets. Trains the
ResNet until the early stopping criterion is met or the maximum number of
epochs is reached. Reports the val. performance in terms of its F1 score.
Parameters
----------
val_size : float
Fraction of the number of training samples used as validation data.
max_epochs : int
Number of epochs the neural network is trained at most.
batch_size : int
Batch size of training & validation data loaders.
patience : int
Maximum number of validation checks with no improvement before the
training is stopped early.
lr : float
Learning rate of the SGD optimizer.
generator: Generator
Random number generator used to train / test split.
Returns
-------
float
Final F1 score of the ResNet on the validation data.
"""
logging.getLogger("pytorch_lightning").setLevel(logging.ERROR)
warnings.filterwarnings("ignore")
transform = transforms.ToTensor()
data = torchvision.datasets.FashionMNIST(
args.store_dir, download=True, train=True, transform=transform
)
train = int(np.floor((1.0 - val_size) * len(data)))
val = int(np.ceil(val_size * len(data)))
train, val = torch.utils.data.random_split(
data, (train, val), generator=generator
)
train = torch.utils.data.DataLoader(train, batch_size=batch_size)
val = torch.utils.data.DataLoader(val, batch_size=batch_size)
model = ResNetModel(num_classes=10)
module = ResNetFashionMnistModule(model, num_classes=10, lr=lr)
early_stopping = pl.callbacks.EarlyStopping(
monitor="val_f1_score", mode="max", patience=patience
)
trainer = pl.Trainer(
gpus=args.num_gpus,
max_epochs=max_epochs,
callbacks=[early_stopping],
enable_progress_bar=False,
enable_model_summary=False,
logger=None,
)
trainer.validate(module, val, verbose=False)
trainer.fit(module, train, val)
f1_score = trainer.logged_metrics["val_f1_score"].item()
return f1_score
def main(args: Namespace) -> None:
"""Optimize the learning rate of a ResNet on FasionMNIST.
Uses Bayesian Optimization to find a learning rate that maximizes the val.
F1 score of a ResNet architecture. Uses a single train / val. split for
each attempt.
Parameters
----------
args : Namespace
Specifies arguments of the optimization script.
"""
# Define search space of LR over log scale.
lr = CSH.UniformFloatHyperparameter(
"lr", lower=args.lower, upper=args.upper, log=True
)
# Define parameters of acquisition function, minimization and surrogate.
acquisition = {"acquisition": "expected_improvement", "xi": 0.01}
minimizer_kwargs = {"method": "L-BFGS-B", "nrestarts": 25}
kernel = 3.0 * Matern(
length_scale=1.0, length_scale_bounds=(1e-1, 10.0), nu=1.5
)
surrogate = {"n_restarts_optimizer": 10, "kernel": kernel}
# Random number generator used to train / test split.
generator = Generator()
generator.manual_seed(args.seed)
# Define parameters of evaluation function except for learning rate.
objective = {
"val_size": 0.3,
"max_epochs": args.max_epochs,
"batch_size": 128,
"patience": 0,
"generator": generator,
}
objective = partial(optimize, **objective)
bayesopt = BayesOpt(
space=lr,
objective=objective,
acquisition=acquisition,
minimizer_kwargs=minimizer_kwargs,
surrogate=surrogate,
log_freq=1,
seed=args.seed,
)
# Store output of Bayesian Optimization in .pdf file.
with PdfPages(args.output) as pdf:
bayesopt.optimize(1)
for _ in range(args.niters - 1):
bayesopt.optimize(1)
fig = bayesopt.render()
pdf.savefig(fig)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--lower", type=float, default=1e-5, help="Lower bound of LR."
)
parser.add_argument(
"--upper", type=float, default=1e-1, help="Upper bound of LR."
)
parser.add_argument(
"--niters", type=int, default=10, help="Num. of BO iterations."
)
parser.add_argument(
"--max_epochs",
type=int,
default=50,
help="Max. Num. of training epochs.",
)
parser.add_argument(
"--output",
type=str,
default="bayesopt.pdf",
help="File that stores the BO plots.",
)
parser.add_argument(
"--store_dir",
type=str,
default="fashion-mnist",
help="Directory that stores the FashionMNIST data.",
)
parser.add_argument(
"--num_gpus", type=int, default=0, help="Num. of GPUs."
)
parser.add_argument("--seed", type=int, default=0, help="Random seed.")
args = parser.parse_args()
main(args)