Skip to content

Commit a52f3b0

Browse files
author
Dan Choi
committed
Add integ tests for tuning jobs
1 parent 0cc5ccc commit a52f3b0

File tree

4 files changed

+264
-47
lines changed

4 files changed

+264
-47
lines changed

src/sagemaker/amazon/lda.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ def __init__(self, role, train_instance_type, num_topics,
7878
tol (float): Optional. Target error tolerance for the ALS phase of the algorithm.
7979
**kwargs: base class keyword argument values.
8080
"""
81-
8281
# this algorithm only supports single instance training
82+
if kwargs.pop('train_instance_count', 1) != 1:
83+
print('LDA only supports single instance training. Defaulting to 1 {}.'.format(train_instance_type))
84+
8385
super(LDA, self).__init__(role, 1, train_instance_type, **kwargs)
8486
self.num_topics = num_topics
8587
self.alpha0 = alpha0

tests/data/chainer_mnist/mnist.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def _preprocess_mnist(raw, withlabel, ndim, scale, image_dtype, label_dtype, rgb
7272
parser.add_argument('--epochs', type=int, default=20)
7373
parser.add_argument('--frequency', type=int, default=20)
7474
parser.add_argument('--batch-size', type=int, default=100)
75+
parser.add_argument('--alpha', type=float, default=0.001)
7576
parser.add_argument('--model-dir', type=str, default=env.model_dir)
7677

7778
parser.add_argument('--train', type=str, default=env.channel_input_dirs['train'])
@@ -103,7 +104,7 @@ def _preprocess_mnist(raw, withlabel, ndim, scale, image_dtype, label_dtype, rgb
103104
chainer.cuda.get_device_from_id(0).use()
104105

105106
# Setup an optimizer
106-
optimizer = chainer.optimizers.Adam()
107+
optimizer = chainer.optimizers.Adam(alpha=args.alpha)
107108
optimizer.setup(model)
108109

109110
# Load the MNIST dataset

tests/data/iris/iris-dnn-classifier.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818

1919

2020
def estimator_fn(run_config, hyperparameters):
21-
input_tensor_name = hyperparameters['input_tensor_name']
21+
input_tensor_name = hyperparameters.get('input_tensor_name', 'inputs')
22+
learning_rate = hyperparameters.get('learning_rate', 0.05)
2223
feature_columns = [tf.feature_column.numeric_column(input_tensor_name, shape=[4])]
2324
return tf.estimator.DNNClassifier(feature_columns=feature_columns,
2425
hidden_units=[10, 20, 10],
26+
optimizer=tf.train.AdagradOptimizer(learning_rate=learning_rate),
2527
n_classes=3,
2628
config=run_config)
2729

tests/integ/test_tuner.py

Lines changed: 256 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,71 +16,178 @@
1616
import os
1717
import pickle
1818
import sys
19+
import time
1920

21+
import numpy as np
2022
import pytest
2123

24+
from sagemaker import LDA, RandomCutForest
25+
from sagemaker.amazon.common import read_records
2226
from sagemaker.amazon.kmeans import KMeans
27+
from sagemaker.chainer import Chainer
2328
from sagemaker.mxnet.estimator import MXNet
29+
from sagemaker.tensorflow import TensorFlow
2430
from sagemaker.tuner import IntegerParameter, ContinuousParameter, CategoricalParameter, HyperparameterTuner
2531
from tests.integ import DATA_DIR
26-
from tests.integ.timeout import timeout
27-
28-
29-
@pytest.mark.skip(reason='functionality is not ready yet')
30-
def test_fit_1p(sagemaker_session):
31-
data_path = os.path.join(DATA_DIR, 'one_p_mnist', 'mnist.pkl.gz')
32-
pickle_args = {} if sys.version_info.major == 2 else {'encoding': 'latin1'}
33-
34-
# Load the data into memory as numpy arrays
35-
with gzip.open(data_path, 'rb') as f:
36-
train_set, _, _ = pickle.load(f, **pickle_args)
37-
38-
kmeans = KMeans(role='SageMakerRole', train_instance_count=1,
39-
train_instance_type='ml.c4.xlarge',
40-
k=10, sagemaker_session=sagemaker_session, base_job_name='tk',
41-
output_path='s3://{}/'.format(sagemaker_session.default_bucket()))
42-
43-
# set kmeans specific hp
44-
kmeans.init_method = 'random'
45-
kmeans.max_iterators = 1
46-
kmeans.tol = 1
47-
kmeans.num_trials = 1
48-
kmeans.local_init_method = 'kmeans++'
49-
kmeans.half_life_time_size = 1
50-
kmeans.epochs = 1
51-
52-
records = kmeans.record_set(train_set[0][:100])
53-
test_records = kmeans.record_set(train_set[0][:100], channel='test')
54-
55-
# specify which hp you want to optimize over
56-
hyperparameter_ranges = {'extra_center_factor': IntegerParameter(1, 10),
57-
'mini_batch_size': IntegerParameter(10, 100),
58-
'epochs': IntegerParameter(1, 2),
59-
'init_method': CategoricalParameter(['kmeans++', 'random'])}
60-
objective_metric_name = 'test:msd'
61-
62-
tuner = HyperparameterTuner(estimator=kmeans, objective_metric_name=objective_metric_name,
63-
hyperparameter_ranges=hyperparameter_ranges, objective_type='Minimize', max_jobs=2,
32+
from tests.integ.record_set import prepare_record_set_from_local_files
33+
from tests.integ.timeout import timeout, timeout_and_delete_endpoint_by_name
34+
35+
DATA_PATH = os.path.join(DATA_DIR, 'iris', 'data')
36+
37+
38+
@pytest.mark.continuous_testing
39+
def test_tuning_kmeans(sagemaker_session):
40+
with timeout(minutes=20):
41+
data_path = os.path.join(DATA_DIR, 'one_p_mnist', 'mnist.pkl.gz')
42+
pickle_args = {} if sys.version_info.major == 2 else {'encoding': 'latin1'}
43+
44+
# Load the data into memory as numpy arrays
45+
with gzip.open(data_path, 'rb') as f:
46+
train_set, _, _ = pickle.load(f, **pickle_args)
47+
48+
kmeans = KMeans(role='SageMakerRole', train_instance_count=1,
49+
train_instance_type='ml.c4.xlarge',
50+
k=10, sagemaker_session=sagemaker_session, base_job_name='tk',
51+
output_path='s3://{}/'.format(sagemaker_session.default_bucket()))
52+
53+
# set kmeans specific hp
54+
kmeans.init_method = 'random'
55+
kmeans.max_iterators = 1
56+
kmeans.tol = 1
57+
kmeans.num_trials = 1
58+
kmeans.local_init_method = 'kmeans++'
59+
kmeans.half_life_time_size = 1
60+
kmeans.epochs = 1
61+
62+
records = kmeans.record_set(train_set[0][:100])
63+
test_records = kmeans.record_set(train_set[0][:100], channel='test')
64+
65+
# specify which hp you want to optimize over
66+
hyperparameter_ranges = {'extra_center_factor': IntegerParameter(1, 10),
67+
'mini_batch_size': IntegerParameter(10, 100),
68+
'epochs': IntegerParameter(1, 2),
69+
'init_method': CategoricalParameter(['kmeans++', 'random'])}
70+
objective_metric_name = 'test:msd'
71+
72+
tuner = HyperparameterTuner(estimator=kmeans, objective_metric_name=objective_metric_name,
73+
hyperparameter_ranges=hyperparameter_ranges, objective_type='Minimize', max_jobs=2,
74+
max_parallel_jobs=2)
75+
76+
tuner.fit([records, test_records])
77+
78+
print('Started hyperparameter tuning job with name:' + tuner.latest_tuning_job.name)
79+
80+
time.sleep(15)
81+
tuner.wait()
82+
83+
best_training_job = tuner.best_training_job()
84+
with timeout_and_delete_endpoint_by_name(best_training_job, sagemaker_session):
85+
predictor = tuner.deploy(1, 'ml.c4.xlarge')
86+
result = predictor.predict(train_set[0][:10])
87+
88+
assert len(result) == 10
89+
for record in result:
90+
assert record.label['closest_cluster'] is not None
91+
assert record.label['distance_to_cluster'] is not None
92+
93+
94+
def test_tuning_lda(sagemaker_session):
95+
with timeout(minutes=20):
96+
data_path = os.path.join(DATA_DIR, 'lda')
97+
data_filename = 'nips-train_1.pbr'
98+
99+
with open(os.path.join(data_path, data_filename), 'rb') as f:
100+
all_records = read_records(f)
101+
102+
# all records must be same
103+
feature_num = int(all_records[0].features['values'].float32_tensor.shape[0])
104+
105+
lda = LDA(role='SageMakerRole', train_instance_type='ml.c4.xlarge', num_topics=10,
106+
sagemaker_session=sagemaker_session, base_job_name='test-lda')
107+
108+
record_set = prepare_record_set_from_local_files(data_path, lda.data_location,
109+
len(all_records), feature_num, sagemaker_session)
110+
test_record_set = prepare_record_set_from_local_files(data_path, lda.data_location,
111+
len(all_records), feature_num, sagemaker_session)
112+
test_record_set.channel = 'test'
113+
114+
# specify which hp you want to optimize over
115+
hyperparameter_ranges = {'alpha0': ContinuousParameter(1, 10),
116+
'num_topics': IntegerParameter(1, 2)}
117+
objective_metric_name = 'test:pwll'
118+
119+
tuner = HyperparameterTuner(estimator=lda, objective_metric_name=objective_metric_name,
120+
hyperparameter_ranges=hyperparameter_ranges, objective_type='Maximize', max_jobs=2,
121+
max_parallel_jobs=2)
122+
123+
tuner.fit([record_set, test_record_set], mini_batch_size=1)
124+
125+
print('Started hyperparameter tuning job with name:' + tuner.latest_tuning_job.name)
126+
127+
time.sleep(15)
128+
tuner.wait()
129+
130+
best_training_job = tuner.best_training_job()
131+
with timeout_and_delete_endpoint_by_name(best_training_job, sagemaker_session):
132+
predictor = tuner.deploy(1, 'ml.c4.xlarge')
133+
predict_input = np.random.rand(1, feature_num)
134+
result = predictor.predict(predict_input)
135+
136+
assert len(result) == 1
137+
for record in result:
138+
assert record.label['topic_mixture'] is not None
139+
140+
141+
@pytest.mark.continuous_testing
142+
def test_stop_tuning_job(sagemaker_session):
143+
feature_num = 14
144+
train_input = np.random.rand(1000, feature_num)
145+
146+
rcf = RandomCutForest(role='SageMakerRole', train_instance_count=1, train_instance_type='ml.c4.xlarge',
147+
num_trees=50, num_samples_per_tree=20, sagemaker_session=sagemaker_session,
148+
base_job_name='test-randomcutforest')
149+
150+
records = rcf.record_set(train_input)
151+
records.distribution = 'FullyReplicated'
152+
153+
test_records = rcf.record_set(train_input, channel='test')
154+
test_records.distribution = 'FullyReplicated'
155+
156+
hyperparameter_ranges = {'num_trees': IntegerParameter(50, 100),
157+
'num_samples_per_tree': IntegerParameter(1, 2)}
158+
159+
objective_metric_name = 'test:f1'
160+
tuner = HyperparameterTuner(estimator=rcf, objective_metric_name=objective_metric_name,
161+
hyperparameter_ranges=hyperparameter_ranges, objective_type='Maximize', max_jobs=2,
64162
max_parallel_jobs=2)
65163

66164
tuner.fit([records, test_records])
67165

68-
print('Started HPO job with name:' + tuner.latest_tuning_job.name)
166+
time.sleep(15)
167+
168+
latest_tuning_job_name = tuner.latest_tuning_job.name
169+
170+
print('Attempting to stop {}'.format(latest_tuning_job_name))
171+
172+
tuner.stop_tuning_job()
69173

174+
desc = tuner.latest_tuning_job.sagemaker_session.sagemaker_client\
175+
.describe_hyper_parameter_tuning_job(HyperParameterTuningJobName=latest_tuning_job_name)
176+
assert desc['HyperParameterTuningJobStatus'] == 'Stopping'
70177

71-
@pytest.mark.skip(reason='functionality is not ready yet')
72-
def test_mxnet_tuning(sagemaker_session, mxnet_full_version):
178+
179+
@pytest.mark.continuous_testing
180+
def test_tuning_mxnet(sagemaker_session):
73181
with timeout(minutes=15):
74182
script_path = os.path.join(DATA_DIR, 'mxnet_mnist', 'tuning.py')
75183
data_path = os.path.join(DATA_DIR, 'mxnet_mnist')
76184

77185
estimator = MXNet(entry_point=script_path,
78186
role='SageMakerRole',
79-
framework_version=mxnet_full_version,
80187
train_instance_count=1,
81188
train_instance_type='ml.m4.xlarge',
82189
sagemaker_session=sagemaker_session,
83-
base_job_name='hpo')
190+
base_job_name='tune-mxnet')
84191

85192
hyperparameter_ranges = {'learning_rate': ContinuousParameter(0.01, 0.2)}
86193
objective_metric_name = 'Validation-accuracy'
@@ -94,4 +201,109 @@ def test_mxnet_tuning(sagemaker_session, mxnet_full_version):
94201
key_prefix='integ-test-data/mxnet_mnist/test')
95202
tuner.fit({'train': train_input, 'test': test_input})
96203

97-
print('tuning job successfully created: {}'.format(tuner.latest_tuning_job.name))
204+
print('Started hyperparameter tuning job with name:' + tuner.latest_tuning_job.name)
205+
206+
time.sleep(15)
207+
tuner.wait()
208+
209+
best_training_job = tuner.best_training_job()
210+
with timeout_and_delete_endpoint_by_name(best_training_job, sagemaker_session):
211+
predictor = tuner.deploy(1, 'ml.c4.xlarge')
212+
data = np.zeros(shape=(1, 1, 28, 28))
213+
predictor.predict(data)
214+
215+
216+
@pytest.mark.continuous_testing
217+
def test_tuning_tf(sagemaker_session):
218+
with timeout(minutes=15):
219+
script_path = os.path.join(DATA_DIR, 'iris', 'iris-dnn-classifier.py')
220+
221+
estimator = TensorFlow(entry_point=script_path,
222+
role='SageMakerRole',
223+
training_steps=1,
224+
evaluation_steps=1,
225+
hyperparameters={'input_tensor_name': 'inputs'},
226+
train_instance_count=1,
227+
train_instance_type='ml.c4.xlarge',
228+
sagemaker_session=sagemaker_session,
229+
base_job_name='tune-tf')
230+
231+
inputs = sagemaker_session.upload_data(path=DATA_PATH, key_prefix='integ-test-data/tf_iris')
232+
hyperparameter_ranges = {'learning_rate': ContinuousParameter(0.05, 0.2)}
233+
234+
objective_metric_name = 'loss'
235+
metric_definitions = [{'Name': 'loss', 'Regex': 'loss=([0-9\\.]+)'}]
236+
237+
tuner = HyperparameterTuner(estimator, objective_metric_name, hyperparameter_ranges, metric_definitions,
238+
objective_type='Minimize', max_jobs=2, max_parallel_jobs=2)
239+
240+
tuner.fit(inputs)
241+
242+
print('Started hyperparameter tuning job with name:' + tuner.latest_tuning_job.name)
243+
244+
time.sleep(15)
245+
tuner.wait()
246+
247+
best_training_job = tuner.best_training_job()
248+
with timeout_and_delete_endpoint_by_name(best_training_job, sagemaker_session):
249+
predictor = tuner.deploy(1, 'ml.c4.xlarge')
250+
251+
features = [6.4, 3.2, 4.5, 1.5]
252+
dict_result = predictor.predict({'inputs': features})
253+
print('predict result: {}'.format(dict_result))
254+
list_result = predictor.predict(features)
255+
print('predict result: {}'.format(list_result))
256+
257+
assert dict_result == list_result
258+
259+
260+
@pytest.mark.continuous_testing
261+
def test_tuning_chainer(sagemaker_session):
262+
with timeout(minutes=15):
263+
script_path = os.path.join(DATA_DIR, 'chainer_mnist', 'mnist.py')
264+
data_path = os.path.join(DATA_DIR, 'chainer_mnist')
265+
266+
estimator = Chainer(entry_point=script_path,
267+
role='SageMakerRole',
268+
train_instance_count=1,
269+
train_instance_type='ml.c4.xlarge',
270+
sagemaker_session=sagemaker_session,
271+
hyperparameters={'epochs': 1})
272+
273+
train_input = estimator.sagemaker_session.upload_data(path=os.path.join(data_path, 'train'),
274+
key_prefix='integ-test-data/chainer_mnist/train')
275+
test_input = estimator.sagemaker_session.upload_data(path=os.path.join(data_path, 'test'),
276+
key_prefix='integ-test-data/chainer_mnist/test')
277+
278+
hyperparameter_ranges = {'alpha': ContinuousParameter(0.001, 0.005)}
279+
280+
objective_metric_name = 'Validation-accuracy'
281+
metric_definitions = [
282+
{'Name': 'Validation-accuracy', 'Regex': '\[J1\s+\d\.\d+\s+\d\.\d+\s+\d\.\d+\s+(\d\.\d+)'}]
283+
284+
tuner = HyperparameterTuner(estimator, objective_metric_name, hyperparameter_ranges, metric_definitions,
285+
max_jobs=2, max_parallel_jobs=2)
286+
287+
tuner.fit({'train': train_input, 'test': test_input})
288+
289+
print('Started hyperparameter tuning job with name:' + tuner.latest_tuning_job.name)
290+
291+
time.sleep(15)
292+
tuner.wait()
293+
294+
best_training_job = tuner.best_training_job()
295+
with timeout_and_delete_endpoint_by_name(best_training_job, sagemaker_session):
296+
predictor = tuner.deploy(1, 'ml.c4.xlarge')
297+
298+
batch_size = 100
299+
data = np.zeros((batch_size, 784), dtype='float32')
300+
output = predictor.predict(data)
301+
assert len(output) == batch_size
302+
303+
data = np.zeros((batch_size, 1, 28, 28), dtype='float32')
304+
output = predictor.predict(data)
305+
assert len(output) == batch_size
306+
307+
data = np.zeros((batch_size, 28, 28), dtype='float32')
308+
output = predictor.predict(data)
309+
assert len(output) == batch_size

0 commit comments

Comments
 (0)