{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Logistic Regression with Grid Search (scikit-learn)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a href=\"https://colab.research.google.com/github/VertaAI/modeldb-client/blob/master/workflows/demos/sklearn.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# restart your notebook if prompted on Colab\n",
    "try:\n",
    "    import verta\n",
    "except ModuleNotFoundError:\n",
    "    !pip install verta"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This example features:\n",
    "- **scikit-learn**'s `LinearRegression` model\n",
    "- **scikit-learn**'s `GridSearchCV` utility for performing grid search and cross-validation\n",
    "- **verta**'s Python client logging the grid search results\n",
    "- **verta**'s Python client retrieving the best run from the grid search to calculate full training accuracy\n",
    "- predictions against a deployed model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "HOST = \"app.verta.ai\"\n",
    "\n",
    "PROJECT_NAME = \"Census Income Classification - MIT Class\"\n",
    "EXPERIMENT_NAME = \"Logistic Regression\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# NOTE: Go to http://app.verta.ai to get an app key.\n",
    "#\n",
    "#import os\n",
    "#os.environ['VERTA_EMAIL'] = ''\n",
    "#os.environ['VERTA_DEV_KEY'] = ''"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "from sklearn.exceptions import ConvergenceWarning\n",
    "warnings.filterwarnings(\"ignore\", category=ConvergenceWarning)\n",
    "warnings.filterwarnings(\"ignore\", category=FutureWarning)\n",
    "\n",
    "import itertools\n",
    "from multiprocessing import Pool\n",
    "import os\n",
    "import time\n",
    "\n",
    "import six\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import sklearn\n",
    "from sklearn import model_selection\n",
    "from sklearn import linear_model\n",
    "from sklearn import metrics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    import wget\n",
    "except ModuleNotFoundError:\n",
    "    !pip install wget  # you may need pip3\n",
    "    import wget"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Log Workflow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Instantiate Client"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from verta import Client\n",
    "from verta.utils import ModelAPI\n",
    "\n",
    "client = Client(HOST)\n",
    "proj = client.set_project(PROJECT_NAME)\n",
    "expt = client.set_experiment(EXPERIMENT_NAME)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prepare Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "DATASET_PATH = \"./\"\n",
    "\n",
    "train_data_filename = DATASET_PATH + \"census-train.csv\"\n",
    "test_data_filename = DATASET_PATH + \"census-test.csv\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df_train = pd.read_csv(train_data_filename)\n",
    "X_train = df_train.iloc[:,:-1]\n",
    "y_train = df_train.iloc[:, -1]\n",
    "\n",
    "\n",
    "df_train.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prepare Hyperparameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hyperparam_candidates = {\n",
    "    'C': [1e-6, 1e-4, 1e-2, 1e2, 1e4],\n",
    "    'solver': ['lbfgs'],\n",
    "    'max_iter': [15, 28],\n",
    "}\n",
    "hyperparam_sets = [dict(zip(hyperparam_candidates.keys(), values))\n",
    "                   for values\n",
    "                   in itertools.product(*hyperparam_candidates.values())]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run Validation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import json\n",
    "\n",
    "def run_experiment(hyperparams):\n",
    "    # create object to track experiment run\n",
    "    run = client.set_experiment_run()\n",
    "    \n",
    "    # create validation split\n",
    "    (X_val_train, X_val_test,\n",
    "     y_val_train, y_val_test) = model_selection.train_test_split(X_train, y_train,\n",
    "                                                                 test_size=0.2,\n",
    "                                                                 shuffle=True)\n",
    "\n",
    "    # log hyperparameters\n",
    "    run.log_hyperparameters(hyperparams)\n",
    "    print(hyperparams)\n",
    "    \n",
    "    # create and train model\n",
    "    model = linear_model.LogisticRegression(**hyperparams)\n",
    "    model.fit(X_train, y_train)\n",
    "    run.log_attribute(\"features\", X_train.columns.tolist())\n",
    "    run.log_attribute(\"resource_requirements\", json.dumps({\"memory\" : \"512Mi\", \"cpu\" : \"100m\"}))\n",
    "    \n",
    "    # calculate and log validation accuracy\n",
    "    val_acc = model.score(X_val_test, y_val_test)\n",
    "    run.log_metric(\"val_acc\", val_acc)\n",
    "    run.log_tags([\"log_regr\"])\n",
    "    print(\"Validation accuracy: {:.4f}\".format(val_acc))\n",
    "    \n",
    "    # create deployment artifacts\n",
    "    model_api = ModelAPI(X_train, y_train)\n",
    "    requirements = six.StringIO(\"scikit-learn=={}\".format(sklearn.__version__))\n",
    "    #requirements = 'requirements.txt'\n",
    "    \n",
    "    # save and log model\n",
    "    run.log_model_for_deployment(model, model_api, requirements, X_train, y_train)\n",
    "    \n",
    "for hyperparams in hyperparam_sets:\n",
    "    run_experiment(hyperparams)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sklearn.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Revisit Workflow"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Retrieve Best Run"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "best_run = expt.expt_runs.sort(\"metrics.val_acc\", descending=True)[0]\n",
    "print(\"Validation Accuracy: {:.4f}\".format(best_run.get_metric(\"val_acc\")))\n",
    "\n",
    "best_hyperparams = best_run.get_hyperparameters()\n",
    "print(\"Hyperparameters: {}\".format(best_hyperparams))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import verta\n",
    "verta.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Train on Full Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = linear_model.LogisticRegression(**best_hyperparams, multi_class='auto')\n",
    "model.fit(X_train, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from platform import python_version\n",
    "\n",
    "print(python_version())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Calculate Accuracy on Full Training Set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_acc = model.score(X_train, y_train)\n",
    "print(\"Training accuracy: {:.4f}\".format(train_acc))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "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.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
