{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Create a Feature Store, use SageMaker Data wrangler for feature engineering and SageMaker Processing Job for Data Ingestion\n", "\n", "---\n", "\n", "#### Note: Please set kernel to Python 3 (Data Science) and select instance to ml.t3.medium\n", "\n", "
💡 Quick Start \n", "To save your processed data to feature store, \n", " Click here to create a feature group and follow the instruction to run a SageMaker processing job.\n", "\n", "
\n", "\n", "This notebook uses Amazon SageMaker Feature Store (Feature Store) to create a feature group, \n", "executes your Data Wrangler Flow `orders.flow` on the entire dataset using a SageMaker \n", "Processing Job and ingest processed data to Feature Store. \n", "\n", "---\n", "\n", "## Contents\n", "\n", "1. [Notebook Preparation](#Notebook-Preparation)\n", " 1. [Imports](#Imports)\n", " 1. [Check and update Sagemaker version](#Check-and-update-Sagemaker-version)\n", " 1. [Logging Settings](#Logging-Settings)\n", " 1. [Custom Functions](#Custom-Functions)\n", " 1. [Module Configurations](#Module-Configurations)\n", "1. [Data Preparation](#Data-Preparation)\n", "1. [Create Feature Group](#Create-Feature-Group)\n", " 1. [Define Feature Group](#Define-Feature-Group)\n", " 1. [Configure Feature Group](#Configure-Feature-Group)\n", " 1. [Initialize & Create Feature Group](#Initialize-&-Create-Feature-Group)\n", "1. [Creating a workflow using Data Wrangler (OPTIONAL)](#Creating-a-workflow-using-Data-Wrangler-(OPTIONAL))\n", "1. [Processing Job: Inputs and Outputs](#Inputs-and-Outputs)\n", "1. [Run Processing Job](#Run-Processing-Job)\n", " 1. [Job Configurations](#Job-Configurations)\n", " 1. [Create and Execute Processing Job](#Create-and-Execute-Processing-Job)\n", " 1. [Job Status](#Job-Status)\n", "1. [Verify Processing Job Results](#Verify-Processing-Job-Results)\n", "1. [Storing Variable Values](#Storing-Variable-Values)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Notebook Preparation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Imports" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [] }, "outputs": [], "source": [ "import sagemaker \n", "from sagemaker.feature_store.feature_group import FeatureGroup\n", "from sagemaker import get_execution_role\n", "from time import gmtime, strftime, sleep\n", "import pandas as pd\n", "import numpy as np\n", "import subprocess\n", "import sagemaker\n", "import logging\n", "import importlib\n", "import time\n", "import sys\n", "import uuid" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Check and update Sagemaker version" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [], "source": [ "if sagemaker.__version__ < '2.48.1':\n", " subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'sagemaker==2.48.1'])\n", " importlib.reload(sagemaker)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Logging Settings" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Using SageMaker version: 2.48.1\n", "Using Pandas version: 1.3.5\n" ] } ], "source": [ "logger = logging.getLogger('__name__')\n", "logger.setLevel(logging.DEBUG)\n", "logger.addHandler(logging.StreamHandler())\n", "logger.info(f'Using SageMaker version: {sagemaker.__version__}')\n", "logger.info(f'Using Pandas version: {pd.__version__}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Custom Functions" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "tags": [] }, "outputs": [], "source": [ "def get_container(region):\n", " registries = {\n", " \"af-south-1\": \"143210264188\",\n", " \"ap-east-1\": \"707077482487\",\n", " \"ap-northeast-1\": \"649008135260\",\n", " \"ap-northeast-2\": \"131546521161\",\n", " \"ap-south-1\": \"089933028263\",\n", " \"ap-southeast-1\": \"119527597002\",\n", " \"ap-southeast-2\": \"422173101802\",\n", " \"ca-central-1\": \"557239378090\",\n", " \"eu-central-1\": \"024640144536\",\n", " \"eu-north-1\": \"054986407534\",\n", " \"eu-south-1\": \"488287956546\",\n", " \"eu-west-1\": \"245179582081\",\n", " \"eu-west-2\": \"894491911112\",\n", " \"eu-west-3\": \"807237891255\",\n", " \"me-south-1\": \"376037874950\",\n", " \"sa-east-1\": \"424196993095\",\n", " \"us-east-1\": \"663277389841\",\n", " \"us-east-2\": \"415577184552\",\n", " \"us-west-1\": \"926135532090\",\n", " \"us-west-2\": \"174368400705\",\n", " \"cn-north-1\": \"245909111842\",\n", " \"cn-northwest-1\": \"249157047649\"\n", " }\n", " return registries[region]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Module Configurations \n", "##### (Sagemaker session, S3 Bucket and Folder settings, etc.)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "tags": [] }, "outputs": [], "source": [ "# You can configure this with your own bucket name, workshop prefix & folder path, etc.\n", "bucket = None\n", "fs_champions_workshop_prefix = None\n", "workshopfolder = None\n", "\n", "# Change this flag to 'True' if you want to create a new feature group with a new name for this module or else it will use the feature group name created in Module - m1_nb1_introduction_to_feature_store\n", "create_new_feature_group = False" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "SageMaker FSCW S3 bucket = sagemaker-us-west-2-227246955871\n", "workshopfolder = sagemaker-feature-store/fscw/\n", "Orders Feature Group Name: fscw-orders-03-30-17-02\n" ] } ], "source": [ "# Sagemaker session\n", "sess = sagemaker.Session()\n", "\n", "if bucket is None:\n", " bucket = sess.default_bucket()\n", "if fs_champions_workshop_prefix is None:\n", " fs_champions_workshop_prefix = \"fscw\"\n", "if workshopfolder is None:\n", " workshopfolder=f'sagemaker-feature-store/{fs_champions_workshop_prefix}/'\n", "bucketlocation=f's3://{bucket}/{workshopfolder}data/'\n", "\n", "logger.info(f'SageMaker FSCW S3 bucket = {bucket}')\n", "logger.info(f'workshopfolder = {workshopfolder}')\n", "# IAM role for executing the processing job.\n", "iam_role = sagemaker.get_execution_role()\n", "\n", "#Load persisted feature store name\n", "%store -r orders_feature_group_name\n", "logger.info(f'Orders Feature Group Name: {orders_feature_group_name}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data Preparation\n", "#### Upload the orders.csv raw file to the S3 bucket folder" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "tags": [] }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
order_idcustomer_idproduct_idpurchase_amountis_reorderedevent_timen_days_since_last_purchase
0O1C5731P160.91346512021-09-13T13:21:30.036Z0.122093
1O2C3541P128020.66316812021-09-13T13:21:30.036Z0.903101
2O3C7402P83200.62960412021-09-13T13:21:30.036Z0.054264
3O4C7356P180000.20277202021-09-13T13:21:30.036Z0.343023
4O5C5806P129400.05316812021-09-13T13:21:30.036Z0.242248
5O6C1570P80250.56316812021-09-13T13:21:30.036Z0.025194
6O7C9635P173430.18782202021-09-13T13:21:30.036Z0.837209
7O8C7971P183720.56386112021-09-13T13:21:30.036Z0.317829
8O9C8461P114220.71257412021-09-13T13:21:30.036Z0.418605
9O10C1702P30580.24435612021-09-13T13:21:30.036Z0.096899
\n", "
" ], "text/plain": [ " order_id customer_id product_id purchase_amount is_reordered \\\n", "0 O1 C5731 P16 0.913465 1 \n", "1 O2 C3541 P12802 0.663168 1 \n", "2 O3 C7402 P8320 0.629604 1 \n", "3 O4 C7356 P18000 0.202772 0 \n", "4 O5 C5806 P12940 0.053168 1 \n", "5 O6 C1570 P8025 0.563168 1 \n", "6 O7 C9635 P17343 0.187822 0 \n", "7 O8 C7971 P18372 0.563861 1 \n", "8 O9 C8461 P11422 0.712574 1 \n", "9 O10 C1702 P3058 0.244356 1 \n", "\n", " event_time n_days_since_last_purchase \n", "0 2021-09-13T13:21:30.036Z 0.122093 \n", "1 2021-09-13T13:21:30.036Z 0.903101 \n", "2 2021-09-13T13:21:30.036Z 0.054264 \n", "3 2021-09-13T13:21:30.036Z 0.343023 \n", "4 2021-09-13T13:21:30.036Z 0.242248 \n", "5 2021-09-13T13:21:30.036Z 0.025194 \n", "6 2021-09-13T13:21:30.036Z 0.837209 \n", "7 2021-09-13T13:21:30.036Z 0.317829 \n", "8 2021-09-13T13:21:30.036Z 0.418605 \n", "9 2021-09-13T13:21:30.036Z 0.096899 " ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", "import json\n", "import boto3\n", "\n", "orders_csv_file = 'orders.csv'\n", "orders_df = pd.read_csv(orders_csv_file)\n", "orders_df.head(10)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "orders_upload_file = sagemaker-feature-store/fscw/data/orders.csv\n" ] } ], "source": [ "# Upload orders.csv to S3\n", "s3_client = boto3.client(\"s3\")\n", "orders_upload_file=f'{workshopfolder}data/{orders_csv_file}'\n", "logger.info(f'orders_upload_file = {orders_upload_file}')\n", "s3_client.upload_file(orders_csv_file, bucket, orders_upload_file)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Data source: s3://sagemaker-us-west-2-227246955871/sagemaker-feature-store/fscw/data/\n" ] } ], "source": [ "print(f\"Data source: {f'{bucketlocation}'}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create Feature Group\n", "\n", "_What is a feature group_\n", "\n", "A single feature corresponds to a column in your dataset. A feature group is a predefined schema for a \n", "collection of features - each feature in the feature group has a specified data type and name. \n", "A single record in a feature group corresponds to a row in your dataframe. A feature store is a \n", "collection of feature groups. To learn more about SageMaker Feature Store, see \n", "[Amazon Feature Store Documentation](http://docs.aws.amazon.com/sagemaker/latest/dg/feature-store.html).\n", "\n", "### Define Feature Group\n", "Select Record identifier and Event time feature name. These are required parameters for feature group\n", "creation.\n", "* **Record identifier name** is the name of the feature defined in the feature group's feature definitions \n", "whose value uniquely identifies a Record defined in the feature group's feature definitions.\n", "* **Event time feature name** is the name of the EventTime feature of a Record in FeatureGroup. An EventTime \n", "is a timestamp that represents the point in time when a new event occurs that corresponds to the creation or \n", "update of a Record in the FeatureGroup. All Records in the FeatureGroup must have a corresponding EventTime.\n", "\n", "
💡Record identifier and Event time feature name are required \n", "for feature group. After filling in the values, you can choose Run Selected Cell and All Below \n", "from the Run Menu from the menu bar. \n", "
" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "tags": [] }, "outputs": [], "source": [ "record_identifier_feature_name = 'order_id'\n", "if record_identifier_feature_name is None:\n", " raise SystemExit(\"Select a column name as the feature group record identifier.\")\n", "\n", "event_time_feature_name = 'event_time'\n", "if event_time_feature_name is None:\n", " raise SystemExit(\"Select a column name as the event time feature name.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Feature Definitions\n", "The following is a list of the feature names and feature types of the final dataset that will be produced \n", "when your data flow is used to process your input dataset. These are automatically generated from the \n", "from `Source: Orders.csv` schema. To save from a different step, go to Data Wrangler to \n", "select a new step to export.\n", "\n", "
💡 Configurable Settings \n", "\n", "1. You can select a subset of the features. By default all columns of the result dataframe will be used as \n", "features.\n", "2. You can change the Data Wrangler data type to one of the Feature Store supported types \n", "(Integral, Fractional, or String). The default type is set to String. \n", "This means that, if a column in your dataset is not a float or long type, it will default \n", "to String in your Feature Store.\n", "\n", "For Event Time features, make sure the format follows the feature store\n", "\n", " \n", " Event Time feature format\n", " \n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following is a list of the feature names and data types of the final dataset that will be produced when your data flow is used to process your input dataset of orders." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "tags": [] }, "outputs": [], "source": [ "column_schemas = [\n", " {\n", " \"name\": \"order_id\",\n", " \"type\": \"string\"\n", " },\n", " {\n", " \"name\": \"customer_id\",\n", " \"type\": \"string\"\n", " },\n", " {\n", " \"name\": \"product_id\",\n", " \"type\": \"string\"\n", " },\n", " {\n", " \"name\": \"purchase_amount\",\n", " \"type\": \"float\"\n", " },\n", " {\n", " \"name\": \"is_reordered\",\n", " \"type\": \"long\"\n", " },\n", " {\n", " \"name\": \"event_time\",\n", " \"type\": \"string\"\n", " },\n", " {\n", " \"name\": \"n_days_since_last_purchase\",\n", " \"type\": \"float\"\n", " } \n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below we create the SDK input for those feature definitions. Some schema types in Data Wrangler are not \n", "supported by Feature Store. The following will create a default_FG_type set to String for these types." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "feature definitions: [FeatureDefinition(feature_name='order_id', feature_type=), FeatureDefinition(feature_name='customer_id', feature_type=), FeatureDefinition(feature_name='product_id', feature_type=), FeatureDefinition(feature_name='purchase_amount', feature_type=), FeatureDefinition(feature_name='is_reordered', feature_type=), FeatureDefinition(feature_name='event_time', feature_type=), FeatureDefinition(feature_name='n_days_since_last_purchase', feature_type=)]\n" ] } ], "source": [ "from sagemaker.feature_store.feature_definition import FeatureDefinition\n", "from sagemaker.feature_store.feature_definition import FeatureTypeEnum\n", "\n", "default_feature_type = FeatureTypeEnum.STRING\n", "column_to_feature_type_mapping = {\n", " \"float\": FeatureTypeEnum.FRACTIONAL,\n", " \"long\": FeatureTypeEnum.INTEGRAL\n", "}\n", "\n", "feature_definitions = [\n", " FeatureDefinition(\n", " feature_name=column_schema['name'], \n", " feature_type=column_to_feature_type_mapping.get(column_schema['type'], default_feature_type)\n", " ) for column_schema in column_schemas\n", "]\n", "logger.info(f'feature definitions: {feature_definitions}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Configure Feature Group\n", "\n", "
💡 Configurable Settings \n", "\n", "1. feature_group_name: name of the feature group.\n", "1. feature_store_offline_s3_uri: SageMaker FeatureStore writes the data in the OfflineStore of a FeatureGroup to a S3 location owned by you.\n", "1. enable_online_store: controls if online store is enabled. Enabling the online store allows quick access to the latest value for a Record via the GetRecord API.\n", "1. iam_role: IAM role for executing the processing job.\n", "1. table_format: Amazon SageMaker Feature Store supports the AWS Glue and Apache Iceberg table formats for the offline store. \n", "
" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Calculate current_timestamp to create unique entries\n", "current_timestamp = strftime('%m-%d-%H-%M-%S', gmtime())" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Feature Group Name: fscw-orders-03-30-19-21-40\n" ] } ], "source": [ "# flow name and an unique ID for this export (used later as the processing job name for the export)\n", "flow_name = \"orders\"\n", "flow_export_name = f'DWF-Orders'\n", "\n", "# feature group name, with flow_name and an unique id. You can give it a customized name\n", "feature_group_name = f\"{fs_champions_workshop_prefix}-{flow_name}-{current_timestamp}\"\n", "logger.info(f'Feature Group Name: {feature_group_name}')\n", "\n", "# SageMaker FeatureStore writes the data in the OfflineStore of a FeatureGroup to a \n", "# S3 location owned by you.\n", "feature_store_offline_s3_uri = 's3://' + bucket\n", "\n", "# controls if online store is enabled. Enabling the online store allows quick access to \n", "# the latest value for a Record via the GetRecord API.\n", "enable_online_store = True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Initialize Feature Group" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "tags": [] }, "outputs": [], "source": [ "# Initialize Boto3 session that is required to create feature group\n", "import boto3\n", "from sagemaker.session import Session\n", "\n", "region = boto3.Session().region_name\n", "boto_session = boto3.Session(region_name=region)\n", "\n", "sagemaker_client = boto_session.client(service_name='sagemaker', region_name=region)\n", "featurestore_runtime = boto_session.client(service_name='sagemaker-featurestore-runtime', region_name=region)\n", "\n", "feature_store_session = Session(\n", " boto_session=boto_session,\n", " sagemaker_client=sagemaker_client,\n", " sagemaker_featurestore_runtime_client=featurestore_runtime\n", ")" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Feature Group Name: fscw-orders-03-30-17-02\n", "Create Feature Group: False\n" ] } ], "source": [ "#Check if we use the persisted feature store name or create a new one\n", "if orders_feature_group_name is None:\n", " create_new_feature_group = True\n", "elif create_new_feature_group is False:\n", " feature_group_name = orders_feature_group_name \n", "\n", "logger.info(f'Feature Group Name: {feature_group_name}')\n", "logger.info(f'Create Feature Group: {create_new_feature_group}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Feature group is initialized and created below" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "tags": [] }, "outputs": [], "source": [ "from sagemaker.feature_store.feature_group import FeatureGroup\n", "from sagemaker.feature_store.inputs import TableFormatEnum\n", "\n", "if create_new_feature_group is True:\n", " feature_group = FeatureGroup(\n", " name=feature_group_name, sagemaker_session=feature_store_session, feature_definitions=feature_definitions)\n", "\n", " feature_group.create(\n", " s3_uri=feature_store_offline_s3_uri,\n", " record_identifier_name=record_identifier_feature_name,\n", " event_time_feature_name=event_time_feature_name,\n", " role_arn=iam_role,\n", " enable_online_store=enable_online_store,\n", " table_format=TableFormatEnum.ICEBERG # or GLUE\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Invoke the Feature Store API to create the feature group and wait until it is ready" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "tags": [] }, "outputs": [], "source": [ "import time\n", "def wait_for_feature_group_creation_complete(feature_group):\n", " \"\"\"Helper function to wait for the completions of creating a feature group\"\"\"\n", " status = feature_group.describe().get(\"FeatureGroupStatus\")\n", " while status == \"Creating\":\n", " print(\"Waiting for Feature Group Creation\")\n", " time.sleep(5)\n", " status = feature_group.describe().get(\"FeatureGroupStatus\")\n", " if status != \"Created\":\n", " raise SystemExit(f\"Failed to create feature group {feature_group.name}: {status}\")\n", " print(f\"FeatureGroup {feature_group.name} successfully created.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create Feature Group" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "tags": [] }, "outputs": [], "source": [ "if create_new_feature_group is True:\n", " wait_for_feature_group_creation_complete(feature_group=feature_group)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that the feature group is created, You will use a processing job to process your \n", " data at scale and ingest the transformed data into this feature group." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Verify created or existing feature group" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'FeatureGroupArn': 'arn:aws:sagemaker:us-west-2:227246955871:feature-group/fscw-orders-03-30-17-02', 'FeatureGroupName': 'fscw-orders-03-30-17-02', 'RecordIdentifierFeatureName': 'order_id', 'EventTimeFeatureName': 'event_time', 'FeatureDefinitions': [{'FeatureName': 'order_id', 'FeatureType': 'String'}, {'FeatureName': 'customer_id', 'FeatureType': 'String'}, {'FeatureName': 'product_id', 'FeatureType': 'String'}, {'FeatureName': 'purchase_amount', 'FeatureType': 'Fractional'}, {'FeatureName': 'is_reordered', 'FeatureType': 'Integral'}, {'FeatureName': 'event_time', 'FeatureType': 'String'}, {'FeatureName': 'n_days_since_last_purchase', 'FeatureType': 'Fractional'}], 'CreationTime': datetime.datetime(2023, 3, 30, 17, 2, 46, 972000, tzinfo=tzlocal()), 'OnlineStoreConfig': {'EnableOnlineStore': True}, 'OfflineStoreConfig': {'S3StorageConfig': {'S3Uri': 's3://sagemaker-us-west-2-227246955871/sagemaker-feature-store', 'ResolvedOutputS3Uri': 's3://sagemaker-us-west-2-227246955871/sagemaker-feature-store/227246955871/sagemaker/us-west-2/offline-store/fscw-orders-03-30-17-02-1680195766/data'}, 'DisableGlueTableCreation': False, 'DataCatalogConfig': {'TableName': 'fscw_orders_03_30_17_02_1680195766', 'Catalog': 'AwsDataCatalog', 'Database': 'SageMaker_FeatureStore'}, 'TableFormat': 'Iceberg'}, 'RoleArn': 'arn:aws:iam::227246955871:role/service-role/AmazonSageMaker-ExecutionRole-20220810T165739', 'FeatureGroupStatus': 'Created', 'OfflineStoreStatus': {'Status': 'Active'}, 'OnlineStoreTotalSizeBytes': 0, 'ResponseMetadata': {'RequestId': 'c71bea09-f8f8-40c0-98a0-2447935aa952', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amzn-requestid': 'c71bea09-f8f8-40c0-98a0-2447935aa952', 'content-type': 'application/x-amz-json-1.1', 'content-length': '1862', 'date': 'Thu, 30 Mar 2023 19:21:56 GMT'}, 'RetryAttempts': 0}}\n" ] } ], "source": [ "# Use Describe command to get the details of the feature group\n", "response = sagemaker_client.describe_feature_group(FeatureGroupName = feature_group_name)\n", "print(response)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a workflow using Data Wrangler (OPTIONAL)\n", "[SKIP this section](#Inputs-and-Outputs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ** BEGIN **\n", "\n", "##### Follow the below steps to create the dwf-orders.flow using Data Wrangler (DW)\n", "\n", " #### 1. Please follow these steps for Opening DW \n", "![DW Steps 1-3](../images/m5_nb3_sm_data_wrangler-step-1-3.png \"DW Steps 1-3\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### 2. Please follow these steps to open S3 as your source. \n", "![DW Steps 4](../images/m5_nb3_sm_data_wrangler-step-4.png \"DW Steps 4\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### 3. Please follow these steps to navigate to the desired S3 bucket/folder path to import the dataset from your source. \n", "![DW Steps 5](../images/m5_nb3_sm_data_wrangler-step-5.png \"DW Steps 5\") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### 4. Please follow these steps to add a 'Transform' to your imported dataset. \n", "![DW Steps 6](../images/m5_nb3_sm_data_wrangler-step-6.png \"DW Steps 6\")\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### 5. Please follow these steps to navigate to the desired transform to apply to a source column. \n", " ![DW Steps 7-8](../images/m5_nb3_sm_data_wrangler-step-7-8.png \"DW Steps 7-8\")\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### 6. Please follow these steps to 'Export' the created workflow to create a notebook that would ingest the data in to a feature store.. \n", "![DW Steps 9-10](../images/m5_nb3_sm_data_wrangler-step-9-10.png \"DW Steps 9-10\")\n", " " ] }, { "attachments": { "image.png": { "image/png": "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" } }, "cell_type": "markdown", "metadata": {}, "source": [ " #### A notebook is generated as shown below \n", "\n", " ![image15.png](attachment:image.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### 7. Please follow these steps to copy the value from the Output name variable (as shown below) that is auto-generated from the selected node's ID from the flowfile. \n", "\n", " ![DW Steps 11](../images/m5_nb3_sm_data_wrangler-step-11.png \"DW Steps 11\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " #### 8. Please follow these steps to navigate to 'Save As' option to rename the Workflow file to 'orders.flow' \n", " \n", " ![DW Steps 12](../images/m5_nb3_sm_data_wrangler-step-12.png \"DW Steps 12\")\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![DW Steps 13](../images/m5_nb3_sm_data_wrangler-step-13.png \"DW Steps 13\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ** END ** Creating a workflow using Data Wrangler (OPTIONAL)\n", "\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Inputs and Outputs\n", "\n", "The below settings configure the inputs and outputs for the flow export.\n", "\n", "
💡 Configurable Settings \n", "\n", "In Input - Source you can configure the data sources that will be used as input by Data Wrangler\n", "\n", "1. For S3 sources, configure the source attribute that points to the input S3 prefixes\n", "2. For all other sources, configure attributes like query_string, database in the source's \n", "DatasetDefinition object.\n", "\n", "If you modify the inputs the provided data must have the same schema and format as the data used in the Flow. \n", "You should also re-execute the cells in this section if you have modified the settings in any data sources.\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sagemaker.processing import ProcessingInput, ProcessingOutput\n", "from sagemaker.dataset_definition.inputs import AthenaDatasetDefinition, DatasetDefinition, RedshiftDatasetDefinition\n", "\n", "data_sources = []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Input: S3 Source: orders.csv" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ordersfilename='orders.csv'\n", "# You can override this to point to other dataset on S3\n", "orders_datasource = f'{bucketlocation}{ordersfilename}'\n", "%store orders_datasource\n", "print(f\"Data source: {orders_datasource}\")\n", "\n", "data_sources.append(ProcessingInput(\n", " source=orders_datasource, \n", " destination=f'/opt/ml/processing/{ordersfilename}',\n", " input_name=ordersfilename,\n", " s3_data_type=\"S3Prefix\",\n", " s3_input_mode=\"File\",\n", " s3_data_distribution_type=\"FullyReplicated\"\n", "))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Output: Feature Store \n", "\n", "##### Use the link below if you want to go back and create a Data Wrangler flow\n", "[Create Data Wrangler Workflow](#Creating-a-workflow-using-Data-Wrangler-(OPTIONAL))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below are the inputs required by the SageMaker Python SDK to launch a processing job with feature store as an output." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Warning!!!

\n", "

The output_name variable shown below should be configured only if you created you own flow file using Data Wrangler and have overwritten the original file.

\n", "

Use the value copied from Step-7 of the process used to create the DW flow.

\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Output name is auto-generated from the select node's ID + output name from the flow file.\n", "output_name = None #Use the Node ID from the provided DW flow file.\n", "if output_name is None: \n", " output_name = \"739f8413-3a63-42c3-99a0-fa678a68c2d6.default\"\n", "logger.info(f'output_name = {output_name}')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sagemaker.processing import FeatureStoreOutput\n", "\n", "processing_job_output = ProcessingOutput(\n", " output_name=output_name,\n", " app_managed=True,\n", " feature_store_output=FeatureStoreOutput(feature_group_name=feature_group_name),\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Upload Flow to S3\n", "\n", "To use the Data Wrangler as an input to the processing job, first upload your flow file to Amazon S3." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "import json\n", "import boto3\n", "\n", "# name of the flow file which should exist in the current notebook working directory\n", "flow_file_name = f'{flow_name}.flow'\n", "\n", "# Load .flow file from current notebook working directory \n", "!echo \"Loading flow file from current notebook working directory: $PWD\"\n", "\n", "#Read file from current notebook working directory\n", "with open(flow_file_name) as f:\n", " flow = json.load(f)\n", "\n", "# Upload flow to S3\n", "s3_client = boto3.client(\"s3\")\n", "orders_flow_file=f'{workshopfolder}data_wrangler_flows/{flow_export_name}.flow'\n", "s3_client.upload_file(flow_file_name, bucket, orders_flow_file)\n", "flow_s3_uri = f's3://{bucket}/{orders_flow_file}'\n", "print(f\"Data Wrangler flow {flow_file_name} uploaded to {flow_s3_uri}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Data Wrangler Flow is also provided to the Processing Job as an input source which we configure below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## Input - Flow: DWF-Orders.flow\n", "flow_input = ProcessingInput(\n", " source=flow_s3_uri,\n", " destination=\"/opt/ml/processing/flow\",\n", " input_name=\"flow\",\n", " s3_data_type=\"S3Prefix\",\n", " s3_input_mode=\"File\",\n", " s3_data_distribution_type=\"FullyReplicated\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Run Processing Job \n", "## Job Configurations\n", "\n", "
💡 Configurable Settings \n", "\n", "You can configure the following settings for Processing Jobs. If you change any configurations you will \n", "need to re-execute this and all cells below it by selecting the Run menu above and click \n", "Run Selected Cells and All Below\n", "\n", "1. IAM role for executing the processing job. \n", "2. A unique name of the processing job. Give a unique name every time you re-execute processing jobs\n", "3. Data Wrangler Container URL.\n", "4. Instance count, instance type and storage volume size in GB.\n", "5. Content type for each output. Data Wrangler supports CSV as default and Parquet.\n", "6. Network Isolation settings\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import uuid\n", "\n", "# IAM role for executing the processing job.\n", "iam_role = sagemaker.get_execution_role()\n", "\n", "# Unique processing job name. Give a unique name every time you re-execute processing jobs\n", "flow_export_id = f\"{strftime('%d-%H-%M-%S', gmtime())}-{str(uuid.uuid4())[:8]}\"\n", "processing_job_name = f\"data-wrangler-flow-processing-{flow_export_id}\"\n", "print(f\"Processing job name: {processing_job_name}\")\n", "%store processing_job_name\n", "\n", "# Data Wrangler Container URL.\n", "container_id = get_container(region)\n", "print(f\"container_id: {container_id}\")\n", "container_uri = f'{container_id}.dkr.ecr.{region}.amazonaws.com/sagemaker-data-wrangler-container:1.x'\n", "print(f\"container_uri: {container_uri}\")\n", "\n", "# Processing Job Instance count and instance type.\n", "instance_count = 2\n", "instance_type = \"ml.m5.4xlarge\"\n", "\n", "# Size in GB of the EBS volume to use for storing data during processing\n", "volume_size_in_gb = 30\n", "\n", "# Content type for each output. Data Wrangler supports CSV as default and Parquet.\n", "output_content_type = \"CSV\"\n", "\n", "# Network Isolation mode; default is off\n", "enable_network_isolation = False\n", "\n", "# Output configuration used as processing job container arguments \n", "output_config = {\n", " output_name: {\n", " \"content_type\": output_content_type\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create and Execute Processing Job\n", "\n", "To launch a Processing Job, you will use the SageMaker Python SDK to create a Processor function." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from sagemaker.processing import Processor\n", "from sagemaker.network import NetworkConfig\n", "\n", "processor = Processor(\n", " role=iam_role,\n", " image_uri=container_uri,\n", " instance_count=instance_count,\n", " instance_type=instance_type,\n", " volume_size_in_gb=volume_size_in_gb,\n", " network_config=NetworkConfig(enable_network_isolation=enable_network_isolation),\n", " sagemaker_session=sess\n", ")\n", "\n", "# Start Job\n", "processor.run(\n", " inputs=[flow_input] + data_sources, \n", " outputs=[processing_job_output],\n", " arguments=[f\"--output-config '{json.dumps(output_config)}'\"],\n", " wait=False,\n", " logs=False,\n", " job_name=processing_job_name\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Job Status\n", "\n", "Below you wait for processing job to finish. If it finishes successfully, your feature group should be populated \n", "with transformed feature values. In addition the raw parameters used by the Processing Job will be printed." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "job_result = sess.wait_for_processing_job(processing_job_name)\n", "logger.info(f'Job result={job_result}') " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can check the status of your processing job as shown below. In case of errors, the log can be very helpful to debug.\n", "\n", "![DW Processing job log](../images/m5_nb3_sm_data_wrangler-processing-job-log.png \"DW Processing job log\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Verify Processing Job Results" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import random\n", "\n", "order_id = f'O{random.randint(1, 5000)}'\n", "logger.info(f\"order_id={order_id}\") \n", "\n", "featurestore_runtime_client = sess.boto_session.client('sagemaker-featurestore-runtime', region_name=region)\n", "# Verify Processing Job Results by querying Feature Store\n", "feature_record = featurestore_runtime_client.get_record(FeatureGroupName=feature_group_name, RecordIdentifierValueAsString=order_id)\n", "logger.info(f\"Feature Record={feature_record}\") " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Storing Variable Values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Storing the values of the Orders Feature Groupname and DW outputname\n", "%store output_name\n", "%store feature_group_name\n", "processing_job = sagemaker.processing.ProcessingJob.from_processing_name(sess, processing_job_name)\n", "processing_job_description = processing_job.describe()\n", "%store processing_job_description\n", "# These variables will be used by Module-6 & Module-8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can view newly created feature group in Studio, refer to [Use Amazon SageMaker Feature Store with Amazon SageMaker Studio](https://docs.aws.amazon.com/sagemaker/latest/dg/feature-store-use-with-studio.html)\n", "for detailed guide. [Learn more about SageMaker Feature Store](https://github.com/aws/amazon-sagemaker-examples/tree/master/sagemaker-featurestore)" ] } ], "metadata": { "availableInstances": [ { "_defaultOrder": 0, "_isFastLaunch": true, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 4, "name": "ml.t3.medium", "vcpuNum": 2 }, { "_defaultOrder": 1, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 8, "name": "ml.t3.large", "vcpuNum": 2 }, { "_defaultOrder": 2, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 16, "name": "ml.t3.xlarge", "vcpuNum": 4 }, { "_defaultOrder": 3, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 32, "name": "ml.t3.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 4, "_isFastLaunch": true, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 8, "name": "ml.m5.large", "vcpuNum": 2 }, { "_defaultOrder": 5, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 16, "name": "ml.m5.xlarge", "vcpuNum": 4 }, { "_defaultOrder": 6, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 32, "name": "ml.m5.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 7, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 64, "name": "ml.m5.4xlarge", "vcpuNum": 16 }, { "_defaultOrder": 8, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 128, "name": "ml.m5.8xlarge", "vcpuNum": 32 }, { "_defaultOrder": 9, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 192, "name": "ml.m5.12xlarge", "vcpuNum": 48 }, { "_defaultOrder": 10, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 256, "name": "ml.m5.16xlarge", "vcpuNum": 64 }, { "_defaultOrder": 11, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 384, "name": "ml.m5.24xlarge", "vcpuNum": 96 }, { "_defaultOrder": 12, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 8, "name": "ml.m5d.large", "vcpuNum": 2 }, { "_defaultOrder": 13, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 16, "name": "ml.m5d.xlarge", "vcpuNum": 4 }, { "_defaultOrder": 14, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 32, "name": "ml.m5d.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 15, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 64, "name": "ml.m5d.4xlarge", "vcpuNum": 16 }, { "_defaultOrder": 16, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 128, "name": "ml.m5d.8xlarge", "vcpuNum": 32 }, { "_defaultOrder": 17, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 192, "name": "ml.m5d.12xlarge", "vcpuNum": 48 }, { "_defaultOrder": 18, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 256, "name": "ml.m5d.16xlarge", "vcpuNum": 64 }, { "_defaultOrder": 19, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 384, "name": "ml.m5d.24xlarge", "vcpuNum": 96 }, { "_defaultOrder": 20, "_isFastLaunch": false, "category": "General purpose", "gpuNum": 0, "hideHardwareSpecs": true, "memoryGiB": 0, "name": "ml.geospatial.interactive", "supportedImageNames": [ "sagemaker-geospatial-v1-0" ], "vcpuNum": 0 }, { "_defaultOrder": 21, "_isFastLaunch": true, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 4, "name": "ml.c5.large", "vcpuNum": 2 }, { "_defaultOrder": 22, "_isFastLaunch": false, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 8, "name": "ml.c5.xlarge", "vcpuNum": 4 }, { "_defaultOrder": 23, "_isFastLaunch": false, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 16, "name": "ml.c5.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 24, "_isFastLaunch": false, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 32, "name": "ml.c5.4xlarge", "vcpuNum": 16 }, { "_defaultOrder": 25, "_isFastLaunch": false, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 72, "name": "ml.c5.9xlarge", "vcpuNum": 36 }, { "_defaultOrder": 26, "_isFastLaunch": false, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 96, "name": "ml.c5.12xlarge", "vcpuNum": 48 }, { "_defaultOrder": 27, "_isFastLaunch": false, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 144, "name": "ml.c5.18xlarge", "vcpuNum": 72 }, { "_defaultOrder": 28, "_isFastLaunch": false, "category": "Compute optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 192, "name": "ml.c5.24xlarge", "vcpuNum": 96 }, { "_defaultOrder": 29, "_isFastLaunch": true, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 16, "name": "ml.g4dn.xlarge", "vcpuNum": 4 }, { "_defaultOrder": 30, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 32, "name": "ml.g4dn.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 31, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 64, "name": "ml.g4dn.4xlarge", "vcpuNum": 16 }, { "_defaultOrder": 32, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 128, "name": "ml.g4dn.8xlarge", "vcpuNum": 32 }, { "_defaultOrder": 33, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 4, "hideHardwareSpecs": false, "memoryGiB": 192, "name": "ml.g4dn.12xlarge", "vcpuNum": 48 }, { "_defaultOrder": 34, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 256, "name": "ml.g4dn.16xlarge", "vcpuNum": 64 }, { "_defaultOrder": 35, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 61, "name": "ml.p3.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 36, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 4, "hideHardwareSpecs": false, "memoryGiB": 244, "name": "ml.p3.8xlarge", "vcpuNum": 32 }, { "_defaultOrder": 37, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 8, "hideHardwareSpecs": false, "memoryGiB": 488, "name": "ml.p3.16xlarge", "vcpuNum": 64 }, { "_defaultOrder": 38, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 8, "hideHardwareSpecs": false, "memoryGiB": 768, "name": "ml.p3dn.24xlarge", "vcpuNum": 96 }, { "_defaultOrder": 39, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 16, "name": "ml.r5.large", "vcpuNum": 2 }, { "_defaultOrder": 40, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 32, "name": "ml.r5.xlarge", "vcpuNum": 4 }, { "_defaultOrder": 41, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 64, "name": "ml.r5.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 42, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 128, "name": "ml.r5.4xlarge", "vcpuNum": 16 }, { "_defaultOrder": 43, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 256, "name": "ml.r5.8xlarge", "vcpuNum": 32 }, { "_defaultOrder": 44, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 384, "name": "ml.r5.12xlarge", "vcpuNum": 48 }, { "_defaultOrder": 45, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 512, "name": "ml.r5.16xlarge", "vcpuNum": 64 }, { "_defaultOrder": 46, "_isFastLaunch": false, "category": "Memory Optimized", "gpuNum": 0, "hideHardwareSpecs": false, "memoryGiB": 768, "name": "ml.r5.24xlarge", "vcpuNum": 96 }, { "_defaultOrder": 47, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 16, "name": "ml.g5.xlarge", "vcpuNum": 4 }, { "_defaultOrder": 48, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 32, "name": "ml.g5.2xlarge", "vcpuNum": 8 }, { "_defaultOrder": 49, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 64, "name": "ml.g5.4xlarge", "vcpuNum": 16 }, { "_defaultOrder": 50, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 128, "name": "ml.g5.8xlarge", "vcpuNum": 32 }, { "_defaultOrder": 51, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 1, "hideHardwareSpecs": false, "memoryGiB": 256, "name": "ml.g5.16xlarge", "vcpuNum": 64 }, { "_defaultOrder": 52, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 4, "hideHardwareSpecs": false, "memoryGiB": 192, "name": "ml.g5.12xlarge", "vcpuNum": 48 }, { "_defaultOrder": 53, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 4, "hideHardwareSpecs": false, "memoryGiB": 384, "name": "ml.g5.24xlarge", "vcpuNum": 96 }, { "_defaultOrder": 54, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 8, "hideHardwareSpecs": false, "memoryGiB": 768, "name": "ml.g5.48xlarge", "vcpuNum": 192 } ], "instance_type": "ml.t3.medium", "kernelspec": { "display_name": "Python 3 (Data Science)", "language": "python", "name": "python3__SAGEMAKER_INTERNAL__arn:aws:sagemaker:us-west-2:236514542706:image/datascience-1.0" }, "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.7.10" } }, "nbformat": 4, "nbformat_minor": 4 }