Skip to content

Commit ce91758

Browse files
author
Prateek Chauhan
committed
feature: Add Framework Version support for PyTorch compilation (Neo)
1 parent b28bb31 commit ce91758

File tree

9 files changed

+1373
-8
lines changed

9 files changed

+1373
-8
lines changed

src/sagemaker/image_uri_config/neo-pytorch.json

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,73 @@
22
"processors": ["cpu", "gpu"],
33
"scope": ["inference"],
44
"version_aliases": {
5-
"0.4.0": "1.4.0",
6-
"1.0.0": "1.4.0",
7-
"1.1.0": "1.4.0",
8-
"1.2.0": "1.4.0",
9-
"1.3.0": "1.4.0"
5+
"0.4.0": "1.4",
6+
"1.0.0": "1.4",
7+
"1.1.0": "1.4",
8+
"1.2.0": "1.4",
9+
"1.3.0": "1.4",
10+
"1.4.0": "1.4"
1011
},
1112
"versions": {
12-
"1.4.0": {
13+
"1.4": {
14+
"py_versions": ["py3"],
15+
"registries": {
16+
"af-south-1": "774647643957",
17+
"ap-east-1": "110948597952",
18+
"ap-northeast-1": "941853720454",
19+
"ap-northeast-2": "151534178276",
20+
"ap-south-1": "763008648453",
21+
"ap-southeast-1": "324986816169",
22+
"ap-southeast-2": "355873309152",
23+
"ca-central-1": "464438896020",
24+
"cn-north-1": "472730292857",
25+
"cn-northwest-1": "474822919863",
26+
"eu-central-1": "746233611703",
27+
"eu-north-1": "601324751636",
28+
"eu-south-1": "966458181534",
29+
"eu-west-1": "802834080501",
30+
"eu-west-2": "205493899709",
31+
"eu-west-3": "254080097072",
32+
"me-south-1": "836785723513",
33+
"sa-east-1": "756306329178",
34+
"us-east-1": "785573368785",
35+
"us-east-2": "007439368137",
36+
"us-gov-west-1": "263933020539",
37+
"us-west-1": "710691900526",
38+
"us-west-2": "301217895009"
39+
},
40+
"repository": "sagemaker-inference-pytorch"
41+
},
42+
"1.5": {
43+
"py_versions": ["py3"],
44+
"registries": {
45+
"af-south-1": "774647643957",
46+
"ap-east-1": "110948597952",
47+
"ap-northeast-1": "941853720454",
48+
"ap-northeast-2": "151534178276",
49+
"ap-south-1": "763008648453",
50+
"ap-southeast-1": "324986816169",
51+
"ap-southeast-2": "355873309152",
52+
"ca-central-1": "464438896020",
53+
"cn-north-1": "472730292857",
54+
"cn-northwest-1": "474822919863",
55+
"eu-central-1": "746233611703",
56+
"eu-north-1": "601324751636",
57+
"eu-south-1": "966458181534",
58+
"eu-west-1": "802834080501",
59+
"eu-west-2": "205493899709",
60+
"eu-west-3": "254080097072",
61+
"me-south-1": "836785723513",
62+
"sa-east-1": "756306329178",
63+
"us-east-1": "785573368785",
64+
"us-east-2": "007439368137",
65+
"us-gov-west-1": "263933020539",
66+
"us-west-1": "710691900526",
67+
"us-west-2": "301217895009"
68+
},
69+
"repository": "sagemaker-inference-pytorch"
70+
},
71+
"1.6": {
1372
"py_versions": ["py3"],
1473
"registries": {
1574
"af-south-1": "774647643957",

src/sagemaker/model.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import json
1717
import logging
1818
import os
19+
import re
1920

2021
import sagemaker
2122
from sagemaker import (
@@ -36,6 +37,8 @@
3637
["mxnet", "tensorflow", "keras", "pytorch", "onnx", "xgboost", "tflite"]
3738
)
3839

40+
NEO_PYTORCH_DEFAULT_VERSION = "1.6"
41+
3942

4043
class Model(object):
4144
"""A SageMaker ``Model`` that can be deployed to an ``Endpoint``."""
@@ -398,6 +401,7 @@ def _compilation_job_config(
398401
target_platform_arch=None,
399402
target_platform_accelerator=None,
400403
compiler_options=None,
404+
framework_version=None,
401405
):
402406
"""Placeholder Docstring"""
403407
input_model_config = {
@@ -407,6 +411,16 @@ def _compilation_job_config(
407411
else input_shape,
408412
"Framework": framework.upper(),
409413
}
414+
415+
if (
416+
framework.lower() == "pytorch"
417+
and re.match("(?=^ml_)(?!ml_inf)", target_instance_type) is not None
418+
):
419+
if framework_version is not None:
420+
input_model_config["FrameworkVersion"] = utils.get_short_version(framework_version)
421+
else:
422+
input_model_config["FrameworkVersion"] = NEO_PYTORCH_DEFAULT_VERSION
423+
410424
role = self.sagemaker_session.expand_role(role)
411425
output_model_config = {
412426
"S3OutputLocation": output_path,
@@ -572,7 +586,8 @@ def compile(
572586
framework (str): The framework that is used to train the original
573587
model. Allowed values: 'mxnet', 'tensorflow', 'keras', 'pytorch',
574588
'onnx', 'xgboost'
575-
framework_version (str):
589+
framework_version (str):The version of framework, for example:
590+
'1.5' for PyTorch
576591
target_platform_os (str): Target Platform OS, for example: 'LINUX'.
577592
For allowed strings see
578593
https://docs.aws.amazon.com/sagemaker/latest/dg/API_OutputConfig.html.
@@ -613,6 +628,7 @@ def compile(
613628
framework_version = framework_version or self._get_framework_version()
614629

615630
self._init_sagemaker_session_if_does_not_exist(target_instance_family)
631+
616632
config = self._compilation_job_config(
617633
target_instance_family,
618634
input_shape,
@@ -626,13 +642,19 @@ def compile(
626642
target_platform_arch,
627643
target_platform_accelerator,
628644
compiler_options,
645+
framework_version,
629646
)
630647
self.sagemaker_session.compile_model(**config)
631648
job_status = self.sagemaker_session.wait_for_compilation_job(job_name)
632649
self.model_data = job_status["ModelArtifacts"]["S3ModelArtifacts"]
633-
634650
if target_instance_family is not None:
635651
if target_instance_family.startswith("ml_"):
652+
if (
653+
framework.lower() == "pytorch"
654+
and "ml_inf" not in target_instance_family
655+
and framework_version is None
656+
):
657+
framework_version = NEO_PYTORCH_DEFAULT_VERSION
636658
self.image_uri = self._compilation_image_uri(
637659
self.sagemaker_session.boto_region_name,
638660
target_instance_family,

tests/conftest.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,26 @@ def pytorch_eia_py_version():
182182
return "py3"
183183

184184

185+
@pytest.fixture(scope="module")
186+
def neo_pytorch_latest_py_version():
187+
return "py3"
188+
189+
190+
@pytest.fixture(scope="module")
191+
def neo_pytorch_compilation_job_name():
192+
return utils.name_from_base("pytorch-neo-model")
193+
194+
195+
@pytest.fixture(scope="module")
196+
def neo_pytorch_target_device():
197+
return "ml_c5"
198+
199+
200+
@pytest.fixture(scope="module")
201+
def neo_pytorch_cpu_instance_type():
202+
return "ml.c5.xlarge"
203+
204+
185205
@pytest.fixture(scope="module")
186206
def xgboost_framework_version(xgboost_version):
187207
if xgboost_version in ("1", "latest"):

tests/data/pytorch_neo/cat.jpg

114 KB
Loading
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import io
2+
import json
3+
import logging
4+
import os
5+
import pickle
6+
7+
import numpy as np
8+
import torch
9+
import neopytorch
10+
import torchvision.transforms as transforms
11+
from PIL import Image # Training container doesn't have this package
12+
13+
logger = logging.getLogger(__name__)
14+
logger.setLevel(logging.DEBUG)
15+
16+
17+
def transform_fn(model, payload, request_content_type, response_content_type):
18+
19+
logger.info("Invoking user-defined transform function")
20+
21+
if request_content_type != "application/octet-stream":
22+
raise RuntimeError(
23+
"Content type must be application/octet-stream. Provided: {0}".format(
24+
request_content_type
25+
)
26+
)
27+
28+
# preprocess
29+
decoded = Image.open(io.BytesIO(payload))
30+
preprocess = transforms.Compose(
31+
[
32+
transforms.Resize(256),
33+
transforms.CenterCrop(224),
34+
transforms.ToTensor(),
35+
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
36+
]
37+
)
38+
normalized = preprocess(decoded)
39+
batchified = normalized.unsqueeze(0)
40+
41+
# predict
42+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
43+
batchified = batchified.to(device)
44+
result = model.forward(batchified)
45+
46+
# Softmax (assumes batch size 1)
47+
result = np.squeeze(result.cpu().numpy())
48+
result_exp = np.exp(result - np.max(result))
49+
result = result_exp / np.sum(result_exp)
50+
51+
response_body = json.dumps(result.tolist())
52+
content_type = "application/json"
53+
54+
return response_body, content_type
55+
56+
57+
def model_fn(model_dir):
58+
59+
logger.info("model_fn")
60+
neopytorch.config(model_dir=model_dir, neo_runtime=True)
61+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
62+
# The compiled model is saved as "compiled.pt"
63+
model = torch.jit.load(os.path.join(model_dir, "compiled.pt"), map_location=device)
64+
65+
# It is recommended to run warm-up inference during model load
66+
sample_input_path = os.path.join(model_dir, "sample_input.pkl")
67+
with open(sample_input_path, "rb") as input_file:
68+
model_input = pickle.load(input_file)
69+
if torch.is_tensor(model_input):
70+
model_input = model_input.to(device)
71+
model(model_input)
72+
elif isinstance(model_input, tuple):
73+
model_input = (inp.to(device) for inp in model_input if torch.is_tensor(inp))
74+
model(*model_input)
75+
else:
76+
print("Only supports a torch tensor or a tuple of torch tensors")
77+
78+
return model

0 commit comments

Comments
 (0)