Skip to content

Commit c098f6b

Browse files
nadiayajesterhazy
authored andcommitted
Add support for intermediate output to a local directory in local mode. (#524)
1 parent 9061757 commit c098f6b

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

CHANGELOG.rst

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77

88
* doc-fix: Change ``distribution`` to ``distributions``
99
* bug-fix: Increase docker-compose http timeout and health check timeout to 120.
10+
* feature: Local Mode: Add support for intermediate output to a local directory.
1011

1112
1.16.1.post1
1213
============

src/sagemaker/local/image.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ def train(self, input_data_config, output_data_config, hyperparameters, job_name
104104
os.mkdir(shared_dir)
105105

106106
data_dir = self._create_tmp_folder()
107-
volumes = self._prepare_training_volumes(data_dir, input_data_config, hyperparameters)
107+
volumes = self._prepare_training_volumes(data_dir, input_data_config, output_data_config,
108+
hyperparameters)
108109

109110
# Create the configuration files for each container that we will create
110111
# Each container will map the additional local volumes (if any).
@@ -281,7 +282,8 @@ def write_config_files(self, host, hyperparameters, input_data_config):
281282
_write_json_file(os.path.join(config_path, 'resourceconfig.json'), resource_config)
282283
_write_json_file(os.path.join(config_path, 'inputdataconfig.json'), json_input_data_config)
283284

284-
def _prepare_training_volumes(self, data_dir, input_data_config, hyperparameters):
285+
def _prepare_training_volumes(self, data_dir, input_data_config, output_data_config,
286+
hyperparameters):
285287
shared_dir = os.path.join(self.container_root, 'shared')
286288
model_dir = os.path.join(self.container_root, 'model')
287289
volumes = []
@@ -309,6 +311,14 @@ def _prepare_training_volumes(self, data_dir, input_data_config, hyperparameters
309311
# Also mount a directory that all the containers can access.
310312
volumes.append(_Volume(shared_dir, '/opt/ml/shared'))
311313

314+
parsed_uri = urlparse(output_data_config['S3OutputPath'])
315+
if parsed_uri.scheme == 'file' \
316+
and sagemaker.rl.estimator.SAGEMAKER_OUTPUT_LOCATION in hyperparameters:
317+
intermediate_dir = os.path.join(parsed_uri.path, 'output', 'intermediate')
318+
if not os.path.exists(intermediate_dir):
319+
os.makedirs(intermediate_dir)
320+
volumes.append(_Volume(intermediate_dir, '/opt/ml/output/intermediate'))
321+
312322
return volumes
313323

314324
def _prepare_serving_volumes(self, model_location):

tests/unit/test_image.py

+35
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,41 @@ def test_train_local_code(tmpdir, sagemaker_session):
396396
assert '%s:/opt/ml/shared' % shared_folder_path in volumes
397397

398398

399+
@patch('sagemaker.local.local_session.LocalSession', Mock())
400+
@patch('sagemaker.local.image._stream_output', Mock())
401+
@patch('sagemaker.local.image._SageMakerContainer._cleanup', Mock())
402+
@patch('sagemaker.local.data.get_data_source_instance', Mock())
403+
@patch('subprocess.Popen', Mock())
404+
def test_train_local_intermediate_output(tmpdir, sagemaker_session):
405+
directories = [str(tmpdir.mkdir('container-root')), str(tmpdir.mkdir('data'))]
406+
with patch('sagemaker.local.image._SageMakerContainer._create_tmp_folder',
407+
side_effect=directories):
408+
instance_count = 2
409+
image = 'my-image'
410+
sagemaker_container = _SageMakerContainer('local', instance_count, image,
411+
sagemaker_session=sagemaker_session)
412+
413+
output_path = str(tmpdir.mkdir('customer_intermediate_output'))
414+
output_data_config = {'S3OutputPath': 'file://%s' % output_path}
415+
hyperparameters = {'sagemaker_s3_output': output_path}
416+
417+
sagemaker_container.train(
418+
INPUT_DATA_CONFIG, output_data_config, hyperparameters, TRAINING_JOB_NAME)
419+
420+
docker_compose_file = os.path.join(sagemaker_container.container_root,
421+
'docker-compose.yaml')
422+
intermediate_folder_path = os.path.join(output_path, 'output/intermediate')
423+
424+
with open(docker_compose_file, 'r') as f:
425+
config = yaml.load(f)
426+
assert len(config['services']) == instance_count
427+
for h in sagemaker_container.hosts:
428+
assert config['services'][h]['image'] == image
429+
assert config['services'][h]['command'] == 'train'
430+
volumes = config['services'][h]['volumes']
431+
assert '%s:/opt/ml/output/intermediate' % intermediate_folder_path in volumes
432+
433+
399434
def test_container_has_gpu_support(tmpdir, sagemaker_session):
400435
instance_count = 1
401436
image = 'my-image'

0 commit comments

Comments
 (0)