{ "cells": [ { "cell_type": "markdown", "id": "7fbdb166-365d-44ea-9288-99a4baba1b56", "metadata": {}, "source": [ "### Direct Marketing in Banking - Propensity Modelling with Tabular Data\n", "\n", "# Part 1: SageMaker Autopilot, XGBoost, and HPO\n", "\n", "> *This notebook works well with the `Python 3 (Data Science 3.0)` kernel on SageMaker Studio*\n", "\n", "This workshop explores a tabular, [binary classification](https://en.wikipedia.org/wiki/Binary_classification) use-case with significant **class imbalance**: predicting which of a bank's customers are likely to respond to a targeted marketing campaign.\n", "\n", "In this first notebook, you'll first tackle the challenge with AutoML using [Amazon SageMaker Autopilot](), and then dive deeper with [SageMaker built-in XGBoost algorithm](https://docs.aws.amazon.com/sagemaker/latest/dg/xgboost.html) and [automatic hyperparameter tuning](https://docs.aws.amazon.com/sagemaker/latest/dg/automatic-model-tuning.html).\n", "\n", "## Contents\n", "\n", "> ℹ️ **Tip:** You can use the Table of Contents panel in the left sidebar on JupyterLab / SageMaker Studio, to view and navigate sections\n", "\n", "1. **[Prepare our environment](#Prepare-our-environment)**\n", "1. **[Fetch the example dataset](#Fetch-the-example-dataset)**\n", "1. **[Starting fast with SageMaker Autopilot](#Starting-fast-with-SageMaker-Autopilot)**\n", "1. **[Diving deeper with XGBoost](#Diving-deeper-with-XGBoost)**\n", "1. **[Hyperparameter Optimization (HPO)](#Hyperparameter-Optimization-(HPO))**\n", "1. **[Conclusions](#Conclusions)**" ] }, { "cell_type": "markdown", "id": "a15522d2-6c27-41ad-a2c9-7a46c628dcf4", "metadata": {}, "source": [ "## Prepare our environment\n", "\n", "To get started, we'll need to:\n", "\n", "- **Import** some useful libraries (as in any Python notebook)\n", "- **Configure** -\n", " - The [Amazon S3 bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html#CoreConcepts) and folder where **data** should be stored (to keep our environment tidy)\n", " - The [IAM role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) defining what **permissions** the jobs you create will have\n", "- **Connect** to AWS in general (with [boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html)) and SageMaker in particular (with the [sagemaker SDK](https://sagemaker.readthedocs.io/en/stable/)), to use the cloud services\n", "\n", "Run the cell below, to set these up.\n", "\n", "> ℹ️ **Tip:** Just like in a regular [JupyterLab notebook](https://jupyterlab.readthedocs.io/en/stable/user/interface.html), you can run code cells by clicking in to target cell - and then pressing the play (▶️) button in the toolbar or `Shift+Enter` on the keyboard." ] }, { "cell_type": "code", "execution_count": null, "id": "ae9e1b6a-41fc-4fb1-8194-76c2d96579d4", "metadata": { "tags": [] }, "outputs": [], "source": [ "%load_ext autoreload\n", "%autoreload 2\n", "\n", "# Python Built-Ins:\n", "import json\n", "import time\n", "\n", "# External Dependencies:\n", "import boto3 # General-purpose AWS SDK for Python\n", "import numpy as np # For matrix operations and numerical processing\n", "import pandas as pd # Tabular data utilities\n", "import sagemaker # High-level SDK specifically for Amazon SageMaker\n", "from sagemaker.automl.automl import AutoML as AutoMLEstimator\n", "from sagemaker.feature_store.feature_group import FeatureGroup\n", "\n", "# Local Helper Functions:\n", "import util\n", "\n", "# Setting up SageMaker parameters\n", "sgmk_session = sagemaker.Session() # Connect to SageMaker APIs\n", "region = sgmk_session.boto_session.region_name # The AWS Region we're using (e.g. 'ap-southeast-1')\n", "bucket_name = sgmk_session.default_bucket() # Select an Amazon S3 bucket\n", "bucket_prefix = \"sm101/direct-marketing\" # Location in the bucket to store our files\n", "sgmk_role = sagemaker.get_execution_role() # IAM Execution Role to use for permissions\n", "\n", "print(f\"s3://{bucket_name}/{bucket_prefix}\")\n", "print(sgmk_role)" ] }, { "cell_type": "markdown", "id": "3679792b-7352-4064-a449-035a68f2c6c4", "metadata": {}, "source": [ "## Fetch the example dataset\n", "\n", "This example uses the [UCI Bank Marketing Dataset](https://archive.ics.uci.edu/ml/datasets/bank+marketing) as per: S. Moro, P. Cortez and P. Rita. *A Data-Driven Approach to Predict the Success of Bank Telemarketing.* Decision Support Systems, Elsevier, 62:22-31, June 2014.\n", "\n", "In the following cells we'll download the dataset locally, store it in Amazon S3, and **also** load a transformed copy into [Amazon SageMaker Feature Store](https://aws.amazon.com/sagemaker/feature-store/).\n", "\n", "> ℹ️ **Tip:** You can train and deploy models in SageMaker **without using** SageMaker Feature Store, but we introduce it in this example to show you to a wider range of SageMaker features.\n", ">\n", "> Don't worry too much about the details of how the data loading is done here - but for the curious, you can check out the code behind these helper functions in [util/data.py](util/data.py)." ] }, { "cell_type": "code", "execution_count": null, "id": "145a4fcd-7c54-42d7-a6a9-09efe63fd59e", "metadata": { "tags": [] }, "outputs": [], "source": [ "raw_data_path = util.data.fetch_sample_data()\n", "print(f\"Got: {raw_data_path}\\n\")\n", "\n", "print(\"Uploading raw dataset to Amazon S3:\")\n", "raw_data_s3uri = f\"s3://{bucket_name}/{bucket_prefix}/raw.csv\"\n", "!aws s3 cp {raw_data_path} {raw_data_s3uri}" ] }, { "cell_type": "code", "execution_count": null, "id": "ae1c1a74-eb5f-4c9f-9bb4-bc147138f8f7", "metadata": { "tags": [] }, "outputs": [], "source": [ "%%time\n", "feature_group_name = \"sm101-direct-marketing\"\n", "print(\"Loading data to SageMaker Feature Store:\")\n", "\n", "# No need to re-run this if you've done it already - just set `feature_group_name` variable.\n", "util.data.load_sample_data(\n", " raw_data_path,\n", " fg_s3_uri=f\"s3://{bucket_name}/{bucket_prefix}/feature-store\",\n", " feature_group_name=feature_group_name,\n", " ignore_cols=[\n", " \"duration\", \"emp.var.rate\", \"cons.price.idx\", \"cons.conf.idx\", \"euribor3m\", \"nr.employed\"\n", " ],\n", ")" ] }, { "cell_type": "markdown", "id": "030e72d9-8b36-4dc3-a43f-ddf892598b95", "metadata": {}, "source": [ "> ⏰ **You don't have to wait** for this cell to finish running: As soon as you reach the `Ingesting data...` step, you're ready to continue on to the next section!\n", "\n", "▶️ As soon as you reach the `Ingesting data...` stage, you'll be able to see your \"feature group\" in the SageMaker Feature Store catalog:\n", "\n", "- Select the 🏠 *SageMaker Home* icon from the left sidebar in SageMaker Studio\n", "- Choose `Data > Feature Store` from the sidebar menu to open the Feature Store UI\n", "\n", "Note you can explore the catalog either by \"feature group\" (table), or searching for individual features themselves. Descriptions and some tags have already been populated for you, based on the dataset description from UCI.\n", "\n", "![](img/feature-store-features.png \"Screenshot of SMStudio Feature Store UI showing feature catalog\")" ] }, { "cell_type": "markdown", "id": "44b0be11-d5a8-4fe4-9f8a-b1c9426d9b15", "metadata": {}, "source": [ "## Starting fast with SageMaker Autopilot\n", "\n", "> ℹ️ **Tip: To skip the following manual steps** - Scroll down and you'll find code to create a similar setup through the API.\n", "\n", "Autopilot makes it easy to get started on tabular ML problems, even without extensive data preparation or writing any code. This is because:\n", "\n", "- Autopilot will automatically explore multiple data pre-processing options, algorithms, and hyperparameters for you - to identify a high-performing model\n", "- Even if you **do** want to perform some manual feature engineering first, Autopilot has direct integrations from [SageMaker Data Wrangler](https://aws.amazon.com/sagemaker/data-wrangler/) (SageMaker's low-code/no-code data preparation tool). You could explore this by creating a new Data Wrangler Flow from the launcher or File > New > Data Wrangler Flow." ] }, { "cell_type": "markdown", "id": "a8cedf0c-ead8-41b0-9b88-154db3acabe6", "metadata": {}, "source": [ "▶️ While your data finishes importing to SageMaker Feature Store, let's **start an Autopilot experiment using the raw CSV file**\n", "\n", "1. **Select** the 🏠 *SageMaker Home* icon from the left sidebar in SageMaker Studio\n", "1. **Choose** `AutoML` from the sidebar menu\n", "1. **Click** the `Create Autopilot Experiment` button" ] }, { "cell_type": "markdown", "id": "1b7e4ae3-6a5c-4a62-9ef8-75c1d0337f8e", "metadata": {}, "source": [ "▶️ In the first **Experiment and data details** step:\n", "\n", "- Choose an **Experiment name** - something like `sm101-autopilot-1` should be fine.\n", "- For **S3 location**, you can use the `Browse` button or just enter the URI from `raw_data_s3uri` earlier in this notebook.\n", "- Leave any other settings as default and click **Next**\n", "\n", "![](img/autopilot-01-select-data.png \"Screenshot of Autopilot workflow selecting the raw data CSV in S3\")" ] }, { "cell_type": "markdown", "id": "60b78a88-0396-410d-a055-bb0ee47a1df6", "metadata": {}, "source": [ "▶️ In the next **Experiment and data details** step:\n", "\n", "- For the **Target** column, select `y`\n", "- **De-select** the features from `ignore_cols` earlier in this notebook, to **exclude** them from the model.\n", " - ⚠️ As discussed on the [UCI dataset page](https://archive.ics.uci.edu/ml/datasets/bank+marketing), the `duration` field is particularly inappropriate to include in modelling as it leaks information about the target variable (calls with longer duration were more likely to be successful sales, and call duration cannot be known at the point of targeting which customers to approach. We also exclude the 5 macro-economic variables `cons.conf.idx`, `cons.price.idx`, `emp.var.rate`, `euribor3m` and `nr.employed` as these may not be easily available at inference time.\n", "\n", "![](img/autopilot-02-select-features.png \"Screenshot of Autopilot workflow excluding ignored features and selecting y as target variable\")" ] }, { "cell_type": "markdown", "id": "3440e3cc-6c71-4dd8-baba-8e61d371f1a8", "metadata": {}, "source": [ "▶️ In the next **Training method** step:\n", "\n", "- You can leave the default **Auto** selection method. Since this dataset is small, the Ensembling method will be automatically selected. If you like, you could create a second Experiment to compare both methods.\n", "\n", "![](img/autopilot-03-select-mode.png \"Select Auto mode in the UI, which will be equivalent to Ensembling for this small dataset\")" ] }, { "cell_type": "markdown", "id": "cc0a7982-5327-4f29-a70d-b25b7fd398b9", "metadata": {}, "source": [ "▶️ In the next **Deployment and advanced settings** step:\n", "\n", "- **IF you selected \"HPO\" training method in the previous screen**: Consider reducing the `Runtime > Max candidates` setting from 250 to ~50. The default setting would likely take several hours to train.\n", "- **Otherwise**, leave all settings as default\n", "\n", "▶️ **Click next**, review your settings, and then **Create experiment**.\n", "\n", "![](img/autopilot-04-runtime.png \"Screenshot selecting smaller number of max candidates in runtime menu - only applicable for HPO jobs\")\n", "\n", "Alternatively, you may like to create Autopilot jobs through the [CreateAutoMLJob API](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateAutoMLJob.html) or - as shown below - the high-level `AutoML` class in the [SageMaker Python SDK](https://sagemaker.readthedocs.io/en/stable/api/training/automl.html):\n", "\n", "> ⚠️ You don't need to run the following cell if you already created an Autopilot job manually!" ] }, { "cell_type": "code", "execution_count": null, "id": "a7ec436a-5082-4f25-aac6-13b318d0573f", "metadata": {}, "outputs": [], "source": [ "# There's no need to run this cell if you created an Autopilot job manually!\n", "autopilot = AutoMLEstimator(\n", " role=sgmk_role,\n", " target_attribute_name=\"y\",\n", "\n", " # At the time of writing, the high-level Python SDK didn't support ensembling mode - so we'll\n", " # use the HPO mode instead with limited max_candidates:\n", " max_candidates=20,\n", "\n", " # Optional params to keep the environment tidy:\n", " base_job_name=\"sm101-autopilot\",\n", " output_path=f\"s3://{bucket_name}/{bucket_prefix}/autopilot\",\n", ")\n", "\n", "autopilot.fit(raw_data_s3uri, wait=False)" ] }, { "cell_type": "markdown", "id": "385544cd-1bf1-44b1-a1dd-fa25ab982354", "metadata": {}, "source": [ "## Diving deeper with XGBoost\n", "\n", "Another useful tool to build high-performing models quickly is the set of [built-in algorithms](https://docs.aws.amazon.com/sagemaker/latest/dg/algos.html) offered by SageMaker for a wide range of use-cases.\n", "\n", "Instead of using Autopilot to automate the process of data pre-processing and hyperparameter tuning, we can directly use these built-in algorithms (or custom ones) for finer-grained control. In this example, we'll show the [XGBoost algorithm](https://docs.aws.amazon.com/sagemaker/latest/dg/xgboost.html).\n", "\n", "\n", "### Understand the algorithm requirements\n", "\n", "The first step to using any SageMaker built-in algorithm is understanding its overall characteristics and the interface it offers. Here we'll refer to:\n", "\n", "- The [algorithm docs](https://docs.aws.amazon.com/sagemaker/latest/dg/xgboost.html) to understand the **detail** of the **data formats** and **(hyper)-parameters** it supports - as well as sample notebooks\n", "- The [Common Parameters doc](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-algo-docker-registry-paths.html) to compare the **high-level configurations** and capabilities between algorithms.\n", "\n", "\n", "As discussed on the algorithm doc page, there are 2 ways to use XGBoost in SageMaker: As a pre-built algorithm (no script required), or as a framework (with your own custom training script).\n", "\n", "In this example, we'll use pre-built algorithm mode so only need to fetch the container image URI:" ] }, { "cell_type": "code", "execution_count": null, "id": "26c0d5df-16ba-4d30-a883-febf7210fba4", "metadata": { "tags": [] }, "outputs": [], "source": [ "image_uri = sagemaker.image_uris.retrieve(\"xgboost\", region=region, version=\"1.5-1\")\n", "print(image_uri)" ] }, { "cell_type": "markdown", "id": "2cc7b804-1adc-4fca-b534-032edce80295", "metadata": { "tags": [] }, "source": [ "### Extract batch data from the SageMaker Feature Store\n", "\n", "Next, we'll extract a snapshot of data from the (offline/batch) SageMaker Feature Store via serverless SQL query with [Amazon Athena](https://aws.amazon.com/athena/), to prepare for model training.\n", "\n", "Feature Store **tracks the history** of records, allowing you to reproduce point-in-time snapshots even when features change over time.\n", "\n", "- **Example queries** for time-travel and other views are available through the SageMaker Studio Feature Store UI: From your Feature Group, switch to the \"Sample queries\" tab.\n", "- The additional `event_time`, `write_time`, `api_invocation_time`, `is_deleted` and `row_number` fields returned in the below query are metadata for this history tracking - so won't be used in the actual model training." ] }, { "cell_type": "code", "execution_count": null, "id": "3c144afc-a7e9-420e-93d9-3cdb22fac8cd", "metadata": { "tags": [] }, "outputs": [], "source": [ "feature_group = FeatureGroup(feature_group_name, sagemaker_session=sgmk_session)\n", "query = feature_group.athena_query()\n", "table_name = query.table_name\n", "\n", "data_extract_s3uri = f\"s3://{bucket_name}/{bucket_prefix}/data-extract\"\n", "!aws s3 rm --quiet --recursive {data_extract_s3uri} # Clear any previous extractions\n", "print(f\"Querying feature store to extract snapshot at:\\n{data_extract_s3uri}\")\n", "query.run(\n", " f\"\"\"\n", " SELECT *\n", " FROM\n", " (SELECT *,\n", " row_number()\n", " OVER\n", " (PARTITION BY \"customer_id\"\n", " ORDER BY \"event_time\" DESC, Api_Invocation_Time DESC, write_time DESC)\n", " AS row_number\n", " FROM \"sagemaker_featurestore\".\"{table_name}\"\n", " WHERE \"event_time\" <= {time.time()})\n", " WHERE row_number = 1 AND NOT is_deleted;\n", " \"\"\",\n", " output_location=data_extract_s3uri,\n", ")\n", "query.wait()\n", "\n", "full_df = query.as_dataframe()\n", "print(f\"Got {len(full_df)} records\")\n", "full_df" ] }, { "cell_type": "markdown", "id": "17151b2f-40d5-4390-8717-25ead7d4ce80", "metadata": { "tags": [] }, "source": [ "### Split and prepare datasets\n", "\n", "From the [Input and Output Interface section](https://docs.aws.amazon.com/sagemaker/latest/dg/xgboost.html#InputOutput-XGBoost) of the algorithm doc, we know that XGBoost expects CSV or LibSVM data inputs for training, and optionally validation.\n", "\n", "Some extra data preparation is also required because (at the time of writing), this XGBoost algorithm version doesn't fully support string categorical features.\n", "\n", "Below we **one-hot encode the categorical fields**, and then split the pre-processed data into randomly shuffled training, validation, and test sets." ] }, { "cell_type": "code", "execution_count": null, "id": "8b662d36-7e35-4cb2-9dc1-d80376386b9b", "metadata": { "tags": [] }, "outputs": [], "source": [ "df_model_data = full_df.drop(\n", " columns=[\n", " \"customer_id\", \"event_time\", \"write_time\", \"api_invocation_time\", \"is_deleted\", \"row_number\"\n", " ],\n", " errors=\"ignore\", # Your DF may not have 'row_number' if you didn't do a time travel query\n", ")\n", "df_model_data\n", "\n", "# Need to one-hot encode?\n", "df_model_data = pd.get_dummies(df_model_data) # Convert categorical variables to sets of indicators\n", "\n", "# Shuffle and splitting dataset\n", "train_data, validation_data, test_data = np.split(\n", " df_model_data.sample(frac=1, random_state=1729),\n", " [int(0.7 * len(df_model_data)), int(0.9 * len(df_model_data))],\n", ")\n", "\n", "# Create CSV files for Train / Validation / Test\n", "train_data.to_csv(\"data/train.csv\", index=False, header=False)\n", "validation_data.to_csv(\"data/validation.csv\", index=False, header=False)\n", "test_data.to_csv(\"data/test.csv\", index=False, header=False)\n", "\n", "df_model_data" ] }, { "cell_type": "markdown", "id": "a8f5be5c-e9a5-4252-8350-40d83be79f98", "metadata": {}, "source": [ "The datasets specific for this algorithm can then be uploaded to Amazon S3, ready to use as inputs to the training job:" ] }, { "cell_type": "code", "execution_count": null, "id": "f647af3b-78e1-491e-a98a-d4af7472a7ae", "metadata": { "tags": [] }, "outputs": [], "source": [ "model_data_s3uri = f\"s3://{bucket_name}/{bucket_prefix}/model-data-xgb\"\n", "\n", "train_data_s3uri = model_data_s3uri + \"/train/data.csv\"\n", "train_data.to_csv(train_data_s3uri, index=False, header=False)\n", "validation_data_s3uri = model_data_s3uri + \"/validation/data.csv\"\n", "validation_data.to_csv(validation_data_s3uri, index=False, header=False)\n", "test_data_s3uri = model_data_s3uri + \"/test/data.csv\"\n", "test_data.to_csv(test_data_s3uri, index=False, header=False)" ] }, { "cell_type": "markdown", "id": "557239c8-2c83-44b0-be71-ac84d24b1825", "metadata": {}, "source": [ "### Train a model\n", "\n", "With the data prepared in a compatible format, and the parameters collected, we're ready to run a training job through the SageMaker SDK [Estimator](https://sagemaker.readthedocs.io/en/stable/api/training/estimators.html) class, which provides a high-level wrapper over the underlying [SageMaker CreateTrainingJob API](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateTrainingJob.html).\n", "\n", "The training job runs on **separate, containerized infrastructure** from this notebook:\n", "\n", "- **You specify** the number and type of instances, and the IAM permissions with which the job runs (which could be separate from the notebook execution role)\n", "- The job is **independent** from the notebook: The input parameters, logs, metrics, and output artifacts are still available through the APIs even if the notebook disconnects/restarts part way through. (See [Estimator.attach(...)](https://sagemaker.readthedocs.io/en/stable/api/training/estimators.html#sagemaker.estimator.Estimator.attach) classmethod for re-attaching to previous/ongoing jobs).\n", "- A range of **other infrastructure parameters** are available like:\n", " - [SageMaker managed spot](https://docs.aws.amazon.com/sagemaker/latest/dg/model-managed-spot-training.html), to optimize infrastructure costs\n", " - [Warm pool keep-alive](https://docs.aws.amazon.com/sagemaker/latest/dg/train-warm-pools.html), to speed up start of sequential jobs" ] }, { "cell_type": "code", "execution_count": null, "id": "717597e0-ee03-4f33-86e2-afaf3aa39e19", "metadata": { "tags": [] }, "outputs": [], "source": [ "%%time\n", "\n", "xgb_estimator = sagemaker.estimator.Estimator(\n", " base_job_name=\"xgboost\",\n", " role=sgmk_role, # IAM role for job permissions (to access the S3 data)\n", " image_uri=image_uri, # XGBoost algorithm container\n", " instance_count=1,\n", " instance_type=\"ml.m5.xlarge\", # Type of compute instance\n", " max_run=25 * 60, # Limit job to 25 minutes\n", "\n", " # OPTIONALLY use spot instances to reduce cost:\n", " use_spot_instances=True,\n", " max_wait=30 * 60, # Maximum clock time (including spot delays)\n", "\n", " output_path=f\"s3://{bucket_name}/{bucket_prefix}/train-output\",\n", ")\n", "\n", "xgb_estimator.set_hyperparameters(\n", " num_round=50, # int: [1,300]\n", " max_depth=5, # int: [1,10]\n", " alpha=2.5, # float: [0,5]\n", " eta=0.5, # float: [0,1]\n", " objective=\"binary:logistic\",\n", " eval_metric=\"auc\",\n", ")\n", "\n", "# Launch a SageMaker Training job by passing the S3 path of the datasets:\n", "xgb_estimator.fit({\n", " \"train\": sagemaker.inputs.TrainingInput(train_data_s3uri, content_type=\"csv\"),\n", " \"validation\": sagemaker.inputs.TrainingInput(validation_data_s3uri, content_type=\"csv\"),\n", "})" ] }, { "cell_type": "markdown", "id": "8963e739-5376-4c7a-b367-e810dc434e3e", "metadata": {}, "source": [ "As well as the logs streamed to the notebook, you can follow the status of the job in:\n", "- The [Training > Training jobs page of the AWS Console for SageMaker](https://console.aws.amazon.com/sagemaker/home?#/jobs)\n", " - Including links to Amazon CloudWatch console to drill in to job logs and metric graphs\n", "- The Resources > Experiments and trials pane in SageMaker Studio\n", " - Jobs started without an explicit Experiment configuration will appear under the \"Unassigned trial components\" folder" ] }, { "cell_type": "markdown", "id": "139c4d06-6890-4dbe-beff-7c4a54215ef6", "metadata": {}, "source": [ "### Batch inference\n", "\n", "Once the model is trained, we can either deploy it to a real-time endpoint to make inference requests on-demand, or use it to run batch jobs on existing datasets.\n", "\n", "In this first example, we'll use [SageMaker Batch Transform](https://docs.aws.amazon.com/sagemaker/latest/dg/batch-transform.html) to run batch inference. SageMaker will spin up a temporary cluster, send our data through the model, and shut down the resources as soon as all the input data is processed.\n", "\n", "To get started, you can create a [Transformer object](https://sagemaker.readthedocs.io/en/stable/api/inference/transformer.html) directly from `estimator.transformer(...)`. However, in this we'll go via `create_model()` first so we can easily add the model to SageMaker Model Registry later:" ] }, { "cell_type": "code", "execution_count": null, "id": "04ecdae4-2d56-48ec-8514-146a5046ba86", "metadata": { "tags": [] }, "outputs": [], "source": [ "xgb_model = xgb_estimator.create_model()" ] }, { "cell_type": "markdown", "id": "ee9e2c7d-84fc-4d1b-a359-143e9cfbffd3", "metadata": {}, "source": [ "Because SageMaker Batch Transform orchestrates the process of sending data through the model and consolidating the outputs, there are a range of extra parameters beyond the basic S3 output location and instance size/type.\n", "\n", "By default, SageMaker Batch Transform treats each file in the input S3 prefix as one request payload and generates an output file of the same name, appending `.out`. Below we configure more specific handling for tabular data though:\n", "\n", "- Interpret each line of input files as a separate record with `split_type`, and interpret each line of output data as separate record with `assemble_with`.\n", "- Make `MultiRecord` batch requests up to `max_payload` Megabytes each - allowing up to `max_concurrent_transforms` concurrent requests per instance.\n", "- Exclude the `y` target label column (which is present in the test data) from model requests with `input_filter`.\n", "- Include the input data as well as the predictions in the result with `join_source`.\n", "\n", "The result will still be a single `.csv.out` file for each `.csv` input, but SageMaker has control of individual request batch sizes to optimize resource use." ] }, { "cell_type": "code", "execution_count": null, "id": "a88304a6-8477-4e1f-83cb-0f031868ee6f", "metadata": { "scrolled": true, "tags": [] }, "outputs": [], "source": [ "eval_s3uri = f\"s3://{bucket_name}/{bucket_prefix}/xgb-evaluation\"\n", "\n", "xgb_transformer = xgb_model.transformer(\n", " output_path=eval_s3uri, # S3 output location\n", " instance_count=1, # Number of instances to spin up for the job\n", " instance_type=\"ml.m5.large\", # Instance type to use for inference\n", " strategy=\"MultiRecord\", # Request inference in batches, for efficiency\n", " accept=\"text/csv\", # Request CSV response format\n", " assemble_with=\"Line\", # Consolidate response records with newlines between\n", " max_concurrent_transforms=2, # Instances sent up to N requests concurrently\n", " max_payload=1, # Max size per request (in Megabytes)\n", ")\n", "\n", "xgb_transformer.base_transform_job_name=\"sm101-dm-xgboost\"\n", "xgb_transformer.transform(\n", " test_data_s3uri,\n", " content_type=\"text/csv\", # Test data is in CSV format\n", " split_type=\"Line\", # Each line of test data is a separate record\n", " join_source=\"Input\", # Output joined data including the input features as well as prediction\n", " input_filter=\"$[1:]\", # Exclude the leading (actual target value) field\n", " # wait=True, # (Default True) Block the notebook kernel until the job completes\n", " # logs=True, # (Default True) Stream job logs to the notebook\n", ")" ] }, { "cell_type": "markdown", "id": "d8b50bd3-c8a0-45a7-936e-40b96df0bf0b", "metadata": {}, "source": [ "Once the job completes, we can read the dataframe direct from Amazon S3:" ] }, { "cell_type": "code", "execution_count": null, "id": "69aa6cad-a63f-4454-aea3-7f5c978a3d7d", "metadata": { "tags": [] }, "outputs": [], "source": [ "df_eval = pd.read_csv(\n", " eval_s3uri + \"/data.csv.out\",\n", " header=None,\n", " names=test_data.columns.tolist() + [\"y_prob\"],\n", ")\n", "df_eval" ] }, { "cell_type": "markdown", "id": "87dc290b-0080-45ce-9acf-1e688cd35d2d", "metadata": {}, "source": [ "This algorithm only outputs positive-class probability scores for binary classification - not including assigned class labels.\n", "\n", "For assessing performance we can either assume a particular `decision_threshold` (for example, scores over than 0.5 are assigned to class 1) - or take whatever threshold maximises the F1 score of the model.\n", "\n", "The utility function below generates a graphical report here in the notebook, but also saves a JSON file in [SageMaker Model Quality Metrics compatible-format](https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality-metrics.html): Similar to if we'd run a [SageMaker Model Quality Monitoring job](https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality.html)." ] }, { "cell_type": "code", "execution_count": null, "id": "ad40ac6c-dd50-4c26-8484-7d681bb0a64c", "metadata": { "tags": [] }, "outputs": [], "source": [ "report = util.reporting.generate_binary_classification_report(\n", " y_real=df_eval[\"y\"].values,\n", " y_predict_proba=df_eval[\"y_prob\"].values,\n", " # y_predict_label not available for XGBoost output format\n", " # Optionally set decision_threshold=0.5 to apply a specific threshold, instead of maximizing F1:\n", " # decision_threshold=0.5,\n", " class_names_list=[\"Did not enroll\", \"Enrolled\"],\n", " title=\"Initial XGBoost model\",\n", ")\n", "\n", "# Store the model quality report locally and on Amazon S3:\n", "with open(\"data/report-xgboost.json\", \"w\") as f:\n", " json.dump(report, f, indent=2)\n", "model_quality_s3uri = f\"s3://{bucket_name}/{bucket_prefix}/{xgb_model.name}/model-quality.json\"\n", "!aws s3 cp data/report-xgboost.json {model_quality_s3uri}" ] }, { "cell_type": "markdown", "id": "11be4fa5-68e7-43d6-b285-644eea4bd9c8", "metadata": { "tags": [] }, "source": [ "### Register and share the model\n", "\n", "The trained model is already available in the SageMaker APIs to deploy and re-use (you should see it, for example, in the [Models page of the SageMaker Console](https://console.aws.amazon.com/sagemaker/home?#/models)).\n", "\n", "However, we can improve discoverability and governance by cataloging it in the [SageMaker Model Registry](https://docs.aws.amazon.com/sagemaker/latest/dg/model-registry.html). Here extra metadata can be associated, including I/O formats and the model quality report generated above:" ] }, { "cell_type": "code", "execution_count": null, "id": "2199e4e7-7f86-4076-b974-3c0bacc6c9f5", "metadata": { "tags": [] }, "outputs": [], "source": [ "xgb_model.register(\n", " content_types=[\"text/csv\"],\n", " response_types=[\"text/csv\"],\n", " model_package_group_name=\"sm101-dm\",\n", " description=\"Initial XGBoost model\",\n", " model_metrics=sagemaker.model_metrics.ModelMetrics(\n", " model_statistics=sagemaker.model_metrics.MetricsSource(\n", " content_type=\"application/json\",\n", " s3_uri=model_quality_s3uri,\n", " ),\n", " ),\n", " domain=\"MACHINE_LEARNING\",\n", " task=\"CLASSIFICATION\",\n", " sample_payload_url=test_data_s3uri,\n", ")" ] }, { "cell_type": "markdown", "id": "36b9aa2d-254f-4eff-af2c-a10785c0fa08", "metadata": {}, "source": [ "You can explore and manage your versioned registry model packages in SageMaker Studio: Including **reviewing and approving** new versions to trigger automated deployments." ] }, { "cell_type": "markdown", "id": "f0dbc9ff-6403-4c14-9c5e-9b94b62c2f6f", "metadata": {}, "source": [ "## Hyperparameter Optimization (HPO)\n", "\n", "> ⏰ *Note, with the default settings below, the hyperparameter tuning job can take up to ~20 minutes to complete.*\n", "\n", "While AutoML frameworks like AutoGluon try to encapsulate model ensembling, single-algorithm approaches like XGBoost can often benefit from **hyperparameter tuning** to find the best values for settings like `alpha`, `eta` and `max_depth` on a particular problem.\n", "\n", "Exploring these parameter combinations by hand can be time-consuming - especially if considering more than a couple of parameters.\n", "\n", "[SageMaker automatic model tuning](https://docs.aws.amazon.com/sagemaker/latest/dg/automatic-model-tuning.html) can run intelligent exploration and optimization jobs for you automatically, so you can focus on building and applying insights - rather than managing these experiments.\n", "\n", "As shown below, you can set up a [HyperparameterTuner](https://sagemaker.readthedocs.io/en/stable/api/training/tuner.html) wrapper around your standard `Estimator`. The key requirements are:\n", "\n", "1. Your job outputs at least one **metric** which the tuner can maximize or minimize (this is handled automatically for most built-in algorithms)\n", "1. Specify **ranges for the hyperparameters** you'd like to explore\n", "1. Specify the **strategy and resource limits** for the job\n", "\n", "SageMaker HPO supports a range of [strategies](https://docs.aws.amazon.com/sagemaker/latest/dg/automatic-model-tuning-how-it-works.html) including exploratory tools like Grid and Random search, and efficient HPO-oriented optimization tools like Bayesian Optimization and Hyperband.\n", "\n", "In this example, we'll use Bayesian search to optimize Area Under the ROC Curve (AUC) of our XGBoost model [See Machine Learning Key Concepts](https://docs.aws.amazon.com/machine-learning/latest/dg/amazon-machine-learning-key-concepts.html) for more info if you're unfamiliar with these metrics." ] }, { "cell_type": "code", "execution_count": null, "id": "8e8e82c2-6c74-48d8-a705-67623cb8753a", "metadata": { "tags": [] }, "outputs": [], "source": [ "%%time\n", "\n", "# import required HPO objects\n", "from sagemaker.tuner import (\n", " CategoricalParameter,\n", " ContinuousParameter,\n", " HyperparameterTuner,\n", " IntegerParameter,\n", ")\n", "\n", "# Target metric is already built in to the algorithm, so we just specify the name:\n", "objective = \"validation:auc\"\n", "\n", "# Configure hyperparameter ranges to explore:\n", "ranges = {\n", " \"num_round\": IntegerParameter(1, 300),\n", " \"max_depth\": IntegerParameter(1, 10),\n", " \"alpha\": ContinuousParameter(0, 5),\n", " \"eta\": ContinuousParameter(0, 1),\n", "}\n", "\n", "# Configure the tuner:\n", "xgb_tuner = HyperparameterTuner(\n", " estimator=xgb_estimator, # The SageMaker estimator object\n", " hyperparameter_ranges=ranges,\n", " max_jobs=15, # Max total number of training jobs\n", " max_parallel_jobs=3, # How many training jobs can run in parallel\n", " strategy=\"Bayesian\", # the internal optimization strategy of HPO\n", " objective_metric_name=objective,\n", " objective_type=\"Maximize\", # For AUC, higher = better\n", ")\n", "\n", "# Start the job:\n", "xgb_tuner.fit(\n", " {\n", " \"train\": sagemaker.inputs.TrainingInput(train_data_s3uri, content_type=\"csv\"),\n", " \"validation\": sagemaker.inputs.TrainingInput(validation_data_s3uri, content_type=\"csv\"),\n", " },\n", " wait=True, # Optionally block the notebook until the job is complete.\n", ")" ] }, { "cell_type": "markdown", "id": "b8774db0-2fff-4a02-afe0-2778d9f9e387", "metadata": {}, "source": [ "Note that `max_parallel_jobs` creates a **trade-off** between job run time and result quality: The more jobs are run in parallel, the faster the `max_jobs` will be completed, but the less information the strategy has about completed jobs when selecting parameter combinations to try next.\n", "\n", "As with training and transform jobs, hyperparameter tuning runs separately from the notebook so won't be interrupted if you lose connection or shut down. You can track job progress in the [Training > Hyperparameter tuning jobs page](https://console.aws.amazon.com/sagemaker/home?#/hyper-tuning-jobs) of the SageMaker console and the [DescribeHyperParameterTuningJob API](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_DescribeHyperParameterTuningJob.html).\n", "\n", "You can always set `wait=False` or interrupt the cell (Square stop ⏹ button in the toolbar) to continue working in the notebook while HPO runs in the background. You can later resume waiting for the active job by calling `tuner.wait()` as shown below. Just like the Estimator, you won't be able to `deploy()` the tuner's model until the tuning job is complete." ] }, { "cell_type": "code", "execution_count": null, "id": "288d2402-9175-42ab-b909-c09ed7b3c8ea", "metadata": { "tags": [] }, "outputs": [], "source": [ "xgb_tuner.wait()" ] }, { "cell_type": "markdown", "id": "68ffc160-f036-4cce-8e2a-f56b731142c3", "metadata": {}, "source": [ "The individual training jobs created by the model tuning are listed in SageMaker just like manually-created ones, and the HPO job builds up a leaderboard of models based on the objective metric.\n", "\n", "In this example we'll simply deploy the \"best\" model, but you can also explore the jobs for deeper insights: See [this sample notebook](https://github.com/aws/amazon-sagemaker-examples/blob/main/hyperparameter_tuning/analyze_results/HPO_Analyze_TuningJob_Results.ipynb) for examples.\n", "\n", "\n", "### Deploy and test the optimized model\n", "\n", "As with single training jobs, you can use the results of your hyperparameter tuning runs for either batch or online inference.\n", "\n", "You can directly call the [HyperparameterTuner.deploy(...)](https://sagemaker.readthedocs.io/en/stable/api/training/tuner.html#sagemaker.tuner.HyperparameterTuner.deploy) method to deploy the winning model to an endpoint - but as before, we'll create a `Model` object first to link back to SageMaker Model Registry later." ] }, { "cell_type": "code", "execution_count": null, "id": "6530540a-2438-46a9-a0af-913e36c92556", "metadata": { "tags": [] }, "outputs": [], "source": [ "best_job_name = xgb_tuner.best_training_job()\n", "print(\"Best training job from HPO run:\", best_job_name)\n", "\n", "hpo_model = sagemaker.estimator.Estimator.attach(best_job_name).create_model()" ] }, { "cell_type": "code", "execution_count": null, "id": "383e4504-5d1f-41f1-a58f-b04dd13c4f18", "metadata": { "tags": [] }, "outputs": [], "source": [ "hpo_predictor = hpo_model.deploy(\n", " initial_instance_count=1,\n", " instance_type=\"ml.m5.large\",\n", " serializer=sagemaker.serializers.CSVSerializer(),\n", " deserializer=sagemaker.deserializers.CSVDeserializer(),\n", ")" ] }, { "cell_type": "markdown", "id": "3e917844-81eb-4020-9247-c1a92c998a81", "metadata": {}, "source": [ "### Use the endpoint\n", "\n", "The [Predictor](https://sagemaker.readthedocs.io/en/stable/api/inference/predictors.html) class in the SageMaker SDK provides a high-level Python wrapper for creating and invoking inference endpoints which is useful in notebooks... Although consumer applications can also use the low-level [SageMaker Runtime API](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_Operations_Amazon_SageMaker_Runtime.html) (with [Boto3 in Python](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-runtime.html), or other AWS SDKs in other languages) to avoid this extra library dependency.\n", "\n", "SageMaker is a general-purpose ML platform supporting a wide range of use-cases beyond tabular data, so we need to explicitly configure what type of content we're sending in endpoint requests (here CSV) and specifying for the response (here JSON) - via the SDK [serializer](https://sagemaker.readthedocs.io/en/stable/api/inference/serializers.html) and [deserializer classes](https://sagemaker.readthedocs.io/en/stable/api/inference/deserializers.html). You can also define your own classes to fully customize the Python I/O interface of the `predictor.predict()` method and the on-the-wire data format transmitted to the HTTPS endpoint. \n", "\n", "Again, when using a pre-built algorithm, refer to the [algorithm docs](https://docs.aws.amazon.com/sagemaker/latest/dg/xgboost.html#InputOutput-XGBoost) to see what input and output formats are supported at inference time.\n", "\n", "We'll send `text/csv` inputs and request `text/csv` outputs from the model, similarly to the batch transform case earlier:" ] }, { "cell_type": "code", "execution_count": null, "id": "91776c91-0ccc-4785-bec4-8d42679b724a", "metadata": { "tags": [] }, "outputs": [], "source": [ "# getting the predicted probabilities of the best model\n", "hpo_probabilities = np.array(\n", " hpo_predictor.predict(test_data.drop([\"y\"], axis=1).values),\n", " dtype=float,\n", ").squeeze()\n", "\n", "hpo_probabilities" ] }, { "cell_type": "markdown", "id": "5e6dc47f-da25-4c69-b59b-d75c63ca1527", "metadata": {}, "source": [ "> ⚠️ **Note:** The above single `predict()` call makes a single `InvokeEndpoint` request with the **entire test dataset in one batch**. That's fine for a small dataset like this one, but practical use-cases will need to balance between throughput efficiency (large batches reduce communication overhead), endpoint memory requirements, and payload size limits (6MB for real-time endpoints, at the time of writing).\n", "\n", "Our model has calculated probability scores (in the interval [0,1]) of a potential customer enrolling for a term deposit. We can join these predictions back on to the original dataframe here in the notebook, to explore performance similar to the batch results earlier:" ] }, { "cell_type": "code", "execution_count": null, "id": "2803ff21-3af9-42e0-a6fb-236cea26be0d", "metadata": { "tags": [] }, "outputs": [], "source": [ "hpo_report = util.reporting.generate_binary_classification_report(\n", " y_real=test_data[\"y\"].values,\n", " y_predict_proba=hpo_probabilities,\n", " # y_predict_label not available for XGBoost output format\n", " # Optionally set decision_threshold=0.5 to apply a specific threshold, instead of maximizing F1:\n", " # decision_threshold=0.5,\n", " class_names_list=[\"Did not enroll\", \"Enrolled\"],\n", " title=\"HP-tuned XGBoost model\",\n", ")\n", "\n", "# Store the model quality report locally and on Amazon S3:\n", "with open(\"data/report-xgbhpo.json\", \"w\") as f:\n", " json.dump(hpo_report, f, indent=2)\n", "hpo_quality_s3uri = f\"s3://{bucket_name}/{bucket_prefix}/{hpo_model.name}/model-quality.json\"\n", "!aws s3 cp data/report-xgbhpo.json {hpo_quality_s3uri}" ] }, { "cell_type": "markdown", "id": "7da70b26-0717-4f6f-aef6-4da3dc05b779", "metadata": {}, "source": [ "...And finally, we can register this tuned model as a second candidate version in our SageMaker Model Registry group:" ] }, { "cell_type": "code", "execution_count": null, "id": "fca13456-796b-4522-b5dd-f7785a603634", "metadata": { "tags": [] }, "outputs": [], "source": [ "hpo_model.register(\n", " content_types=[\"text/csv\"],\n", " response_types=[\"text/csv\"],\n", " model_package_group_name=\"sm101-dm\",\n", " description=\"HP-tuned XGBoost model\",\n", " model_metrics=sagemaker.model_metrics.ModelMetrics(\n", " model_statistics=sagemaker.model_metrics.MetricsSource(\n", " content_type=\"application/json\",\n", " s3_uri=hpo_quality_s3uri,\n", " ),\n", " ),\n", " domain=\"MACHINE_LEARNING\",\n", " task=\"CLASSIFICATION\",\n", " sample_payload_url=test_data_s3uri,\n", ")" ] }, { "cell_type": "markdown", "id": "7a703f62-7f03-43f4-b94b-3763502c4f74", "metadata": {}, "source": [ "▶️ **Open** your model group in SageMaker Model Registry\n", "\n", "- You can `Shift+Click` or `Control+Click` to **select multiple versions** in the model group\n", "- With multiple versions selected, you can `Right Click` to `Compare model versions` for a side-by-side comparison of different models' charts and statistics." ] }, { "cell_type": "markdown", "id": "e0fbc0c2-a74f-4680-8c31-37a5e354827d", "metadata": {}, "source": [ "## Conclusions\n", "\n", "In this notebook, we saw how [**SageMaker Autopilot**](https://docs.aws.amazon.com/sagemaker/latest/dg/autopilot-automate-model-development.html) can accelerate new tabular ML projects to a high-accuracy, deployable model with no coding required. We also saw how you can dive deeper using the [SageMaker built-in algorithms](https://docs.aws.amazon.com/sagemaker/latest/dg/algos.html) and [automatic hyperparameter tuning](https://docs.aws.amazon.com/sagemaker/latest/dg/automatic-model-tuning.html), to customize your models without implementing common algorithms from scratch.\n", "\n", "We also saw brief intros to how [SageMaker Feature Store](https://docs.aws.amazon.com/sagemaker/latest/dg/feature-store.html) can help catalog shared feature data, and how [SageMaker Model Registry](https://docs.aws.amazon.com/sagemaker/latest/dg/model-registry.html) helps with tracking and managing trained models. For more information on these MLOps features, you can refer to the documentation and the official [SageMaker notebook examples repository](https://github.com/aws/amazon-sagemaker-examples).\n", "\n", "We used a relatively small number of trials in this HPO run to keep the run-time fast, so you might not have seen much improvement: But HPO is particularly useful when the space of parameters becomes large and you can allocate sufficient compute resources for the algorithm to explore best combinations for you.\n", "\n", "Although [SageMaker Autopilot](https://docs.aws.amazon.com/sagemaker/latest/dg/autopilot-automate-model-development.html) is perhaps the quickest way to deliver strong initial results on a new tabular data project, SageMaker built-in algorithms support a wide range of use-cases from text and vision to more niche tabular problem types. Combining built-in algorithms with SageMaker HPO can really boost their accuracy.\n", "\n", "In fact, you'll see that Autopilot uses many of these same tools under the hood: Creating HPO jobs when running in HPO mode, using SageMaker Processing for data pre-processing experiments, and making use of the XGBoost and AutoGluon-Tabular algorithms.\n", "\n", "▶️ In the optional [Notebook 2 AutoGluon](2%20AutoGluon%20(Optional).ipynb), you'll see another SageMaker built-in algorithm in action and learn how automatic ensembling can help deliver even more accurate models." ] }, { "cell_type": "markdown", "id": "e42ee733-482f-4b2e-bda7-12d4f43ee603", "metadata": {}, "source": [ "## Releasing cloud resources\n", "\n", "While training job clusters are shut down automatically when the job stops, inference endpoints stay provisioned until explicitly deleted.\n", "\n", "To avoid unnecessary charges, un-comment and run the following code to clean up your AutoGluon model endpoint when finished experimenting. You can also check the [Inference > Endpoints page of the SageMaker console](https://console.aws.amazon.com/sagemaker/home?#/endpoints) for any other running endpoints." ] }, { "cell_type": "code", "execution_count": null, "id": "1c64a6ee-4bef-49fb-bd0c-fc1861c709d9", "metadata": {}, "outputs": [], "source": [ "# hpo_predictor.delete_endpoint(delete_endpoint_config=True)" ] } ], "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 }, { "_defaultOrder": 55, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 8, "hideHardwareSpecs": false, "memoryGiB": 1152, "name": "ml.p4d.24xlarge", "vcpuNum": 96 }, { "_defaultOrder": 56, "_isFastLaunch": false, "category": "Accelerated computing", "gpuNum": 8, "hideHardwareSpecs": false, "memoryGiB": 1152, "name": "ml.p4de.24xlarge", "vcpuNum": 96 } ], "instance_type": "ml.t3.medium", "kernelspec": { "display_name": "Python 3 (Data Science 3.0)", "language": "python", "name": "python3__SAGEMAKER_INTERNAL__arn:aws:sagemaker:us-east-1:081325390199:image/sagemaker-data-science-310-v1" }, "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.10.6" }, "toc-showmarkdowntxt": false }, "nbformat": 4, "nbformat_minor": 5 }