Skip to content

Commit b40476e

Browse files
authored
Merge branch 'master' into mvs-kms-sse
2 parents 34bdbf7 + 1787f78 commit b40476e

15 files changed

+363
-109
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ CHANGELOG
66
==========
77

88
* bug-fix: pass kms id as parameter for uploading code with Server side encryption
9+
* feature: ``PipelineModel``: Create a Transformer from a PipelineModel
910

1011
1.18.4
1112
======
1213

1314
* doc-fix: Remove incorrect parameter for EI TFS Python README
1415
* feature: ``Predictor``: delete SageMaker model
15-
* feature: ``Pipeline``: delete SageMaker model
16+
* feature: ``PipelineModel``: delete SageMaker model
1617
* bug-fix: Estimator.attach works with training jobs without hyperparameters
1718
* doc-fix: remove duplicate content from mxnet/README.rst
1819
* doc-fix: move overview content in main README into sphynx project

README.rst

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -965,8 +965,9 @@ the ML Pipeline.
965965
endpoint_name = 'inference-pipeline-endpoint'
966966
sm_model = PipelineModel(name=model_name, role=sagemaker_role, models=[sparkml_model, xgb_model])
967967
968-
This will define a ``PipelineModel`` consisting of SparkML model and an XGBoost model stacked sequentially. For more
969-
information about how to train an XGBoost model, please refer to the XGBoost notebook here_.
968+
This will define a ``PipelineModel`` consisting of SparkML model and an XGBoost model stacked sequentially.
969+
970+
For more information about how to train an XGBoost model, please refer to the XGBoost notebook here_.
970971
971972
.. _here: https://docs.aws.amazon.com/sagemaker/latest/dg/xgboost.html#xgboost-sample-notebooks
972973
@@ -978,6 +979,37 @@ This returns a predictor the same way an ``Estimator`` does when ``deploy()`` is
978979
request using this predictor, you should pass the data that the first container expects and the predictor will return the
979980
output from the last container.
980981
982+
You can also use a ``PipelineModel`` to create Transform Jobs for batch transformations. Using the same ``PipelineModel`` ``sm_model`` as above:
983+
984+
.. code:: python
985+
986+
# Only instance_type and instance_count are required.
987+
transformer = sm_model.transformer(instance_type='ml.c5.xlarge',
988+
instance_count=1,
989+
strategy='MultiRecord',
990+
max_payload=6,
991+
max_concurrent_transforms=8,
992+
accept='text/csv',
993+
assemble_with='Line',
994+
output_path='s3://my-output-bucket/path/to/my/output/data/')
995+
# Only data is required.
996+
transformer.transform(data='s3://my-input-bucket/path/to/my/csv/data',
997+
content_type='text/csv',
998+
split_type='Line')
999+
# Waits for the Pipeline Transform Job to finish.
1000+
transformer.wait()
1001+
1002+
This runs a transform job against all the files under ``s3://mybucket/path/to/my/csv/data``, transforming the input
1003+
data in order with each model container in the pipeline. For each input file that was successfully transformed, one output file in ``s3://my-output-bucket/path/to/my/output/data/``
1004+
will be created with the same name, appended with '.out'.
1005+
1006+
This transform job will split CSV files by newline separators, which is especially useful if the input files are large. The Transform Job will
1007+
assemble the outputs with line separators when writing each input file's corresponding output file.
1008+
1009+
Each payload entering the first model container will be up to six megabytes, and up to eight inference requests will be sent at the
1010+
same time to the first model container. Since each payload will consist of a mini-batch of multiple CSV records, the model
1011+
containers will transform each mini-batch of records.
1012+
9811013
For comprehensive examples on how to use Inference Pipelines please refer to the following notebooks:
9821014
9831015
- `inference_pipeline_sparkml_xgboost_abalone.ipynb <https://github.com/awslabs/amazon-sagemaker-examples/blob/master/advanced_functionality/inference_pipeline_sparkml_xgboost_abalone/inference_pipeline_sparkml_xgboost_abalone.ipynb>`__

src/sagemaker/model.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
from sagemaker import fw_utils, local, session, utils
2020
from sagemaker.transformer import Transformer
2121

22+
logging.basicConfig()
23+
LOGGER = logging.getLogger('sagemaker')
24+
LOGGER.setLevel(logging.INFO)
2225

2326
NEO_ALLOWED_TARGET_INSTANCE_FAMILY = set(['ml_c5', 'ml_m5', 'ml_c4', 'ml_m4', 'jetson_tx1', 'jetson_tx2', 'ml_p2',
2427
'ml_p3', 'deeplens', 'rasp3b'])
@@ -99,7 +102,9 @@ def _create_sagemaker_model(self, instance_type, accelerator_type=None):
99102
Args:
100103
instance_type (str): The EC2 instance type that this Model will be used for, this is only
101104
used to determine if the image needs GPU support or not.
102-
accelerator_type (str): <put docs here>
105+
accelerator_type (str): Type of Elastic Inference accelerator to attach to an endpoint for model loading
106+
and inference, for example, 'ml.eia1.medium'. If not specified, no Elastic Inference accelerator
107+
will be attached to the endpoint.
103108
"""
104109
container_def = self.prepare_container_def(instance_type, accelerator_type=accelerator_type)
105110
self.name = self.name or utils.name_from_image(container_def['Image'])
@@ -190,9 +195,13 @@ def compile(self, target_instance_family, input_shape, output_path, role,
190195
self.sagemaker_session.compile_model(**config)
191196
job_status = self.sagemaker_session.wait_for_compilation_job(job_name)
192197
self.model_data = job_status['ModelArtifacts']['S3ModelArtifacts']
193-
self.image = self._neo_image(self.sagemaker_session.boto_region_name, target_instance_family, framework,
194-
framework_version)
195-
self._is_compiled_model = True
198+
if target_instance_family.startswith('ml_'):
199+
self.image = self._neo_image(self.sagemaker_session.boto_region_name, target_instance_family, framework,
200+
framework_version)
201+
self._is_compiled_model = True
202+
else:
203+
LOGGER.warning("The intance type {} is not supported to deploy via SageMaker,"
204+
"please deploy the model on the device by yourself.".format(target_instance_family))
196205
return self
197206

198207
def deploy(self, initial_instance_count, instance_type, accelerator_type=None, endpoint_name=None,
@@ -285,10 +294,6 @@ def transformer(self, instance_count, instance_type, strategy=None, assemble_wit
285294
max_payload (int): Maximum size of the payload in a single HTTP request to the container in MB.
286295
tags (list[dict]): List of tags for labeling a transform job. If none specified, then the tags used for
287296
the training job are used for the transform job.
288-
role (str): The ``ExecutionRoleArn`` IAM Role ARN for the ``Model``, which is also used during
289-
transform jobs. If not specified, the role from the Model will be used.
290-
model_server_workers (int): Optional. The number of worker processes used by the inference server.
291-
If None, server will use one worker per vCPU.
292297
volume_kms_key (str): Optional. KMS key ID for encrypting the volume attached to the ML
293298
compute instance (default: None).
294299
"""

src/sagemaker/pipeline.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import sagemaker
1616
from sagemaker.session import Session
1717
from sagemaker.utils import name_from_image
18+
from sagemaker.transformer import Transformer
1819

1920

2021
class PipelineModel(object):
@@ -104,6 +105,56 @@ def deploy(self, initial_instance_count, instance_type, endpoint_name=None, tags
104105
if self.predictor_cls:
105106
return self.predictor_cls(self.endpoint_name, self.sagemaker_session)
106107

108+
def _create_sagemaker_pipeline_model(self, instance_type):
109+
"""Create a SageMaker Model Entity
110+
111+
Args:
112+
instance_type (str): The EC2 instance type that this Model will be used for, this is only
113+
used to determine if the image needs GPU support or not.
114+
accelerator_type (str): Type of Elastic Inference accelerator to attach to an endpoint for model loading
115+
and inference, for example, 'ml.eia1.medium'. If not specified, no Elastic Inference accelerator
116+
will be attached to the endpoint.
117+
"""
118+
if not self.sagemaker_session:
119+
self.sagemaker_session = Session()
120+
121+
containers = self.pipeline_container_def(instance_type)
122+
123+
self.name = self.name or name_from_image(containers[0]['Image'])
124+
self.sagemaker_session.create_model(self.name, self.role, containers, vpc_config=self.vpc_config)
125+
126+
def transformer(self, instance_count, instance_type, strategy=None, assemble_with=None, output_path=None,
127+
output_kms_key=None, accept=None, env=None, max_concurrent_transforms=None,
128+
max_payload=None, tags=None, volume_kms_key=None):
129+
"""Return a ``Transformer`` that uses this Model.
130+
131+
Args:
132+
instance_count (int): Number of EC2 instances to use.
133+
instance_type (str): Type of EC2 instance to use, for example, 'ml.c4.xlarge'.
134+
strategy (str): The strategy used to decide how to batch records in a single request (default: None).
135+
Valid values: 'MULTI_RECORD' and 'SINGLE_RECORD'.
136+
assemble_with (str): How the output is assembled (default: None). Valid values: 'Line' or 'None'.
137+
output_path (str): S3 location for saving the transform result. If not specified, results are stored to
138+
a default bucket.
139+
output_kms_key (str): Optional. KMS key ID for encrypting the transform output (default: None).
140+
accept (str): The content type accepted by the endpoint deployed during the transform job.
141+
env (dict): Environment variables to be set for use during the transform job (default: None).
142+
max_concurrent_transforms (int): The maximum number of HTTP requests to be made to
143+
each individual transform container at one time.
144+
max_payload (int): Maximum size of the payload in a single HTTP request to the container in MB.
145+
tags (list[dict]): List of tags for labeling a transform job. If none specified, then the tags used for
146+
the training job are used for the transform job.
147+
volume_kms_key (str): Optional. KMS key ID for encrypting the volume attached to the ML
148+
compute instance (default: None).
149+
"""
150+
self._create_sagemaker_pipeline_model(instance_type)
151+
152+
return Transformer(self.name, instance_count, instance_type, strategy=strategy, assemble_with=assemble_with,
153+
output_path=output_path, output_kms_key=output_kms_key, accept=accept,
154+
max_concurrent_transforms=max_concurrent_transforms, max_payload=max_payload,
155+
env=env, tags=tags, base_transform_job_name=self.name,
156+
volume_kms_key=volume_kms_key, sagemaker_session=self.sagemaker_session)
157+
107158
def delete_model(self):
108159
"""Delete the SageMaker model backing this pipeline model. This does not delete the list of SageMaker models used
109160
in multiple containers to build the inference pipeline.

src/sagemaker/transformer.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# language governing permissions and limitations under the License.
1313
from __future__ import absolute_import
1414

15+
from botocore import exceptions
16+
1517
from sagemaker.job import _Job
1618
from sagemaker.session import Session
1719
from sagemaker.utils import base_name_from_image, name_from_base
@@ -119,8 +121,14 @@ def delete_model(self):
119121
self.sagemaker_session.delete_model(self.model_name)
120122

121123
def _retrieve_image_name(self):
122-
model_desc = self.sagemaker_session.sagemaker_client.describe_model(ModelName=self.model_name)
123-
return model_desc['PrimaryContainer']['Image']
124+
try:
125+
model_desc = self.sagemaker_session.sagemaker_client.describe_model(ModelName=self.model_name)
126+
return model_desc['PrimaryContainer']['Image']
127+
except exceptions.ClientError:
128+
raise ValueError('Failed to fetch model information for %s. '
129+
'Please ensure that the model exists. '
130+
'Local instance types require locally created models.'
131+
% self.model_name)
124132

125133
def wait(self):
126134
self._ensure_last_transform_job()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0,28.0,C,38.0,71.5,1.0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0,C,38.0,71.5,1.0,female

tests/integ/kms_utils.py

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from botocore import exceptions
1616

17-
KEY_ALIAS = "SageMakerKmsKey"
17+
KEY_ALIAS = "SageMakerIntegTestKmsKey"
1818
KEY_POLICY = '''
1919
{{
2020
"Version": "2012-10-17",
@@ -28,39 +28,6 @@
2828
}},
2929
"Action": "kms:*",
3030
"Resource": "*"
31-
}},
32-
{{
33-
"Sid": "Allow use of the key",
34-
"Effect": "Allow",
35-
"Principal": {{
36-
"AWS": "{account_id}"
37-
}},
38-
"Action": [
39-
"kms:Encrypt",
40-
"kms:Decrypt",
41-
"kms:ReEncrypt*",
42-
"kms:GenerateDataKey*",
43-
"kms:DescribeKey"
44-
],
45-
"Resource": "*"
46-
}},
47-
{{
48-
"Sid": "Allow attachment of persistent resources",
49-
"Effect": "Allow",
50-
"Principal": {{
51-
"AWS": "{account_id}"
52-
}},
53-
"Action": [
54-
"kms:CreateGrant",
55-
"kms:ListGrants",
56-
"kms:RevokeGrant"
57-
],
58-
"Resource": "*",
59-
"Condition": {{
60-
"Bool": {{
61-
"kms:GrantIsForAWSResource": "true"
62-
}}
63-
}}
6431
}}
6532
]
6633
}}

tests/integ/marketplace_utils.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
from __future__ import absolute_import
14+
15+
REGION_ACCOUNT_MAP = {
16+
'us-east-1': '865070037744',
17+
'us-east-2': '057799348421',
18+
'us-west-2': '594846645681',
19+
'eu-west-1': '985815980388',
20+
'eu-central-1': '446921602837',
21+
'ap-northeast-1': '977537786026',
22+
'ap-northeast-2': '745090734665',
23+
'ap-southeast-2': '666831318237',
24+
'ap-southeast-1': '192199979996',
25+
'ap-south-1': '077584701553',
26+
'ca-central-1': '470592106596',
27+
'eu-west-2': '856760150666',
28+
'us-west-1': '382657785993'
29+
}

0 commit comments

Comments
 (0)