{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2db061e3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import dependencies and packages neededfrom sklearn import datasets\n",
    "from sklearn import datasets\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.model_selection import train_test_split\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import seaborn as sn\n",
    "import matplotlib.pyplot as plt\n",
    "# import the dataset (stored in a variable)\n",
    "mnist = datasets.load_digits()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "bc9f25c2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'C': 1.0,\n",
       " 'break_ties': False,\n",
       " 'cache_size': 200,\n",
       " 'class_weight': None,\n",
       " 'coef0': 0.0,\n",
       " 'decision_function_shape': 'ovr',\n",
       " 'degree': 3,\n",
       " 'gamma': 'scale',\n",
       " 'kernel': 'rbf',\n",
       " 'max_iter': -1,\n",
       " 'probability': False,\n",
       " 'random_state': None,\n",
       " 'shrinking': True,\n",
       " 'tol': 0.001,\n",
       " 'verbose': False}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# this step is just to demonstrate the adjustable parameters within the algorithm and not a necessary step\n",
    "SVC().get_params()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "ce532ed7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['__abstractmethods__',\n",
       " '__class__',\n",
       " '__delattr__',\n",
       " '__dict__',\n",
       " '__dir__',\n",
       " '__doc__',\n",
       " '__eq__',\n",
       " '__format__',\n",
       " '__ge__',\n",
       " '__getattribute__',\n",
       " '__getstate__',\n",
       " '__gt__',\n",
       " '__hash__',\n",
       " '__init__',\n",
       " '__init_subclass__',\n",
       " '__le__',\n",
       " '__lt__',\n",
       " '__module__',\n",
       " '__ne__',\n",
       " '__new__',\n",
       " '__reduce__',\n",
       " '__reduce_ex__',\n",
       " '__repr__',\n",
       " '__setattr__',\n",
       " '__setstate__',\n",
       " '__sizeof__',\n",
       " '__str__',\n",
       " '__subclasshook__',\n",
       " '__weakref__',\n",
       " '_abc_impl',\n",
       " '_check_feature_names',\n",
       " '_check_n_features',\n",
       " '_check_proba',\n",
       " '_compute_kernel',\n",
       " '_decision_function',\n",
       " '_dense_decision_function',\n",
       " '_dense_fit',\n",
       " '_dense_predict',\n",
       " '_dense_predict_proba',\n",
       " '_estimator_type',\n",
       " '_get_coef',\n",
       " '_get_param_names',\n",
       " '_get_tags',\n",
       " '_impl',\n",
       " '_more_tags',\n",
       " '_pairwise',\n",
       " '_repr_html_',\n",
       " '_repr_html_inner',\n",
       " '_repr_mimebundle_',\n",
       " '_sparse_decision_function',\n",
       " '_sparse_fit',\n",
       " '_sparse_kernels',\n",
       " '_sparse_predict',\n",
       " '_sparse_predict_proba',\n",
       " '_validate_data',\n",
       " '_validate_for_predict',\n",
       " '_validate_targets',\n",
       " '_warn_from_fit_status',\n",
       " 'coef_',\n",
       " 'decision_function',\n",
       " 'fit',\n",
       " 'get_params',\n",
       " 'n_support_',\n",
       " 'predict',\n",
       " 'predict_log_proba',\n",
       " 'predict_proba',\n",
       " 'probA_',\n",
       " 'probB_',\n",
       " 'score',\n",
       " 'set_params']"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# this step is just to demonstrate the api methods and not a necessary step\n",
    "dir(SVC)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1d98d7c7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "training data points: 1212\n",
      "validation data points: 135\n",
      "testing data points: 450\n"
     ]
    }
   ],
   "source": [
    "# take the MNIST data and construct the training and testing split, using 75% of the\n",
    "# data for training and 25% for testing\n",
    "(trainData, testData, trainLabels, testLabels) = train_test_split(np.array(mnist.data),\n",
    "mnist.target, test_size=0.25, random_state=42)\n",
    "\n",
    "# Further take 10% of the training data and use that for validation\n",
    "(trainData, valData, trainLabels, valLabels) = train_test_split(trainData, trainLabels,\n",
    "test_size=0.1, random_state=84)\n",
    "\n",
    "# show the sizes of each data split\n",
    "print(\"training data points: {}\".format(len(trainLabels)))\n",
    "print(\"validation data points: {}\".format(len(valLabels)))\n",
    "print(\"testing data points: {}\".format(len(testLabels)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cc5f64d0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SVC()"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create a model for Support Vector (Machine) Classifier using default parameters\n",
    "model = SVC()\n",
    "# fit the data to the model, both features and labels to train it\n",
    "model.fit(trainData, trainLabels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "8909d123",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([6, 9, 3, 7, 2, 1, 5, 2, 5, 2, 1, 9, 4, 0, 4, 2, 3, 7, 8, 8, 4, 3,\n",
       "       9, 7, 5, 6, 3, 5, 6, 3, 4, 9, 1, 4, 4, 6, 9, 4, 7, 6, 6, 9, 1, 3,\n",
       "       6, 1, 3, 0, 6, 5, 5, 1, 9, 5, 6, 0, 9, 0, 0, 1, 0, 4, 5, 2, 4, 5,\n",
       "       7, 0, 7, 5, 9, 5, 5, 4, 7, 0, 4, 5, 5, 9, 9, 0, 2, 3, 8, 0, 6, 4,\n",
       "       4, 9, 1, 2, 8, 3, 5, 2, 9, 0, 4, 4, 4, 3, 5, 3, 1, 3, 5, 9, 4, 2,\n",
       "       7, 7, 4, 4, 1, 9, 2, 7, 9, 7, 2, 6, 9, 4, 0, 7, 2, 7, 5, 8, 7, 5,\n",
       "       7, 9, 0, 6, 6, 4, 2, 8, 0, 9, 4, 6, 9, 9, 6, 9, 0, 3, 5, 6, 6, 0,\n",
       "       6, 4, 3, 9, 3, 7, 7, 2, 9, 0, 4, 5, 3, 6, 5, 9, 9, 8, 4, 2, 1, 3,\n",
       "       7, 7, 2, 2, 3, 9, 8, 0, 3, 2, 2, 5, 6, 9, 9, 4, 1, 5, 4, 2, 3, 6,\n",
       "       4, 8, 5, 9, 5, 7, 8, 9, 4, 8, 1, 5, 4, 4, 9, 6, 1, 8, 6, 0, 4, 5,\n",
       "       2, 7, 4, 6, 4, 5, 6, 0, 3, 2, 3, 6, 7, 1, 5, 1, 4, 7, 6, 8, 8, 5,\n",
       "       5, 1, 6, 2, 8, 8, 9, 9, 7, 6, 2, 2, 2, 3, 4, 8, 8, 3, 6, 0, 9, 7,\n",
       "       7, 0, 1, 0, 4, 5, 1, 5, 3, 6, 0, 4, 1, 0, 0, 3, 6, 5, 9, 7, 3, 5,\n",
       "       5, 9, 9, 8, 5, 3, 3, 2, 0, 5, 8, 3, 4, 0, 2, 4, 6, 4, 3, 4, 5, 0,\n",
       "       5, 2, 1, 3, 1, 4, 1, 1, 7, 0, 1, 5, 2, 1, 2, 8, 7, 0, 6, 4, 8, 8,\n",
       "       5, 1, 8, 4, 5, 8, 7, 9, 8, 6, 0, 6, 2, 0, 7, 9, 8, 9, 5, 2, 7, 7,\n",
       "       1, 8, 7, 4, 3, 8, 3, 5, 6, 0, 0, 3, 0, 5, 0, 0, 4, 1, 2, 8, 4, 5,\n",
       "       9, 6, 3, 1, 8, 8, 4, 2, 3, 8, 9, 8, 8, 5, 0, 6, 3, 3, 7, 1, 6, 4,\n",
       "       1, 2, 1, 1, 6, 4, 7, 4, 8, 3, 4, 0, 5, 1, 9, 4, 5, 7, 6, 3, 7, 0,\n",
       "       5, 9, 7, 5, 9, 7, 4, 2, 1, 9, 0, 7, 5, 8, 3, 6, 3, 9, 6, 9, 5, 0,\n",
       "       1, 5, 5, 8, 3, 3, 6, 2, 6, 5])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# use the model to classify the datapoints within the test set\n",
    "y_pred2=model.predict(testData)\n",
    "# print predicted classifications\n",
    "y_pred2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "74fcc243",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([6, 9, 3, 7, 2, 1, 5, 2, 5, 2, 1, 9, 4, 0, 4, 2, 3, 7, 8, 8, 4, 3,\n",
       "       9, 7, 5, 6, 3, 5, 6, 3, 4, 9, 1, 4, 4, 6, 9, 4, 7, 6, 6, 9, 1, 3,\n",
       "       6, 1, 3, 0, 6, 5, 5, 1, 9, 5, 6, 0, 9, 0, 0, 1, 0, 4, 5, 2, 4, 5,\n",
       "       7, 0, 7, 5, 9, 5, 5, 4, 7, 0, 4, 5, 5, 9, 9, 0, 2, 3, 8, 0, 6, 4,\n",
       "       4, 9, 1, 2, 8, 3, 5, 2, 9, 0, 4, 4, 4, 3, 5, 3, 1, 3, 5, 9, 4, 2,\n",
       "       7, 7, 4, 4, 1, 9, 2, 7, 8, 7, 2, 6, 9, 4, 0, 7, 2, 7, 5, 8, 7, 5,\n",
       "       7, 7, 0, 6, 6, 4, 2, 8, 0, 9, 4, 6, 9, 9, 6, 9, 0, 3, 5, 6, 6, 0,\n",
       "       6, 4, 3, 9, 3, 9, 7, 2, 9, 0, 4, 5, 3, 6, 5, 9, 9, 8, 4, 2, 1, 3,\n",
       "       7, 7, 2, 2, 3, 9, 8, 0, 3, 2, 2, 5, 6, 9, 9, 4, 1, 5, 4, 2, 3, 6,\n",
       "       4, 8, 5, 9, 5, 7, 8, 9, 4, 8, 1, 5, 4, 4, 9, 6, 1, 8, 6, 0, 4, 5,\n",
       "       2, 7, 4, 6, 4, 5, 6, 0, 3, 2, 3, 6, 7, 1, 5, 1, 4, 7, 6, 8, 8, 5,\n",
       "       5, 1, 6, 2, 8, 8, 9, 9, 7, 6, 2, 2, 2, 3, 4, 8, 8, 3, 6, 0, 9, 7,\n",
       "       7, 0, 1, 0, 4, 5, 1, 5, 3, 6, 0, 4, 1, 0, 0, 3, 6, 5, 9, 7, 3, 5,\n",
       "       5, 9, 9, 8, 5, 3, 3, 2, 0, 5, 8, 3, 4, 0, 2, 4, 6, 4, 3, 4, 5, 0,\n",
       "       5, 2, 1, 3, 1, 4, 1, 1, 7, 0, 1, 5, 2, 1, 2, 8, 7, 0, 6, 4, 8, 8,\n",
       "       5, 1, 8, 4, 5, 8, 7, 9, 8, 5, 0, 6, 2, 0, 7, 9, 8, 9, 5, 2, 7, 7,\n",
       "       1, 8, 7, 4, 3, 8, 3, 5, 6, 0, 0, 3, 0, 5, 0, 0, 4, 1, 2, 8, 4, 5,\n",
       "       9, 6, 3, 1, 8, 8, 4, 2, 3, 8, 9, 8, 8, 5, 0, 6, 3, 3, 7, 1, 6, 4,\n",
       "       1, 2, 1, 1, 6, 4, 7, 4, 8, 3, 4, 0, 5, 1, 9, 4, 5, 7, 6, 3, 7, 0,\n",
       "       5, 9, 7, 5, 9, 7, 4, 2, 1, 9, 0, 7, 5, 3, 3, 6, 3, 9, 6, 9, 5, 0,\n",
       "       1, 5, 5, 8, 3, 3, 6, 2, 6, 5])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# print test labels (ground truth) that is compared to classifier\n",
    "testLabels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "07924776",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[43  0  0  0  0  0  0  0  0  0]\n",
      " [ 0 37  0  0  0  0  0  0  0  0]\n",
      " [ 0  0 38  0  0  0  0  0  0  0]\n",
      " [ 0  0  0 45  0  0  0  0  1  0]\n",
      " [ 0  0  0  0 55  0  0  0  0  0]\n",
      " [ 0  0  0  0  0 58  1  0  0  0]\n",
      " [ 0  0  0  0  0  0 45  0  0  0]\n",
      " [ 0  0  0  0  0  0  0 40  0  1]\n",
      " [ 0  0  0  0  0  0  0  0 37  1]\n",
      " [ 0  0  0  0  0  0  0  1  0 47]]\n",
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0      1.000     1.000     1.000        43\n",
      "           1      1.000     1.000     1.000        37\n",
      "           2      1.000     1.000     1.000        38\n",
      "           3      1.000     0.978     0.989        46\n",
      "           4      1.000     1.000     1.000        55\n",
      "           5      1.000     0.983     0.991        59\n",
      "           6      0.978     1.000     0.989        45\n",
      "           7      0.976     0.976     0.976        41\n",
      "           8      0.974     0.974     0.974        38\n",
      "           9      0.959     0.979     0.969        48\n",
      "\n",
      "    accuracy                          0.989       450\n",
      "   macro avg      0.989     0.989     0.989       450\n",
      "weighted avg      0.989     0.989     0.989       450\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# display the metrics of the model's classification through confusion matrix and classification report\n",
    "# (accuracy / precision / recall / f1)\n",
    "from sklearn import metrics\n",
    "print(metrics.confusion_matrix(testLabels, y_pred2))\n",
    "\n",
    "\n",
    "print(metrics.classification_report(testLabels, y_pred2, digits=3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "dbe4d51b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "     Actual  Predicted\n",
      "0         6          6\n",
      "1         9          9\n",
      "2         3          3\n",
      "3         7          7\n",
      "4         2          2\n",
      "..      ...        ...\n",
      "445       3          3\n",
      "446       6          6\n",
      "447       2          2\n",
      "448       6          6\n",
      "449       5          5\n",
      "\n",
      "[450 rows x 2 columns]\n"
     ]
    }
   ],
   "source": [
    "# use Pandas to create a dataframe to view the ground truth and classifications side by side\n",
    "data = {'Actual': testLabels,\n",
    "        'Predicted': y_pred2}\n",
    "df = pd.DataFrame(data)\n",
    "print(df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2ffc4924",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "           0      1.000     1.000     1.000        43\n",
      "           1      1.000     1.000     1.000        37\n",
      "           2      1.000     1.000     1.000        38\n",
      "           3      1.000     0.978     0.989        46\n",
      "           4      1.000     1.000     1.000        55\n",
      "           5      1.000     0.983     0.991        59\n",
      "           6      0.978     1.000     0.989        45\n",
      "           7      0.976     0.976     0.976        41\n",
      "           8      0.974     0.974     0.974        38\n",
      "           9      0.959     0.979     0.969        48\n",
      "\n",
      "    accuracy                          0.989       450\n",
      "   macro avg      0.989     0.989     0.989       450\n",
      "weighted avg      0.989     0.989     0.989       450\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Use Seaborn to create a confusion matrix heatmap and MatPlotLib to display\n",
    "confusion_matrix = pd.crosstab(df['Actual'], df['Predicted'], rownames=['Actual'], colnames=['Predicted'])\n",
    "sn.heatmap(confusion_matrix, annot=True)\n",
    "plt.show()\n",
    "print(metrics.classification_report(testLabels, y_pred2, digits=3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "f704725b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create an iterative grid search to tune the parameters\n",
    "import joblib\n",
    "from sklearn.model_selection import GridSearchCV"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "fe31837a",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a function to show the results of the classifier under different parameter values\n",
    "def print_results(results):\n",
    "    print('Best Parameters: {}\\n'.format(results.best_params_))\n",
    "    \n",
    "    means = results.cv_results_['mean_test_score']\n",
    "    stds = results.cv_results_['std_test_score']\n",
    "    for mean, std, params in zip(means, stds, results.cv_results_['params']):\n",
    "        print('{} (+/-{}) for {}'.format(round(mean, 3), round(std * 2, 3), params))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "219de4fc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Best Parameters: {'C': 10, 'kernel': 'rbf'}\n",
      "\n",
      "0.974 (+/-0.006) for {'C': 0.1, 'kernel': 'linear'}\n",
      "0.94 (+/-0.027) for {'C': 0.1, 'kernel': 'rbf'}\n",
      "0.974 (+/-0.006) for {'C': 1, 'kernel': 'linear'}\n",
      "0.986 (+/-0.011) for {'C': 1, 'kernel': 'rbf'}\n",
      "0.974 (+/-0.006) for {'C': 10, 'kernel': 'linear'}\n",
      "0.987 (+/-0.006) for {'C': 10, 'kernel': 'rbf'}\n"
     ]
    }
   ],
   "source": [
    "# Create a model using the Support Vector Classifier / Machine algorithm\n",
    "svm = SVC()\n",
    "# Set the parameters to be tuned\n",
    "parameters = {\n",
    "    'kernel' : ['linear', 'rbf'],\n",
    "    'C' : [0.1, 1, 10]\n",
    "}\n",
    "# instantiate the grid search\n",
    "cv = GridSearchCV(svm, parameters, cv=5)\n",
    "# fit the training data to the model\n",
    "cv.fit(trainData, trainLabels.ravel())\n",
    "# display the model evaluation with the different paramter combination values (C value and kernel)\n",
    "print_results(cv)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "58ab5b9f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'C': 10,\n",
       " 'break_ties': False,\n",
       " 'cache_size': 200,\n",
       " 'class_weight': None,\n",
       " 'coef0': 0.0,\n",
       " 'decision_function_shape': 'ovr',\n",
       " 'degree': 3,\n",
       " 'gamma': 'scale',\n",
       " 'kernel': 'rbf',\n",
       " 'max_iter': -1,\n",
       " 'probability': False,\n",
       " 'random_state': None,\n",
       " 'shrinking': True,\n",
       " 'tol': 0.001,\n",
       " 'verbose': False}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# show the best paramters for classification (not a necesary step unless you are saving best parameter models to compare)\n",
    "cv.best_estimator_.get_params()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e6276cc",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
