aws / aws/amazon-sagemaker-feedback
Allow singleton interval for integer or continuous range hyperparameter and duplicate values for categorical range parameter of tuning job.
- Dominant language
- No language data
- Stars
- 10
- Forks
- 3
- PR merge metrics
- No merged PRs in 30d
Description
### Product Version
- [ ] Amazon SageMaker Studio Classic
- [ ] Amazon SageMaker Studio
- [x] It is not related to SageMaker Studio
### Product Category
Jobs
### Description
I would like to be able to pass a singleton range parameter to HyperparameterTuner, e.g. IntegerParameter(1,1), ContinousParameter(1,1) or CategoricalParameter(["cat1"]) or even .CategoricalParameter(["cat1", "cat1"]). Other tuning frameworks, such as ray tune, support such parameter ranges. I would like the following code to work by setting for each training job: `integer-fixed-hyperparam=1`, `float-fixed-hyperparam=1.0`, `categorical-fixed-hyperparam="val1`", and `continous-hyperparam` randomly from range `[0,1]`.
```python
from sagemaker.pytorch import PyTorch
from sagemaker.tuner import (
HyperparameterTuner,
CategoricalParameter,
ContinuousParameter,
IntegerParameter
)
from sagemaker.session import Session
if __name__ == "__main__":
estimator = PyTorch(
sagemaker_session=Session(),
instance_type='ml.m5.large',
instance_count=1,
framework_version="2.3",
py_version="py311",
source_dir='source',
entry_point='main.py',
metric_definitions=[
{'Name': 'valid:loss', 'Regex': 'valid_loss=([0-9]+\\.?[0-9]*)'}
]
)
tuner = HyperparameterTuner(
estimator=estimator,
objective_metric_name='valid:loss',
objective_type='Minimize',
hyperparameter_ranges={
"continous-hyperparam": ContinuousParameter(0.0, 1.0),
"fixed-continous-hparam": ContinuousParameter(1.0, 1.0),
"fixed-integer-hparam": IntegerParameter(1, 1),
"fixed-categorical-hparam": CategoricalParameter(["val1", "val1"])
},
max_jobs=2,
max_parallel_jobs=1,
base_tuning_job_name='test-tuning',
strategy='Random',
metric_definitions=estimator.metric_definitions,
)
print(tuner.fit())
```
Sample source/main.py
```python
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--continous-hyperparam", type=float, required=True)
parser.add_argument("--fixed-continous-hyperparam", type=float, required=True)
parser.add_argument("--fixed-integer-hyperparam", type=int, required=True)
parser.add_argument("--fixed-categorical-hyperparam", type=str, required=True)
args, _ = parser.parse_known_args()
print(f"Hparams: {args}")
print("valid_loss=0.1")
```
Why do I need this, and why can't I simply pass fixed hyperparameters as static hyperparameters to the estimator? I'd like to have an ML pipeline for tuning various hyperparameters, with a user-defined search space via pipeline parameters. In some cases, the user might want to tune a subset of hyperparameters while keeping other hyperparameters constant, while in other cases, they might want to tune a different subset of hyperparameters. The following code illustrates this:
```python
from sagemaker.pytorch import PyTorch
from sagemaker.tuner import (
HyperparameterTuner,
ContinuousParameter,
IntegerParameter
)
from sagemaker.workflow.parameters import (
ParameterString,
ParameterInteger,
ParameterFloat
)
from sagemaker.workflow.steps import TuningStep
from sagemaker.workflow.pipeline import Pipeline
from sagemaker.workflow.pipeline_context import (
PipelineSession
)
if __name__ == "__main__":
pipeline_session = PipelineSession()
estimator = PyTorch(
sagemaker_session=pipeline_session,
instance_type='ml.m5.large',
instance_count=1,
framework_version="2.3",
py_version="py311",
source_dir='source',
entry_point='main.py',
metric_definitions=[
{'Name': 'valid:loss', 'Regex': 'valid_loss=([0-9]+\\.?[0-9]*)'}
]
)
min_integer_hparam_group1 = ParameterInteger("MinIntegerHparamGroup1")
max_integer_hparam_group1 = ParameterInteger("MaxIntegerHparamGroup1")
min_float_hparam_group1 = ParameterFloat("MinFloatHparamGroup1")
max_float_hparam_group1 = ParameterFloat("MaxFloatHparamGroup1")
option1_categorical_hparam_group1 = ParameterString("Option1CategoricalHparamGroup1")
option2_categorical_hparam_group1 = ParameterString("Option2CategoricalHparamGroup1")
min_integer_hparam_group2 = ParameterInteger("MinIntegerHparamGroup2")
max_integer_hparam_group2 = ParameterInteger("MaxIntegerHparamGroup2")
min_float_hparam_group2 = ParameterFloat("MinFloatHparamGroup2")
max_float_hparam_group2 = ParameterFloat("MaxFloatHparamGroup2")
option1_categorical_hparam_group2 = ParameterString("Option1CategoricalHparamGroup2")
option2_categorical_hparam_group2 = ParameterString("Option2CategoricalHparamGroup2")
tuner = HyperparameterTuner(
estimator=estimator,
objective_metric_name='valid:loss',
objective_type='Minimize',
hyperparameter_ranges={
"integer-hparam-group1": IntegerParameter(
min_integer_hparam_group1,
max_integer_hparam_group1
),
"float-hparam-group1": ContinuousParameter(
min_float_hparam_group1,
max_float_hparam_group1
),
# "categorical-hparam-group1": CategoricalParameter([
# option1_categorical_hparam_group1,
# option2_categorical_hparam_group1
# ]), # that can not work due to that issue: https://github.com/aws/sagemaker-python-sdk/issues/5240
"integer-hparam-group2": IntegerParameter(
min_integer_hparam_group2,
max_integer_hparam_group2
),
"float-hparam-group2": ContinuousParameter(
min_float_hparam_group2,
max_float_hparam_group2
),
# "categorical-hparam-group2": CategoricalParameter([
# option1_categorical_hparam_group2,
# option2_categorical_hparam_group2
# ]) # that can not work due to that issue: https://github.com/aws/sagemaker-python-sdk/issues/5240
},
max_jobs=2,
max_parallel_jobs=1,
strategy='Random',
metric_definitions=estimator.metric_definitions,
)
print(tuner.fit())
tuning_step = TuningStep(
name="Tuning",
step_args=tuner.fit(),
)
pipeline = Pipeline(
name="TestTuningPipeline",
parameters=[
min_integer_hparam_group1,
max_integer_hparam_group1,
min_integer_hparam_group2,
max_integer_hparam_group2,
min_float_hparam_group1,
max_float_hparam_group1,
min_float_hparam_group2,
max_float_hparam_group2,
option1_categorical_hparam_group1,
option2_categorical_hparam_group1,
option1_categorical_hparam_group2,
option2_categorical_hparam_group2
],
steps=[tuning_step],
sagemaker_session=pipeline_session
)
pipeline.create()
pipeline.start(
{
"MinIntegerHparamGroup1": 0,
"MaxIntegerHparamGroup1": 1,
"MinFloatHparamGroup1": 0.0,
"MaxFloatHparamGroup1": 1.0,
"Option1CategoricalHparamGroup1": "val1",
"Option2CategoricalHparamGroup1": "val2",
"MinIntegerHparamGroup2": 0,
"MaxIntegerHparamGroup2": 0,
"MinFloatHparamGroup2": 0.0,
"MaxFloatHparamGroup2": 0.0,
"Option1CategoricalHparamGroup2": "val",
"Option2CategoricalHparamGroup2": "val"
}
)
pipeline.start(
{
"MinIntegerHparamGroup1": 0,
"MaxIntegerHparamGroup1": 0,
"MinFloatHparamGroup1": 0.0,
"MaxFloatHparamGroup1": 0.0,
"Option1CategoricalHparamGroup1": "val",
"Option2CategoricalHparamGroup1": "val",
"MinIntegerHparamGroup2": 0,
"MaxIntegerHparamGroup2": 1,
"MinFloatHparamGroup2": 0.0,
"MaxFloatHparamGroup2": 1.0,
"Option1CategoricalHparamGroup2": "val1",
"Option2CategoricalHparamGroup2": "val2",
}
)
```
Sample source/main.py
```python
import argparse
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--integer-hparam-group1", type=int, required=True)
parser.add_argument("--float-hparam-group1", type=float, required=True)
# parser.add_argument("--categorical-hparam-group1", type=str, required=True)
parser.add_argument("--integer-hparam-group2", type=int, required=True)
parser.add_argument("--float-hparam-group2", type=float, required=True)
# parser.add_argument("--categorical-hparam-group2", type=str, required=True)
args, _ = parser.parse_known_args()
print(f"Hparams: {args}")
print("valid_loss=0.1")
```
I'd like the above code to create a pipeline with a tuning step and run it the first time by tuning the hyperparameters with the `hparam-group1` suffix, leaving the hyperparameters with the `hparam-group2` suffix constant. The second time, it would do the opposite, tuning the hyperparameters with the `hparam-group2` suffix while leaving the hyperparameters with the `hparam-group1` suffix constant.
Unfortunately, both of these pipeline executions will fail almost immediately with an error, failing to run any training jobs.
An alternative solution that would satisfy me: allow to define additional dictionary for hyperparameter tuner that keys-values define which hyperparameters are tunable and each are static. However solution with supporting singleton intervals seems to me more natural, consistent with other tuning framework (as ray tune), and probably simpler to implement.
### Other Details
_No response_
Contributor guide
Research direction
Start with HyperparameterTuner and the IntegerParameter, ContinuousParameter, CategoricalParameter, and TuningStep entry points shown in the examples. Reproduce both pipeline.start calls, then verify that singleton numeric ranges and duplicate categorical values are accepted and that the tuning jobs launch with the intended fixed parameters.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, python
- Domain
- cloud, machine-learning
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100