Skip to content

Commit 76a72fc

Browse files
authored
Merge branch 'master' into env_support_training
2 parents 39752f3 + fc19b6e commit 76a72fc

File tree

11 files changed

+158
-37
lines changed

11 files changed

+158
-37
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## v2.30.0 (2021-03-17)
4+
5+
### Features
6+
7+
* add support for PyTorch 1.8.0
8+
* Allow users to send custom attributes to the model endpoint
9+
10+
### Bug Fixes and Other Changes
11+
12+
* use ResolvedOutputS3Uir for Hive DDL LOCATION
13+
* Do lazy initialization in predictor
14+
315
## v2.29.2 (2021-03-11)
416

517
### Bug Fixes and Other Changes

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.29.3.dev0
1+
2.30.1.dev0

src/sagemaker/feature_store/feature_group.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -512,14 +512,11 @@ def as_hive_ddl(self, database: str = "sagemaker_featurestore", table_name: str
512512
if not table_name:
513513
table_name = self.name
514514

515-
s3_uri = self.describe().get("OfflineStoreConfig").get("S3StorageConfig").get("S3Uri")
516-
offline_store_s3_uri = os.path.join(
517-
s3_uri,
518-
self.sagemaker_session.account_id(),
519-
"sagemaker",
520-
self.sagemaker_session.boto_session.region_name,
521-
"offline-store",
522-
self.name,
515+
resolved_output_s3_uri = (
516+
self.describe()
517+
.get("OfflineStoreConfig")
518+
.get("S3StorageConfig")
519+
.get("ResolvedOutputS3Uri")
523520
)
524521

525522
ddl = f"CREATE EXTERNAL TABLE IF NOT EXISTS {database}.{table_name} (\n"
@@ -537,6 +534,6 @@ def as_hive_ddl(self, database: str = "sagemaker_featurestore", table_name: str
537534
" STORED AS\n"
538535
" INPUTFORMAT 'parquet.hive.DeprecatedParquetInputFormat'\n"
539536
" OUTPUTFORMAT 'parquet.hive.DeprecatedParquetOutputFormat'\n"
540-
f"LOCATION '{offline_store_s3_uri}'"
537+
f"LOCATION '{resolved_output_s3_uri}'"
541538
)
542539
return ddl

src/sagemaker/fw_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
)
6161
SM_DATAPARALLEL_SUPPORTED_FRAMEWORK_VERSIONS = {
6262
"tensorflow": ["2.3.0", "2.3.1", "2.4.1"],
63-
"pytorch": ["1.6.0", "1.7.1"],
63+
"pytorch": ["1.6.0", "1.7.1", "1.8.0"],
6464
}
6565
SMDISTRIBUTED_SUPPORTED_STRATEGIES = ["dataparallel", "modelparallel"]
6666

src/sagemaker/image_uri_config/pytorch.json

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@
5555
"1.4": "1.4.0",
5656
"1.5": "1.5.0",
5757
"1.6": "1.6.0",
58-
"1.7": "1.7.1"
58+
"1.7": "1.7.1",
59+
"1.8": "1.8.0"
5960
},
6061
"versions": {
6162
"0.4.0": {
@@ -352,6 +353,39 @@
352353
"us-west-2": "763104351884"
353354
},
354355
"repository": "pytorch-inference"
356+
},
357+
"1.8.0": {
358+
"py_versions": [
359+
"py3",
360+
"py36"
361+
],
362+
"registries": {
363+
"af-south-1": "626614931356",
364+
"ap-east-1": "871362719292",
365+
"ap-northeast-1": "763104351884",
366+
"ap-northeast-2": "763104351884",
367+
"ap-south-1": "763104351884",
368+
"ap-southeast-1": "763104351884",
369+
"ap-southeast-2": "763104351884",
370+
"ca-central-1": "763104351884",
371+
"cn-north-1": "727897471807",
372+
"cn-northwest-1": "727897471807",
373+
"eu-central-1": "763104351884",
374+
"eu-north-1": "763104351884",
375+
"eu-west-1": "763104351884",
376+
"eu-west-2": "763104351884",
377+
"eu-west-3": "763104351884",
378+
"eu-south-1": "692866216735",
379+
"me-south-1": "217643126080",
380+
"sa-east-1": "763104351884",
381+
"us-east-1": "763104351884",
382+
"us-east-2": "763104351884",
383+
"us-gov-west-1": "442386744353",
384+
"us-iso-east-1": "886529160074",
385+
"us-west-1": "763104351884",
386+
"us-west-2": "763104351884"
387+
},
388+
"repository": "pytorch-inference"
355389
}
356390
}
357391
},
@@ -369,7 +403,8 @@
369403
"1.4": "1.4.0",
370404
"1.5": "1.5.0",
371405
"1.6": "1.6.0",
372-
"1.7": "1.7.1"
406+
"1.7": "1.7.1",
407+
"1.8": "1.8.0"
373408
},
374409
"versions": {
375410
"0.4.0": {
@@ -667,6 +702,39 @@
667702
"us-west-2": "763104351884"
668703
},
669704
"repository": "pytorch-training"
705+
},
706+
"1.8.0": {
707+
"py_versions": [
708+
"py3",
709+
"py36"
710+
],
711+
"registries": {
712+
"af-south-1": "626614931356",
713+
"ap-east-1": "871362719292",
714+
"ap-northeast-1": "763104351884",
715+
"ap-northeast-2": "763104351884",
716+
"ap-south-1": "763104351884",
717+
"ap-southeast-1": "763104351884",
718+
"ap-southeast-2": "763104351884",
719+
"ca-central-1": "763104351884",
720+
"cn-north-1": "727897471807",
721+
"cn-northwest-1": "727897471807",
722+
"eu-central-1": "763104351884",
723+
"eu-north-1": "763104351884",
724+
"eu-west-1": "763104351884",
725+
"eu-west-2": "763104351884",
726+
"eu-west-3": "763104351884",
727+
"eu-south-1": "692866216735",
728+
"me-south-1": "217643126080",
729+
"sa-east-1": "763104351884",
730+
"us-east-1": "763104351884",
731+
"us-east-2": "763104351884",
732+
"us-gov-west-1": "442386744353",
733+
"us-iso-east-1": "886529160074",
734+
"us-west-1": "763104351884",
735+
"us-west-2": "763104351884"
736+
},
737+
"repository": "pytorch-training"
670738
}
671739
}
672740
}

src/sagemaker/predictor.py

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,17 @@ def __init__(
9191
self.sagemaker_session = sagemaker_session or Session()
9292
self.serializer = serializer
9393
self.deserializer = deserializer
94-
self._endpoint_config_name = self._get_endpoint_config_name()
95-
self._model_names = self._get_model_names()
94+
self._endpoint_config_name = None
95+
self._model_names = None
9696
self._context = None
9797

9898
def predict(
99-
self, data, initial_args=None, target_model=None, target_variant=None, inference_id=None
99+
self,
100+
data,
101+
initial_args=None,
102+
target_model=None,
103+
target_variant=None,
104+
inference_id=None,
100105
):
101106
"""Return the inference from the specified endpoint.
102107
@@ -138,7 +143,12 @@ def _handle_response(self, response):
138143
return self.deserializer.deserialize(response_body, content_type)
139144

140145
def _create_request_args(
141-
self, data, initial_args=None, target_model=None, target_variant=None, inference_id=None
146+
self,
147+
data,
148+
initial_args=None,
149+
target_model=None,
150+
target_variant=None,
151+
inference_id=None,
142152
):
143153
"""Placeholder docstring"""
144154
args = dict(initial_args) if initial_args else {}
@@ -223,24 +233,30 @@ def update_endpoint(
223233
associated with the endpoint.
224234
"""
225235
production_variants = None
236+
current_model_names = self._get_model_names()
226237

227238
if initial_instance_count or instance_type or accelerator_type or model_name:
228239
if instance_type is None or initial_instance_count is None:
229240
raise ValueError(
230241
"Missing initial_instance_count and/or instance_type. Provided values: "
231242
"initial_instance_count={}, instance_type={}, accelerator_type={}, "
232243
"model_name={}.".format(
233-
initial_instance_count, instance_type, accelerator_type, model_name
244+
initial_instance_count,
245+
instance_type,
246+
accelerator_type,
247+
model_name,
234248
)
235249
)
236250

237251
if model_name is None:
238-
if len(self._model_names) > 1:
252+
if len(current_model_names) > 1:
239253
raise ValueError(
240254
"Unable to choose a default model for a new EndpointConfig because "
241-
"the endpoint has multiple models: {}".format(", ".join(self._model_names))
255+
"the endpoint has multiple models: {}".format(
256+
", ".join(current_model_names)
257+
)
242258
)
243-
model_name = self._model_names[0]
259+
model_name = current_model_names[0]
244260
else:
245261
self._model_names = [model_name]
246262

@@ -252,9 +268,10 @@ def update_endpoint(
252268
)
253269
production_variants = [production_variant_config]
254270

255-
new_endpoint_config_name = name_from_base(self._endpoint_config_name)
271+
current_endpoint_config_name = self._get_endpoint_config_name()
272+
new_endpoint_config_name = name_from_base(current_endpoint_config_name)
256273
self.sagemaker_session.create_endpoint_config_from_existing(
257-
self._endpoint_config_name,
274+
current_endpoint_config_name,
258275
new_endpoint_config_name,
259276
new_tags=tags,
260277
new_kms_key=kms_key,
@@ -268,7 +285,8 @@ def update_endpoint(
268285

269286
def _delete_endpoint_config(self):
270287
"""Delete the Amazon SageMaker endpoint configuration"""
271-
self.sagemaker_session.delete_endpoint_config(self._endpoint_config_name)
288+
current_endpoint_config_name = self._get_endpoint_config_name()
289+
self.sagemaker_session.delete_endpoint_config(current_endpoint_config_name)
272290

273291
def delete_endpoint(self, delete_endpoint_config=True):
274292
"""Delete the Amazon SageMaker endpoint backing this predictor.
@@ -291,7 +309,8 @@ def delete_model(self):
291309
"""Deletes the Amazon SageMaker models backing this predictor."""
292310
request_failed = False
293311
failed_models = []
294-
for model_name in self._model_names:
312+
current_model_names = self._get_model_names()
313+
for model_name in current_model_names:
295314
try:
296315
self.sagemaker_session.delete_model(model_name)
297316
except Exception: # pylint: disable=broad-except
@@ -460,26 +479,33 @@ def endpoint_context(self):
460479
if len(contexts) != 0:
461480
# create endpoint context object
462481
self._context = EndpointContext.load(
463-
sagemaker_session=self.sagemaker_session, context_name=contexts[0].context_name
482+
sagemaker_session=self.sagemaker_session,
483+
context_name=contexts[0].context_name,
464484
)
465485

466486
return self._context
467487

468488
def _get_endpoint_config_name(self):
469489
"""Placeholder docstring"""
490+
if self._endpoint_config_name is not None:
491+
return self._endpoint_config_name
470492
endpoint_desc = self.sagemaker_session.sagemaker_client.describe_endpoint(
471493
EndpointName=self.endpoint_name
472494
)
473-
endpoint_config_name = endpoint_desc["EndpointConfigName"]
474-
return endpoint_config_name
495+
self._endpoint_config_name = endpoint_desc["EndpointConfigName"]
496+
return self._endpoint_config_name
475497

476498
def _get_model_names(self):
477499
"""Placeholder docstring"""
500+
if self._model_names is not None:
501+
return self._model_names
502+
current_endpoint_config_name = self._get_endpoint_config_name()
478503
endpoint_config = self.sagemaker_session.sagemaker_client.describe_endpoint_config(
479-
EndpointConfigName=self._endpoint_config_name
504+
EndpointConfigName=current_endpoint_config_name
480505
)
481506
production_variants = endpoint_config["ProductionVariants"]
482-
return [d["ModelName"] for d in production_variants]
507+
self._model_names = [d["ModelName"] for d in production_variants]
508+
return self._model_names
483509

484510
@property
485511
def content_type(self):

tests/integ/test_feature_store.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,7 @@ def create_table_ddl():
142142
" STORED AS\n"
143143
" INPUTFORMAT 'parquet.hive.DeprecatedParquetInputFormat'\n"
144144
" OUTPUTFORMAT 'parquet.hive.DeprecatedParquetOutputFormat'\n"
145-
"LOCATION 's3://sagemaker-test-featurestore-{region}-{account}"
146-
"/{account}/sagemaker/us-east-2/offline-store/{feature_group_name}'"
145+
"LOCATION '{resolved_output_s3_uri}'"
147146
)
148147

149148

@@ -191,6 +190,12 @@ def test_create_feature_store(
191190
)
192191
_wait_for_feature_group_create(feature_group)
193192

193+
resolved_output_s3_uri = (
194+
feature_group.describe()
195+
.get("OfflineStoreConfig")
196+
.get("S3StorageConfig")
197+
.get("ResolvedOutputS3Uri")
198+
)
194199
# Ingest data
195200
feature_group.put_record(record=record)
196201
ingestion_manager = feature_group.ingest(
@@ -225,6 +230,7 @@ def test_create_feature_store(
225230
feature_group_name=feature_group_name,
226231
region=feature_store_session.boto_session.region_name,
227232
account=feature_store_session.account_id(),
233+
resolved_output_s3_uri=resolved_output_s3_uri,
228234
)
229235
== feature_group.as_hive_ddl()
230236
)

tests/integ/test_mxnet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def _deploy_estimator_and_assert_instance_type(estimator, instance_type):
9797
try:
9898
predictor = estimator.deploy(1, instance_type)
9999

100-
model_name = predictor._model_names[0]
100+
model_name = predictor._get_model_names()[0]
101101
config_name = sagemaker_session.sagemaker_client.describe_endpoint(
102102
EndpointName=predictor.endpoint_name
103103
)["EndpointConfigName"]

tests/unit/sagemaker/feature_store/test_feature_store.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ def create_table_ddl():
6666
" STORED AS\n"
6767
" INPUTFORMAT 'parquet.hive.DeprecatedParquetInputFormat'\n"
6868
" OUTPUTFORMAT 'parquet.hive.DeprecatedParquetOutputFormat'\n"
69-
"LOCATION 's3://some-bucket"
70-
"/{account}/sagemaker/{region}/offline-store/{feature_group_name}'"
69+
"LOCATION 's3://resolved_output_s3_uri'"
7170
)
7271

7372

@@ -217,7 +216,12 @@ def test_as_hive_ddl_with_default_values(
217216
create_table_ddl, feature_group_dummy_definitions, sagemaker_session_mock
218217
):
219218
sagemaker_session_mock.describe_feature_group.return_value = {
220-
"OfflineStoreConfig": {"S3StorageConfig": {"S3Uri": "s3://some-bucket"}}
219+
"OfflineStoreConfig": {
220+
"S3StorageConfig": {
221+
"S3Uri": "s3://some-bucket",
222+
"ResolvedOutputS3Uri": "s3://resolved_output_s3_uri",
223+
}
224+
}
221225
}
222226
sagemaker_session_mock.account_id.return_value = "1234"
223227
sagemaker_session_mock.boto_session.region_name = "us-west-2"
@@ -238,7 +242,12 @@ def test_as_hive_ddl_with_default_values(
238242

239243
def test_as_hive_ddl(create_table_ddl, feature_group_dummy_definitions, sagemaker_session_mock):
240244
sagemaker_session_mock.describe_feature_group.return_value = {
241-
"OfflineStoreConfig": {"S3StorageConfig": {"S3Uri": "s3://some-bucket"}}
245+
"OfflineStoreConfig": {
246+
"S3StorageConfig": {
247+
"S3Uri": "s3://some-bucket",
248+
"ResolvedOutputS3Uri": "s3://resolved_output_s3_uri",
249+
}
250+
}
242251
}
243252
sagemaker_session_mock.account_id.return_value = "1234"
244253
sagemaker_session_mock.boto_session.region_name = "us-west-2"

tests/unit/test_fw_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ def test_validate_smdataparallel_args_not_raises():
634634
("ml.p3.16xlarge", "tensorflow", "2.4.1", "py3", smdataparallel_enabled),
635635
("ml.p3.16xlarge", "pytorch", "1.6.0", "py3", smdataparallel_enabled),
636636
("ml.p3.16xlarge", "pytorch", "1.7.1", "py3", smdataparallel_enabled),
637+
("ml.p3.16xlarge", "pytorch", "1.8.0", "py3", smdataparallel_enabled),
637638
]
638639
for instance_type, framework_name, framework_version, py_version, distribution in good_args:
639640
fw_utils._validate_smdataparallel_args(

tests/unit/test_predictor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def test_predict_call_pass_through():
6262
result = predictor.predict(data)
6363

6464
assert sagemaker_session.sagemaker_runtime_client.invoke_endpoint.called
65+
assert sagemaker_session.sagemaker_client.describe_endpoint.not_called
66+
assert sagemaker_session.sagemaker_client.describe_endpoint_config.not_called
6567

6668
expected_request_args = {
6769
"Accept": DEFAULT_ACCEPT,

0 commit comments

Comments
 (0)