This repository was archived by the owner on May 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 101
change: update MME Pre/Post-Processing model and script paths #153
Merged
Merged
Changes from 7 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
d0fce0b
fix: increasing max_retry for model availability check
ddbcfb3
Merge branch 'master' into master
chuyang-deng 8462353
adjust retries max number
8ee6c5f
Merge branch 'master' of github.com:ChuyangDeng/sagemaker-tensorflow-…
f3433a6
update tfs pre-post-processing file path and test
1602159
update readme
c229871
Merge branch 'master' into master
chuyang-deng aaf2c5c
Update test/integration/local/test_multi_model_endpoint.py
chuyang-deng 1ac1ed4
Update docker/build_artifacts/sagemaker/python_service.py
chuyang-deng 12433f3
fix: run sagemaker tests for PR build
27411cb
Merge branch 'master' of github.com:ChuyangDeng/sagemaker-tensorflow-…
667ae52
revert buildspec
18931ac
revert build and publish script
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
import os | ||
import subprocess | ||
import time | ||
import sys | ||
|
||
import falcon | ||
import requests | ||
|
@@ -27,10 +28,8 @@ | |
import tfs_utils | ||
|
||
SAGEMAKER_MULTI_MODEL_ENABLED = os.environ.get('SAGEMAKER_MULTI_MODEL', 'false').lower() == 'true' | ||
INFERENCE_SCRIPT_PATH = '/opt/ml/{}/code/inference.py'.format('models' | ||
if SAGEMAKER_MULTI_MODEL_ENABLED | ||
else 'model') | ||
PYTHON_PROCESSING_ENABLED = os.path.exists(INFERENCE_SCRIPT_PATH) | ||
INFERENCE_SCRIPT_PATH = '/opt/ml/model/code/inference.py' | ||
|
||
SAGEMAKER_BATCHING_ENABLED = os.environ.get('SAGEMAKER_TFS_ENABLE_BATCHING', 'false').lower() | ||
MODEL_CONFIG_FILE_PATH = '/sagemaker/model-config.cfg' | ||
TFS_GRPC_PORT = os.environ.get('TFS_GRPC_PORT') | ||
|
@@ -64,21 +63,24 @@ def __init__(self): | |
self._model_tfs_grpc_port = {} | ||
self._model_tfs_pid = {} | ||
self._tfs_ports = self._parse_sagemaker_port_range(SAGEMAKER_TFS_PORT_RANGE) | ||
# If Multi-Model mode is enabled, dependencies/handlers will be imported | ||
# during the _handle_load_model_post() | ||
self.model_handlers = {} | ||
else: | ||
self._tfs_grpc_port = TFS_GRPC_PORT | ||
self._tfs_rest_port = TFS_REST_PORT | ||
|
||
if os.path.exists(INFERENCE_SCRIPT_PATH): | ||
self._handler, self._input_handler, self._output_handler = self._import_handlers() | ||
self._handlers = self._make_handler(self._handler, | ||
self._input_handler, | ||
self._output_handler) | ||
else: | ||
self._handlers = default_handler | ||
|
||
self._tfs_enable_batching = SAGEMAKER_BATCHING_ENABLED == 'true' | ||
self._tfs_default_model_name = os.environ.get('TFS_DEFAULT_MODEL_NAME', "None") | ||
|
||
if PYTHON_PROCESSING_ENABLED: | ||
self._handler, self._input_handler, self._output_handler = self._import_handlers() | ||
self._handlers = self._make_handler(self._handler, | ||
self._input_handler, | ||
self._output_handler) | ||
else: | ||
self._handlers = default_handler | ||
|
||
def on_post(self, req, res, model_name=None): | ||
log.info(req.uri) | ||
if model_name or "invocations" in req.uri: | ||
|
@@ -129,6 +131,9 @@ def _handle_load_model_post(self, res, data): # noqa: C901 | |
# validate model files are in the specified base_path | ||
if self.validate_model_dir(base_path): | ||
try: | ||
# install custom dependencies, import handlers | ||
self._import_custom_modules(model_name) | ||
|
||
tfs_config = tfs_utils.create_tfs_config_individual_model(model_name, base_path) | ||
tfs_config_file = '/sagemaker/tfs-config/{}/model-config.cfg'.format(model_name) | ||
log.info('tensorflow serving model config: \n%s\n', tfs_config) | ||
|
@@ -197,6 +202,33 @@ def _handle_load_model_post(self, res, data): # noqa: C901 | |
model_name) | ||
}) | ||
|
||
def _import_custom_modules(self, model_name): | ||
inference_script_path = "/opt/ml/models/{}/model/code/inference.py".format(model_name) | ||
requirements_file_path = "/opt/ml/models/{}/model/code/requirements.txt".format(model_name) | ||
python_lib_path = "/opt/ml/models/{}/model/code/lib".format(model_name) | ||
|
||
if os.path.exists(requirements_file_path): | ||
log.info("pip install dependencies from requirements.txt") | ||
pip_install_cmd = "pip3 install -r {}".format(requirements_file_path) | ||
try: | ||
subprocess.check_call(pip_install_cmd.split()) | ||
except subprocess.CalledProcessError: | ||
log.error('failed to install required packages, exiting.') | ||
raise ChildProcessError('failed to install required packages.') | ||
|
||
if os.path.exists(python_lib_path): | ||
log.info("add Python code library path") | ||
sys.path.append(python_lib_path) | ||
|
||
if os.path.exists(inference_script_path): | ||
handler, input_handler, output_handler = self._import_handlers(model_name) | ||
model_handlers = self._make_handler(handler, | ||
input_handler, | ||
output_handler) | ||
chuyang-deng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.model_handlers[model_name] = model_handlers | ||
else: | ||
self.model_handlers[model_name] = default_handler | ||
|
||
def _cleanup_config_file(self, config_file): | ||
if os.path.exists(config_file): | ||
os.remove(config_file) | ||
|
@@ -249,16 +281,24 @@ def _handle_invocation_post(self, req, res, model_name=None): | |
|
||
try: | ||
res.status = falcon.HTTP_200 | ||
res.body, res.content_type = self._handlers(data, context) | ||
if SAGEMAKER_MULTI_MODEL_ENABLED: | ||
with lock(): | ||
handlers = self.model_handlers[model_name] | ||
res.body, res.content_type = handlers(data, context) | ||
else: | ||
res.body, res.content_type = self._handlers(data, context) | ||
except Exception as e: # pylint: disable=broad-except | ||
log.exception('exception handling request: {}'.format(e)) | ||
res.status = falcon.HTTP_500 | ||
res.body = json.dumps({ | ||
'error': str(e) | ||
}).encode('utf-8') # pylint: disable=E1101 | ||
|
||
def _import_handlers(self): | ||
spec = importlib.util.spec_from_file_location('inference', INFERENCE_SCRIPT_PATH) | ||
def _import_handlers(self, model_name=None): | ||
inference_script = INFERENCE_SCRIPT_PATH | ||
if model_name: | ||
inference_script = "/opt/ml/models/{}/model/code/inference.py".format(model_name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it might make more sense to pass this string to the function so that we don't have it duplicated across the code? |
||
spec = importlib.util.spec_from_file_location('inference', inference_script) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I have a separate PR to address the quotes issue: #149 |
||
inference = importlib.util.module_from_spec(spec) | ||
spec.loader.exec_module(inference) | ||
|
||
|
@@ -358,7 +398,6 @@ def validate_model_dir(self, model_path): | |
versions = [] | ||
for _, dirs, _ in os.walk(model_path): | ||
for dirname in dirs: | ||
log.info("dirname: {}".format(dirname)) | ||
if dirname.isdigit(): | ||
versions.append(dirname) | ||
return self.validate_model_versions(versions) | ||
|
@@ -383,7 +422,6 @@ def on_get(self, req, res): # pylint: disable=W0613 | |
|
||
class ServiceResources: | ||
def __init__(self): | ||
self._enable_python_processing = PYTHON_PROCESSING_ENABLED | ||
self._enable_model_manager = SAGEMAKER_MULTI_MODEL_ENABLED | ||
self._python_service_resource = PythonServiceResource() | ||
self._ping_resource = PingResource() | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it doesn't matter here, but for future reference: https://docs.python.org/3.7/library/shlex.html#shlex.split