Customer Churn Prediction with Amazon SageMaker Autopilot
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.
Using AutoPilot to Predict Mobile Customer Departure
Kernel Python 3 (Data Science)
works well with this notebook.
Contents
Introduction
Amazon SageMaker Autopilot is an automated machine learning (commonly referred to as AutoML) solution for tabular datasets. You can use SageMaker Autopilot in different ways: on autopilot (hence the name) or with human guidance, without code through SageMaker Studio, or using the AWS SDKs. This notebook, as a first glimpse, will use the AWS SDKs to simply create and deploy a machine learning model.
Losing customers is costly for any business. Identifying unhappy customers early on gives you a chance to offer them incentives to stay. This notebook describes using machine learning (ML) for the automated identification of unhappy customers, also known as customer churn prediction. ML models rarely give perfect predictions though, so this notebook is also about how to incorporate the relative costs of prediction mistakes when determining the financial outcome of using ML.
We use an example of churn that is familiar to all of us–leaving a mobile phone operator. Seems like I can always find fault with my provider du jour! And if my provider knows that I’m thinking of leaving, it can offer timely incentives–I can always use a phone upgrade or perhaps have a new feature activated–and I might just stick around. Incentives are often much more cost effective than losing and reacquiring a customer.
Setup
This notebook was created and tested on an ml.m4.xlarge notebook instance.
Let’s start by specifying:
The S3 bucket and prefix that you want to use for training and model data. This should be within the same region as the Notebook Instance, training, and hosting.
The IAM role arn used to give training and hosting access to your data. See the documentation for how to create these. Note, if more than one role is required for notebook instances, training, and/or hosting, please replace the boto regexp with a the appropriate full IAM role arn string(s).
[5]:
import sagemaker
import boto3
from sagemaker import get_execution_role
region = boto3.Session().region_name
session = sagemaker.Session()
# You can modify the following to use a bucket of your choosing
bucket = session.default_bucket()
prefix = "sagemaker/DEMO-autopilot-churn"
role = get_execution_role()
# This is the client we will use to interact with SageMaker AutoPilot
sm = boto3.Session().client(service_name="sagemaker", region_name=region)
Next, we’ll import the Python libraries we’ll need for the remainder of the exercise.
[6]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import io
import os
import sys
import time
import json
from IPython.display import display
from time import strftime, gmtime
import boto3
import sagemaker
from sagemaker.predictor import csv_serializer
Data
Mobile operators have historical records on which customers ultimately ended up churning and which continued using the service. We can use this historical information to construct an ML model of one mobile operator’s churn using a process called training. After training the model, we can pass the profile information of an arbitrary customer (the same profile information that we used to train the model) to the model, and have the model predict whether this customer is going to churn. Of course, we expect the model to make mistakes–after all, predicting the future is tricky business! But I’ll also show how to deal with prediction errors.
The dataset we will use is synthetically generated, but indictive of the types of features you’d see in this use case.
[ ]:
s3 = boto3.client("s3")
s3.download_file(
f"sagemaker-example-files-prod-{region}", "datasets/tabular/synthetic/churn.txt", "churn.txt"
)
Upload the dataset to S3
Before you run Autopilot on the dataset, first perform a check of the dataset to make sure that it has no obvious errors. The Autopilot process can take long time, and it’s generally a good practice to inspect the dataset before you start a job. This particular dataset is small, so you can inspect it in the notebook instance itself. If you have a larger dataset that will not fit in a notebook instance memory, inspect the dataset offline using a big data analytics tool like Apache Spark. Deequ is a library built on top of Apache Spark that can be helpful for performing checks on large datasets. Autopilot is capable of handling datasets up to 5 GB.
Read the data into a Pandas data frame and take a look.
[7]:
churn = pd.read_csv("./churn.txt")
pd.set_option("display.max_columns", 500)
churn
[7]:
State | Account Length | Area Code | Phone | Int'l Plan | VMail Plan | VMail Message | Day Mins | Day Calls | Day Charge | Eve Mins | Eve Calls | Eve Charge | Night Mins | Night Calls | Night Charge | Intl Mins | Intl Calls | Intl Charge | CustServ Calls | Churn? | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | KS | 128 | 415 | 382-4657 | no | yes | 25 | 265.1 | 110 | 45.07 | 197.4 | 99 | 16.78 | 244.7 | 91 | 11.01 | 10.0 | 3 | 2.70 | 1 | False. |
1 | OH | 107 | 415 | 371-7191 | no | yes | 26 | 161.6 | 123 | 27.47 | 195.5 | 103 | 16.62 | 254.4 | 103 | 11.45 | 13.7 | 3 | 3.70 | 1 | False. |
2 | NJ | 137 | 415 | 358-1921 | no | no | 0 | 243.4 | 114 | 41.38 | 121.2 | 110 | 10.30 | 162.6 | 104 | 7.32 | 12.2 | 5 | 3.29 | 0 | False. |
3 | OH | 84 | 408 | 375-9999 | yes | no | 0 | 299.4 | 71 | 50.90 | 61.9 | 88 | 5.26 | 196.9 | 89 | 8.86 | 6.6 | 7 | 1.78 | 2 | False. |
4 | OK | 75 | 415 | 330-6626 | yes | no | 0 | 166.7 | 113 | 28.34 | 148.3 | 122 | 12.61 | 186.9 | 121 | 8.41 | 10.1 | 3 | 2.73 | 3 | False. |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
3328 | AZ | 192 | 415 | 414-4276 | no | yes | 36 | 156.2 | 77 | 26.55 | 215.5 | 126 | 18.32 | 279.1 | 83 | 12.56 | 9.9 | 6 | 2.67 | 2 | False. |
3329 | WV | 68 | 415 | 370-3271 | no | no | 0 | 231.1 | 57 | 39.29 | 153.4 | 55 | 13.04 | 191.3 | 123 | 8.61 | 9.6 | 4 | 2.59 | 3 | False. |
3330 | RI | 28 | 510 | 328-8230 | no | no | 0 | 180.8 | 109 | 30.74 | 288.8 | 58 | 24.55 | 191.9 | 91 | 8.64 | 14.1 | 6 | 3.81 | 2 | False. |
3331 | CT | 184 | 510 | 364-6381 | yes | no | 0 | 213.8 | 105 | 36.35 | 159.6 | 84 | 13.57 | 139.2 | 137 | 6.26 | 5.0 | 10 | 1.35 | 2 | False. |
3332 | TN | 74 | 415 | 400-4344 | no | yes | 25 | 234.4 | 113 | 39.85 | 265.9 | 82 | 22.60 | 241.4 | 77 | 10.86 | 13.7 | 4 | 3.70 | 0 | False. |
3333 rows × 21 columns
By modern standards, it’s a relatively small dataset, with only 5,000 records, where each record uses 21 attributes to describe the profile of a customer of an unknown US mobile operator. The attributes are:
State
: the US state in which the customer resides, indicated by a two-letter abbreviation; for example, OH or NJAccount Length
: the number of days that this account has been activeArea Code
: the three-digit area code of the corresponding customer’s phone numberPhone
: the remaining seven-digit phone numberInt’l Plan
: whether the customer has an international calling plan: yes/noVMail Plan
: whether the customer has a voice mail feature: yes/noVMail Message
: presumably the average number of voice mail messages per monthDay Mins
: the total number of calling minutes used during the dayDay Calls
: the total number of calls placed during the dayDay Charge
: the billed cost of daytime callsEve Mins, Eve Calls, Eve Charge
: the billed cost for calls placed during the eveningNight Mins
,Night Calls
,Night Charge
: the billed cost for calls placed during nighttimeIntl Mins
,Intl Calls
,Intl Charge
: the billed cost for international callsCustServ Calls
: the number of calls placed to Customer ServiceChurn?
: whether the customer left the service: true/false
The last attribute, Churn?
, is known as the target attribute–the attribute that we want the ML model to predict.
Reserve some data for calling inference on the model
Divide the data into training and testing splits. The training split is used by SageMaker Autopilot. The testing split is reserved to perform inference using the suggested model.
[8]:
train_data = churn.sample(frac=0.8, random_state=200)
test_data = churn.drop(train_data.index)
test_data_no_target = test_data.drop(columns=["Churn?"])
Now we’ll upload these files to S3.
[9]:
train_file = "train_data.csv"
train_data.to_csv(train_file, index=False, header=True)
train_data_s3_path = session.upload_data(path=train_file, key_prefix=prefix + "/train")
print("Train data uploaded to: " + train_data_s3_path)
test_file = "test_data.csv"
test_data_no_target.to_csv(test_file, index=False, header=False)
test_data_s3_path = session.upload_data(path=test_file, key_prefix=prefix + "/test")
print("Test data uploaded to: " + test_data_s3_path)
Train data uploaded to: s3://sagemaker-us-west-2-262002448484/sagemaker/DEMO-autopilot-churn/train/train_data.csv
Test data uploaded to: s3://sagemaker-us-west-2-262002448484/sagemaker/DEMO-autopilot-churn/test/test_data.csv
Setting up the SageMaker Autopilot Job
After uploading the dataset to Amazon S3, you can invoke Autopilot to find the best ML pipeline to train a model on this dataset.
The required inputs for invoking a Autopilot job are: * Amazon S3 location for input dataset and for all output artifacts * Name of the column of the dataset you want to predict (Churn?
in this case) * An IAM role
Currently Autopilot supports only tabular datasets in CSV format. Either all files should have a header row, or the first file of the dataset, when sorted in alphabetical/lexical order by name, is expected to have a header row.
[10]:
input_data_config = [
{
"DataSource": {
"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": "s3://{}/{}/train".format(bucket, prefix),
}
},
"TargetAttributeName": "Churn?",
}
]
output_data_config = {"S3OutputPath": "s3://{}/{}/output".format(bucket, prefix)}
You can also specify the type of problem you want to solve with your dataset (Regression, MulticlassClassification, BinaryClassification
). In case you are not sure, SageMaker Autopilot will infer the problem type based on statistics of the target column (the column you want to predict).
Because the target attribute, Churn?
, is binary, our model will be performing binary prediction, also known as binary classification. In this example we will let AutoPilot infer the type of problem for us.
You have the option to limit the running time of a SageMaker Autopilot job by providing either the maximum number of pipeline evaluations or candidates (one pipeline evaluation is called a Candidate
because it generates a candidate model) or providing the total time allocated for the overall Autopilot job. Under default settings, this job takes about four hours to run. This varies between runs because of the nature of the exploratory process Autopilot uses to find optimal training
parameters.
Launching the SageMaker Autopilot Job
You can now launch the Autopilot job by calling the create_auto_ml_job
API. We limit the number of candidates to 20 so that the job finishes in a few minutes.
[11]:
from time import gmtime, strftime, sleep
timestamp_suffix = strftime("%d-%H-%M-%S", gmtime())
auto_ml_job_name = "automl-churn-" + timestamp_suffix
print("AutoMLJobName: " + auto_ml_job_name)
sm.create_auto_ml_job(
AutoMLJobName=auto_ml_job_name,
InputDataConfig=input_data_config,
OutputDataConfig=output_data_config,
AutoMLJobConfig={"CompletionCriteria": {"MaxCandidates": 20}},
RoleArn=role,
)
AutoMLJobName: automl-churn-03-17-50-14
[11]:
{'AutoMLJobArn': 'arn:aws:sagemaker:us-west-2:262002448484:automl-job/automl-churn-03-17-50-14',
'ResponseMetadata': {'RequestId': '346f751d-4738-4c0f-86a4-513f15e2b2ea',
'HTTPStatusCode': 200,
'HTTPHeaders': {'x-amzn-requestid': '346f751d-4738-4c0f-86a4-513f15e2b2ea',
'content-type': 'application/x-amz-json-1.1',
'content-length': '95',
'date': 'Wed, 03 Feb 2021 17:50:14 GMT'},
'RetryAttempts': 0}}
[12]:
# Store the AutoMLJobName name for use in subsequent notebooks
%store auto_ml_job_name
auto_ml_job_name
Stored 'auto_ml_job_name' (str)
[12]:
'automl-churn-03-17-50-14'
Tracking SageMaker Autopilot job progress
SageMaker Autopilot job consists of the following high-level steps : * Analyzing Data, where the dataset is analyzed and Autopilot comes up with a list of ML pipelines that should be tried out on the dataset. The dataset is also split into train and validation sets. * Feature Engineering, where Autopilot performs feature transformation on individual features of the dataset as well as at an aggregate level. * Model Tuning, where the top performing pipeline is selected along with the optimal hyperparameters for the training algorithm (the last stage of the pipeline).
[13]:
print("JobStatus - Secondary Status")
print("------------------------------")
describe_response = sm.describe_auto_ml_job(AutoMLJobName=auto_ml_job_name)
print(describe_response["AutoMLJobStatus"] + " - " + describe_response["AutoMLJobSecondaryStatus"])
job_run_status = describe_response["AutoMLJobStatus"]
while job_run_status not in ("Failed", "Completed", "Stopped"):
describe_response = sm.describe_auto_ml_job(AutoMLJobName=auto_ml_job_name)
job_run_status = describe_response["AutoMLJobStatus"]
print(
describe_response["AutoMLJobStatus"] + " - " + describe_response["AutoMLJobSecondaryStatus"]
)
sleep(30)
JobStatus - Secondary Status
------------------------------
InProgress - Starting
InProgress - Starting
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - AnalyzingData
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - FeatureEngineering
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
InProgress - ModelTuning
Completed - MaxCandidatesReached
AutoPilot automatically generates two executable Jupyter Notebooks: - SageMakerAutopilotDataExplorationNotebook.ipynb and - SageMakerAutopilotCandidateDefinitionNotebook.ipynb. These notebooks are stored in S3. Let us download them onto our SageMaker Notebook instance so we could explore them later.
[14]:
# print(describe_response)
print(describe_response["AutoMLJobArtifacts"]["CandidateDefinitionNotebookLocation"])
print(describe_response["AutoMLJobArtifacts"]["DataExplorationNotebookLocation"])
candidate_nbk = describe_response["AutoMLJobArtifacts"]["CandidateDefinitionNotebookLocation"]
data_explore_nbk = describe_response["AutoMLJobArtifacts"]["DataExplorationNotebookLocation"]
s3://sagemaker-us-west-2-262002448484/sagemaker/DEMO-autopilot-churn/output/automl-churn-03-17-50-14/sagemaker-automl-candidates/pr-1-087be24846d8436faecf8de3c2d70c10cd7e5218bb62499f822657956b/notebooks/SageMakerAutopilotCandidateDefinitionNotebook.ipynb
s3://sagemaker-us-west-2-262002448484/sagemaker/DEMO-autopilot-churn/output/automl-churn-03-17-50-14/sagemaker-automl-candidates/pr-1-087be24846d8436faecf8de3c2d70c10cd7e5218bb62499f822657956b/notebooks/SageMakerAutopilotDataExplorationNotebook.ipynb
[15]:
def split_s3_path(s3_path):
path_parts = s3_path.replace("s3://", "").split("/")
bucket = path_parts.pop(0)
key = "/".join(path_parts)
return bucket, key
s3_bucket, candidate_nbk_key = split_s3_path(candidate_nbk)
_, data_explore_nbk_key = split_s3_path(data_explore_nbk)
print(s3_bucket, candidate_nbk_key, data_explore_nbk_key)
session.download_data(path="./", bucket=s3_bucket, key_prefix=candidate_nbk_key)
session.download_data(path="./", bucket=s3_bucket, key_prefix=data_explore_nbk_key)
sagemaker-us-west-2-262002448484 sagemaker/DEMO-autopilot-churn/output/automl-churn-03-17-50-14/sagemaker-automl-candidates/pr-1-087be24846d8436faecf8de3c2d70c10cd7e5218bb62499f822657956b/notebooks/SageMakerAutopilotCandidateDefinitionNotebook.ipynb sagemaker/DEMO-autopilot-churn/output/automl-churn-03-17-50-14/sagemaker-automl-candidates/pr-1-087be24846d8436faecf8de3c2d70c10cd7e5218bb62499f822657956b/notebooks/SageMakerAutopilotDataExplorationNotebook.ipynb
Results
Now use the describe_auto_ml_job API to look up the best candidate selected by the SageMaker Autopilot job.
[21]:
best_candidate = sm.describe_auto_ml_job(AutoMLJobName=auto_ml_job_name)["BestCandidate"]
best_candidate_name = best_candidate["CandidateName"]
print(best_candidate)
print("\n")
print("CandidateName: " + best_candidate_name)
print(
"FinalAutoMLJobObjectiveMetricName: "
+ best_candidate["FinalAutoMLJobObjectiveMetric"]["MetricName"]
)
print(
"FinalAutoMLJobObjectiveMetricValue: "
+ str(best_candidate["FinalAutoMLJobObjectiveMetric"]["Value"])
)
{'CandidateName': 'tuning-job-1-3572d1c0effe4c2db9-017-ab2e9b77', 'FinalAutoMLJobObjectiveMetric': {'MetricName': 'validation:f1', 'Value': 0.923229992389679}, 'ObjectiveStatus': 'Succeeded', 'CandidateSteps': [{'CandidateStepType': 'AWS::SageMaker::ProcessingJob', 'CandidateStepArn': 'arn:aws:sagemaker:us-west-2:262002448484:processing-job/db-1-dc376427266845c88039d2bdb3410fdf23ec309a025e4f1eb84b5eb853', 'CandidateStepName': 'db-1-dc376427266845c88039d2bdb3410fdf23ec309a025e4f1eb84b5eb853'}, {'CandidateStepType': 'AWS::SageMaker::TrainingJob', 'CandidateStepArn': 'arn:aws:sagemaker:us-west-2:262002448484:training-job/automl-chu-dpp5-1-705bf26de0f842c3b3cd5e0e57f12bce7834aa5c88b04', 'CandidateStepName': 'automl-chu-dpp5-1-705bf26de0f842c3b3cd5e0e57f12bce7834aa5c88b04'}, {'CandidateStepType': 'AWS::SageMaker::TransformJob', 'CandidateStepArn': 'arn:aws:sagemaker:us-west-2:262002448484:transform-job/automl-chu-dpp5-rpb-1-61fcb42b552d4a4b8ab2655f68ff1f4e8048ada21', 'CandidateStepName': 'automl-chu-dpp5-rpb-1-61fcb42b552d4a4b8ab2655f68ff1f4e8048ada21'}, {'CandidateStepType': 'AWS::SageMaker::TrainingJob', 'CandidateStepArn': 'arn:aws:sagemaker:us-west-2:262002448484:training-job/tuning-job-1-3572d1c0effe4c2db9-017-ab2e9b77', 'CandidateStepName': 'tuning-job-1-3572d1c0effe4c2db9-017-ab2e9b77'}], 'CandidateStatus': 'Completed', 'InferenceContainers': [{'Image': '246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-sklearn-automl:0.2-1-cpu-py3', 'ModelDataUrl': 's3://sagemaker-us-west-2-262002448484/sagemaker/DEMO-autopilot-churn/output/automl-churn-03-17-50-14/data-processor-models/automl-chu-dpp5-1-705bf26de0f842c3b3cd5e0e57f12bce7834aa5c88b04/output/model.tar.gz', 'Environment': {'AUTOML_SPARSE_ENCODE_RECORDIO_PROTOBUF': '1', 'AUTOML_TRANSFORM_MODE': 'feature-transform', 'SAGEMAKER_DEFAULT_INVOCATIONS_ACCEPT': 'application/x-recordio-protobuf', 'SAGEMAKER_PROGRAM': 'sagemaker_serve', 'SAGEMAKER_SUBMIT_DIRECTORY': '/opt/ml/model/code'}}, {'Image': '246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-xgboost:1.0-1-cpu-py3', 'ModelDataUrl': 's3://sagemaker-us-west-2-262002448484/sagemaker/DEMO-autopilot-churn/output/automl-churn-03-17-50-14/tuning/automl-chu-dpp5-xgb/tuning-job-1-3572d1c0effe4c2db9-017-ab2e9b77/output/model.tar.gz', 'Environment': {'MAX_CONTENT_LENGTH': '20971520', 'SAGEMAKER_DEFAULT_INVOCATIONS_ACCEPT': 'text/csv', 'SAGEMAKER_INFERENCE_OUTPUT': 'predicted_label', 'SAGEMAKER_INFERENCE_SUPPORTED': 'predicted_label,probability,probabilities'}}, {'Image': '246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-sklearn-automl:0.2-1-cpu-py3', 'ModelDataUrl': 's3://sagemaker-us-west-2-262002448484/sagemaker/DEMO-autopilot-churn/output/automl-churn-03-17-50-14/data-processor-models/automl-chu-dpp5-1-705bf26de0f842c3b3cd5e0e57f12bce7834aa5c88b04/output/model.tar.gz', 'Environment': {'AUTOML_TRANSFORM_MODE': 'inverse-label-transform', 'SAGEMAKER_DEFAULT_INVOCATIONS_ACCEPT': 'text/csv', 'SAGEMAKER_INFERENCE_INPUT': 'predicted_label', 'SAGEMAKER_INFERENCE_OUTPUT': 'predicted_label', 'SAGEMAKER_INFERENCE_SUPPORTED': 'predicted_label,probability,labels,probabilities', 'SAGEMAKER_PROGRAM': 'sagemaker_serve', 'SAGEMAKER_SUBMIT_DIRECTORY': '/opt/ml/model/code'}}], 'CreationTime': datetime.datetime(2021, 2, 3, 18, 12, 19, tzinfo=tzlocal()), 'EndTime': datetime.datetime(2021, 2, 3, 18, 13, 2, tzinfo=tzlocal()), 'LastModifiedTime': datetime.datetime(2021, 2, 3, 18, 15, 52, 159000, tzinfo=tzlocal())}
CandidateName: tuning-job-1-3572d1c0effe4c2db9-017-ab2e9b77
FinalAutoMLJobObjectiveMetricName: validation:f1
FinalAutoMLJobObjectiveMetricValue: 0.923229992389679
Due to some randomness in the algorithms involved, different runs will provide slightly different results, but accuracy will be around or above \(93\%\), which is a good result.
If you are curious to explore the performance of other algorithms that AutoPilot explored, they are enumerated for you below via list_candidates_for_auto_ml_job() API call
[56]:
# sm.describe_auto_ml_job(AutoMLJobName=auto_ml_job_name)
# sm.list_auto_ml_jobs()
sm_dict = sm.list_candidates_for_auto_ml_job(AutoMLJobName=auto_ml_job_name)
[109]:
for item in sm_dict["Candidates"]:
print(item["CandidateName"], item["FinalAutoMLJobObjectiveMetric"])
print(item["InferenceContainers"][1]["Image"], "\n")
tuning-job-1-3572d1c0effe4c2db9-020-e82606f5 {'MetricName': 'validation:binary_f_beta', 'Value': 0.35087719559669495}
174872318107.dkr.ecr.us-west-2.amazonaws.com/linear-learner:latest
tuning-job-1-3572d1c0effe4c2db9-019-343c7bdb {'MetricName': 'validation:f1', 'Value': 0.752780020236969}
246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-xgboost:1.0-1-cpu-py3
tuning-job-1-3572d1c0effe4c2db9-018-7eb3911a {'MetricName': 'validation:f1', 'Value': 0.923229992389679}
246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-xgboost:1.0-1-cpu-py3
tuning-job-1-3572d1c0effe4c2db9-017-ab2e9b77 {'MetricName': 'validation:f1', 'Value': 0.923229992389679}
246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-xgboost:1.0-1-cpu-py3
tuning-job-1-3572d1c0effe4c2db9-016-5aa287c4 {'MetricName': 'validation:binary_f_beta', 'Value': 0.23999999463558197}
174872318107.dkr.ecr.us-west-2.amazonaws.com/linear-learner:latest
tuning-job-1-3572d1c0effe4c2db9-015-56ca82e5 {'MetricName': 'validation:binary_f_beta', 'Value': 0.23999999463558197}
174872318107.dkr.ecr.us-west-2.amazonaws.com/linear-learner:latest
tuning-job-1-3572d1c0effe4c2db9-014-e9ec7e8c {'MetricName': 'validation:f1', 'Value': 0.8602499961853027}
246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-xgboost:1.0-1-cpu-py3
tuning-job-1-3572d1c0effe4c2db9-013-b86fdc91 {'MetricName': 'validation:f1', 'Value': 0.8618000149726868}
246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-xgboost:1.0-1-cpu-py3
tuning-job-1-3572d1c0effe4c2db9-012-c8883e70 {'MetricName': 'validation:f1', 'Value': 0.7779200077056885}
246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-xgboost:1.0-1-cpu-py3
tuning-job-1-3572d1c0effe4c2db9-011-c4159396 {'MetricName': 'validation:binary_f_beta', 'Value': 0.47926267981529236}
174872318107.dkr.ecr.us-west-2.amazonaws.com/linear-learner:latest
Host
Now that we’ve trained the algorithm, let’s create a model and deploy it to a hosted endpoint.
[17]:
timestamp_suffix = strftime("%d-%H-%M-%S", gmtime())
model_name = best_candidate_name + timestamp_suffix + "-model"
model_arn = sm.create_model(
Containers=best_candidate["InferenceContainers"], ModelName=model_name, ExecutionRoleArn=role
)
epc_name = best_candidate_name + timestamp_suffix + "-epc"
ep_config = sm.create_endpoint_config(
EndpointConfigName=epc_name,
ProductionVariants=[
{
"InstanceType": "ml.m5.2xlarge",
"InitialInstanceCount": 1,
"ModelName": model_name,
"VariantName": "main",
}
],
)
ep_name = best_candidate_name + timestamp_suffix + "-ep"
create_endpoint_response = sm.create_endpoint(EndpointName=ep_name, EndpointConfigName=epc_name)
[18]:
sm.get_waiter("endpoint_in_service").wait(EndpointName=ep_name)
Evaluate
Now that we have a hosted endpoint running, we can make real-time predictions from our model very easily, simply by making an http POST request. But first, we’ll need to setup serializers and deserializers for passing our test_data
NumPy arrays to the model behind the endpoint.
[19]:
from io import StringIO
if sagemaker.__version__ < "2":
from sagemaker.predictor import RealTimePredictor
from sagemaker.content_types import CONTENT_TYPE_CSV
predictor = RealTimePredictor(
endpoint=ep_name,
sagemaker_session=session,
content_type=CONTENT_TYPE_CSV,
accept=CONTENT_TYPE_CSV,
)
# Remove the target column from the test data
test_data_inference = test_data.drop("Churn?", axis=1)
# Obtain predictions from SageMaker endpoint
prediction = predictor.predict(
test_data_inference.to_csv(sep=",", header=False, index=False)
).decode("utf-8")
# Load prediction in pandas and compare to ground truth
prediction_df = pd.read_csv(StringIO(prediction), header=None)
accuracy = (test_data.reset_index()["Churn?"] == prediction_df[0]).sum() / len(
test_data_inference
)
print("Accuracy: {}".format(accuracy))
else:
from sagemaker.predictor import Predictor
from sagemaker.serializers import CSVSerializer
from sagemaker.deserializers import CSVDeserializer
predictor = Predictor(
endpoint_name=ep_name,
sagemaker_session=session,
serializer=CSVSerializer(),
deserializer=CSVDeserializer(),
)
# Remove the target column from the test data
test_data_inference = test_data.drop("Churn?", axis=1)
# Obtain predictions from SageMaker endpoint
prediction = predictor.predict(test_data_inference.to_csv(sep=",", header=False, index=False))
# Load prediction in pandas and compare to ground truth
prediction_df = pd.DataFrame(prediction)
accuracy = (test_data.reset_index()["Churn?"] == prediction_df[0]).sum() / len(
test_data_inference
)
print("Accuracy: {}".format(accuracy))
Accuracy: 0.9610194902548725
[20]:
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
accuracy = accuracy_score(test_data.reset_index()["Churn?"], prediction_df[0])
precision = precision_score(test_data.reset_index()["Churn?"], prediction_df[0], pos_label="True.")
recall = recall_score(
test_data.reset_index()["Churn?"], prediction_df[0], pos_label="True.", average="binary"
)
f1 = f1_score(test_data.reset_index()["Churn?"], prediction_df[0], pos_label="True.")
print("Accuracy: {}".format(accuracy))
print("Precision: {}".format(precision))
print("Recall: {}".format(recall))
print("F1: {}".format(f1))
Accuracy: 0.9610194902548725
Precision: 0.896551724137931
Recall: 0.8210526315789474
F1: 0.8571428571428572
Cleanup
The Autopilot job creates many underlying artifacts such as dataset splits, preprocessing scripts, or preprocessed data, etc. This code, when un-commented, deletes them. This operation deletes all the generated models and the auto-generated notebooks as well.
s3 = boto3.resource(‘s3’) s3_bucket = s3.Bucket(bucket)
print(s3_bucket) job_outputs_prefix = ‘{}/output/{}’.format(prefix, auto_ml_job_name) print(job_outputs_prefix) #s3_bucket.objects.filter(Prefix=job_outputs_prefix).delete()
Finally, we delete the endpoint and associated resources.
sm.delete_endpoint(EndpointName=ep_name) sm.delete_endpoint_config(EndpointConfigName=epc_name) sm.delete_model(ModelName=model_name)
Notebook CI Test Results
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.