<jupyter_start><jupyter_text>MNIST Training using PyTorch This notebook is built from [SageMaker's PyTorch MNIST example](https://github.com/aws/amazon-sagemaker-examples/tree/master/sagemaker-python-sdk/pytorch_mnist). The objective here is to show how one might use SageMaker's Python SDK to build models and iterate. SetupThese next cells are copied from the example notebook. If you want a more thorough explanation of what each one is doing, read through the original example.<jupyter_code>import sagemaker

sagemaker_session = sagemaker.Session()

bucket = sagemaker_session.default_bucket()
prefix = 'sagemaker/DEMO-pytorch-mnist'

role = sagemaker.get_execution_role()
from torchvision.datasets import MNIST
from torchvision import transforms

MNIST.mirrors = ["https://sagemaker-sample-files.s3.amazonaws.com/datasets/image/MNIST/"]

MNIST(
    'data',
    download=True,
    transform=transforms.Compose(
        [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]
    )
)
inputs = sagemaker_session.upload_data(path='data', bucket=bucket, key_prefix=prefix)
print('input spec (in this case, just an S3 path): {}'.format(inputs))
from sagemaker.pytorch import PyTorch

estimator = PyTorch(entry_point='mnist.py',
                    role=role,
                    py_version='py3',
                    framework_version='1.8.0',
                    instance_count=2,
                    instance_type='ml.c5.2xlarge',
                    hyperparameters={
                        'epochs': 1,
                        'backend': 'gloo'
                    })
estimator.fit({'training': inputs})
predictor = estimator.deploy(initial_instance_count=1, instance_type='ml.m4.xlarge')<jupyter_output>---------------!<jupyter_text>Let's save our endpoint name in a variable so we can use it later<jupyter_code>ENDPOINT_NAME = predictor.endpoint_name<jupyter_output><empty_output><jupyter_text>Improve Your ModelLet's say you deployed your model and it's doing very well. You've set up some monitoring and several internal services are calling your endpoint. Let's try to improve the model by training it for more epochs.<jupyter_code>second_estimator = PyTorch(entry_point='mnist.py',
                    role=role,
                    py_version='py3',
                    framework_version='1.8.0',
                    instance_count=2,
                    instance_type='ml.c5.2xlarge',
                    hyperparameters={
                        'epochs': 10,  # 10 epochs instead of 1
                        'backend': 'gloo'
                    })

second_estimator.fit({'training': inputs})<jupyter_output>2021-06-10 14:46:04 Starting - Starting the training job...
2021-06-10 14:46:28 Starting - Launching requested ML instancesProfilerReport-1623336364: InProgress
......
2021-06-10 14:47:28 Starting - Preparing the instances for training......
2021-06-10 14:48:28 Downloading - Downloading input data...
2021-06-10 14:48:58 Training - Downloading the training image..[35mbash: cannot set terminal process group (-1): Inappropriate ioctl for device[0m
[35mbash: no job control in this shell[0m
[35m2021-06-10 14:49:13,043 sagemaker-training-toolkit INFO     Imported framework sagemaker_pytorch_container.training[0m
[35m2021-06-10 14:49:13,045 sagemaker-training-toolkit INFO     No GPUs detected (normal if no gpus installed)[0m
[35m2021-06-10 14:49:13,053 sagemaker_pytorch_container.training INFO     Block until all host DNS lookups succeed.[0m
[34mbash: cannot set terminal process group (-1): Inappropriate ioctl for device[0m
[34mbash: no job control in this shell[0m
[34m2021-06-[...]<jupyter_text>We improved our model. Great! Deploying the Improved ModelWe want a way of deploying that model to the same endpoint we have set up before. It's normally a good idea to that instead of creating a new endpoint every time you train a new model, so we don't have to update our calling code.The [Using Estimators](https://sagemaker.readthedocs.io/en/stable/overview.htmlusing-estimators) section of the Python SDK documentation states that:> Additionally, it is possible to deploy a different endpoint configuration, which links to your model, to an already existing SageMaker endpoint. This can be done by specifying the existing endpoint name for the `endpoint_name` parameter along with the `update_endpoint` parameter as True within your `deploy()` call.Then it goes ahead and shows us a code example doing just that:```pythonmxnet_predictor = mxnet_estimator.deploy(initial_instance_count=1,                                         instance_type='ml.p2.xlarge',                                         update_endpoint=True,                                         endpoint_name='existing-endpoint')```So... Let's try it!<jupyter_code>second_estimator.deploy(
    initial_instance_count=1,
    instance_type='ml.m4.xlarge',
    update_endpoint=True,
    endpoint_name=ENDPOINT_NAME
)<jupyter_output>update_endpoint is a no-op in sagemaker>=2.
See: https://sagemaker.readthedocs.io/en/stable/v2.html for details.<jupyter_text>Weird... This error message says that we're using deprecated stuff. Surpring, since we read in the `stable` version of the documentation that this should work. Let's read the link the error message gave us to see how we can fix this.> The `update_endpoint` argument in `deploy()` methods for estimators and models is now a no-op. Please use `sagemaker.predictor.Predictor.update_endpoint()` instead.Ok, so now we gotta have a `Predictor` before we deploy our model to the existing endpoint. [This piece of documentation](https://sagemaker.readthedocs.io/en/stable/overview.htmlhow-do-i-make-predictions-against-an-existing-endpoint) (in the same page that proved to be out of date) tells us that we can instantiate a new predictor by passing the existing endpoint name. Let's try that then.<jupyter_code>from sagemaker.predictor import Predictor

existing_predictor = Predictor(ENDPOINT_NAME)<jupyter_output><empty_output><jupyter_text>And now we can look at the [`Predictor.update_endpoint` method documentation](https://sagemaker.readthedocs.io/en/stable/api/inference/predictors.htmlsagemaker.predictor.Predictor.update_endpoint) to figure out how to update our existing endpoint.Give yourself a minute to try to figure this out...So what did you get? To me it seems that the `model_name` parameter holds promise, right? We're updating the endpoint to point to a new model. But what's our trained model name? Maybe our `second_estimator` instance holds the answer.<jupyter_code>[m for m in dir(second_estimator) if 'model' in m]<jupyter_output><empty_output><jupyter_text>Ok... Maybe `model_uri`?<jupyter_code>print(second_estimator.model_uri)<jupyter_output>None<jupyter_text>That's not it... Our not-so-much trustworthy [Python SDK](https://sagemaker.readthedocs.io/en/stable/) documentation doesn't seem to have an answer for us. We could spend some time on this, but let's just skip to the fix.If you're new to SageMaker you might be surprised to discover that **you haven't created a SageMaker model yet**. We have trained a model and we have the model saved on S3, but no "Model object" has been created. Normally the `Estimator.deploy()` method creates a model for you (although the [method documentation](https://sagemaker.readthedocs.io/en/stable/api/training/estimators.htmlsagemaker.estimator.EstimatorBase.deploy) doesn't say anything about that). Since we're not using the `deploy()` method anymore, we gotta create the model manually using the `create_model` method:<jupyter_code>second_model = second_estimator.create_model()
print(f'🎉 Model name: {second_model.name} 🎉')<jupyter_output>🎉 Model name: pytorch-training-2021-06-10-17-08-22-677 🎉<jupyter_text>Finally! Let's finally try the `Predictor.update_endpoint` method.<jupyter_code>existing_predictor.update_endpoint(
    initial_instance_count=1,
    instance_type='ml.m4.xlarge',
    model_name=second_model.name
)<jupyter_output>---------------!<jupyter_text>Hurrah! 🎉 Cleaning up<jupyter_code>existing_predictor.delete_endpoint()
existing_predictor.delete_model()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09657626702662904, 'fraction_numerical': 0.027242606454779727, 'mean_word_length': 4.003069367710252, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 38, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '521126', 'license': 'MIT', 'n_tokens_mistral': 2587, 'n_tokens_neox': 2354, 'n_words': 751, 'path': 'python-sdk-intro/pytorch_mnist.ipynb', 'repo_name': 'getsensibill/MLOps-World-2021', 'size': '8149'}