{
"cells": [
{
"cell_type": "markdown",
"id": "d79aeaf0-df88-45ad-8514-60071e41e107",
"metadata": {},
"source": [
"# Deploy NCF model and create Endpoint in SageMaker\n",
"\n",
"This notebook uses `conda_python3` as the default kernel."
]
},
{
"cell_type": "markdown",
"id": "20e909dc-28bf-422e-95b6-49481ba8dfcc",
"metadata": {},
"source": [
"## 0. Setting Environment"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c9f96893-0a50-4eac-8154-029d86a16682",
"metadata": {},
"outputs": [],
"source": [
"import boto3\n",
"import time\n",
"import os\n",
"import sagemaker\n",
"from datetime import datetime"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9b69dd72-5617-4c48-87bb-8928822cf59f",
"metadata": {},
"outputs": [],
"source": [
"sagemaker_session = sagemaker.session.Session()\n",
"role = sagemaker.get_execution_role()\n",
"bucket = sagemaker.session.Session().default_bucket()\n",
"\n",
"print(\"role: \", role)\n",
"print(\"bucket: \", bucket)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"id": "34990d73",
"metadata": {},
"source": [
"### Upload dataset to S3"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a37d8592",
"metadata": {},
"outputs": [],
"source": [
"item_meta_bucket='' # replace with the name of your S3 bucket\n",
"data_filename = \"dataset/merged_data.csv\"\n",
"\n",
"response_upload = boto3.Session().resource('s3').Bucket(item_meta_bucket).Object(data_filename).upload_file(data_filename)\n",
"\n",
"s3_its_filename = \"s3://{}/{}\".format(item_meta_bucket, data_filename)\n",
"\n",
"print(\"s3_merged_data_filename: \\n\", s3_its_filename)"
]
},
{
"cell_type": "markdown",
"id": "96437587-6e27-4206-a484-d114194afe26",
"metadata": {},
"source": [
"## 1. Upload the model (model.tar.gz) to S3"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a56a3b80-6085-4296-b719-c33d93fa483e",
"metadata": {},
"outputs": [],
"source": [
"model_prefix = 'ncf/model'\n",
"model_filename = \"./model/model.tar.gz\"\n",
"\n",
"model_s3_path = sagemaker_session.upload_data(model_filename, bucket, model_prefix)\n",
"print(\"model: \\n\", model_s3_path)"
]
},
{
"cell_type": "markdown",
"id": "bd8e9390-49f9-4070-bf1c-9788fe4a47f9",
"metadata": {},
"source": [
"## 2. Create a model in SageMaker\n",
"Register in Model Registry"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "612a086f-cef0-4f8b-9082-ce4166b89a86",
"metadata": {},
"outputs": [],
"source": [
"# Define model name\n",
"model_name = 'ncf-tf-model'"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "933ab37b-f753-4866-aecc-69a7752936fe",
"metadata": {},
"outputs": [],
"source": [
"# Get the current session's region\n",
"session = boto3.Session()\n",
"region = session.region_name\n",
"\n",
"print(\"Current region:\", region)"
]
},
{
"cell_type": "markdown",
"id": "5a6bd2ae-0011-4741-a84d-ddbdc8449071",
"metadata": {},
"source": [
"
\n",
"The model to register (model.tar.gz) was written in the TensorFlow 2.6 environment.
\n",
"So the container image uses 'tensorflow-inference:2.6-cpu'."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "96c51467-10d8-41be-9db2-03b700d64014",
"metadata": {},
"outputs": [],
"source": [
"sagemaker_client = boto3.client('sagemaker', region_name=region)\n",
"\n",
"# Create a model in SageMaker\n",
"create_model_response = sagemaker_client.create_model(\n",
" ModelName=model_name,\n",
" ExecutionRoleArn=role,\n",
" PrimaryContainer={\n",
" 'Image': '763104351884.dkr.ecr.{}.amazonaws.com/tensorflow-inference:2.6-cpu'.format(region),\n",
" 'ModelDataUrl': 's3://{}/{}'.format(bucket, model_prefix + '/model.tar.gz'),\n",
" }\n",
")"
]
},
{
"cell_type": "markdown",
"id": "b1208a58-c773-4bc3-bf66-20c7ab6b45ee",
"metadata": {},
"source": [
"## 3. Create SageMaker Endpoint"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f43bc2b5-ac7a-4dda-b74e-b75aed005693",
"metadata": {},
"outputs": [],
"source": [
"# Define the endpoint config and endpoint names\n",
"endpoint_config_name = 'ncf-model-endpoint-config'\n",
"endpoint_name = 'ncf-model-endpoint'"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "91999e06-ec82-4386-9c5d-635d7be48e6d",
"metadata": {},
"outputs": [],
"source": [
"# Create endpoint configuration\n",
"create_endpoint_config_response = sagemaker_client.create_endpoint_config(\n",
" EndpointConfigName=endpoint_config_name,\n",
" ProductionVariants=[\n",
" {\n",
" 'VariantName': 'AllTraffic',\n",
" 'ModelName': model_name,\n",
" 'InitialInstanceCount': 1,\n",
" 'InstanceType': 'ml.m5.xlarge', # Choose the desired instance type\n",
" 'InitialVariantWeight': 1\n",
" }\n",
" ]\n",
")\n",
"\n",
"print(\"Endpoint config created:\", create_endpoint_config_response['EndpointConfigArn'])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "83339d2d-fadb-47af-ae83-1ddb803fd8ab",
"metadata": {},
"outputs": [],
"source": [
"# Create endpoint\n",
"create_endpoint_response = sagemaker_client.create_endpoint(\n",
" EndpointName=endpoint_name,\n",
" EndpointConfigName=endpoint_config_name\n",
")\n",
"\n",
"print(\"Creating endpoint...\")"
]
},
{
"cell_type": "markdown",
"id": "5847541d-89cf-4f96-8387-1689c6bbb04e",
"metadata": {},
"source": [
"####Wait until Endpoint creation is complete"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cbddae5e-18a3-4075-9a9e-43bfbd84a8ea",
"metadata": {},
"outputs": [],
"source": [
"# Wait for the endpoint to be in service\n",
"endpoint_status = 'Creating'\n",
"while endpoint_status == 'Creating':\n",
" time.sleep(30)\n",
" endpoint_status = sagemaker_client.describe_endpoint(EndpointName=endpoint_name)['EndpointStatus']\n",
" print(\"Endpoint status:\", endpoint_status)\n",
"\n",
"print(\"Endpoint created:\", create_endpoint_response['EndpointArn'])"
]
},
{
"cell_type": "markdown",
"id": "0134b33c-5773-47aa-98f3-ae32970415e0",
"metadata": {},
"source": [
"# You can make inference requests to the SageMaker Endpoint ARNs below.\n",
"NCF model inference in Lambda Function uses the SageMaker Endpoint ARN below."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2ee05597-373b-44ae-a1c1-84c2717becb7",
"metadata": {},
"outputs": [],
"source": [
"print(\"SageMaker Endpoint ARN : \", create_endpoint_response['EndpointArn'])"
]
},
{
"cell_type": "markdown",
"id": "50015b8d-50ae-47b8-aef9-021ca1be9eca",
"metadata": {},
"source": [
"#### save variable\n",
"Save variables needed for clean-up"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3cbf1830-e87a-4762-80e4-cad7e92f83d6",
"metadata": {},
"outputs": [],
"source": [
"%store endpoint_config_name\n",
"%store endpoint_name\n",
"%store model_name"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "910b921f-bb83-4ab4-aaeb-da59f6d61b9a",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "d50410f1-b712-4094-ae49-9829f58b2bc4",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "331f3da3-d36c-4b72-adbe-a35b61ecd82f",
"metadata": {},
"source": [
"## (Optional) Inference Test\n",
"In the code below, put values such as 1, 2, 100, etc. into user_id to check if 10 recommended items (item_id) are displayed well."
]
},
{
"cell_type": "markdown",
"id": "35dc60d0-8b5c-4397-99a1-71b3bb22df31",
"metadata": {},
"source": [
"### a. Output ITEM_ID (10 items) for the input user_id"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d94938fc-66c2-495a-b86e-c5abd65f3d76",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import json\n",
"import sagemaker\n",
"\n",
"# Load dataset and create user_to_index and item_to_index dictionaries\n",
"data = pd.read_csv('./dataset/merged_data.csv') \n",
"user_ids = data['user_id'].unique()\n",
"item_ids = data.groupby('item_id').size().sort_values(ascending=False).index.to_numpy()\n",
"\n",
"user_to_index = {user_id: index for index, user_id in enumerate(user_ids)}\n",
"item_to_index = {item_id: index for index, item_id in enumerate(item_ids)}\n",
"\n",
"# Enter USER_ID to infer\n",
"user_id = 3\n",
"\n",
"user_idx = user_to_index[user_id]\n",
"item_idx_list = np.array([item_to_index[item_id] for item_id in item_ids])\n",
"user_input = np.full(len(item_ids), user_idx).reshape(-1, 1)\n",
"item_input = item_idx_list.reshape(-1, 1)\n",
"\n",
"\n",
"# Define SageMaker client\n",
"sagemaker_client = boto3.client('sagemaker-runtime')\n",
"\n",
"# Perform inference using invoke_endpoint()\n",
"response = sagemaker_client.invoke_endpoint(\n",
" EndpointName=endpoint_name,\n",
" ContentType='application/json',\n",
" Body=json.dumps(\n",
" {\"user_input\": user_input.tolist(),\n",
" \"item_input\": item_input.tolist()}\n",
" )\n",
")\n",
"\n",
"# Parse response\n",
"predictions = json.loads(response['Body'].read().decode('utf-8'))\n",
"\n",
"# Convert predictions to a 1-dimensional array\n",
"predictions_array = np.array(predictions['predictions']).reshape(-1)\n",
"\n",
"# Get the indices of the top 10 items\n",
"top_10_indices = np.argsort(predictions_array)[-10:][::-1]\n",
"\n",
"# Get the item_ids for the top 10 items\n",
"top_10_item_ids = [item_ids[idx] for idx in top_10_indices]\n",
"\n",
"print(\"Top 10 item IDs for user ID\", user_id, \":\\n\", top_10_item_ids)"
]
},
{
"cell_type": "markdown",
"id": "f6a82a48-019e-4a86-a019-76d75f993502",
"metadata": {},
"source": [
"### b. Output ITEM_ID and detailed information (10 items) for the input user_id together"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "842b115a-0b2e-4b22-91a1-d685495e14c6",
"metadata": {},
"outputs": [],
"source": [
"def get_item_list_details(items_df, item_id_list):\n",
" '''\n",
" Provides additional information of the corresponding ITEM_ID\n",
" '''\n",
" df = pd.DataFrame(data={'ITEM_ID':item_id_list})\n",
" rec_item_df = df.merge(items_df)\n",
" return rec_item_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "14b228b4-3a0b-48b8-9441-bda171b65ad1",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import json\n",
"import sagemaker\n",
"\n",
"# Load dataset and create user_to_index and item_to_index dictionaries\n",
"data = pd.read_csv('./dataset/merged_data.csv') \n",
"user_ids = data['user_id'].unique()\n",
"item_ids = data.groupby('item_id').size().sort_values(ascending=False).index.to_numpy()\n",
"\n",
"user_to_index = {user_id: index for index, user_id in enumerate(user_ids)}\n",
"item_to_index = {item_id: index for index, item_id in enumerate(item_ids)}\n",
"\n",
"# Enter USER_ID to infer\n",
"user_id = 3\n",
"\n",
"user_idx = user_to_index[user_id]\n",
"item_idx_list = np.array([item_to_index[item_id] for item_id in item_ids])\n",
"user_input = np.full(len(item_ids), user_idx).reshape(-1, 1)\n",
"item_input = item_idx_list.reshape(-1, 1)\n",
"\n",
"\n",
"# Define SageMaker client\n",
"sagemaker_client = boto3.client('sagemaker-runtime')\n",
"\n",
"# Perform inference using invoke_endpoint()\n",
"response = sagemaker_client.invoke_endpoint(\n",
" EndpointName=endpoint_name,\n",
" ContentType='application/json',\n",
" Body=json.dumps(\n",
" {\"user_input\": user_input.tolist(),\n",
" \"item_input\": item_input.tolist()}\n",
" )\n",
")\n",
"\n",
"# Parse response\n",
"predictions = json.loads(response['Body'].read().decode('utf-8'))\n",
"\n",
"# Convert predictions to a 1-dimensional array\n",
"predictions_array = np.array(predictions['predictions']).reshape(-1)\n",
"\n",
"# Get the indices of the top 10 items\n",
"top_10_indices = np.argsort(predictions_array)[-10:][::-1]\n",
"\n",
"# Get the item_ids for the top 10 items\n",
"top_10_item_ids = [item_ids[idx] for idx in top_10_indices]\n",
"\n",
"print(\"Top 10 item IDs for user ID\", user_id, \":\\n\")\n",
"\n",
"## join with 'training_item.csv'\n",
"items_df = pd.read_csv(\"./dataset/training_item.csv\")\n",
"get_item_list_details(items_df, top_10_item_ids)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f6dae47a-c2bd-4a98-af08-33ddae759e26",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "conda_python3",
"language": "python",
"name": "conda_python3"
},
"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
}