{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploying Voting ensemble Sklearn models on SageMaker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "rm: cannot remove ‘src’: No such file or directory\r\n"
     ]
    }
   ],
   "source": [
    "!rm modelscript_ensemble_sklearn.py\n",
    "!rm *.joblib\n",
    "!rm src"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.7617053998632947\n"
     ]
    }
   ],
   "source": [
    "# Voting Ensemble for Classification\n",
    "import pandas\n",
    "from sklearn import model_selection\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.tree import DecisionTreeClassifier\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.ensemble import VotingClassifier\n",
    "\n",
    "# Get Data\n",
    "url = \"https://raw.githubusercontent.com/jbrownlee/Datasets/master/pima-indians-diabetes.data.csv\"\n",
    "names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']\n",
    "dataframe = pandas.read_csv(url, names=names)\n",
    "array = dataframe.values\n",
    "X = array[:,0:8]\n",
    "Y = array[:,8]\n",
    "\n",
    "# Fit\n",
    "kfold = model_selection.KFold(n_splits=10)\n",
    "# create the sub models\n",
    "estimators = []\n",
    "model1 = LogisticRegression(solver='newton-cg')\n",
    "estimators.append(('logistic', model1))\n",
    "model2 = DecisionTreeClassifier()\n",
    "estimators.append(('cart', model2))\n",
    "model3 = SVC()\n",
    "estimators.append(('svm', model3))\n",
    "# create the ensemble model\n",
    "ensemble = VotingClassifier(estimators)\n",
    "\n",
    "ensemble.fit(X,Y)\n",
    "results = model_selection.cross_val_score(ensemble, X, Y, cv=kfold)\n",
    "print(results.mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save model files"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[33mWARNING: You are using pip version 20.3.3; however, version 21.0.1 is available.\r\n",
      "You should consider upgrading via the '/home/ec2-user/anaconda3/envs/python3/bin/python -m pip install --upgrade pip' command.\u001b[0m\r\n"
     ]
    }
   ],
   "source": [
    "!pip install joblib -q\n",
    "from joblib import dump"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "logistic\n",
      "cart\n",
      "svm\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "['ensemble.joblib']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "for estimator in ensemble.estimators:\n",
    "    print(estimator[0])\n",
    "    dump(estimator[1],estimator[0]+'.joblib')\n",
    "\n",
    "dump(ensemble,'ensemble.joblib')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "cart.joblib  ensemble.joblib  logistic.joblib  svm.joblib\r\n"
     ]
    }
   ],
   "source": [
    "!ls *.joblib"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ensemble.predict(X[0].reshape(1,8))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1 : Write a model transform script\n",
    "\n",
    "#### Make sure you have a ...\n",
    "\n",
    "- \"load_model\" function\n",
    "    - input args are model path\n",
    "    - returns loaded model object\n",
    "    - model name is the same as what you saved the model file as (see above step)\n",
    "<br><br>\n",
    "- \"predict\" function\n",
    "    - input args are the loaded model object and a payload\n",
    "    - returns the result of model.predict\n",
    "    - make sure you format it as a single (or multiple) string return inside a list for real time (for mini batch)\n",
    "    - from a client, a list  or string or np.array that is sent for prediction is interpreted as bytes. Do what you have to for converting back to list or string or np.array\n",
    "    - return the error for debugging\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Writing modelscript_ensemble_sklearn.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile modelscript_ensemble_sklearn.py\n",
    "import sklearn\n",
    "from joblib import load\n",
    "import numpy as np\n",
    "import os\n",
    "\n",
    "#Return loaded model\n",
    "def load_model(modelpath):\n",
    "    print(modelpath)\n",
    "    \n",
    "    # Either load individually\n",
    "    print(\"loading individuals\")\n",
    "    logistic = load(os.path.join(modelpath,'logistic.joblib'))\n",
    "    cart = load(os.path.join(modelpath,'cart.joblib'))\n",
    "    svm = load(os.path.join(modelpath,'svm.joblib'))\n",
    "    \n",
    "    # Or load the entire ensemble\n",
    "    print(\"loading ensemble\")\n",
    "    ensemble = load(os.path.join(modelpath,'ensemble.joblib'))\n",
    "    print(\"loaded\")\n",
    "    return ensemble\n",
    "\n",
    "# return prediction based on loaded model (from the step above) and an input payload\n",
    "def predict(model, payload):\n",
    "    try:\n",
    "        # locally, payload may come in as an np.ndarray\n",
    "        if type(payload)==np.ndarray:\n",
    "            out = [str(model.predict(payload.reshape((1,8))))]\n",
    "        # in remote / container based deployment, payload comes in as a stream of bytes\n",
    "        else:\n",
    "\n",
    "            out = [str(model.predict(np.frombuffer(payload).reshape((1,8))))]\n",
    "    except Exception as e:\n",
    "        out = [type(payload),str(e)] #useful for debugging!\n",
    "    \n",
    "    return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Does this work locally? (not \"_in a container locally_\", but _actually_ in local)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      ".\n",
      "loading individuals\n",
      "loading ensemble\n",
      "loaded\n"
     ]
    }
   ],
   "source": [
    "from modelscript_ensemble_sklearn import *\n",
    "model = load_model('.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['[1.]']"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "predict(model,X[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ok great! Now let's install ezsmdeploy\n",
    "In some cases, installs fail due to an existing package installed called greenlet.\n",
    "This is not a direct dependency of ezsmdeploy but interferes with the installation. \n",
    "To fix this, either install in a virtualenv as seen above, or do:\n",
    "pip install ezsmdeploy[locust] --ignore-installed greenlet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[33mWARNING: Skipping ezsmdeploy as it is not installed.\u001b[0m\n",
      "Collecting ezsmdeploy==1.0.8\n",
      "  Downloading ezsmdeploy-1.0.8-py3-none-any.whl (23 kB)\n",
      "Requirement already satisfied: boto3>=1.14.12 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from ezsmdeploy==1.0.8) (1.16.63)\n",
      "Requirement already satisfied: shortuuid==1.0.1 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from ezsmdeploy==1.0.8) (1.0.1)\n",
      "Requirement already satisfied: sagemaker-studio-image-build==0.5.0 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from ezsmdeploy==1.0.8) (0.5.0)\n",
      "Requirement already satisfied: yaspin==0.16.0 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from ezsmdeploy==1.0.8) (0.16.0)\n",
      "Requirement already satisfied: sagemaker==2.16.1 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from ezsmdeploy==1.0.8) (2.16.1)\n",
      "Requirement already satisfied: smdebug-rulesconfig==0.1.5 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from sagemaker==2.16.1->ezsmdeploy==1.0.8) (0.1.5)\n",
      "Requirement already satisfied: numpy>=1.9.0 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from sagemaker==2.16.1->ezsmdeploy==1.0.8) (1.19.5)\n",
      "Requirement already satisfied: protobuf>=3.1 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from sagemaker==2.16.1->ezsmdeploy==1.0.8) (3.14.0)\n",
      "Requirement already satisfied: packaging>=20.0 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from sagemaker==2.16.1->ezsmdeploy==1.0.8) (20.8)\n",
      "Requirement already satisfied: importlib-metadata>=1.4.0 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from sagemaker==2.16.1->ezsmdeploy==1.0.8) (3.4.0)\n",
      "Requirement already satisfied: google-pasta in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from sagemaker==2.16.1->ezsmdeploy==1.0.8) (0.2.0)\n",
      "Requirement already satisfied: protobuf3-to-dict>=0.1.5 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from sagemaker==2.16.1->ezsmdeploy==1.0.8) (0.1.5)\n",
      "Requirement already satisfied: botocore<1.20.0,>=1.19.63 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from boto3>=1.14.12->ezsmdeploy==1.0.8) (1.19.63)\n",
      "Requirement already satisfied: s3transfer<0.4.0,>=0.3.0 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from boto3>=1.14.12->ezsmdeploy==1.0.8) (0.3.4)\n",
      "Requirement already satisfied: jmespath<1.0.0,>=0.7.1 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from boto3>=1.14.12->ezsmdeploy==1.0.8) (0.10.0)\n",
      "Requirement already satisfied: urllib3<1.27,>=1.25.4 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from botocore<1.20.0,>=1.19.63->boto3>=1.14.12->ezsmdeploy==1.0.8) (1.26.2)\n",
      "Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from botocore<1.20.0,>=1.19.63->boto3>=1.14.12->ezsmdeploy==1.0.8) (2.8.1)\n",
      "Requirement already satisfied: typing-extensions>=3.6.4 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from importlib-metadata>=1.4.0->sagemaker==2.16.1->ezsmdeploy==1.0.8) (3.7.4.3)\n",
      "Requirement already satisfied: zipp>=0.5 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from importlib-metadata>=1.4.0->sagemaker==2.16.1->ezsmdeploy==1.0.8) (3.4.0)\n",
      "Requirement already satisfied: pyparsing>=2.0.2 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from packaging>=20.0->sagemaker==2.16.1->ezsmdeploy==1.0.8) (2.4.7)\n",
      "Requirement already satisfied: six>=1.9 in /home/ec2-user/anaconda3/envs/python3/lib/python3.6/site-packages (from protobuf>=3.1->sagemaker==2.16.1->ezsmdeploy==1.0.8) (1.15.0)\n",
      "Installing collected packages: ezsmdeploy\n",
      "Successfully installed ezsmdeploy-1.0.8\n",
      "\u001b[33mWARNING: You are using pip version 20.3.3; however, version 21.0.1 is available.\n",
      "You should consider upgrading via the '/home/ec2-user/anaconda3/envs/python3/bin/python -m pip install --upgrade pip' command.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "!pip uninstall -y ezsmdeploy\n",
    "!pip install ezsmdeploy==1.0.8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ezsmdeploy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### If you have been running other inference containers in local mode, stop existing containers to avoid conflict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "!docker container stop $(docker container ls -aq) >/dev/null"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploy locally"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### tar all models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "!rm model.tar.gz\n",
    "!find ./ -name \"*.joblib\" | tar -czf model.tar.gz -T -"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[K0:00:00.082217 | compressed model(s)\n",
      "\u001b[K0:00:00.205353 | uploaded model tarball(s) ; check returned modelpath\n",
      "\u001b[K0:00:00.206102 | added requirements file\n",
      "\u001b[K0:00:00.207730 | added source file\n",
      "\u001b[K0:00:00.208891 | added Dockerfile\n",
      "\u001b[K0:00:00.210776 | added model_handler and docker utils\n",
      "\u001b[K0:00:00.210855 | building docker container\n",
      "\u001b[K0:00:43.321145 | built docker container\n",
      "\u001b[K2m∙∙∙\u001b[0m \u001b[K"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "update_endpoint is a no-op in sagemaker>=2.\n",
      "See: https://sagemaker.readthedocs.io/en/stable/v2.html for details.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[K0:00:43.453548 | created model(s). Now deploying on local\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[KAttaching to tmpctykbmbm_algo-1-tj704_1\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m Starting the inference server with 64 workers.\n",
      "\u001b[32m∙●∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [9] [INFO] Starting gunicorn 20.0.4\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [9] [INFO] Listening at: unix:/tmp/gunicorn.sock (9)\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [9] [INFO] Using worker: gevent\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [13] [INFO] Booting worker with pid: 13\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [14] [INFO] Booting worker with pid: 14\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [15] [INFO] Booting worker with pid: 15\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [16] [INFO] Booting worker with pid: 16\n",
      "\u001b[32m∙∙●\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [18] [INFO] Booting worker with pid: 18\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [24] [INFO] Booting worker with pid: 24\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [85] [INFO] Booting worker with pid: 85\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [212] [INFO] Booting worker with pid: 212\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [214] [INFO] Booting worker with pid: 214\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [215] [INFO] Booting worker with pid: 215\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [344] [INFO] Booting worker with pid: 344\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [345] [INFO] Booting worker with pid: 345\n",
      "\u001b[32m●∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [409] [INFO] Booting worker with pid: 409\n",
      "\u001b[32m∙●∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [476] [INFO] Booting worker with pid: 476\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [666] [INFO] Booting worker with pid: 666\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [730] [INFO] Booting worker with pid: 730\n",
      "\u001b[32m∙∙●\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [795] [INFO] Booting worker with pid: 795\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:49 +0000] [796] [INFO] Booting worker with pid: 796\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [923] [INFO] Booting worker with pid: 923\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1115] [INFO] Booting worker with pid: 1115\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1180] [INFO] Booting worker with pid: 1180\n",
      "\u001b[32m●∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1244] [INFO] Booting worker with pid: 1244\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1309] [INFO] Booting worker with pid: 1309\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1373] [INFO] Booting worker with pid: 1373\n",
      "\u001b[32m∙●∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1438] [INFO] Booting worker with pid: 1438\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1565] [INFO] Booting worker with pid: 1565\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1567] [INFO] Booting worker with pid: 1567\n",
      "\u001b[32m∙∙●\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1631] [INFO] Booting worker with pid: 1631\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1695] [INFO] Booting worker with pid: 1695\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1696] [INFO] Booting worker with pid: 1696\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1762] [INFO] Booting worker with pid: 1762\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [1905] [INFO] Booting worker with pid: 1905\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [2018] [INFO] Booting worker with pid: 2018\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [2020] [INFO] Booting worker with pid: 2020\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [2021] [INFO] Booting worker with pid: 2021\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [2086] [INFO] Booting worker with pid: 2086\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [2151] [INFO] Booting worker with pid: 2151\n",
      "\u001b[32m●∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [2152] [INFO] Booting worker with pid: 2152\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:50 +0000] [2153] [INFO] Booting worker with pid: 2153\n",
      "\u001b[32m∙●∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2475] [INFO] Booting worker with pid: 2475\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2474] [INFO] Booting worker with pid: 2474\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2524] [INFO] Booting worker with pid: 2524\n",
      "\u001b[32m∙∙●\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2540] [INFO] Booting worker with pid: 2540\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2686] [INFO] Booting worker with pid: 2686\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2796] [INFO] Booting worker with pid: 2796\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2797] [INFO] Booting worker with pid: 2797\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2927] [INFO] Booting worker with pid: 2927\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2903] [INFO] Booting worker with pid: 2903\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [2929] [INFO] Booting worker with pid: 2929\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3418] [INFO] Booting worker with pid: 3418\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3309] [INFO] Booting worker with pid: 3309\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3478] [INFO] Booting worker with pid: 3478\n",
      "\u001b[32m●∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3502] [INFO] Booting worker with pid: 3502\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3501] [INFO] Booting worker with pid: 3501\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3503] [INFO] Booting worker with pid: 3503\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3506] [INFO] Booting worker with pid: 3506\n",
      "\u001b[32m∙●∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3634] [INFO] Booting worker with pid: 3634\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3699] [INFO] Booting worker with pid: 3699\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3726] [INFO] Booting worker with pid: 3726\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3744] [INFO] Booting worker with pid: 3744\n",
      "\u001b[32m∙∙●\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3844] [INFO] Booting worker with pid: 3844\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [3845] [INFO] Booting worker with pid: 3845\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [4139] [INFO] Booting worker with pid: 4139\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:06:51 +0000] [4301] [INFO] Booting worker with pid: 4301\n",
      "\u001b[32m∙●∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m /opt/ml/model\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m loading individuals\n",
      "\u001b[32m∙∙●\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m loading ensemble\n",
      "\u001b[32m∙∙∙\u001b[0m \u001b[K\u001b[36malgo-1-tj704_1  |\u001b[0m loaded\n",
      "!\u001b[36malgo-1-tj704_1  |\u001b[0m 172.18.0.1 - - [12/Feb/2021:01:06:52 +0000] \"GET /ping HTTP/1.1\" 200 1 \"-\" \"python-urllib3/1.26.2\"\n",
      "\u001b[K0:00:49.014930 | deployed model\n",
      "\u001b[K\u001b[32m0:00:49.015318 | Done! ✔\u001b[0m \n"
     ]
    }
   ],
   "source": [
    "ez = ezsmdeploy.Deploy(model = 'model.tar.gz', # if you intend to add models later, pass model as list, otherwise str\n",
    "                  script = 'modelscript_ensemble_sklearn.py',\n",
    "                  requirements = ['scikit-learn=='+sklearn.__version__,'numpy','joblib'], #or pass in the path to requirements.txt\n",
    "                  instance_type = 'local',\n",
    "                  wait = True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test containerized version locally"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[36malgo-1-tj704_1  |\u001b[0m received input data\r\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m b'\\x00\\x00\\x00\\x00\\x00\\x00\\x18@\\x00\\x00\\x00\\x00\\x00\\x80b@\\x00\\x00\\x00\\x00\\x00\\x00R@\\x00\\x00\\x00\\x00\\x00\\x80A@\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xcd\\xcc\\xcc\\xcc\\xcc\\xcc@@\\xaa\\xf1\\xd2Mb\\x10\\xe4?\\x00\\x00\\x00\\x00\\x00\\x00I@'\r\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m /opt/ml/model\r\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m loading individuals\r\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m loading ensemble\r\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m loaded\r\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m predictions from model\r\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m ['[1.]']\r\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'[1.]'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[36malgo-1-tj704_1  |\u001b[0m 172.18.0.1 - - [12/Feb/2021:01:07:29 +0000] \"POST /invocations HTTP/1.1\" 200 4 \"-\" \"python-urllib3/1.26.2\"\r\n"
     ]
    }
   ],
   "source": [
    "out = ez.predictor.predict(X[0].reshape((1,8)).tobytes()).decode()\n",
    "out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:07:29 +0000] [9] [INFO] Handling signal: term\n",
      "\u001b[36malgo-1-tj704_1  |\u001b[0m [2021-02-12 01:07:30 +0000] [212] [INFO] Worker exiting (pid: 212)\n",
      "\u001b[36mtmpctykbmbm_algo-1-tj704_1 exited with code 0\n",
      "\u001b[0mAborting on container exit...\n"
     ]
    }
   ],
   "source": [
    "!docker container stop $(docker container ls -aq) >/dev/null"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deploy on SageMaker"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[K0:00:00.081814 | compressed model(s)\n",
      "\u001b[K0:00:00.277634 | uploaded model tarball(s) ; check returned modelpath\n",
      "\u001b[K0:00:00.278338 | added requirements file\n",
      "\u001b[K0:00:00.279678 | added source file\n",
      "\u001b[K0:00:00.280726 | added Dockerfile\n",
      "\u001b[K0:00:00.282357 | added model_handler and docker utils\n",
      "\u001b[K0:00:00.282483 | building docker container\n",
      "\u001b[K0:00:43.422978 | built docker container\n",
      "\u001b[K2m●∙∙\u001b[0m \u001b[K"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "update_endpoint is a no-op in sagemaker>=2.\n",
      "See: https://sagemaker.readthedocs.io/en/stable/v2.html for details.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[K0:00:43.630886 | created model(s). Now deploying on ml.m5.xlarge\n",
      "\u001b[K0:07:15.817639 | deployed model\n",
      "\u001b[K0:07:15.818150 | estimated cost is $0.3 per hour\n",
      "\u001b[K\u001b[32m0:07:15.818299 | Done! ✔\u001b[0m \n"
     ]
    }
   ],
   "source": [
    "ezonsm = ezsmdeploy.Deploy(model = 'model.tar.gz', \n",
    "                  script = 'modelscript_ensemble_sklearn.py',\n",
    "                  requirements = ['scikit-learn=='+sklearn.__version__,'numpy','joblib'], #or pass in the path to requirements.txt\n",
    "                  prefix = 'testingprefix',\n",
    "                  wait = True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# !./src/build-docker.sh test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'[1.]'"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "out = ezonsm.predictor.predict(X[0].reshape((1,8)).tobytes()).decode()\n",
    "out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "ezonsm.predictor.delete_endpoint()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "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.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}