{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# SageMaker Edge Manager Example" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "This notebook's CI test result for us-west-2 is as follows. CI test results in other regions can be found at the end of the notebook. \n", "\n", "![This us-west-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-west-2/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction\n", "\n", "SageMaker Edge Manager is a service from Amazon SageMaker that lets you:\n", "\n", "+ prepare custom models for edge device hardware\n", "+ include a runtime for running machine learning inference efficiently on edge devices\n", "+ enable the device to send samples of data from each model securely to SageMaker for relabeling and retraining.\n", "\n", "There are two main components to this service:\n", "+ SageMaker Edge Manager in the Cloud \n", "+ SageMaker Edge Agent on the Edge device\n", "\n", "This notebook demonstrates the end-to-end workflow for getting a running SageMaker Edge Agent on the edge device. This will involve the following steps:\n", "\n", "+ Compile the model using SageMaker Neo\n", "+ Package the compiled model with SageMaker Edge Manager\n", "+ Deploy with SageMaker Edge Manager Agent\n", "+ Run inference with the model\n", "+ Capture the model's input and output data to S3\n", "\n", "**Note**:\n", "Typically, the SageMaker Edge Agent is run on an edge device. For the sake of this notebook, we will run the Agent on an EC2 instance. We show how to package the compiled model and then load it to the Agent on the edge Device to make predictions with. Finally, we show how to capture model's input and output to S3 via the Agent.\n", "\n", "This notebook is intended only for notebook instances. When you run this notebook, choose the kernel: `conda_tensorflow2_p36`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Please note**: There are pricing implications to the use of this notebook. Please refer to [Edge Manager](https://aws.amazon.com/sagemaker/edge-manager/pricing) for more information." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Demo Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need an AWS account role with SageMaker access. This role is used to give SageMaker access to S3, launch an EC2 instance and send command with Systems Manager." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sagemaker\n", "from sagemaker import get_execution_role\n", "import boto3\n", "import botocore\n", "import json\n", "\n", "role = get_execution_role()\n", "sess = sagemaker.Session()\n", "region = boto3.Session().region_name" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(role)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Locate the above printed sagemaker role from [IAM console](https://console.aws.amazon.com/iam), find and attach the following policies to role:\n", "\n", "- AmazonEC2FullAccess \n", "- AmazonEC2RoleforSSM \n", "- AmazonS3FullAccess \n", "- AmazonSSMManagedInstanceCore \n", "- AmazonSSMFullAccess \n", "- AWSIoTFullAccess \n", "\n", "You can find more information about how to attach policies to role [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html#add-policies-console).\n", "\n", "**If you try this example with a real device, only attach AWSIoTFullAccess to create certificates on AWS IoT.**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then need an S3 bucket that would be used for storing the model artifacts generated after compilation and packaged artifacts generated after edge packaging job." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# S3 bucket and folders for saving model artifacts.\n", "# Feel free to specify different bucket/folders here if you wish.\n", "bucket = sess.default_bucket()\n", "folder = \"DEMO-Sagemaker-Edge\"\n", "compilation_output_sub_folder = folder + \"/compilation-output\"\n", "iot_folder = folder + \"/iot\"\n", "\n", "# S3 Location to save the model artifact after compilation\n", "s3_compilation_output_location = \"s3://{}/{}\".format(bucket, compilation_output_sub_folder)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we upload the test image to S3 bucket. This image will be used in inference later." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "img_path = sess.upload_data(\"mobilenet_tf2.bmp\", bucket, iot_folder)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Launch EC2 Instance" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As mentioned earlier, this EC2 instance is used in place of an edge device for running the agent software." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ec2_client = boto3.client(\"ec2\", region_name=region)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Generate a key pair for the EC2 instance, and save the key PEM file. We can use this key with SSH to connect to the instance. But in this notebook example, we will not use SSH, instead, we will use AWS Systems Manager to send commands to the instance instead." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import datetime\n", "\n", "ec2_key_name = \"edge-manager-key-\" + str(datetime.datetime.now())\n", "ec2_key_pair = ec2_client.create_key_pair(\n", " KeyName=ec2_key_name,\n", ")\n", "\n", "key_pair = str(ec2_key_pair[\"KeyMaterial\"])\n", "key_pair_file = open(\"ec2-key-pair.pem\", \"w\")\n", "key_pair_file.write(key_pair)\n", "key_pair_file.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a new IAM role for the EC2 instance. Read for detailed information about [IAM roles for Amazon EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html).\n", "\n", "Follow steps here to [create an IAM role](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#create-iam-role). Note down the role name and role ARN; this role name will be used later when we launch the EC2 instance, and will be needed to create inline policy.\n", "\n", "After creation, make sure the following policies are attached to role:\n", "\n", "- AmazonS3FullAccess \n", "- AmazonSSMManagedInstanceCore \n", "- CloudWatchAgentAdminPolicy \n", "\n", "Refer [Adding and removing IAM identity permissions](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html) for details on adding policies.\n", "\n", "Locate the same SageMaker role used for running this notebook in [Demo Setup](#Demo-Setup) in [IAM console](https://console.aws.amazon.com/iam), on the role summary page under `Permissions` tab select the `Add permissions` drop down option and click on `Create inline policy` button , choose JSON format and replace the content with below statement:\n", "\n", "Before copying the following content, make sure you use the EC2 role ARN you just created in the `Resource` field for `iam:PassRole` action.\n", "\n", "```\n", "{\n", " \"Version\": \"2012-10-17\",\n", " \"Statement\": [\n", " {\n", " \"Effect\": \"Allow\",\n", " \"Action\": \"iam:PassRole\",\n", " \"Resource\": \"arn:aws:iam:::role/\"\n", " }\n", " ]\n", "}\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Launch an EC2 C5 instance. In this example we will use an AWS deep learning AMI." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ami = ec2_client.describe_images(\n", " Filters=[{\"Name\": \"name\", \"Values\": [\"Deep Learning AMI (Ubuntu 18.04) Version 36.0\"]}]\n", ")[\"Images\"][0][\"ImageId\"]\n", "ami" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ec2_profile_name = \"\" # the name of the role created for EC2\n", "\n", "ec2_instance = ec2_client.run_instances(\n", " ImageId=ami,\n", " MinCount=1,\n", " MaxCount=1,\n", " InstanceType=\"c5.large\",\n", " KeyName=ec2_key_name,\n", " IamInstanceProfile={\n", " \"Name\": ec2_profile_name,\n", " },\n", " TagSpecifications=[\n", " {\n", " \"ResourceType\": \"instance\",\n", " \"Tags\": [{\"Key\": \"Name\", \"Value\": \"edge-manager-notebook\"}],\n", " }\n", " ],\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "instance_id = ec2_instance[\"Instances\"][0][\"InstanceId\"]\n", "print(instance_id)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Compile Model using SageMaker Neo\n", "\n", "Create a SageMaker client." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sagemaker_client = boto3.client(\"sagemaker\", region_name=region)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Download pretrained Mobilenet model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "import tensorflow as tf\n", "\n", "# Download pretrained model\n", "\n", "!wget \"https://tfhub.dev/google/imagenet/mobilenet_v1_100_224/classification/5?tf-hub-format=compressed\"\n", "os.rename(\"5?tf-hub-format=compressed\", \"imagenet_mobilenet_v2_100_224_classification_5.tar.gz\")\n", "# unzip the compressed tar file to save_model folder to make it compatible with neo compiler\n", "!mkdir save_model\n", "!tar -zxvf imagenet_mobilenet_v2_100_224_classification_5.tar.gz -C save_model\n", "# Compress the tar file and save it in 'mobilenet_v2.tar.gz'\n", "!tar czvf mobilenet_v2.tar.gz save_model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_path = sess.upload_data(\"mobilenet_v2.tar.gz\", bucket, folder)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note**: When calling ``create_compilation_job()`` the user is expected to provide all the correct input shapes required by the model for successful compilation. If using a different model, you will need to specify the framework and data shape correctly." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_data_shape = '{\"inputs\":[1,224,224,3]}'\n", "model_framework = \"TENSORFLOW\"\n", "target_os = \"LINUX\"\n", "target_arch = \"X86_64\"\n", "framework_version = \"2.4\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import time\n", "\n", "compilation_job_name = \"Sagemaker-Edge-\" + str(time.time()).split(\".\")[0]\n", "print(\"Compilation job for %s started\" % compilation_job_name)\n", "\n", "response = sagemaker_client.create_compilation_job(\n", " CompilationJobName=compilation_job_name,\n", " RoleArn=role,\n", " InputConfig={\n", " \"S3Uri\": model_path,\n", " \"DataInputConfig\": model_data_shape,\n", " \"Framework\": model_framework,\n", " \"FrameworkVersion\": framework_version,\n", " },\n", " OutputConfig={\n", " \"S3OutputLocation\": s3_compilation_output_location,\n", " \"TargetPlatform\": {\"Arch\": target_arch, \"Os\": target_os},\n", " },\n", " StoppingCondition={\"MaxRuntimeInSeconds\": 900},\n", ")\n", "\n", "print(response)\n", "\n", "# Poll every 30 sec\n", "while True:\n", " response = sagemaker_client.describe_compilation_job(CompilationJobName=compilation_job_name)\n", " if response[\"CompilationJobStatus\"] == \"COMPLETED\":\n", " break\n", " elif response[\"CompilationJobStatus\"] == \"FAILED\":\n", " print(response)\n", " raise RuntimeError(\"Compilation failed\")\n", " print(\"Compiling ...\")\n", " time.sleep(30)\n", "print(\"Done!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Package Mobilenet Model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "packaged_model_name = \"mobilenet-model\"\n", "model_version = \"1.0\"\n", "model_package = \"{}-{}.tar.gz\".format(packaged_model_name, model_version)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "packaging_job_name = compilation_job_name + \"-packaging\"\n", "response = sagemaker_client.create_edge_packaging_job(\n", " RoleArn=role,\n", " OutputConfig={\n", " \"S3OutputLocation\": s3_compilation_output_location,\n", " },\n", " ModelName=packaged_model_name,\n", " ModelVersion=model_version,\n", " EdgePackagingJobName=packaging_job_name,\n", " CompilationJobName=compilation_job_name,\n", ")\n", "\n", "print(response)\n", "\n", "# Poll every 30 sec\n", "while True:\n", " job_status = sagemaker_client.describe_edge_packaging_job(\n", " EdgePackagingJobName=packaging_job_name\n", " )\n", " if job_status[\"EdgePackagingJobStatus\"] == \"COMPLETED\":\n", " break\n", " elif job_status[\"EdgePackagingJobStatus\"] == \"FAILED\":\n", " print(job_status)\n", " raise RuntimeError(\"Edge Packaging failed\")\n", " print(\"Packaging ...\")\n", " time.sleep(30)\n", "print(\"Done!\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "model_data = job_status[\"ModelArtifact\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create AWS IoT thing\n", "\n", "SageMaker Edge Manager uses AWS IoT Core to authenticate the device in order to make calls to SageMaker Edge Manager endpoints in AWS Cloud. \n", "\n", "In order for an edge device to use AWS services, it is necessary for it to first authenticate. We recommend doing this via AWS IoT based authentication, for more details refer [here](https://docs.aws.amazon.com/iot/latest/developerguide/authorizing-direct-aws.html) and [here](https://aws.amazon.com/blogs/security/how-to-eliminate-the-need-for-hardcoded-aws-credentials-in-devices-by-using-the-aws-iot-credentials-provider/)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_client = boto3.client(\"iot\", region_name=region)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_thing_name = \"sagemaker-edge-thing-demo\"\n", "iot_thing_type = \"SagemakerEdgeDemo\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_client.create_thing_type(thingTypeName=iot_thing_type)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_client.create_thing(thingName=iot_thing_name, thingTypeName=iot_thing_type)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create Device Fleet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Create IAM role for device fleet" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Configure an IAM role in your AWS account that will be assumed by the credentials' provider on behalf of the devices in your device fleet. \n", "\n", "**Notice**: The name of the role must start with `SageMaker`.\n", "\n", "Go to [IAM console](https://console.aws.amazon.com/iam), create role for IoT, attach the following policies:\n", "\n", "- AmazonSageMakerEdgeDeviceFleetPolicy\n", "\n", "Add the statement to trust relationship:\n", "```\n", "{\n", " \"Version\": \"2012-10-17\",\n", " \"Statement\": [\n", " {\n", " \"Effect\": \"Allow\",\n", " \"Principal\": {\"Service\": \"credentials.iot.amazonaws.com\"},\n", " \"Action\": \"sts:AssumeRole\"\n", " },\n", " {\n", " \"Effect\": \"Allow\",\n", " \"Principal\": {\"Service\": \"sagemaker.amazonaws.com\"},\n", " \"Action\": \"sts:AssumeRole\"\n", " }\n", " ]\n", "}\n", "```\n", "\n", "Note down the role ARN, it will be later used for creating the device fleet." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "device_fleet_name = \"demo-device-fleet\" + str(time.time()).split(\".\")[0]\n", "\n", "# This is the role you created following the instructions in the above cell\n", "device_fleet_role_arn = \":role/SageMaker*>\"\n", "\n", "sagemaker_client.create_device_fleet(\n", " DeviceFleetName=device_fleet_name,\n", " RoleArn=device_fleet_role_arn,\n", " OutputConfig={\"S3OutputLocation\": s3_compilation_output_location},\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Register device to the fleet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "device_name = (\n", " \"sagemaker-edge-demo-device\" + str(time.time()).split(\".\")[0]\n", ") # device name should be 36 charactors\n", "\n", "sagemaker_client.register_devices(\n", " DeviceFleetName=device_fleet_name,\n", " Devices=[\n", " {\n", " \"DeviceName\": device_name,\n", " \"IotThingName\": iot_thing_name,\n", " \"Description\": \"this is a sample virtual device\",\n", " }\n", " ],\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create and register client certificate with AWS IoT" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create private key, public key, and X.509 certificate files to register and activate the certificate with AWS IoT. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_cert = iot_client.create_keys_and_certificate(setAsActive=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Save the files and upload to S3 bucket, these files will be used to provide credentials on device to communicate with AWS services." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(\"./iot.pem.crt\", \"w\") as f:\n", " for line in iot_cert[\"certificatePem\"].split(\"\\n\"):\n", " f.write(line)\n", " f.write(\"\\n\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(\"./iot_key.pem.key\", \"w\") as f:\n", " for line in iot_cert[\"keyPair\"][\"PrivateKey\"].split(\"\\n\"):\n", " f.write(line)\n", " f.write(\"\\n\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "with open(\"./iot_key_pair.pem.key\", \"w\") as f:\n", " for line in iot_cert[\"keyPair\"][\"PublicKey\"].split(\"\\n\"):\n", " f.write(line)\n", " f.write(\"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Associate the role alias generated from `create_device_fleet()` with AWS IoT." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "role_alias_name = \"SageMakerEdge-\" + device_fleet_name\n", "\n", "role_alias = iot_client.describe_role_alias(roleAlias=role_alias_name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We created and registered a certificate with AWS IoT earlier for successful authentication of your device. Now, we need to create and attach a policy to the certificate to authorize the request for the security token." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "alias_policy = {\n", " \"Version\": \"2012-10-17\",\n", " \"Statement\": {\n", " \"Effect\": \"Allow\",\n", " \"Action\": \"iot:AssumeRoleWithCertificate\",\n", " \"Resource\": role_alias[\"roleAliasDescription\"][\"roleAliasArn\"],\n", " },\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "policy_name = \"aliaspolicy-\" + str(time.time()).split(\".\")[0]\n", "aliaspolicy = iot_client.create_policy(\n", " policyName=policy_name,\n", " policyDocument=json.dumps(alias_policy),\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_client.attach_policy(policyName=policy_name, target=iot_cert[\"certificateArn\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_endpoint = iot_client.describe_endpoint(endpointType=\"iot:CredentialProvider\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "endpoint = \"https://{}/role-aliases/{}/credentials\".format(\n", " iot_endpoint[\"endpointAddress\"], role_alias_name\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get official Amazon Root CA file and upload to S3 bucket. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!wget https://www.amazontrust.com/repository/AmazonRootCA1.pem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use the endpoint to make an HTTPS request to the credentials' provider to return a security token. The following example command uses curl, but you can use any HTTP client.\n", "\n", "**Optional: verify the credentials.**\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!curl --cert iot.pem.crt --key iot_key.pem.key --cacert AmazonRootCA1.pem $endpoint" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the certificate can be verified with the endpoint without error, upload certificate files to S3 bucket.\n", "\n", "These files will be used in the [Setup SageMaker Edge Manager Agent](#Setup-Sagemaker-Edge-Manager-Agent) section on EC2/device as Credential Provider." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "root_ca_path = sess.upload_data(\"AmazonRootCA1.pem\", bucket, iot_folder)\n", "device_cert_path = sess.upload_data(\"iot.pem.crt\", bucket, iot_folder)\n", "device_key_path = sess.upload_data(\"iot_key.pem.key\", bucket, iot_folder)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inference on the Edge" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In our example, we will use [AWS Systems Manager](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) to remotely perform actions on the EC2 instance. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client = boto3.client(\"ssm\", region_name=region)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To see the SSM logs from CloudWatch, refer to [Install CloudWatch Agent section](#(Optional)Install-CloudWatch-Agent). \n", "\n", "Execution status of send_command is available in [AWS Systems Manager console](https://console.aws.amazon.com/systems-manager/run-command/complete-commands) command history." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setup SageMaker Edge Agent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Download SageMaker Edge Agent binary examples to EC2 instance.\n", "\n", "Please fetch the latest version of binaries from SageMaker Edge release bucket. For more information about [Inference engine (Edge Manager agent)](https://docs.aws.amazon.com/sagemaker/latest/dg/edge-device-fleet-about.html)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "release_bucket_map = {\n", " \"linux-armv8\": \"sagemaker-edge-release-store-us-west-2-linux-armv8\",\n", " \"linux-x64\": \"sagemaker-edge-release-store-us-west-2-linux-x64\",\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# In this example, we will run inference on Linux\n", "release_bucket = release_bucket_map[\"linux-x64\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To download the artifacts, specify the `VERSION`. The `VERSION` is broken into three components: `.-`, where:\n", "\n", "- MAJOR_VERSION: The release version. The release version is currently set to 1.\n", "\n", "- ``: Time stamp of the artifacts release.\n", "\n", "- SHA-7: repository commit ID the release is built from.\n", "\n", "We suggest you use the latest artifact release time stamp. Use the following to get the latest time stamp." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!aws s3 ls s3://$release_bucket/Releases/ | sort -r" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# A version string from the above cell\n", "version = \"MAJOR_VERSION.YYYY-MM-DD.SHA-7\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"#!/bin/bash\",\n", " \"mkdir -p /demo\",\n", " \"aws s3 cp s3://{}/Releases/{}/{}.tgz demo.tgz\".format(\n", " release_bucket, version, version\n", " ),\n", " \"tar -xf demo.tgz -C /demo\",\n", " \"cd /demo/bin\",\n", " \"chmod +x sagemaker_edge_agent_binary\",\n", " \"chmod +x sagemaker_edge_agent_client_example\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=response[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Understanding AWS System manager Response**\n", "\n", "SSM client response contains fields which describe command execution status. For example, `'Status': 'InProgress'` would mean the command is still executing on the EC2 instance. If you come across `InProgress` status, you can rerun the codeblock to retrieve new response after waiting for a couple of seconds.\n", " \n", "`StandardOutputContent` and `StandardOutputUrl` provide more details on the command output. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Get model signing root certificates from the release bucket." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"#!/bin/bash\",\n", " \"cd /demo\",\n", " \"mkdir certificates\",\n", " \"aws s3 cp s3://{}/Certificates/{}/{}.pem certificates\".format(\n", " release_bucket, region, region\n", " ),\n", " \"chmod 400 certificates/*\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=response[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Download IoT certificates, private key, models, and test images to the EC2 instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"#!/bin/bash\",\n", " \"cd /demo\",\n", " \"mkdir iot-credentials\",\n", " \"cd iot-credentials\",\n", " \"aws s3 cp \" + root_ca_path + \" .\",\n", " \"aws s3 cp \" + device_cert_path + \" .\",\n", " \"aws s3 cp \" + device_key_path + \" .\",\n", " \"cd /demo\",\n", " \"aws s3 cp \" + img_path + \" .\",\n", " \"aws s3 cp \" + model_data + \" .\",\n", " \"mkdir model_path\",\n", " \"tar -xf \" + model_package + \" -C model_path\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=response[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Configure SageMaker Edge Agent\n", "\n", "Generate SageMaker Edge Agent configuration file. [For more information see the documentation](https://docs.aws.amazon.com/sagemaker/latest/dg/edge-device-fleet-about.html#edge-device-fleet-running-agent)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sagemaker_edge_config = {\n", " \"sagemaker_edge_core_device_name\": device_name,\n", " \"sagemaker_edge_core_device_fleet_name\": device_fleet_name,\n", " \"sagemaker_edge_core_capture_data_buffer_size\": 30,\n", " \"sagemaker_edge_core_capture_data_batch_size\": 10,\n", " \"sagemaker_edge_core_capture_data_push_period_seconds\": 4,\n", " \"sagemaker_edge_core_folder_prefix\": \"demo_capture\",\n", " \"sagemaker_edge_core_region\": region,\n", " \"sagemaker_edge_core_root_certs_path\": \"/demo/certificates\",\n", " \"sagemaker_edge_provider_aws_ca_cert_file\": \"/demo/iot-credentials/AmazonRootCA1.pem\",\n", " \"sagemaker_edge_provider_aws_cert_file\": \"/demo/iot-credentials/iot.pem.crt\",\n", " \"sagemaker_edge_provider_aws_cert_pk_file\": \"/demo/iot-credentials/iot_key.pem.key\",\n", " \"sagemaker_edge_provider_aws_iot_cred_endpoint\": endpoint,\n", " \"sagemaker_edge_provider_provider\": \"Aws\",\n", " \"sagemaker_edge_provider_provider_path\": \"/demo/lib/libprovider_aws.so\",\n", " \"sagemaker_edge_provider_s3_bucket_name\": bucket,\n", " \"sagemaker_edge_core_capture_data_destination\": \"Cloud\",\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "edge_config_file = open(\"sagemaker_edge_config.json\", \"w\")\n", "json.dump(sagemaker_edge_config, edge_config_file, indent=6)\n", "edge_config_file.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Upload SageMaker Edge Agent configuration to an S3 bucket." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "config_path = sess.upload_data(\"sagemaker_edge_config.json\", bucket, iot_folder)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Download the config file to the EC2 instance." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\"commands\": [\"#!/bin/bash\", \"aws s3 cp \" + config_path + \" /demo\"]},\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=response[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Launch SageMaker Edge Agent" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Initialize the SageMaker Edge Manager agent. Note that we are using a long timeout on this command. In production, we recommend running the agent as a daemon, using a service like systemd." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "agent_out = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " TimeoutSeconds=24 * 60 * 60,\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"cd /demo\",\n", " \"rm -f /tmp/sagemaker_edge_agent_example.sock\",\n", " \"./bin/sagemaker_edge_agent_binary -a /tmp/sagemaker_edge_agent_example.sock -c sagemaker_edge_config.json -f\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Load Model\n", "\n", "In this section, we show the model management capabilities offered by SageMaker Edge Manager. We will load the two compiled and packaged models using the Agent. This keeps both models ready to run inference with. As you will see, once the models are loaded you can run multiple inferences as many times as necessary until the models are unloaded. This reliefs the client applications from the logic and operational burden of managing them separately. These models are now simply an API away from running inference with.\n", "\n", "When loading the model with the SageMaker Edge Agent, the argument to the API points the Agent to a directory containing the packaged model (without any extraneous files within the directory). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Load Mobilenet model\n", "\n", "`model_path` is the path containing the packaged model in this notebook. `demo-model` is the name given to this model. This name will be used later to refer to this model for, making predictions, capturing data, unload." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import time\n", "\n", "load_model_out = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"cd /demo\",\n", " \"./bin/sagemaker_edge_agent_client_example LoadModel model_path demo-model\",\n", " ]\n", " },\n", ")\n", "time.sleep(2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=load_model_out[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### List Models\n", "\n", "This API simply lists all the models and their names that are loaded by the Agent. Note that the names shown here are same as the ones provided during the LoadModel in the previous sections." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "list_model_out = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\"commands\": [\"cd /demo\", \"./bin/sagemaker_edge_agent_client_example ListModels\"]},\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=list_model_out[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Run Predict\n", "\n", "In this API, we pass the model name, input data file that will be directly fed into the neural network, input tensor name that was passed earlier during the compilation phase, along with its size and shape." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Run prediction on Mobilenet model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "predict_out = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"cd /demo\",\n", " \"./bin/sagemaker_edge_agent_client_example Predict demo-model mobilenet_tf2.bmp inputs NHWC 1 224 224 3\",\n", " ]\n", " },\n", ")\n", "time.sleep(2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=predict_out[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Capture Data\n", "\n", "Capture the inputs and outputs of an inference call to cloud or disk. The specific parameters were configured earlier in the config file.\n", "\n", "**NOTE:** `sagemaker_edge_core_capture_data_batch_size` is set to `10`, this command needs to be run on 10 captures, before a batch of data is uploaded. Alternatively, before starting the agent, `sagemaker_edge_core_capture_data_batch_size` can be set to `1` for instant upload on capture." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "capture_out = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"cd /demo\",\n", " \"./bin/sagemaker_edge_agent_client_example PredictAndCapture demo-model mobilenet_tf2.bmp inputs NHWC 1 224 224 3\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=capture_out[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Unload Model\n", "\n", "After unloading a model, the same name can be reused for future `LoadModel` APIs calls." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "unload_model_out = ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"cd /demo\",\n", " \"./bin/sagemaker_edge_agent_client_example UnloadModel demo-model\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.get_command_invocation(\n", " CommandId=unload_model_out[\"Command\"][\"CommandId\"],\n", " InstanceId=instance_id,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Clean Up" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Stop the Agent" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.cancel_command(CommandId=agent_out[\"Command\"][\"CommandId\"], InstanceIds=[instance_id])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Stop the EC2 instance" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ec2_client.stop_instances(InstanceIds=[instance_id])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Detach and delete policy" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "iot_client.detach_policy(policyName=policy_name, target=iot_cert[\"certificateArn\"])\n", "\n", "iot_client.delete_policy(policyName=policy_name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Deregister device and delete device fleet" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sagemaker_client.deregister_devices(DeviceFleetName=device_fleet_name, DeviceNames=[device_name])\n", "\n", "sagemaker_client.delete_device_fleet(DeviceFleetName=device_fleet_name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Appendix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### (Optional)Install CloudWatch Agent " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "CW_log_config = {\n", " \"agent\": {\n", " \"metrics_collection_interval\": 10,\n", " \"logfile\": \"/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log\",\n", " },\n", " \"logs\": {\n", " \"logs_collected\": {\n", " \"files\": {\n", " \"collect_list\": [\n", " {\n", " \"file_path\": \"/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log\",\n", " \"log_group_name\": \"amazon-cloudwatch-agent.log\",\n", " \"log_stream_name\": \"amazon-cloudwatch-agent.log\",\n", " \"timezone\": \"UTC\",\n", " },\n", " {\n", " \"file_path\": \"/opt/aws/amazon-cloudwatch-agent/logs/test.log\",\n", " \"log_group_name\": \"test.log\",\n", " \"log_stream_name\": \"test.log\",\n", " \"timezone\": \"Local\",\n", " },\n", " ]\n", " }\n", " },\n", " \"log_stream_name\": \"my_log_stream_name\",\n", " \"force_flush_interval\": 15,\n", " },\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "CW_file = open(\"cloudwatch.json\", \"w\")\n", "json.dump(CW_log_config, CW_file, indent=6)\n", "CW_file.close()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "CW_config_path = sess.upload_data(\"cloudwatch.json\", bucket, iot_folder)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"#!/bin/bash\",\n", " \"aws s3 cp \"\n", " + CW_config_path\n", " + \" /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Parameters={\n", " \"commands\": [\n", " \"#!/bin/bash\",\n", " \"wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb\",\n", " \"sudo dpkg -i -E ./amazon-cloudwatch-agent.deb\",\n", " ]\n", " },\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Install Cloud Watch Agent to SSM agent." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ssm_client.send_command(\n", " DocumentName=\"AWS-ConfigureAWSPackage\",\n", " DocumentVersion=\"1\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " Targets=[\n", " {\"Key\": \"InstanceIds\", \"Values\": [instance_id]},\n", " ],\n", " TimeoutSeconds=600,\n", " Parameters={\"action\": [\"Install\"], \"name\": [\"AmazonCloudWatchAgent\"]},\n", " MaxConcurrency=\"50\",\n", " MaxErrors=\"0\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To debug with CloudWatch, add a parameter `CloudWatchOutputConfig` to `send_command`\n", "```\n", "CloudWatchOutputConfig={\n", " 'CloudWatchOutputEnabled': True\n", "}\n", "```\n", "\n", "Example:\n", "```\n", "ssm_client.send_command(\n", " InstanceIds=[instance_id],\n", " DocumentName=\"AWS-RunShellScript\",\n", " OutputS3BucketName=bucket,\n", " OutputS3KeyPrefix=folder,\n", " CloudWatchOutputConfig={\n", " 'CloudWatchOutputEnabled': True\n", " },\n", " Parameters={\n", " 'commands':[\n", " \"cd /demo\",\n", " \"./bin/neo_agent_binary -a /tmp/sagemaker_edge_agent_example.sock -c neo_config.json\" \n", " ]\n", " }\n", ")\n", "```\n", "\n", "Running log can be found in cloud watch log group `/aws/ssm/AWS-RunShellScript`" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Notebook CI Test Results\n", "\n", "This notebook was tested in multiple regions. The test results are as follows, except for us-west-2 which is shown at the top of the notebook.\n", "\n", "![This us-east-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-east-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This us-east-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-east-2/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This us-west-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-west-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This ca-central-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ca-central-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This sa-east-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/sa-east-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This eu-west-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-west-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This eu-west-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-west-2/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This eu-west-3 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-west-3/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This eu-central-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-central-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This eu-north-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-north-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This ap-southeast-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-southeast-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This ap-southeast-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-southeast-2/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This ap-northeast-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-northeast-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This ap-northeast-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-northeast-2/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n", "\n", "![This ap-south-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-south-1/sagemaker_edge_manager|sagemaker_edge_example|sagemaker_edge_example.ipynb)\n" ] } ], "metadata": { "instance_type": "ml.t3.medium", "kernelspec": { "display_name": "conda_amazonei_tensorflow2_p36", "language": "python", "name": "conda_amazonei_tensorflow2_p36" }, "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.6.13" } }, "nbformat": 4, "nbformat_minor": 4 }