-
Notifications
You must be signed in to change notification settings - Fork 455
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
[GSOC] hyperopt
suggestion service logic update
#2412
Changes from all commits
65593a3
2d995dc
0a885c9
76f471e
b2f5257
94c6eaf
db3d83a
0a87259
325cd3e
0c15b3a
8dfacf9
73add59
77b5df0
a5acb75
e6f82a0
df01c1c
ad0281f
6e6722e
7aedeed
627d32a
b0aed17
03ee74e
b91b0ac
9e6a398
630141c
6b64073
8c465c1
7506e55
4fc897d
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,74 @@ | ||
--- | ||
apiVersion: kubeflow.org/v1beta1 | ||
kind: Experiment | ||
metadata: | ||
namespace: kubeflow | ||
name: hyperopt-distribution | ||
spec: | ||
objective: | ||
type: minimize | ||
goal: 0.05 | ||
objectiveMetricName: loss | ||
algorithm: | ||
algorithmName: random | ||
parallelTrialCount: 3 | ||
maxTrialCount: 12 | ||
maxFailedTrialCount: 3 | ||
parameters: | ||
- name: lr | ||
parameterType: double | ||
feasibleSpace: | ||
min: "0.01" | ||
max: "0.05" | ||
step: "0.01" | ||
distribution: normal | ||
- name: momentum | ||
parameterType: double | ||
feasibleSpace: | ||
min: "0.001" | ||
max: "1" | ||
distribution: uniform | ||
- name: epochs | ||
parameterType: int | ||
feasibleSpace: | ||
min: "1" | ||
max: "3" | ||
distribution: logUniform | ||
- name: batch_size | ||
parameterType: int | ||
feasibleSpace: | ||
min: "32" | ||
max: "64" | ||
distribution: logNormal | ||
trialTemplate: | ||
primaryContainerName: training-container | ||
trialParameters: | ||
- name: learningRate | ||
description: Learning rate for the training model | ||
reference: lr | ||
- name: momentum | ||
description: Momentum for the training model | ||
reference: momentum | ||
- name: epochs | ||
description: Epochs | ||
reference: epochs | ||
- name: batchSize | ||
description: Batch Size | ||
reference: batch_size | ||
trialSpec: | ||
apiVersion: batch/v1 | ||
kind: Job | ||
spec: | ||
template: | ||
spec: | ||
containers: | ||
- name: training-container | ||
image: docker.io/kubeflowkatib/pytorch-mnist-cpu:latest | ||
command: | ||
- "python3" | ||
- "/opt/pytorch-mnist/mnist.py" | ||
- "--epochs=${trialParameters.epochs}" | ||
- "--batch-size=${trialParameters.batchSize}" | ||
- "--lr=${trialParameters.learningRate}" | ||
- "--momentum=${trialParameters.momentum}" | ||
restartPolicy: Never |
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -13,10 +13,12 @@ | |||||||||
# limitations under the License. | ||||||||||
|
||||||||||
import logging | ||||||||||
import math | ||||||||||
|
||||||||||
import hyperopt | ||||||||||
import numpy as np | ||||||||||
|
||||||||||
from pkg.apis.manager.v1beta1.python import api_pb2 | ||||||||||
from pkg.suggestion.v1beta1.internal.constant import ( | ||||||||||
CATEGORICAL, | ||||||||||
DISCRETE, | ||||||||||
|
@@ -62,14 +64,87 @@ def create_hyperopt_domain(self): | |||||||||
# hyperopt.hp.uniform('x2', -10, 10)} | ||||||||||
hyperopt_search_space = {} | ||||||||||
for param in self.search_space.params: | ||||||||||
if param.type == INTEGER: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.quniform( | ||||||||||
param.name, float(param.min), float(param.max), float(param.step) | ||||||||||
) | ||||||||||
elif param.type == DOUBLE: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.uniform( | ||||||||||
param.name, float(param.min), float(param.max) | ||||||||||
) | ||||||||||
if param.type in [INTEGER, DOUBLE]: | ||||||||||
if param.distribution == api_pb2.UNIFORM or param.distribution is None: | ||||||||||
# Uniform distribution: values are sampled between min and max. | ||||||||||
# If step is defined, we use the quantized version quniform. | ||||||||||
if param.step: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.quniform( | ||||||||||
shashank-iitbhu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
param.name, | ||||||||||
float(param.min), | ||||||||||
float(param.max), | ||||||||||
float(param.step), | ||||||||||
) | ||||||||||
elif param.type == INTEGER: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.uniformint( | ||||||||||
param.name, float(param.min), float(param.max) | ||||||||||
) | ||||||||||
else: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.uniform( | ||||||||||
param.name, float(param.min), float(param.max) | ||||||||||
) | ||||||||||
elif param.distribution == api_pb2.LOG_UNIFORM: | ||||||||||
# Log-uniform distribution: used for parameters that vary exponentially. | ||||||||||
# We convert min and max to their logarithmic scale using math.log, because | ||||||||||
# the log-uniform distribution is applied over the logarithmic range. | ||||||||||
if param.step: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.qloguniform( | ||||||||||
param.name, | ||||||||||
math.log(float(param.min)), | ||||||||||
math.log(float(param.max)), | ||||||||||
float(param.step), | ||||||||||
) | ||||||||||
else: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.loguniform( | ||||||||||
param.name, | ||||||||||
math.log(float(param.min)), | ||||||||||
math.log(float(param.max)), | ||||||||||
andreyvelich marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
) | ||||||||||
elif param.distribution == api_pb2.NORMAL: | ||||||||||
# Normal distribution: used when values are centered around the mean (mu) | ||||||||||
# and spread out by sigma. We calculate mu as the midpoint between | ||||||||||
# min and max, and sigma as (max - min) / 6. This is based on the assumption | ||||||||||
# that 99.7% of the values in a normal distribution fall within ±3 sigma. | ||||||||||
mu = (float(param.min) + float(param.max)) / 2 | ||||||||||
shashank-iitbhu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
sigma = (float(param.max) - float(param.min)) / 6 | ||||||||||
shashank-iitbhu marked this conversation as resolved.
Show resolved
Hide resolved
shashank-iitbhu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
if param.step: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.qnormal( | ||||||||||
param.name, | ||||||||||
mu, | ||||||||||
sigma, | ||||||||||
float(param.step), | ||||||||||
) | ||||||||||
else: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.normal( | ||||||||||
param.name, | ||||||||||
mu, | ||||||||||
sigma, | ||||||||||
) | ||||||||||
elif param.distribution == api_pb2.LOG_NORMAL: | ||||||||||
# Log-normal distribution: applies when the logarithm | ||||||||||
# of the parameter follows a normal distribution. | ||||||||||
# We convert min and max to logarithmic scale and calculate | ||||||||||
# mu and sigma similarly to the normal distribution, | ||||||||||
# but on the log-transformed values to ensure the distribution is correct. | ||||||||||
log_min = math.log(float(param.min)) | ||||||||||
log_max = math.log(float(param.max)) | ||||||||||
Comment on lines
+130
to
+131
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. Shouldn't we use the fixed value when the min and max are scalers the same as Nevergrad, right? 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. Yeah we can, but that was an edge case when elif isinstance(param, (p.Log, p.Scalar)):
if (param.bounds[0][0] is None) or (param.bounds[1][0] is None):
if isinstance(param, p.Scalar) and not param.integer:
return hp.lognormal(label=param_name, mu=0, sigma=1) For example,
The above parameter will be sampled out from this graph: 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. That makes sense. 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. @shashank-iitbhu This is still pending. 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. katib/pkg/webhook/v1beta1/experiment/validator/validator.go Lines 287 to 290 in 867c40a
The webhook validator requires 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. But when either min or max is empty, this validation does not reject the request, right? 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. Yes, the validation webhook does not reject the request when either min or max is empty. But I created an example where:
For this, the experiment is being created but the suggestion service is not sampling out any value hence the trials are not running, though handled this case (when either min or max are not specified) in |
||||||||||
mu = (log_min + log_max) / 2 | ||||||||||
sigma = (log_max - log_min) / 6 | ||||||||||
|
||||||||||
if param.step: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.qlognormal( | ||||||||||
param.name, | ||||||||||
mu, | ||||||||||
sigma, | ||||||||||
float(param.step), | ||||||||||
) | ||||||||||
else: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.lognormal( | ||||||||||
param.name, | ||||||||||
mu, | ||||||||||
sigma, | ||||||||||
) | ||||||||||
elif param.type == CATEGORICAL or param.type == DISCRETE: | ||||||||||
hyperopt_search_space[param.name] = hyperopt.hp.choice( | ||||||||||
param.name, param.list | ||||||||||
|
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.
Should we set the uniform distribution as a default one ?
We can mutate the Uniform distribution in the Experiment defaulter: https://github.com/kubeflow/katib/blob/master/pkg/apis/controller/experiments/v1beta1/experiment_defaults.go
For example, in Optuna the Uniform distribution will be removed in favour of floatDistribution: https://optuna.readthedocs.io/en/stable/reference/generated/optuna.distributions.UniformDistribution.html