{ "cells": [ { "cell_type": "markdown", "id": "19215575", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "# SageMaker Pipelines California Housing - Taking different steps based on model performance" ] }, { "cell_type": "markdown", "id": "dde270e2", "metadata": {}, "source": [ "---\n", "\n", "This notebook's CI test result for us-west-2 is as follows. CI test results in other regions can be found at the end of the notebook. \n", "\n", "![This us-west-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-west-2/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "---" ] }, { "cell_type": "markdown", "id": "30bd1bfa", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "This notebook illustrates how to take different actions based on model performance in a SageMaker Pipeline.\n", "\n", "The steps in this pipeline include:\n", "* Preprocessing the California Housing dataset.\n", "* Train a TensorFlow2 Artificial Neural Network (ANN) Model.\n", "* Evaluate the model performance - mean square error (MSE).\n", "* If MSE is higher than threshold, use a Lambda step to send an E-Mail to the Data Science team.\n", "* If MSE is lower than threshold, register the model into the Model Registry, and use a Lambda step to deploy the model to SageMaker Endpoint." ] }, { "cell_type": "markdown", "id": "75907ff2", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Prerequisites\n", "\n", "#### Add `AmazonSageMakerPipelinesIntegrations` policy\n", "\n", "The notebook execution role should have policies which enable the notebook to create a Lambda function. The Amazon managed policy `AmazonSageMakerPipelinesIntegrations` can be added to the notebook execution role. \n", "\n", "The policy description is:\n", "\n", "```\n", "\n", "{\n", " \"Version\": \"2012-10-17\",\n", " \"Statement\": [\n", " {\n", " \"Effect\": \"Allow\",\n", " \"Action\": [\n", " \"lambda:CreateFunction\",\n", " \"lambda:DeleteFunction\",\n", " \"lambda:InvokeFunction\",\n", " \"lambda:UpdateFunctionCode\"\n", " ],\n", " \"Resource\": [\n", " \"arn:aws:lambda:*:*:function:*sagemaker*\",\n", " \"arn:aws:lambda:*:*:function:*sageMaker*\",\n", " \"arn:aws:lambda:*:*:function:*SageMaker*\"\n", " ]\n", " },\n", " {\n", " \"Effect\": \"Allow\",\n", " \"Action\": [\n", " \"sqs:CreateQueue\",\n", " \"sqs:SendMessage\"\n", " ],\n", " \"Resource\": [\n", " \"arn:aws:sqs:*:*:*sagemaker*\",\n", " \"arn:aws:sqs:*:*:*sageMaker*\",\n", " \"arn:aws:sqs:*:*:*SageMaker*\"\n", " ]\n", " },\n", " {\n", " \"Effect\": \"Allow\",\n", " \"Action\": [\n", " \"iam:PassRole\"\n", " ],\n", " \"Resource\": \"arn:aws:iam::*:role/*\",\n", " \"Condition\": {\n", " \"StringEquals\": {\n", " \"iam:PassedToService\": [\n", " \"lambda.amazonaws.com\"\n", " ]\n", " }\n", " }\n", " }\n", " ]\n", "}\n", " \n", "```\n", "\n", "#### Add inline policy to enable creation of IAM role required for the Lambda Function\n", "\n", "The notebook execution role should have an inline policy which enable the notebook to create the IAM role required for the Lambda function. An inline policy can be added to the notebook execution role. \n", "\n", "The policy description is:\n", "\n", "```\n", "{\n", " \"Version\": \"2012-10-17\",\n", " \"Statement\": [\n", " {\n", " \"Effect\": \"Allow\",\n", " \"Action\": [\n", " \"iam:GetRole\",\n", " \"iam:CreateRole\",\n", " \"iam:AttachRolePolicy\"\n", " ],\n", " \"Resource\": \"*\"\n", " }\n", " ]\n", "}\n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "id": "85d9d259", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "import sys\n", "\n", "!{sys.executable} -m pip install \"sagemaker>=2.139.0\"" ] }, { "cell_type": "code", "execution_count": null, "id": "3ee837d6", "metadata": { "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "import os\n", "import time\n", "import boto3\n", "import numpy as np\n", "import pandas as pd\n", "from sklearn.model_selection import train_test_split\n", "import sagemaker\n", "from sagemaker import get_execution_role" ] }, { "cell_type": "code", "execution_count": null, "id": "fb2e9cec", "metadata": { "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "sess = boto3.Session()\n", "sm = sess.client(\"sagemaker\")\n", "role = get_execution_role()\n", "sagemaker_session = sagemaker.Session(boto_session=sess)\n", "bucket = sagemaker_session.default_bucket()\n", "region = boto3.Session().region_name\n", "model_package_group_name = \"TF2-California-Housing\" # Model name in model registry\n", "prefix = \"tf2-california-housing-pipelines\"\n", "pipeline_name = \"TF2CaliforniaHousingPipeline\" # SageMaker Pipeline name\n", "current_time = time.strftime(\"%m-%d-%H-%M-%S\", time.localtime())" ] }, { "cell_type": "markdown", "id": "1040cc38", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Download California Housing dataset and upload to Amazon S3\n", "\n", "We use the California housing dataset.\n", "\n", "More info on the dataset:\n", "\n", "This dataset was obtained from the `StatLib` repository. http://lib.stat.cmu.edu/datasets/\n", "\n", "The target variable is the median house value for California districts.\n", "\n", "This dataset was derived from the 1990 U.S. census, using one row per census block group. A block group is the smallest geographical unit for which the U.S. Census Bureau publishes sample data (a block group typically has a population of 600 to 3,000 people)." ] }, { "cell_type": "code", "execution_count": null, "id": "abf6e279", "metadata": { "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "data_dir = os.path.join(os.getcwd(), \"data\")\n", "os.makedirs(data_dir, exist_ok=True)\n", "\n", "raw_dir = os.path.join(os.getcwd(), \"data/raw\")\n", "os.makedirs(raw_dir, exist_ok=True)" ] }, { "cell_type": "code", "execution_count": null, "id": "fc4c1053", "metadata": { "pycharm": { "name": "#%%\n" }, "tags": [] }, "outputs": [], "source": [ "import boto3\n", "\n", "s3 = boto3.client(\"s3\")\n", "s3.download_file(\n", " f\"sagemaker-example-files-prod-{region}\",\n", " \"datasets/tabular/california_housing/cal_housing.tgz\",\n", " \"cal_housing.tgz\",\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "a6744929", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "!tar -zxf cal_housing.tgz" ] }, { "cell_type": "code", "execution_count": null, "id": "a6e28a11", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "columns = [\n", " \"longitude\",\n", " \"latitude\",\n", " \"housingMedianAge\",\n", " \"totalRooms\",\n", " \"totalBedrooms\",\n", " \"population\",\n", " \"households\",\n", " \"medianIncome\",\n", " \"medianHouseValue\",\n", "]\n", "cal_housing_df = pd.read_csv(\"CaliforniaHousing/cal_housing.data\", names=columns, header=None)" ] }, { "cell_type": "code", "execution_count": null, "id": "550568f1", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "cal_housing_df.head()" ] }, { "cell_type": "code", "execution_count": null, "id": "c29f19ea", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "X = cal_housing_df[\n", " [\n", " \"longitude\",\n", " \"latitude\",\n", " \"housingMedianAge\",\n", " \"totalRooms\",\n", " \"totalBedrooms\",\n", " \"population\",\n", " \"households\",\n", " \"medianIncome\",\n", " ]\n", "]\n", "Y = cal_housing_df[[\"medianHouseValue\"]] / 100000\n", "\n", "x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.33)\n", "\n", "np.save(os.path.join(raw_dir, \"x_train.npy\"), x_train)\n", "np.save(os.path.join(raw_dir, \"x_test.npy\"), x_test)\n", "np.save(os.path.join(raw_dir, \"y_train.npy\"), y_train)\n", "np.save(os.path.join(raw_dir, \"y_test.npy\"), y_test)\n", "rawdata_s3_prefix = \"{}/data/raw\".format(prefix)\n", "raw_s3 = sagemaker_session.upload_data(path=\"./data/raw/\", key_prefix=rawdata_s3_prefix)\n", "print(raw_s3)" ] }, { "cell_type": "code", "execution_count": null, "id": "65a0d944", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.workflow.parameters import ParameterInteger, ParameterString, ParameterFloat\n", "\n", "# raw input data\n", "input_data = ParameterString(name=\"InputData\", default_value=raw_s3)\n", "\n", "# training step parameters\n", "training_epochs = ParameterString(name=\"TrainingEpochs\", default_value=\"100\")\n", "\n", "# model performance step parameters\n", "accuracy_mse_threshold = ParameterFloat(name=\"AccuracyMseThreshold\", default_value=0.75)\n", "\n", "# Inference step parameters\n", "endpoint_instance_type = ParameterString(name=\"EndpointInstanceType\", default_value=\"ml.m5.large\")" ] }, { "cell_type": "markdown", "id": "23fa5dd8", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Processing Step \n", "\n", "The first step in the pipeline will preprocess the data to prepare it for training. We create a `SKLearnProcessor` object similar to the one above, but now parameterized, so we can separately track and change the job configuration as needed, for example to increase the instance type size and count to accommodate a growing dataset." ] }, { "cell_type": "code", "execution_count": null, "id": "599ec436", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "%%writefile preprocess.py\n", "\n", "import glob\n", "import numpy as np\n", "import os\n", "from sklearn.preprocessing import StandardScaler\n", "\n", "if __name__ == \"__main__\":\n", " input_files = glob.glob(\"{}/*.npy\".format(\"/opt/ml/processing/input\"))\n", " print(\"\\nINPUT FILE LIST: \\n{}\\n\".format(input_files))\n", " scaler = StandardScaler()\n", " x_train = np.load(os.path.join(\"/opt/ml/processing/input\", \"x_train.npy\"))\n", " scaler.fit(x_train)\n", " for file in input_files:\n", " raw = np.load(file)\n", " # only transform feature columns\n", " if \"y_\" not in file:\n", " transformed = scaler.transform(raw)\n", " if \"train\" in file:\n", " if \"y_\" in file:\n", " output_path = os.path.join(\"/opt/ml/processing/train\", \"y_train.npy\")\n", " np.save(output_path, raw)\n", " print(\"SAVED LABEL TRAINING DATA FILE\\n\")\n", " else:\n", " output_path = os.path.join(\"/opt/ml/processing/train\", \"x_train.npy\")\n", " np.save(output_path, transformed)\n", " print(\"SAVED TRANSFORMED TRAINING DATA FILE\\n\")\n", " else:\n", " if \"y_\" in file:\n", " output_path = os.path.join(\"/opt/ml/processing/test\", \"y_test.npy\")\n", " np.save(output_path, raw)\n", " print(\"SAVED LABEL TEST DATA FILE\\n\")\n", " else:\n", " output_path = os.path.join(\"/opt/ml/processing/test\", \"x_test.npy\")\n", " np.save(output_path, transformed)\n", " print(\"SAVED TRANSFORMED TEST DATA FILE\\n\")" ] }, { "cell_type": "code", "execution_count": null, "id": "43e25edd", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.sklearn.processing import SKLearnProcessor\n", "from sagemaker.processing import ProcessingInput, ProcessingOutput\n", "from sagemaker.workflow.steps import ProcessingStep\n", "\n", "framework_version = \"1.2-1\"\n", "\n", "# Create SKlearn processor object,\n", "# The object contains information about what instance type to use, the IAM role to use etc.\n", "# A managed processor comes with a preconfigured container, so only specifying version is required.\n", "sklearn_processor = SKLearnProcessor(\n", " framework_version=framework_version,\n", " role=role,\n", " instance_type=\"ml.m5.large\",\n", " instance_count=1,\n", " base_job_name=\"tf2-california-housing-processing-job\",\n", ")\n", "\n", "# Use the sklearn_processor in a Sagemaker pipelines ProcessingStep\n", "step_preprocess_data = ProcessingStep(\n", " name=\"Preprocess-California-Housing-Data\",\n", " processor=sklearn_processor,\n", " inputs=[\n", " ProcessingInput(source=input_data, destination=\"/opt/ml/processing/input\"),\n", " ],\n", " outputs=[\n", " ProcessingOutput(output_name=\"train\", source=\"/opt/ml/processing/train\"),\n", " ProcessingOutput(output_name=\"test\", source=\"/opt/ml/processing/test\"),\n", " ],\n", " code=\"preprocess.py\",\n", ")" ] }, { "cell_type": "markdown", "id": "32f6536d", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Train model step\n", "In the second step, the train and validation output from the precious processing step are used to train a model. " ] }, { "cell_type": "code", "execution_count": null, "id": "6b86f819", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.tensorflow import TensorFlow\n", "from sagemaker.inputs import TrainingInput\n", "from sagemaker.workflow.steps import TrainingStep\n", "from sagemaker.workflow.step_collections import RegisterModel\n", "import time\n", "\n", "# Where to store the trained model\n", "model_path = f\"s3://{bucket}/{prefix}/model/\"\n", "\n", "hyperparameters = {\"epochs\": training_epochs}\n", "tensorflow_version = \"2.11.0\"\n", "python_version = \"py39\"\n", "\n", "tf2_estimator = TensorFlow(\n", " source_dir=\"code\",\n", " entry_point=\"train.py\",\n", " instance_type=\"ml.m5.large\",\n", " instance_count=1,\n", " framework_version=tensorflow_version,\n", " role=role,\n", " base_job_name=\"tf2-california-housing-train\",\n", " output_path=model_path,\n", " hyperparameters=hyperparameters,\n", " py_version=python_version,\n", ")\n", "\n", "# Use the tf2_estimator in a Sagemaker pipelines ProcessingStep.\n", "# NOTE how the input to the training job directly references the output of the previous step.\n", "step_train_model = TrainingStep(\n", " name=\"Train-California-Housing-Model\",\n", " estimator=tf2_estimator,\n", " inputs={\n", " \"train\": TrainingInput(\n", " s3_data=step_preprocess_data.properties.ProcessingOutputConfig.Outputs[\n", " \"train\"\n", " ].S3Output.S3Uri,\n", " content_type=\"text/csv\",\n", " ),\n", " \"test\": TrainingInput(\n", " s3_data=step_preprocess_data.properties.ProcessingOutputConfig.Outputs[\n", " \"test\"\n", " ].S3Output.S3Uri,\n", " content_type=\"text/csv\",\n", " ),\n", " },\n", ")" ] }, { "cell_type": "markdown", "id": "064c282c", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Evaluate model step\n", "When a model is trained, it's common to evaluate the model on unseen data before registering it with the model registry. This ensures the model registry isn't cluttered with poorly performing model versions. To evaluate the model, create a ScriptProcessor object and use it in a ProcessingStep.\n", "\n", "**Note** that a separate preprocessed test dataset is used to evaluate the model, and not the output of the processing step. This is only for demo purposes, to ensure the second run of the pipeline creates a model with better performance. In a real-world scenario, the test output of the processing step would be used." ] }, { "cell_type": "code", "execution_count": null, "id": "afe9f0d5", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "%%writefile evaluate.py\n", "\n", "import os\n", "import json\n", "import subprocess\n", "import sys\n", "import numpy as np\n", "import pathlib\n", "import tarfile\n", "\n", "\n", "def install(package):\n", " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", package])\n", "\n", "\n", "if __name__ == \"__main__\":\n", " install(\"tensorflow==2.11.0\")\n", " model_path = f\"/opt/ml/processing/model/model.tar.gz\"\n", " with tarfile.open(model_path, \"r:gz\") as tar:\n", " tar.extractall(\"./model\")\n", " import tensorflow as tf\n", "\n", " model = tf.keras.models.load_model(\"./model/1\")\n", " test_path = \"/opt/ml/processing/test/\"\n", " x_test = np.load(os.path.join(test_path, \"x_test.npy\"))\n", " y_test = np.load(os.path.join(test_path, \"y_test.npy\"))\n", " scores = model.evaluate(x_test, y_test, verbose=2)\n", " print(\"\\nTest MSE :\", scores)\n", "\n", " # Available metrics to add to model: https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality-metrics.html\n", " report_dict = {\n", " \"regression_metrics\": {\n", " \"mse\": {\"value\": scores, \"standard_deviation\": \"NaN\"},\n", " },\n", " }\n", "\n", " output_dir = \"/opt/ml/processing/evaluation\"\n", " pathlib.Path(output_dir).mkdir(parents=True, exist_ok=True)\n", "\n", " evaluation_path = f\"{output_dir}/evaluation.json\"\n", " with open(evaluation_path, \"w\") as f:\n", " f.write(json.dumps(report_dict))" ] }, { "cell_type": "code", "execution_count": null, "id": "f6328380", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.workflow.properties import PropertyFile\n", "\n", "# Create SKLearnProcessor object.\n", "# The object contains information about what container to use, what instance type etc.\n", "evaluate_model_processor = SKLearnProcessor(\n", " framework_version=framework_version,\n", " instance_type=\"ml.m5.large\",\n", " instance_count=1,\n", " base_job_name=\"tf2-california-housing-evaluate\",\n", " role=role,\n", ")\n", "\n", "# Create a PropertyFile\n", "# A PropertyFile is used to be able to reference outputs from a processing step, for instance to use in a condition step.\n", "# For more information, visit https://docs.aws.amazon.com/sagemaker/latest/dg/build-and-manage-propertyfile.html\n", "evaluation_report = PropertyFile(\n", " name=\"EvaluationReport\", output_name=\"evaluation\", path=\"evaluation.json\"\n", ")\n", "\n", "# Use the evaluate_model_processor in a Sagemaker pipelines ProcessingStep.\n", "step_evaluate_model = ProcessingStep(\n", " name=\"Evaluate-California-Housing-Model\",\n", " processor=evaluate_model_processor,\n", " inputs=[\n", " ProcessingInput(\n", " source=step_train_model.properties.ModelArtifacts.S3ModelArtifacts,\n", " destination=\"/opt/ml/processing/model\",\n", " ),\n", " ProcessingInput(\n", " source=step_preprocess_data.properties.ProcessingOutputConfig.Outputs[\n", " \"test\"\n", " ].S3Output.S3Uri,\n", " destination=\"/opt/ml/processing/test\",\n", " ),\n", " ],\n", " outputs=[\n", " ProcessingOutput(output_name=\"evaluation\", source=\"/opt/ml/processing/evaluation\"),\n", " ],\n", " code=\"evaluate.py\",\n", " property_files=[evaluation_report],\n", ")" ] }, { "cell_type": "markdown", "id": "4a3b941b", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Send E-Mail Lambda Step\n", "\n", "When defining the `LambdaStep`, the SageMaker Lambda helper class provides helper functions for creating the Lambda function. Users can either use the `lambda_func` argument to provide the function ARN to an already deployed Lambda function OR use the `Lambda` class to create a Lambda function by providing a script, function name and role for the Lambda function.\n", "\n", "When passing inputs to the Lambda, the `inputs` argument can be used and within the Lambda function's handler, the `event` argument can be used to retrieve the inputs.\n", "\n", "The dictionary response from the Lambda function is parsed through the `LambdaOutput` objects provided to the `outputs` argument. The `output_name` in `LambdaOutput` corresponds to the dictionary key in the Lambda's return dictionary." ] }, { "cell_type": "markdown", "id": "bc99b6b7", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "#### Define the Lambda function\n", "\n", "Users can choose the leverage the Lambda helper class to create a Lambda function and provide that function object to the `LambdaStep`. Alternatively, users can use a pre-deployed Lambda function and provide the function ARN to the `Lambda` helper class in the lambda step.\n", "\n", "Here, If the MSE is lower than threshold, an E-Mail will be sent to Data Science team.\n", "\n", "Note that the E-Mail sending part is left for you to implement by the framework you choose." ] }, { "cell_type": "code", "execution_count": null, "id": "4305f405", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "%%writefile send_email_lambda.py\n", "\n", "\"\"\"\n", "This Lambda function sends an E-Mail to the Data Science team with the MSE from model evaluation step. \n", "The evaluation.json location in S3 is provided via the `event` argument\n", "\"\"\"\n", "\n", "import json\n", "import boto3\n", "\n", "\n", "s3_client = client = boto3.client(\"s3\")\n", "\n", "\n", "def lambda_handler(event, context):\n", " print(f\"Received Event: {event}\")\n", "\n", " evaluation_s3_uri = event[\"evaluation_s3_uri\"]\n", " path_parts = evaluation_s3_uri.replace(\"s3://\", \"\").split(\"/\")\n", " bucket = path_parts.pop(0)\n", " key = \"/\".join(path_parts)\n", "\n", " content = s3_client.get_object(Bucket=bucket, Key=key)\n", " text = content[\"Body\"].read().decode()\n", " evaluation_json = json.loads(text)\n", " mse = evaluation_json[\"regression_metrics\"][\"mse\"][\"value\"]\n", "\n", " subject_line = \"Please check high MSE ({}) detected on model evaluation\".format(mse)\n", " print(f\"Sending E-Mail to Data Science Team with subject line: {subject_line}\")\n", "\n", " # TODO - ADD YOUR CODE TO SEND EMAIL...\n", "\n", " return {\"statusCode\": 200, \"body\": json.dumps(\"E-Mail Sent Successfully\")}" ] }, { "cell_type": "markdown", "id": "2dabb9f4", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "#### IAM Role\n", "\n", "The Lambda function needs an IAM role that will allow it to read the `evaluation.json` from S3. The role ARN must be provided in the `LambdaStep`.\n", "\n", "A helper function in `iam_helper.py` is available to create the Lambda function role. Please note that the role uses the Amazon managed policy - `AmazonS3ReadOnlyAccess`. This should be replaced with an IAM policy with the least privileges as per AWS IAM best practices." ] }, { "cell_type": "code", "execution_count": null, "id": "25598115", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from iam_helper import create_s3_lambda_role\n", "\n", "lambda_role = create_s3_lambda_role(\"send-email-to-ds-team-lambda-role\")" ] }, { "cell_type": "markdown", "id": "edcfb5a3", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "#### Create the Lambda Function step" ] }, { "cell_type": "code", "execution_count": null, "id": "eeb988d9", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.workflow.lambda_step import LambdaStep\n", "from sagemaker.lambda_helper import Lambda\n", "\n", "evaluation_s3_uri = \"{}/evaluation.json\".format(\n", " step_evaluate_model.arguments[\"ProcessingOutputConfig\"][\"Outputs\"][0][\"S3Output\"][\"S3Uri\"]\n", ")\n", "\n", "send_email_lambda_function_name = \"sagemaker-send-email-to-ds-team-lambda-\" + current_time\n", "\n", "send_email_lambda_function = Lambda(\n", " function_name=send_email_lambda_function_name,\n", " execution_role_arn=lambda_role,\n", " script=\"send_email_lambda.py\",\n", " handler=\"send_email_lambda.lambda_handler\",\n", ")\n", "\n", "step_higher_mse_send_email_lambda = LambdaStep(\n", " name=\"Send-Email-To-DS-Team\",\n", " lambda_func=send_email_lambda_function,\n", " inputs={\"evaluation_s3_uri\": evaluation_s3_uri},\n", ")" ] }, { "cell_type": "markdown", "id": "0d5935ba", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Register model step\n", "If the trained model meets the model performance requirements a new model version is registered with the model registry for further analysis. To attach model metrics to the model version, create a [`ModelMetrics`](https://docs.aws.amazon.com/sagemaker/latest/dg/model-monitor-model-quality-metrics.html) object using the evaluation report created in the evaluation step. Then, create the RegisterModel step." ] }, { "cell_type": "code", "execution_count": null, "id": "68bb7f5d", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.model_metrics import MetricsSource, ModelMetrics\n", "from sagemaker.workflow.step_collections import RegisterModel\n", "\n", "# Create ModelMetrics object using the evaluation report from the evaluation step\n", "# A ModelMetrics object contains metrics captured from a model.\n", "model_metrics = ModelMetrics(\n", " model_statistics=MetricsSource(\n", " s3_uri=evaluation_s3_uri,\n", " content_type=\"application/json\",\n", " )\n", ")\n", "\n", "# Create a RegisterModel step, which registers the model with Sagemaker Model Registry.\n", "step_register_model = RegisterModel(\n", " name=\"Register-California-Housing-Model\",\n", " estimator=tf2_estimator,\n", " model_data=step_train_model.properties.ModelArtifacts.S3ModelArtifacts,\n", " content_types=[\"text/csv\"],\n", " response_types=[\"text/csv\"],\n", " inference_instances=[\"ml.m5.large\", \"ml.m5.xlarge\"],\n", " transform_instances=[\"ml.m5.xlarge\"],\n", " model_package_group_name=model_package_group_name,\n", " model_metrics=model_metrics,\n", ")" ] }, { "cell_type": "markdown", "id": "1f8e48d8", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Create the model\n", "\n", "The model is created and the name of the model is provided to the Lambda function for deployment. The `CreateModelStep` dynamically assigns a name to the model." ] }, { "cell_type": "code", "execution_count": null, "id": "5988ca19", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.workflow.step_collections import CreateModelStep\n", "from sagemaker.tensorflow.model import TensorFlowModel\n", "\n", "model = TensorFlowModel(\n", " role=role,\n", " model_data=step_train_model.properties.ModelArtifacts.S3ModelArtifacts,\n", " framework_version=tensorflow_version,\n", " sagemaker_session=sagemaker_session,\n", ")\n", "\n", "step_create_model = CreateModelStep(\n", " name=\"Create-California-Housing-Model\",\n", " model=model,\n", " inputs=sagemaker.inputs.CreateModelInput(instance_type=\"ml.m5.large\"),\n", ")" ] }, { "cell_type": "markdown", "id": "430c26ff", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Deploy model to SageMaker Endpoint Lambda Step\n", "\n", "When defining the `LambdaStep`, the SageMaker Lambda helper class provides helper functions for creating the Lambda function. Users can either use the `lambda_func` argument to provide the function ARN to an already deployed Lambda function OR use the `Lambda` class to create a Lambda function by providing a script, function name and role for the Lambda function.\n", "\n", "When passing inputs to the Lambda, the `inputs` argument can be used and within the Lambda function's handler, the `event` argument can be used to retrieve the inputs.\n", "\n", "The dictionary response from the Lambda function is parsed through the `LambdaOutput` objects provided to the `outputs` argument. The `output_name` in `LambdaOutput` corresponds to the dictionary key in the Lambda's return dictionary." ] }, { "cell_type": "markdown", "id": "85e25e59", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "### Define the Lambda function\n", "\n", "Here, the Lambda Function will deploy the model to SageMaker Endpoint." ] }, { "cell_type": "code", "execution_count": null, "id": "d56cf4b4", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "%%writefile deploy_model_lambda.py\n", "\n", "\n", "\"\"\"\n", "This Lambda function deploys the model to SageMaker Endpoint. \n", "If Endpoint exists, then Endpoint will be updated with new Endpoint Config.\n", "\"\"\"\n", "\n", "import json\n", "import boto3\n", "import time\n", "\n", "\n", "sm_client = boto3.client(\"sagemaker\")\n", "\n", "\n", "def lambda_handler(event, context):\n", " print(f\"Received Event: {event}\")\n", "\n", " current_time = time.strftime(\"%m-%d-%H-%M-%S\", time.localtime())\n", " endpoint_instance_type = event[\"endpoint_instance_type\"]\n", " model_name = event[\"model_name\"]\n", " endpoint_config_name = \"{}-{}\".format(event[\"endpoint_config_name\"], current_time)\n", " endpoint_name = event[\"endpoint_name\"]\n", "\n", " # Create Endpoint Configuration\n", " create_endpoint_config_response = sm_client.create_endpoint_config(\n", " EndpointConfigName=endpoint_config_name,\n", " ProductionVariants=[\n", " {\n", " \"InstanceType\": endpoint_instance_type,\n", " \"InitialVariantWeight\": 1,\n", " \"InitialInstanceCount\": 1,\n", " \"ModelName\": model_name,\n", " \"VariantName\": \"AllTraffic\",\n", " }\n", " ],\n", " )\n", " print(f\"create_endpoint_config_response: {create_endpoint_config_response}\")\n", "\n", " # Check if an endpoint exists. If no - Create new endpoint, if yes - Update existing endpoint\n", " list_endpoints_response = sm_client.list_endpoints(\n", " SortBy=\"CreationTime\",\n", " SortOrder=\"Descending\",\n", " NameContains=endpoint_name,\n", " )\n", " print(f\"list_endpoints_response: {list_endpoints_response}\")\n", "\n", " if len(list_endpoints_response[\"Endpoints\"]) > 0:\n", " print(\"Updating Endpoint with new Endpoint Configuration\")\n", " update_endpoint_response = sm_client.update_endpoint(\n", " EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name\n", " )\n", " print(f\"update_endpoint_response: {update_endpoint_response}\")\n", " else:\n", " print(\"Creating Endpoint\")\n", " create_endpoint_response = sm_client.create_endpoint(\n", " EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name\n", " )\n", " print(f\"create_endpoint_response: {create_endpoint_response}\")\n", "\n", " return {\"statusCode\": 200, \"body\": json.dumps(\"Endpoint Created Successfully\")}" ] }, { "cell_type": "markdown", "id": "d58d4485", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "#### IAM Role\n", "\n", "The Lambda function needs an IAM role that will allow it to deploy a SageMaker Endpoint. The role ARN must be provided in the `LambdaStep`.\n", "\n", "A helper function in `iam_helper.py` is available to create the Lambda function role. Please note that the role uses the Amazon managed policy - `AmazonSageMakerFullAccess`. This should be replaced with an IAM policy with the least privileges as per AWS IAM best practices." ] }, { "cell_type": "code", "execution_count": null, "id": "8e1f2a35", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from iam_helper import create_sagemaker_lambda_role\n", "\n", "lambda_role = create_sagemaker_lambda_role(\"deploy-model-lambda-role\")" ] }, { "cell_type": "code", "execution_count": null, "id": "37a56740", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.workflow.lambda_step import LambdaStep\n", "from sagemaker.lambda_helper import Lambda\n", "\n", "endpoint_config_name = \"tf2-california-housing-endpoint-config\"\n", "endpoint_name = \"tf2-california-housing-endpoint-\" + current_time\n", "\n", "deploy_model_lambda_function_name = \"sagemaker-deploy-model-lambda-\" + current_time\n", "\n", "deploy_model_lambda_function = Lambda(\n", " function_name=deploy_model_lambda_function_name,\n", " execution_role_arn=lambda_role,\n", " script=\"deploy_model_lambda.py\",\n", " handler=\"deploy_model_lambda.lambda_handler\",\n", ")\n", "\n", "step_lower_mse_deploy_model_lambda = LambdaStep(\n", " name=\"Deploy-California-Housing-Model-To-Endpoint\",\n", " lambda_func=deploy_model_lambda_function,\n", " inputs={\n", " \"model_name\": step_create_model.properties.ModelName,\n", " \"endpoint_config_name\": endpoint_config_name,\n", " \"endpoint_name\": endpoint_name,\n", " \"endpoint_instance_type\": endpoint_instance_type,\n", " },\n", ")" ] }, { "cell_type": "markdown", "id": "787921fb", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Accuracy condition step\n", "Adding conditions to the pipeline is done with a ConditionStep.\n", "In this case, we only want to register the new model version with the model registry if the new model meets an accuracy condition." ] }, { "cell_type": "code", "execution_count": null, "id": "b27277dd", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.workflow.conditions import ConditionLessThanOrEqualTo\n", "from sagemaker.workflow.condition_step import ConditionStep\n", "from sagemaker.workflow.functions import JsonGet\n", "\n", "# Create accuracy condition to ensure the model meets performance requirements.\n", "# Models with a test accuracy lower than the condition will not be registered with the model registry.\n", "cond_lte = ConditionLessThanOrEqualTo(\n", " left=JsonGet(\n", " step_name=step_evaluate_model.name,\n", " property_file=evaluation_report,\n", " json_path=\"regression_metrics.mse.value\",\n", " ),\n", " right=accuracy_mse_threshold,\n", ")\n", "\n", "# Create a Sagemaker Pipelines ConditionStep, using the condition above.\n", "# Enter the steps to perform if the condition returns True / False.\n", "step_cond = ConditionStep(\n", " name=\"MSE-Lower-Than-Threshold-Condition\",\n", " conditions=[cond_lte],\n", " if_steps=[step_register_model, step_create_model, step_lower_mse_deploy_model_lambda],\n", " else_steps=[step_higher_mse_send_email_lambda],\n", ")" ] }, { "cell_type": "markdown", "id": "3d243e6e", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Pipeline Creation: Orchestrate all steps\n", "\n", "Now that all pipeline steps are created, a pipeline is created." ] }, { "cell_type": "code", "execution_count": null, "id": "e6bd9d5e", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "from sagemaker.workflow.pipeline import Pipeline\n", "\n", "# Create a Sagemaker Pipeline.\n", "# Each parameter for the pipeline must be set as a parameter explicitly when the pipeline is created.\n", "# Also pass in each of the steps created above.\n", "# Note that the order of execution is determined from each step's dependencies on other steps,\n", "# not on the order they are passed in below.\n", "pipeline = Pipeline(\n", " name=pipeline_name,\n", " parameters=[\n", " input_data,\n", " training_epochs,\n", " accuracy_mse_threshold,\n", " endpoint_instance_type,\n", " ],\n", " steps=[step_preprocess_data, step_train_model, step_evaluate_model, step_cond],\n", ")" ] }, { "cell_type": "markdown", "id": "ca4af181", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Execute the Pipeline" ] }, { "cell_type": "markdown", "id": "86f6cb54", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "### List the execution steps to check out the status and artifacts:" ] }, { "cell_type": "code", "execution_count": null, "id": "198c583a", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "import json\n", "\n", "definition = json.loads(pipeline.definition())\n", "definition" ] }, { "cell_type": "markdown", "id": "079623b1", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "### Submit pipeline" ] }, { "cell_type": "code", "execution_count": null, "id": "438e8401", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "pipeline.upsert(role_arn=role)" ] }, { "cell_type": "markdown", "id": "fb6a7e29", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "### Execute pipeline using the default parameters" ] }, { "cell_type": "code", "execution_count": null, "id": "11c27cd7", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "execution = pipeline.start()" ] }, { "cell_type": "markdown", "id": "72eb39b1", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "### Wait for pipeline to complete" ] }, { "cell_type": "code", "execution_count": null, "id": "95e32f80", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "execution.wait()" ] }, { "cell_type": "markdown", "id": "e93d8920", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Visualize SageMaker Pipeline - MSE lower than the threshold\n", "In SageMaker Studio, choose `SageMaker Components and registries` in the left pane and under `Pipelines`, click the pipeline that was created. Then all pipeline executions are shown, and the one just created should have a status of `Succeded`. Selecting that execution, the different pipeline steps can be tracked as they execute.\n", "\n", "You can see that the `Register-California-Housing-Model` step was executed.\n", "\n", "![](images/pipeline_run_1_mse_lower_than_threshold.png \"Pipeline - MSE lower than the threshold\")" ] }, { "cell_type": "markdown", "id": "60731a94", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Start a pipeline with 2 epochs to trigger the `send-email-to-ds-team-lambda` Lambda Function\n", "\n", "\n", "Run the pipeline again, but this time, with only 2 epochs and a lower MSE Threshold of 0.2. This will result in a higher MSE value on model evaluation, and will cause the `send-email-to-ds-team-lambda` Lambda Function to be triggered. " ] }, { "cell_type": "code", "execution_count": null, "id": "e15ba8ba", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "# Execute pipeline with explicit parameters\n", "execution = pipeline.start(parameters=dict(TrainingEpochs=2, AccuracyMseThreshold=0.2))" ] }, { "cell_type": "code", "execution_count": null, "id": "c1d274be", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "execution.wait()" ] }, { "cell_type": "markdown", "id": "26176917", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Visualize SageMaker Pipeline - MSE higher than the threshold\n", "In SageMaker Studio, choose `SageMaker Components and registries` in the left pane and under `Pipelines`, click the pipeline that was created. Then all pipeline executions are shown, and the one just created should have a status of `Succeded`. Selecting that execution, the different pipeline steps can be tracked as they execute.\n", "\n", "You can see that the `Send-Email-To-DS-Team` step was executed.\n", "\n", "![](images/pipeline_run_1_mse_higher_than_threshold.png \"Pipeline - MSE higher than the threshold\")" ] }, { "cell_type": "markdown", "id": "9d042db6", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "## Clean up (optional)" ] }, { "cell_type": "markdown", "id": "76beb434", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "#### Stop / Close the Endpoint\n", "You should delete the endpoint before you close the notebook if you don't need to keep the endpoint running for serving real-time predictions." ] }, { "cell_type": "code", "execution_count": null, "id": "e7fd2ea6", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "import boto3\n", "\n", "client = boto3.client(\"sagemaker\")\n", "client.delete_endpoint(EndpointName=endpoint_name)" ] }, { "cell_type": "markdown", "id": "530a3c09", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "#### Delete the model registry and the pipeline to keep the studio environment tidy." ] }, { "cell_type": "code", "execution_count": null, "id": "00f02f22", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "def delete_model_package_group(sm_client, package_group_name):\n", " try:\n", " model_versions = sm_client.list_model_packages(ModelPackageGroupName=package_group_name)\n", "\n", " except Exception as e:\n", " print(\"{} \\n\".format(e))\n", " return\n", "\n", " for model_version in model_versions[\"ModelPackageSummaryList\"]:\n", " try:\n", " sm_client.delete_model_package(ModelPackageName=model_version[\"ModelPackageArn\"])\n", " except Exception as e:\n", " print(\"{} \\n\".format(e))\n", " time.sleep(0.5) # Ensure requests aren't throttled\n", "\n", " try:\n", " sm_client.delete_model_package_group(ModelPackageGroupName=package_group_name)\n", " print(\"{} model package group deleted\".format(package_group_name))\n", " except Exception as e:\n", " print(\"{} \\n\".format(e))\n", " return\n", "\n", "\n", "def delete_sagemaker_pipeline(sm_client, pipeline_name):\n", " try:\n", " sm_client.delete_pipeline(\n", " PipelineName=pipeline_name,\n", " )\n", " print(\"{} pipeline deleted\".format(pipeline_name))\n", " except Exception as e:\n", " print(\"{} \\n\".format(e))\n", " return" ] }, { "cell_type": "code", "execution_count": null, "id": "66fc04ba", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "delete_model_package_group(client, model_package_group_name)\n", "delete_sagemaker_pipeline(client, pipeline_name)" ] }, { "cell_type": "markdown", "id": "fba7e83e", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "#### Delete the Lambda functions." ] }, { "cell_type": "code", "execution_count": null, "id": "e12be1f1", "metadata": { "pycharm": { "name": "#%%\n" } }, "outputs": [], "source": [ "send_email_lambda_function.delete()\n", "deploy_model_lambda_function.delete()" ] }, { "cell_type": "markdown", "id": "3085969d", "metadata": {}, "source": [ "## Notebook CI Test Results\n", "\n", "This notebook was tested in multiple regions. The test results are as follows, except for us-west-2 which is shown at the top of the notebook.\n", "\n", "![This us-east-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-east-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This us-east-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-east-2/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This us-west-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/us-west-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This ca-central-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ca-central-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This sa-east-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/sa-east-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This eu-west-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-west-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This eu-west-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-west-2/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This eu-west-3 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-west-3/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This eu-central-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-central-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This eu-north-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/eu-north-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This ap-southeast-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-southeast-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This ap-southeast-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-southeast-2/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This ap-northeast-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-northeast-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This ap-northeast-2 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-northeast-2/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n", "\n", "![This ap-south-1 badge failed to load. Check your device's internet connectivity, otherwise the service is currently unavailable](https://h75twx4l60.execute-api.us-west-2.amazonaws.com/sagemaker-nb/ap-south-1/sagemaker-pipelines|tabular|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint|tensorflow2-california-housing-sagemaker-pipelines-deploy-endpoint.ipynb)\n" ] } ], "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 } ], "kernelspec": { "display_name": "Python 3 (Data Science 3.0)", "language": "python", "name": "python3__SAGEMAKER_INTERNAL__arn:aws:sagemaker:us-west-2:236514542706: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" } }, "nbformat": 4, "nbformat_minor": 5 }