-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial implementation of sequential batch optimization #74
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Copyright 2017 Joachim van der Herten | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
|
||
from .ei import ExpectedImprovement | ||
import numpy as np | ||
|
||
|
||
class QEI_CL(ExpectedImprovement): | ||
""" | ||
This class is an implementation of the constant liar heuristic (min case) | ||
for using Expected Improvement in the batch case. | ||
|
||
See: | ||
Ginsbourger D., Le Riche R., Carraro L. (2010) | ||
Kriging Is Well-Suited to Parallelize Optimization. | ||
""" | ||
|
||
def __init__(self, model, batch_size): | ||
super(QEI_CL, self).__init__(model, batch_size=batch_size) | ||
self.in_batch = False | ||
|
||
def set_batch(self, *args): | ||
assert self.in_batch, 'Set batch must be called within a context' | ||
|
||
X = np.vstack((self.X_,) + args) | ||
Y = np.vstack((self.Y_,) + (self.fmin.value,)*len(args)) | ||
self.set_data(X, Y) | ||
|
||
def __enter__(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two notes:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about the following in acquisition: class SequentialBatch(object):
def __init__(acquisition, size):
self.acquisition = acquisition
self.size = size
def optimize(optimizer):
points = []
def inverse_acquisition(X):
....
for j in range(self.acquisition.batch_size):
result = optimizer.optimize(inverse_acquisition)
batch.append(result.x)
self.acquisition.set_batch(np.concatenate(points))
return np.concatenate(points)
...
class Acquisition(Parameterized):
....
def set_batch(self, batch_points):
pass # To be implemented in subclasses, e.g. qEI
@contextmanager
def batch(size):
X_orig, Y_orig = self.data
yield SequentialBatch(self, size)
self.set_batch(np.empty((0, self.domain.size)))
self.set_data(X_orig, Y_orig) Then in BayesianOptimizer you could do: if self.batch_size > 1:
with self.acquisition.batch(self.batch_size) as batch:
points = batch.optimize(self.optimizer)
self._update_model_data(points) with some further thought I think you could even see the one point strategy here as a batch with size 1 here and simply do this by default in BayesianOptimizer. What about my proposal for set_batch? It seems intuitive to simply pass in an array which is the current batch. Then if you pass a 0 x dim it signals clearing. This way has some advantages I think
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One more thing I just realized, with the pseudocode of the previous reply, the logic of selecting the points relays to a different policy object (the SequentialBatch). It would be perfectly possible to add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey, thanks for the very detailed response! Sorry the delay from my part; the past week was busy. Having a If we only plan to use the
then there is no need to use the context, at least in But I do believe that a I could add a function
and the
Finally, this might be on the pedantic side, but I would call the function Looking forward to hearing your thoughts on this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have discussed this today and considered some scenario's. I think we won't be able to solve everything in one PR unfortunately, we are proposing the following for now to get started which should be a nice compromise: I think this is a good design to start with, its also a plain OO solution to solve this without extra complexity. We also added for instance an ABAcquisition, which could be used to implement for instance https://arxiv.org/pdf/1704.03651.pdf which isn't a true batch approach but it shares some semantics. Unfortunately, this currently isn't compatible with the aggregation mechanics and MCMC components we have in place currently. I'm still in the process of discussing this with @icouckuy but we should be able to resolve this later on it will however involve some reshuffling but right now I think its fully manageable. What do you think. You could add SequentialBatchAcquisition, I'll do ParallelBatchAcquisition. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, I think it's a good plan. I will try to update the PR accordingly for SequentialBatchAcquisition by early next week. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great, to give you an idea: this is where we are heading at the moment: |
||
self.in_batch = True | ||
|
||
# Save original dataset of the model | ||
self.X_, self.Y_ = np.copy(self.data[0]), np.copy(self.data[1]) | ||
|
||
def __exit__(self, exc_type, exc_value, traceback): | ||
# Restore original dataset of the model | ||
self.set_data(self.X_, self.Y_) | ||
|
||
self.in_batch = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking to derive batch_size in acquisition each time set_batch is called, so we can keep batch_size in BayesianOptimizer. This would also permit changing the size of the batches during the optimization.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree