Skip to content
This repository was archived by the owner on Apr 8, 2024. It is now read-only.

Commit d6c0b4c

Browse files
authored
Upgrade ray tune component (#270)
* add more optimizer/distribution for ray tune component and add ray tune distributed component. * revise lightgbm_training to use ray tune distributed component. * use distributed component to set up ray cluster with more than 1 node. * update requirements for ray tune components. * provide ray tune example yaml. * provide ray tune distributed example yaml file.
1 parent 6fa7c5d commit d6c0b4c

18 files changed

Lines changed: 1437 additions & 85 deletions

File tree

.github/workflows/docs.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ jobs:
3333
- name: pip install
3434
run: |
3535
python -m pip install --upgrade pip==21.3.1
36-
python -m pip install markdown-include==0.6.0 mkdocstrings==0.15.0 mkdocs-material==7.1.3 livereload==2.6.3
37-
36+
37+
python -m pip install markdown-include==0.7.0 mkdocstrings==0.19.0 mkdocstrings-python==0.7.1 mkdocs-material==8.4.2 livereload==2.6.3
38+
3839
# NOTE: we need requirements to be able to parse reference docs scripts
3940
sudo apt-get install libopenmpi-dev
4041
python -m pip install -r ./requirements.txt
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# to execute:
2+
# > python src/pipelines/azureml/lightgbm_training.py --exp-config conf/experiments/lightgbm_training/raytune.yaml
3+
4+
defaults:
5+
- aml: lightgbm-benchmark-eus2
6+
- compute: lightgbm-benchmark-eus2
7+
8+
### CUSTOM PARAMETERS ###
9+
10+
experiment:
11+
name: "dev_lightgbm_ray_tune"
12+
description: "something interesting to say about this"
13+
14+
lightgbm_training_config:
15+
# name of your particular benchmark
16+
benchmark_name: "lightgbm-ray-tune" # override this with a unique name
17+
18+
# list all the train/test pairs to train on
19+
tasks:
20+
- train:
21+
name: "data-synthetic-headercsv-regression-10cols-100000samples-train"
22+
test:
23+
name: "data-synthetic-headercsv-regression-10cols-10000samples-test"
24+
task_key: "dev_ray" # optional, user to register outputs
25+
26+
# NOTE: this example uses only 1 training (reference)
27+
# see other config files for creating training variants
28+
reference:
29+
framework: lightgbm_ray_tune
30+
31+
# input parameters
32+
data:
33+
auto_partitioning: True # inserts partitioning to match expected number of partitions (if nodes*processes > 1)
34+
pre_convert_to_binary: False # inserts convertion of train/test data into binary to speed up training (not compatible with auto_partitioning yet)
35+
header: true # IMPORTANT
36+
label_column: "0"
37+
group_column: null
38+
39+
# lightgbm training parameters
40+
training:
41+
objective: "regression"
42+
metric: "rmse"
43+
boosting: "gbdt"
44+
tree_learner: "data"
45+
num_iterations: "choice([30,40,50,60])"
46+
num_leaves: "31"
47+
min_data_in_leaf: "20"
48+
learning_rate: "0.1"
49+
max_bin: "255"
50+
feature_fraction: "1.0"
51+
52+
# compute parameters
53+
device_type: "cpu"
54+
55+
# you can add anything under custom_params, it will be sent as a dictionary
56+
# to the lightgbm training module to override its parameters (see lightgbm docs for list)
57+
custom_params:
58+
deterministic: True
59+
use_two_round_loading: True
60+
61+
# compute parameters
62+
runtime:
63+
target: "linux-cpu-ds14v2" # optional: force target for this training job
64+
nodes: 1
65+
processes: 1
66+
67+
# model registration
68+
# naming convention: "{register_model_prefix}-{task_key}-{num_iterations}trees-{num_leaves}leaves-{register_model_suffix}"
69+
output:
70+
register_model: False
71+
#register_model_prefix: "model"
72+
#register_model_suffix: null
73+
74+
raytune:
75+
mode: "min"
76+
search_alg: "BasicVariantGenerator"
77+
scheduler: "FIFOScheduler"
78+
num_samples: 5
79+
time_budget: 1800
80+
concurrent_trials: 0
81+
cpus_per_trial: 16
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# to execute:
2+
# > python src/pipelines/azureml/lightgbm_training.py --exp-config conf/experiments/lightgbm_training/raytune_distributed.yaml
3+
4+
defaults:
5+
- aml: lightgbm-benchmark-eus2
6+
- compute: lightgbm-benchmark-eus2
7+
8+
### CUSTOM PARAMETERS ###
9+
10+
experiment:
11+
name: "dev_lightgbm_ray_tune"
12+
description: "something interesting to say about this"
13+
14+
lightgbm_training_config:
15+
# name of your particular benchmark
16+
benchmark_name: "lightgbm-ray-tune" # override this with a unique name
17+
18+
# list all the train/test pairs to train on
19+
tasks:
20+
- train:
21+
name: "data-synthetic-headercsv-regression-10cols-100000samples-train"
22+
test:
23+
name: "data-synthetic-headercsv-regression-10cols-10000samples-test"
24+
task_key: "dev_ray" # optional, user to register outputs
25+
26+
# NOTE: this example uses only 1 training (reference)
27+
# see other config files for creating training variants
28+
reference:
29+
framework: lightgbm_ray_tune_distributed
30+
31+
# input parameters
32+
data:
33+
auto_partitioning: True # inserts partitioning to match expected number of partitions (if nodes*processes > 1)
34+
pre_convert_to_binary: False # inserts convertion of train/test data into binary to speed up training (not compatible with auto_partitioning yet)
35+
header: true # IMPORTANT
36+
label_column: "0"
37+
group_column: null
38+
train_data_format: 'CSV'
39+
test_data_format: 'CSV'
40+
41+
# lightgbm training parameters
42+
training:
43+
objective: "regression"
44+
metric: "rmse"
45+
boosting: "gbdt"
46+
tree_learner: "data"
47+
num_iterations: "choice([30,40,50,60])"
48+
num_leaves: "31"
49+
min_data_in_leaf: "20"
50+
learning_rate: "0.1"
51+
max_bin: "255"
52+
feature_fraction: "1.0"
53+
54+
# compute parameters
55+
device_type: "cpu"
56+
57+
# you can add anything under custom_params, it will be sent as a dictionary
58+
# to the lightgbm training module to override its parameters (see lightgbm docs for list)
59+
custom_params:
60+
deterministic: True
61+
use_two_round_loading: True
62+
63+
# compute parameters
64+
runtime:
65+
target: "linux-cpu-ds14v2" # optional: force target for this training job
66+
nodes: 4
67+
processes: 1
68+
69+
# model registration
70+
# naming convention: "{register_model_prefix}-{task_key}-{num_iterations}trees-{num_leaves}leaves-{register_model_suffix}"
71+
output:
72+
register_model: False
73+
#register_model_prefix: "model"
74+
#register_model_suffix: null
75+
76+
raytune:
77+
mode: "min"
78+
search_alg: "BasicVariantGenerator"
79+
scheduler: "FIFOScheduler"
80+
num_samples: 5
81+
time_budget: 1800
82+
concurrent_trials: 2
83+
lightgbm_ray_actors: 2
84+
cpus_per_actor: 16

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ lightgbm==3.3.1
1212
treelite==2.1.0
1313
treelite_runtime==2.1.0
1414
flaml==0.9.6
15+
hpbandster==0.7.4
16+
ConfigSpace==0.5.0
17+
optuna==2.8.0
1518

1619
# pipelines
1720
shrike[pipeline]==1.14.7

src/__init__.py

Whitespace-only changes.

src/common/aml.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def apply_sweep_settings(step, sweep_settings_config):
111111
)
112112
elif sweep_settings_config.early_termination.policy_type == "truncation_selection":
113113
step.runsettings.sweep.early_termination.configure(
114-
policy_type="bandit",
114+
policy_type="truncation_selection",
115115
truncation_percentage=sweep_settings_config.early_termination.truncation_percentage,
116116
evaluation_interval=sweep_settings_config.early_termination.evaluation_interval,
117117
delay_evaluation=sweep_settings_config.early_termination.delay_evaluation

src/common/raytune_param.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,25 @@
44
"""
55
Parses Ray Tune parameters from text arguments (cli or yaml)
66
"""
7-
import re
87
import argparse
98
import logging
10-
from azureml.core import Workspace, Datastore, Dataset
11-
import ray
12-
from ray import tune
139
from ray.tune import (
1410
uniform,
1511
quniform,
16-
loguniform,
17-
qloguniform,
18-
randn,
19-
qrandn,
2012
randint,
2113
qrandint,
22-
lograndint,
23-
qlograndint,
2414
choice,
2515
)
2616

2717

2818
class RayTuneParameterParser():
2919

20+
# TODO: allow more distributions
3021
DISTRIBUTIONS_MAP = {"choice": choice,
3122
"uniform": uniform,
32-
# "loguniform": loguniform,
33-
# "normal": normal,
34-
# "lognormal": lognormal,
35-
# "quniform": quniform,
36-
# "qloguniform": qloguniform,
37-
# "qnormal": qnormal,
38-
# "qlognormal": qlognormal,
23+
"quniform": quniform,
24+
"randint": randint,
25+
"qrandint": qrandint,
3926
}
4027

4128
def __init__(self, tunable_parameters):

src/common/tasks.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ class lightgbm_training_data_variant_parameters:
117117
group_column: Optional[str] = None
118118
construct: bool = True
119119

120+
# data formats
121+
train_data_format: Optional[str] = None
122+
test_data_format: Optional[str] = None
123+
120124
@dataclass
121125
class lightgbm_training_environment_variant_parameters:
122126
# COMPUTE

src/pipelines/azureml/lightgbm_training.py

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ class lightgbm_training_config: # pragma: no cover
8686
# load ray tune module.
8787
lightgbm_ray_tune_module = Component.from_yaml(yaml_file=os.path.join(COMPONENTS_ROOT, "training", "ray_tune", "spec.yaml"))
8888

89+
# load ray tune distributed module.
90+
lightgbm_ray_tune_distributed_module = Component.from_yaml(yaml_file=os.path.join(
91+
COMPONENTS_ROOT, "training", "ray_tune_distributed", "spec.yaml"))
8992
### PIPELINE SPECIFIC CODE ###
9093

9194
def process_sweep_parameters(params_dict, sweep_algorithm):
@@ -182,8 +185,20 @@ def lightgbm_training_pipeline_function(config,
182185

183186
# if we're using multinode, add partitioning
184187
if variant_params.data.auto_partitioning and (variant_params.training.tree_learner == "data" or variant_params.training.tree_learner == "voting"):
188+
# if training is distributed to multiple nodes using ray:
189+
if variant_params.framework == 'lightgbm_ray_tune_distributed' and variant_params.raytune.lightgbm_ray_actors > 1:
190+
partition_data_step = partition_data_module(
191+
input_data=train_dataset,
192+
mode="roundrobin",
193+
number=variant_params.raytune.lightgbm_ray_actors,
194+
header=variant_params.data.header,
195+
verbose=variant_params.training.verbose
196+
)
197+
partition_data_step.runsettings.configure(
198+
target=config.compute.linux_cpu)
199+
partitioned_train_data = partition_data_step.outputs.output_data
185200
# if using data parallel, train data has to be partitioned first
186-
if (variant_params.runtime.nodes * variant_params.runtime.processes) > 1:
201+
elif (variant_params.runtime.nodes * variant_params.runtime.processes) > 1:
187202
partition_data_step = partition_data_module(
188203
input_data=train_dataset,
189204
mode="roundrobin",
@@ -309,20 +324,50 @@ def lightgbm_training_pipeline_function(config,
309324
training_params['num_samples'] = variant_params.raytune.num_samples
310325
training_params['time_budget'] = variant_params.raytune.time_budget
311326
training_params['concurrent_trials'] = variant_params.raytune.concurrent_trials
327+
training_params['cpus_per_trial'] = variant_params.raytune.cpus_per_trial
328+
if 'low_num_iterations' in variant_params.raytune:
329+
training_params['low_num_iterations'] = variant_params.raytune.low_num_iterations
330+
if 'low_num_leaves' in variant_params.raytune:
331+
training_params['low_num_leaves'] = variant_params.raytune.low_num_leaves
332+
333+
# remove arguments that are not in lightgbm_ray_tune component
334+
if 'multinode_driver' in training_params:
335+
del training_params['multinode_driver']
336+
if 'custom_properties' in training_params:
337+
del training_params['custom_properties']
338+
if 'verbose' in training_params:
339+
del training_params['verbose']
340+
341+
elif variant_params.framework == 'lightgbm_ray_tune_distributed':
342+
lightgbm_train_module = lightgbm_ray_tune_distributed_module
343+
use_sweep = False
344+
345+
# manually add parameters for lightgbm_ray
346+
training_params['train_data_format'] = variant_params.data.train_data_format
347+
training_params['test_data_format'] = variant_params.data.test_data_format
348+
349+
# manually add ray tune parameters.
350+
training_params['mode'] = variant_params.raytune.mode
351+
training_params['search_alg'] = variant_params.raytune.search_alg
352+
training_params['scheduler'] = variant_params.raytune.scheduler
353+
training_params['num_samples'] = variant_params.raytune.num_samples
354+
training_params['time_budget'] = variant_params.raytune.time_budget
355+
training_params['concurrent_trials'] = variant_params.raytune.concurrent_trials
356+
training_params['lightgbm_ray_actors'] = variant_params.raytune.lightgbm_ray_actors
357+
training_params['cpus_per_actor'] = variant_params.raytune.cpus_per_actor
312358

313359
# remove arguments that are not in lightgbm_ray_tune component
314360
if 'multinode_driver' in training_params:
315361
del training_params['multinode_driver']
316-
if 'header' in training_params:
317-
del training_params['header']
318362
if 'construct' in training_params:
319363
del training_params['construct']
320364
if 'custom_properties' in training_params:
321365
del training_params['custom_properties']
322366
if 'verbose' in training_params:
323367
del training_params['verbose']
324368
else:
325-
raise NotImplementedError(f"training framework {variant_params.framework} hasn't been implemented yet.")
369+
raise NotImplementedError(
370+
f"training framework {variant_params.framework} hasn't been implemented yet.")
326371

327372
# configure the training module
328373
lightgbm_train_step = lightgbm_train_module(
@@ -452,13 +497,38 @@ def main():
452497
"```"
453498
])
454499

500+
# add pipeline tags
501+
autotags = {}
502+
reference_configs = config.lightgbm_training_config.reference
503+
print(f"tags: {autotags}")
504+
# add the information of the reference
505+
if reference_configs.raytune:
506+
autotags.update({
507+
'search_algo': reference_configs.raytune.search_alg,
508+
'scheduler': reference_configs.raytune.scheduler,
509+
'concurrent_trials': str(reference_configs.raytune.concurrent_trials),
510+
'time_minutes': str(reference_configs.raytune.time_budget/60),
511+
'cluster_nodes': str(reference_configs.runtime.nodes),
512+
})
513+
if reference_configs.sweep:
514+
autotags.update({
515+
"search_algo": reference_configs.sweep.algorithm,
516+
"truncate_percentage": str(reference_configs.sweep.early_termination.truncation_percentage),
517+
"concurrent_trials": str(reference_configs.sweep.limits.max_concurrent_trials),
518+
"time_minutes": str(reference_configs.sweep.limits.timeout_minutes),
519+
'cluster_nodes': str(reference_configs.runtime.nodes * reference_configs.sweep.limits.max_concurrent_trials),
520+
})
521+
522+
print(f"tags: {autotags}")
455523
# validate/submit the pipeline (if run.submit=True)
456524
pipeline_submit(
457525
workspace,
458526
config,
459527
pipeline_instance,
460-
experiment_description=experiment_description
528+
experiment_description=experiment_description,
529+
tags=autotags
461530
)
462531

532+
463533
if __name__ == "__main__":
464534
main()

src/scripts/sample/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)