{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Spacy Pre-Trained NER Deployment Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Container Structure](https://sagemaker-workshop.com/custom/containers.html)\n",
    "- NER\n",
    "    - predictor.py: (Flask app for inference)\n",
    "    - wsgi.py: (Wrapper around predictor)\n",
    "    - nginx.conf: (Config for nginx front-end)\n",
    "    - serve: program for container hosting, launches gunicorn server\n",
    "    - Note that there is no train for pre-trained\n",
    "- Dockerfile"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Push Docker Image to ECR"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%sh\n",
    "\n",
    "# Name of algo -> ECR\n",
    "algorithm_name=sm-pretrained-spacy\n",
    "\n",
    "cd container\n",
    "\n",
    "#make serve executable\n",
    "chmod +x NER/serve\n",
    "\n",
    "account=$(aws sts get-caller-identity --query Account --output text)\n",
    "\n",
    "# Region, defaults to us-west-2\n",
    "region=$(aws configure get region)\n",
    "region=${region:-us-east-1}\n",
    "\n",
    "fullname=\"${account}.dkr.ecr.${region}.amazonaws.com/${algorithm_name}:latest\"\n",
    "\n",
    "# If the repository doesn't exist in ECR, create it.\n",
    "aws ecr describe-repositories --repository-names \"${algorithm_name}\" > /dev/null 2>&1\n",
    "\n",
    "if [ $? -ne 0 ]\n",
    "then\n",
    "    aws ecr create-repository --repository-name \"${algorithm_name}\" > /dev/null\n",
    "fi\n",
    "\n",
    "# Get the login command from ECR and execute it directly\n",
    "aws ecr get-login-password --region ${region}|docker login --username AWS --password-stdin ${fullname}\n",
    "\n",
    "# Build the docker image locally with the image name and then push it to ECR\n",
    "# with the full name.\n",
    "\n",
    "docker build  -t ${algorithm_name} .\n",
    "docker tag ${algorithm_name} ${fullname}\n",
    "\n",
    "docker push ${fullname}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SageMaker Client Setup\n",
    "\n",
    "- [SageMaker Client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_model): Model, Endpoint Config, and Endpoint Creation\n",
    "- [SageMaker RunTime Client](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-runtime.html#SageMakerRuntime.Client.invoke_endpoint): Endpoint Invocation/Testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import boto3\n",
    "from sagemaker import get_execution_role\n",
    "\n",
    "sm_client = boto3.client(service_name='sagemaker')\n",
    "runtime_sm_client = boto3.client(service_name='sagemaker-runtime')\n",
    "\n",
    "account_id = boto3.client('sts').get_caller_identity()['Account']\n",
    "region = boto3.Session().region_name\n",
    "\n",
    "#not really used in this use case, use when need to store model artifacts (Ex: MME)\n",
    "s3_bucket = 'spacy-sagemaker-us-east-1-bucket'\n",
    "\n",
    "role = get_execution_role()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Model Creation\n",
    "[Documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from time import gmtime, strftime\n",
    "\n",
    "model_name = 'spacy-nermodel-' + strftime(\"%Y-%m-%d-%H-%M-%S\", gmtime())\n",
    "model_url = 's3://{}/spacy/'.format(s3_bucket) ## MODEL S3 URL\n",
    "container = '{}.dkr.ecr.{}.amazonaws.com/sm-pretrained-spacy:latest'.format(account_id, region)\n",
    "instance_type = 'ml.c5d.18xlarge'\n",
    "\n",
    "print('Model name: ' + model_name)\n",
    "print('Model data Url: ' + model_url)\n",
    "print('Container image: ' + container)\n",
    "\n",
    "container = {\n",
    "    'Image': container\n",
    "}\n",
    "\n",
    "create_model_response = sm_client.create_model(\n",
    "    ModelName = model_name,\n",
    "    ExecutionRoleArn = role,\n",
    "    Containers = [container])\n",
    "\n",
    "print(\"Model Arn: \" + create_model_response['ModelArn'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Endpoint Config Creation\n",
    "\n",
    "[Documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_endpoint_config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "endpoint_config_name = 'spacy-ner-config' + strftime(\"%Y-%m-%d-%H-%M-%S\", gmtime())\n",
    "print('Endpoint config name: ' + endpoint_config_name)\n",
    "\n",
    "create_endpoint_config_response = sm_client.create_endpoint_config(\n",
    "    EndpointConfigName = endpoint_config_name,\n",
    "    ProductionVariants=[{\n",
    "        'InstanceType': instance_type,\n",
    "        'InitialInstanceCount': 1,\n",
    "        'InitialVariantWeight': 1,\n",
    "        'ModelName': model_name,\n",
    "        'VariantName': 'AllTraffic'}])\n",
    "\n",
    "print(\"Endpoint config Arn: \" + create_endpoint_config_response['EndpointConfigArn'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Endpoint Creation\n",
    "[Documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_endpoint)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "\n",
    "import time\n",
    "\n",
    "endpoint_name = 'spacy-ner-endpoint' + strftime(\"%Y-%m-%d-%H-%M-%S\", gmtime())\n",
    "print('Endpoint name: ' + endpoint_name)\n",
    "\n",
    "create_endpoint_response = sm_client.create_endpoint(\n",
    "    EndpointName=endpoint_name,\n",
    "    EndpointConfigName=endpoint_config_name)\n",
    "print('Endpoint Arn: ' + create_endpoint_response['EndpointArn'])\n",
    "\n",
    "resp = sm_client.describe_endpoint(EndpointName=endpoint_name)\n",
    "status = resp['EndpointStatus']\n",
    "print(\"Endpoint Status: \" + status)\n",
    "\n",
    "print('Waiting for {} endpoint to be in service...'.format(endpoint_name))\n",
    "waiter = sm_client.get_waiter('endpoint_in_service')\n",
    "waiter.wait(EndpointName=endpoint_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Endpoint Invocation\n",
    "\n",
    "[Documentation](https://boto3.amazonaws.com/v1/documentation/api/1.9.42/reference/services/sagemaker-runtime.html#SageMakerRuntime.Client.invoke_endpoint)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "content_type = \"application/json\"\n",
    "request_body = {\"input\": \"This is a test with NER in America with Amazon and Microsoft in Seattle, writing random stuff.\"}\n",
    "\n",
    "#Serialize data for endpoint\n",
    "data = json.loads(json.dumps(request_body))\n",
    "payload = json.dumps(data)\n",
    "\n",
    "#Endpoint invocation\n",
    "response = runtime_sm_client.invoke_endpoint(\n",
    "    EndpointName=endpoint_name,\n",
    "    ContentType=content_type,\n",
    "    Body=payload)\n",
    "\n",
    "#Parse results\n",
    "result = json.loads(response['Body'].read().decode())['output']\n",
    "result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Delete Endpoint"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before leaving this exercise, it is a good practice to delete the resources created."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# sm_client.delete_endpoint(EndpointName=endpoint_name)\n",
    "# sm_client.delete_endpoint_config(EndpointConfigName=EndpointConfigName)\n",
    "# sm_client.delete_model(ModelName=ModelName)"
   ]
  }
 ],
 "metadata": {
  "instance_type": "ml.t3.medium",
  "kernelspec": {
   "display_name": "Python 3 (Data Science)",
   "language": "python",
   "name": "python3__SAGEMAKER_INTERNAL__arn:aws:sagemaker:us-west-2:236514542706:image/datascience-1.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}