Skip to main content

Tune - AzureML pipeline

This example uses flaml to tune an Azure ML pipeline that fits a lightgbm classifier on the sklearn breast cancer dataset. If you already have an Azure ML pipeline, you can use the approach to tune your pipeline with flaml.

Prepare for tuning

Requirements

We recommend using conda or venv to create a virtual env to install the dependencies.

# set up new conda environment
conda create -n pipeline_tune python=3.8 pip=20.2 -y
conda activate pipeline_tune

# install azureml packages for runnig AzureML pipelines
pip install azureml-core==1.39.0
pip install azure-ml-component[notebooks]==0.9.10.post1
pip install azureml-dataset-runtime==1.39.0

# install hydra-core for passing AzureML pipeline parameters
pip install hydra-core==1.1.1

# install flaml
pip install flaml[blendsearch,ray]==1.0.9

Azure ML training pipeline

Before we are ready for tuning, we must first have an Azure ML pipeline. In this example, we use the following toy pipeline for illustration. The pipeline consists of two steps: (1) data preparation and (2) model training.

png.

The code example discussed in the page is included in test/pipeline_tuning_example/. We will use the relative path in the rest of the page.

Data

The example data exsits in data/data.csv. It will be uploaded to AzureML workspace to be consumed by the training pipeline using the following code.

Dataset.File.upload_directory(
src_dir=to_absolute_path(LOCAL_DIR / "data"),
target=(datastore, "classification_data"),
overwrite=True,
)

dataset = Dataset.File.from_files(path=(datastore, 'classification_data'))

Configurations for the pipeline

The pipeline configuration is defined in configs/train_config.yaml.

hydra:
searchpath:
- file://.

aml_config:
workspace_name: your_workspace_name
resource_group: your_resource_group
subscription_id: your_subscription_id
cpu_target: cpucluster

train_config:
exp_name: sklearn_breast_cancer_classification
test_train_ratio: 0.4
learning_rate: 0.05
n_estimators: 50

Define and submit the pipeline

The pipeline was defined in submit_train_pipeline.py.

To submit the pipeline, please specify your AzureML resources in the configs/train_config.yaml and run

cd test/pipeline_tuning_example
python submit_train_pipeline.py

To get the pipeline ready for HPO, in the training step, we need to log the metrics of interest to AzureML using

run.log(f"{data_name}_{eval_name}", result)

Hyperparameter Optimization

We are now ready to set up the HPO job for the AzureML pipeline, including:

  • config the HPO job,
  • set up the interaction between the HPO job and the training job.

These two steps are done in tuner/tuner_func.py.

Set up the tune job

tuner_func.tune_pipeline sets up the search space, metric to optimize, mode, etc.

def tune_pipeline(concurrent_run=1):
start_time = time.time()

# config the HPO job
search_space = {
"train_config.n_estimators": flaml.tune.randint(50, 200),
"train_config.learning_rate": flaml.tune.uniform(0.01, 0.5),
}

hp_metric = "eval_binary_error"
mode = "max"
num_samples = 2


if concurrent_run > 1:
import ray # For parallel tuning

ray.init(num_cpus=concurrent_run)
use_ray = True
else:
use_ray = False

# launch the HPO job
analysis = flaml.tune.run(
run_with_config,
config=search_space,
metric=hp_metric,
mode=mode,
num_samples=num_samples, # number of trials
use_ray=use_ray,
)

# get the best config
best_trial = analysis.get_best_trial(hp_metric, mode, "all")
metric = best_trial.metric_analysis[hp_metric][mode]
print(f"n_trials={len(analysis.trials)}")
print(f"time={time.time()-start_time}")
print(f"Best {hp_metric}: {metric:.4f}")
print(f"Best coonfiguration: {best_trial.config}")

Interact with AzureML pipeline jobs

The interaction between FLAML and AzureML pipeline jobs is in tuner_func.run_with_config.

def run_with_config(config: dict):
"""Run the pipeline with a given config dict
"""

# pass the hyperparameters to AzureML jobs by overwriting the config file.
overrides = [f"{key}={value}" for key, value in config.items()]

print(overrides)
run = submit_train_pipeline.build_and_submit_aml_pipeline(overrides)

print(run.get_portal_url())

# retrieving the metrics to optimize before the job completes.
stop = False
while not stop:
# get status
status = run._core_run.get_status()
print(f'status: {status}')

# get metrics
metrics = run._core_run.get_metrics(recursive=True)
if metrics:
run_metrics = list(metrics.values())

new_metric = run_metrics[0]['eval_binary_error']

if type(new_metric) == list:
new_metric = new_metric[-1]

print(f'eval_binary_error: {new_metric}')

tune.report(eval_binary_error=new_metric)

time.sleep(5)

if status == 'FAILED' or status == 'Completed':
stop = True

print("The run is terminated.")
print(status)

return

Overall, to tune the hyperparameters of the AzureML pipeline, run:

# the training job will run remotely as an AzureML job in both choices
# run the tuning job locally
python submit_tune.py --local
# run the tuning job remotely
python submit_tune.py --remote --subscription_id <your subscription_id> --resource_group <your resource_group> --workspace <your workspace>

The local option runs the tuner/tuner_func.py in your local machine. The remote option wraps up the tuner/tuner_func.py as an AzureML component and starts another AzureML job to tune the AzureML pipeline.