{ "cells": [ { "cell_type": "markdown", "id": "4161eb1f", "metadata": {}, "source": [ "## Resnet50 TensorFlow Pre-Trained Model Deployment With SageMaker Migration Toolkit\n", "\n", "- [SageMaker Migration Toolkit]()\n", "- [Resnet50 Model]()\n", "- Notebook Setting: conda_tf2_p38 Kernel\n", "- If you would like to deploy the model without using the API to abstract boto3 calls check the following [example](https://github.com/RamVegiraju/SageMaker-Deployment/tree/master/Misc/resnet-tf-sme)." ] }, { "cell_type": "markdown", "id": "c9ea5c88", "metadata": {}, "source": [ "## API Setup\n", "\n", "1. Clone GitHub repo\n", "2. python setup.py bdist_wheel\n", "3. pip install dist/sagemaker_migration_toolkit-0.0.1-py3-none-any.whl\n", "4. sagemaker_migration-configure --module-name sagemaker_migration.configure\n", "\n", "Step 4 is where you provide your role for sagemaker execution, do this via the terminal in your notebook path. To get the role you are currently using you can also run ```sagemaker.get_execution_role()```." ] }, { "cell_type": "markdown", "id": "178343c9", "metadata": {}, "source": [ "Alternatively you can also install the package after cloning the repo with the following command in the notebook." ] }, { "cell_type": "code", "execution_count": 1, "id": "8eb1f553", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Looking in indexes: https://pypi.org/simple, https://pip.repos.neuron.amazonaws.com\n", "Processing ./sagemaker-migration-toolkit/dist/sagemaker_migration_toolkit-0.0.1-py3-none-any.whl\n", "Requirement already satisfied: boto3>=1.24.0 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker-migration-toolkit==0.0.1) (1.26.5)\n", "Requirement already satisfied: sagemaker>=2.92.1 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker-migration-toolkit==0.0.1) (2.116.0)\n", "Requirement already satisfied: s3transfer<0.7.0,>=0.6.0 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from boto3>=1.24.0->sagemaker-migration-toolkit==0.0.1) (0.6.0)\n", "Collecting botocore<1.30.0,>=1.29.5\n", " Downloading botocore-1.29.15-py3-none-any.whl (9.9 MB)\n", "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m9.9/9.9 MB\u001b[0m \u001b[31m80.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n", "\u001b[?25hRequirement already satisfied: jmespath<2.0.0,>=0.7.1 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from boto3>=1.24.0->sagemaker-migration-toolkit==0.0.1) (0.10.0)\n", "Requirement already satisfied: google-pasta in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (0.2.0)\n", "Requirement already satisfied: importlib-metadata<5.0,>=1.4.0 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (4.8.2)\n", "Requirement already satisfied: pandas in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (1.3.4)\n", "Requirement already satisfied: protobuf<4.0,>=3.1 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (3.19.1)\n", "Requirement already satisfied: attrs<23,>=20.3.0 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (21.2.0)\n", "Requirement already satisfied: pathos in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (0.2.8)\n", "Requirement already satisfied: schema in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (0.7.5)\n", "Requirement already satisfied: numpy<2.0,>=1.9.0 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (1.20.3)\n", "Requirement already satisfied: protobuf3-to-dict<1.0,>=0.1.5 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (0.1.5)\n", "Requirement already satisfied: smdebug-rulesconfig==1.0.1 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (1.0.1)\n", "Requirement already satisfied: packaging>=20.0 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (21.0)\n", "Requirement already satisfied: urllib3<1.27,>=1.25.4 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from botocore<1.30.0,>=1.29.5->boto3>=1.24.0->sagemaker-migration-toolkit==0.0.1) (1.26.8)\n", "Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from botocore<1.30.0,>=1.29.5->boto3>=1.24.0->sagemaker-migration-toolkit==0.0.1) (2.8.2)\n", "Requirement already satisfied: zipp>=0.5 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from importlib-metadata<5.0,>=1.4.0->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (3.6.0)\n", "Requirement already satisfied: pyparsing>=2.0.2 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from packaging>=20.0->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (3.0.6)\n", "Requirement already satisfied: six in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from protobuf3-to-dict<1.0,>=0.1.5->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (1.16.0)\n", "Requirement already satisfied: pytz>=2017.3 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from pandas->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (2021.3)\n", "Requirement already satisfied: pox>=0.3.0 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from pathos->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (0.3.0)\n", "Requirement already satisfied: ppft>=1.6.6.4 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from pathos->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (1.6.6.4)\n", "Requirement already satisfied: multiprocess>=0.70.12 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from pathos->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (0.70.12.2)\n", "Requirement already satisfied: dill>=0.3.4 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from pathos->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (0.3.4)\n", "Requirement already satisfied: contextlib2>=0.5.5 in /home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages (from schema->sagemaker>=2.92.1->sagemaker-migration-toolkit==0.0.1) (21.6.0)\n", "Installing collected packages: botocore, sagemaker-migration-toolkit\n", " Attempting uninstall: botocore\n", " Found existing installation: botocore 1.24.19\n", " Uninstalling botocore-1.24.19:\n", " Successfully uninstalled botocore-1.24.19\n", "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", "awscli 1.27.5 requires botocore==1.29.5, but you have botocore 1.29.15 which is incompatible.\n", "aiobotocore 2.0.1 requires botocore<1.22.9,>=1.22.8, but you have botocore 1.29.15 which is incompatible.\u001b[0m\u001b[31m\n", "\u001b[0mSuccessfully installed botocore-1.29.15 sagemaker-migration-toolkit-0.0.1\n", "\u001b[33mWARNING: You are using pip version 22.0.4; however, version 22.3.1 is available.\n", "You should consider upgrading via the '/home/ec2-user/anaconda3/envs/tensorflow2_p38/bin/python -m pip install --upgrade pip' command.\u001b[0m\u001b[33m\n", "\u001b[0m" ] } ], "source": [ "!pip install sagemaker-migration-toolkit/dist/sagemaker_migration_toolkit-0.0.1-py3-none-any.whl" ] }, { "cell_type": "markdown", "id": "3eba2b28", "metadata": {}, "source": [ "## Resnet50 Model Artifacts\n", "\n", "The API expects the model data to be serialized in the format compliant with the framework's serving stack. For [TensorFlow Serving](https://towardsdatascience.com/hosting-models-with-tf-serving-on-docker-aceff9fbf533) it should be the following structure.\n", "\n", "```\n", "model_artifact_version/\n", " assets\n", " variables/\n", " variables.data\n", " variables.index\n", " keras_metadata.pb\n", " saved_model.pb \n", "inference.py\n", "```\n", "\n", "For our example we don't need an inference.py, but this is mandatory for the package so we will make a dummy script. In the next few cells we will create the model artifacts for the pre-trained resnet50 model in the structure TensorFlow Serving expects." ] }, { "cell_type": "code", "execution_count": 2, "id": "efd7c8d9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'arn:aws:iam::474422712127:role/sagemaker-role-BYOC'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", "import tensorflow as tf\n", "from tensorflow.keras.applications import resnet50\n", "from tensorflow.keras import backend\n", "import numpy as np\n", "from tensorflow.keras.preprocessing import image\n", "import sagemaker\n", "role = sagemaker.get_execution_role()\n", "role #pass this role in step 4 of API setup" ] }, { "cell_type": "code", "execution_count": 3, "id": "b2a3c066", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages/keras/backend.py:414: UserWarning: `tf.keras.backend.set_learning_phase` is deprecated and will be removed after 2020-10-11. To update it, simply pass a True/False value to the `training` argument of the `__call__` method of your layer or model.\n", " warnings.warn('`tf.keras.backend.set_learning_phase` is deprecated and '\n", "2022-11-22 20:41:03.270035: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected\n", "2022-11-22 20:41:03.270098: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:156] kernel driver does not appear to be running on this host (ip-172-16-96-163.ec2.internal): /proc/driver/nvidia/version does not exist\n", "2022-11-22 20:41:03.270842: I tensorflow/core/platform/cpu_feature_guard.cc:151] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations: AVX2 AVX512F FMA\n", "To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/resnet/resnet50_weights_tf_dim_ordering_tf_kernels.h5\n", "102973440/102967424 [==============================] - 0s 0us/step\n", "102981632/102967424 [==============================] - 0s 0us/step\n" ] } ], "source": [ "tf.keras.backend.set_learning_phase(0)\n", "model = resnet50.ResNet50()" ] }, { "cell_type": "code", "execution_count": 4, "id": "af449863", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Directory 00002 Created \n", "WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2022-11-22 20:41:20.617096: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Assets written to: 00002/assets\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages/keras/engine/functional.py:1410: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.\n", " layer_config = serialize_layer_fn(layer)\n", "/home/ec2-user/anaconda3/envs/tensorflow2_p38/lib/python3.8/site-packages/keras/saving/saved_model/layer_serialization.py:112: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.\n", " return generic_utils.serialize_keras_object(obj)\n" ] } ], "source": [ "export_dir = \"00002\" #directory to store model artifacts\n", "model = tf.keras.applications.ResNet50()\n", "\n", "if not os.path.exists(export_dir):\n", " os.makedirs(export_dir)\n", " print(\"Directory \", export_dir, \" Created \")\n", "else:\n", " print(\"Directory \", export_dir, \" already exists\")\n", "# Save to SavedModel\n", "model.save(export_dir, save_format=\"tf\", include_optimizer=False)" ] }, { "cell_type": "markdown", "id": "90c4bb35", "metadata": {}, "source": [ "Here we will create a dummy inference.py script, you should replace this with your actual pre and post processing as necessary" ] }, { "cell_type": "code", "execution_count": 5, "id": "88987147", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Writing inference.py\n" ] } ], "source": [ "%%writefile inference.py\n", "import os\n", "import json" ] }, { "cell_type": "markdown", "id": "105fa447", "metadata": {}, "source": [ "## SageMaker Migration Toolkit Deployment" ] }, { "cell_type": "code", "execution_count": 6, "id": "b97e2f17", "metadata": {}, "outputs": [], "source": [ "from sagemaker_migration import frameworks as fwk\n", "import os" ] }, { "cell_type": "code", "execution_count": 7, "id": "836f6d03", "metadata": {}, "outputs": [], "source": [ "# Create a TensorFlow Model Object Entity, you can create a real-time or serverless endpoint\n", "tf_model = fwk.TensorFlowModel(\n", " version = '2.3.0',\n", " model_data = '00002',\n", " inference_option = 'real-time',\n", " inference = 'inference.py',\n", " instance_type = 'ml.m5.xlarge')" ] }, { "cell_type": "code", "execution_count": 8, "id": "0f36b6d2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model data contains: 00002\n", "['assets', 'keras_metadata.pb', 'variables', 'saved_model.pb']\n", "00002\n", "inference.py\n", "model.tar.gz\n", "Uploading model.tar.gz to S3...\n", "Creating model in SageMaker...\n", "Created model: sm-model-tensorflow-2022-11-22-20-42-30\n", "Creating endpoint config in SageMaker...\n", "Created endpoint config: sm-endpoint-config-tensorflow-2022-11-22-20-42-30\n", "Creating endpoint in SageMaker...\n", "Endpoint creation in process: sm-endpoint-tensorflow-2022-11-22-20-42-30\n", "Monitoring endpoint creation...\n", "Creating\n", "Creating\n", "Creating\n", "Creating\n", "Creating\n", "Creating\n", "Creating\n", "InService\n" ] }, { "data": { "text/plain": [ "{'EndpointName': 'sm-endpoint-tensorflow-2022-11-22-20-42-30',\n", " 'EndpointArn': 'arn:aws:sagemaker:us-east-1:474422712127:endpoint/sm-endpoint-tensorflow-2022-11-22-20-42-30',\n", " 'EndpointConfigName': 'sm-endpoint-config-tensorflow-2022-11-22-20-42-30',\n", " 'ProductionVariants': [{'VariantName': 'primaryvariant',\n", " 'DeployedImages': [{'SpecifiedImage': '763104351884.dkr.ecr.us-east-1.amazonaws.com/tensorflow-inference:2.3.0-cpu',\n", " 'ResolvedImage': '763104351884.dkr.ecr.us-east-1.amazonaws.com/tensorflow-inference@sha256:91ebb7428846c5f7b515d5d9b8389a14c73d0c5d02657f4a6413592124333278',\n", " 'ResolutionTime': datetime.datetime(2022, 11, 22, 20, 42, 31, 650000, tzinfo=tzlocal())}],\n", " 'CurrentWeight': 1.0,\n", " 'DesiredWeight': 1.0,\n", " 'CurrentInstanceCount': 1,\n", " 'DesiredInstanceCount': 1}],\n", " 'EndpointStatus': 'InService',\n", " 'CreationTime': datetime.datetime(2022, 11, 22, 20, 42, 31, 147000, tzinfo=tzlocal()),\n", " 'LastModifiedTime': datetime.datetime(2022, 11, 22, 20, 44, 9, 1000, tzinfo=tzlocal()),\n", " 'ResponseMetadata': {'RequestId': '107c4e22-7577-40ac-bfa3-3aabdb5042bc',\n", " 'HTTPStatusCode': 200,\n", " 'HTTPHeaders': {'x-amzn-requestid': '107c4e22-7577-40ac-bfa3-3aabdb5042bc',\n", " 'content-type': 'application/x-amz-json-1.1',\n", " 'content-length': '791',\n", " 'date': 'Tue, 22 Nov 2022 20:44:16 GMT'},\n", " 'RetryAttempts': 0}}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tf_model.deploy_to_sagemaker()" ] }, { "cell_type": "markdown", "id": "9e2aa879", "metadata": {}, "source": [ "## Inference" ] }, { "cell_type": "code", "execution_count": 13, "id": "8d92957a", "metadata": {}, "outputs": [], "source": [ "import boto3\n", "#runtime client for inference\n", "runtime = boto3.client(service_name=\"sagemaker-runtime\") #runtime boto3 client for inference\n", "#grab endpoint name\n", "endpoint_name = 'sm-endpoint-tensorflow-2022-11-22-20-42-30'" ] }, { "cell_type": "markdown", "id": "41fdd950", "metadata": {}, "source": [ "You can retrieve the data from the public Amazon datasets with the following curl command. I've also posted the same image in the repo incase you cannot retrieve.\n", "\n", "```\n", "file_name = 'kitten.jpg'\n", "!wget -q https://s3.amazonaws.com/model-server/inputs/kitten.jpg -O {file_name}\n", "```" ] }, { "cell_type": "code", "execution_count": 11, "id": "ce02c89f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "import numpy as np\n", "from tensorflow.keras.preprocessing import image\n", "\n", "# preprocess image\n", "img = image.load_img(\"kitten.jpg\", target_size=(224, 224))\n", "x = image.img_to_array(img)\n", "x = np.expand_dims(x, axis=0)\n", "x = resnet50.preprocess_input(x)\n", "print(type(x))" ] }, { "cell_type": "code", "execution_count": 12, "id": "f2ffe237", "metadata": {}, "outputs": [], "source": [ "import json\n", "payload = json.dumps(x.tolist())\n", "#payload" ] }, { "cell_type": "code", "execution_count": 15, "id": "4e92c9d7", "metadata": {}, "outputs": [], "source": [ "response = runtime.invoke_endpoint(EndpointName=endpoint_name,\n", " ContentType='application/json',\n", " Body=payload)\n", "result = json.loads(response['Body'].read().decode())\n", "#result" ] } ], "metadata": { "kernelspec": { "display_name": "conda_tensorflow2_p38", "language": "python", "name": "conda_tensorflow2_p38" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.12" } }, "nbformat": 4, "nbformat_minor": 5 }