Reputation: 2133
I have a set of choices A. I would like to get back a subset of choices A. Is this possible with Hyperopt?
input:
{
'A': hp.choice('A', [0, 1, 2, 3])
}
output:
{
'A': [0, 2]
}
Upvotes: 0
Views: 1586
Reputation: 452
Yes, there is, though the code is a little more cumbersome. Define each element of A seperately as a choice between inclusion and exclusion. For example:
space = {
"A0": hp.choice("A0", [False, True]),
"A1": hp.choice("A1", [False, True]),
"A2": hp.choice("A2", [False, True]),
...
}
Code to interpret this in your objective function is pretty easy as well:
A = [i for i in range(num_choices) if space["A"+str(i)]]
# A = [0, 2] for example
This will return true random subsets of the A (anywhere from an empty set to the entirety of A).
Upvotes: 1
Reputation: 36619
No, but if you specifically want that functionality, there are multiple ways to do so.
You can define a new expression and add it to existing hyperopt parameter expressions as given in the documentation. For example, in your case, you can do:
import hyperopt.pyll
from hyperopt.pyll import scope
from hyperopt.pyll.stochastic import sample
# Add a new method as you want
@scope.define
def foo(choices, num_choices):
return np.random.choice(choices, size=num_choices, replace=False)
choices = [1,2,3,4]
# Define the space like below and use in fmin
space = scope.foo(choices, 2)
# Call this multiple times to see how space behaves
print(sample(space))
See the documention of numpy.random.choice to know how that works.
Note:
foo
method will return a subset of choices (in a numpy array). So make sure that in your objective function, you are using the internal multiple values like x[0], x[1], ...
etc.scope.undefine(foo)
in the end of your code, or else you will have to restart your terminal / kernel each time before running the code. If you are allowed to choose two values with replacement (that means that sometimes both values in the subset will be same. This is the reason we used replace=False
in point 1), then the following can be done:
choices = [1,2,3,4]
space = [hp.choice('c1', choices),
hp.choice('c2', choices)]
Then in your objective function, you can access your two values as x[0]
, x[1]
.
But from your question, it seems like you want to have the sub-choices and that implies without replacement, so a sub-set of [1,1]
or [2,2]
etc. is invalid. In that case, you should use the Trials
object to define status
.
A sample program for point 2 is below:
from hyperopt import fmin, tpe, hp, STATUS_OK, STATUS_FAIL, Trials
def objective(x):
# Check if the supplied choices are valid or not
x1 = x[0]
x2 = x[1]
if x1 == x2:
# If invalid, only return the status and hyperopt will understand
return {'status': STATUS_FAIL}
# Normal flow of objective
# Do your coding here
# In the end, return this
return {
'loss': actual_loss, # Fill the actual loss here
'status': STATUS_OK
}
choices = [1,2,3,4]
space = [hp.choice('c1', choices),
hp.choice('c2', choices)]
trials = Trials()
best = fmin(objective, space=space, algo=tpe.suggest, max_evals=100, trials=trials)
from hyperopt import space_eval
print(space_eval(space, best))
Hope this helps.
Upvotes: 4