{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "419ad209-85bf-c4ef-d3d7-d8fa2b6be0ea"
      },
      "source": [
        "# Cervix EDA\n",
        "\n",
        "In this competition we have a multi-class classification problem with **three** classes. We are asked, given an image, to identify the cervix type.\n",
        "\n",
        "From the data description:\n",
        "\n",
        "*In this competition, you will develop algorithms to correctly classify cervix types based on cervical images. These different types of cervix in our data set are all considered normal (not cancerous), but since the transformation zones aren't always visible, some of the patients require further testing while some don't. This decision is very important for the healthcare provider and critical for the patient. Identifying the transformation zones is not an easy task for the healthcare providers, therefore, an algorithm-aided decision will significantly improve the quality and efficiency of cervical cancer screening for these patients.*\n",
        "\n",
        "The submission format is asking for a probability for each of the three different cervix types.\n",
        "\n",
        "In this notebook we will be looking at:\n",
        "\n",
        "* basic dataset stats like number of samples per class, image sizes\n",
        "* different embeddings of RGB image space\n",
        "* pairwise distances and a clustermap of images in RGB space\n",
        "* (linear) model selection with basic multi class evaluation metrics.\n",
        "\n",
        "**If you like this kernel, please give an upvote, thanks! :)**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "6f6e8717-2620-3fbc-dff4-7093bde04be9"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt\n",
        "import seaborn as sns\n",
        "from skimage.io import imread, imshow\n",
        "import cv2\n",
        "\n",
        "%matplotlib inline\n",
        "import plotly.offline as py\n",
        "py.init_notebook_mode(connected=True)\n",
        "import plotly.graph_objs as go\n",
        "import plotly.tools as tls\n",
        "\n",
        "from subprocess import check_output\n",
        "print(check_output([\"ls\", \"../input/train\"]).decode(\"utf8\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "a640fa62-669c-a08c-aa2b-20c4c713bcdd"
      },
      "source": [
        "We are given training images for each of cervix types. Lets first count them for each class."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "df96bdda-f3bf-84f9-0abd-ee6161018846"
      },
      "outputs": [],
      "source": [
        "from glob import glob\n",
        "basepath = '../input/train/'\n",
        "\n",
        "all_cervix_images = []\n",
        "\n",
        "for path in sorted(glob(basepath + \"*\")):\n",
        "    cervix_type = path.split(\"/\")[-1]\n",
        "    cervix_images = sorted(glob(basepath + cervix_type + \"/*\"))\n",
        "    all_cervix_images = all_cervix_images + cervix_images\n",
        "\n",
        "all_cervix_images = pd.DataFrame({'imagepath': all_cervix_images})\n",
        "all_cervix_images['filetype'] = all_cervix_images.apply(lambda row: row.imagepath.split(\".\")[-1], axis=1)\n",
        "all_cervix_images['type'] = all_cervix_images.apply(lambda row: row.imagepath.split(\"/\")[-2], axis=1)\n",
        "all_cervix_images.head()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "172accdb-dc4d-17da-fa39-0740c32a54f9"
      },
      "source": [
        "## Image types\n",
        "\n",
        "Now that we have the data in a handy dataframe we can do a few aggregations on the data. Let us first see how many images there are for each cervix type and which file types they have.\n",
        "\n",
        "All files are in JPG format and Type 2 is the most common one with a little bit more than 50% in the training data in total, Type 1 on the other hand has a little bit less than 20% in the training data."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "1c42f54f-45fb-7a2e-6c89-ba7c7a6a48a3"
      },
      "outputs": [],
      "source": [
        "print('We have a total of {} images in the whole dataset'.format(all_cervix_images.shape[0]))\n",
        "type_aggregation = all_cervix_images.groupby(['type', 'filetype']).agg('count')\n",
        "type_aggregation_p = type_aggregation.apply(lambda row: 1.0*row['imagepath']/all_cervix_images.shape[0], axis=1)\n",
        "\n",
        "fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(10, 8))\n",
        "\n",
        "type_aggregation.plot.barh(ax=axes[0])\n",
        "axes[0].set_xlabel(\"image count\")\n",
        "type_aggregation_p.plot.barh(ax=axes[1])\n",
        "axes[1].set_xlabel(\"training size fraction\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "4b3581d5-c9a3-49e4-427c-08ef8ede1053"
      },
      "source": [
        "Now, lets read the files for each type to get an idea about how the images look like.\n",
        "\n",
        "The images seem to vary alot in they formats, the first two samples have only a circular area with the actual image, the last sample has the image in a rectangle."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "0b500f12-8d54-4dab-a9d1-8bc2fb2de755"
      },
      "outputs": [],
      "source": [
        "fig = plt.figure(figsize=(12,8))\n",
        "\n",
        "i = 1\n",
        "for t in all_cervix_images['type'].unique():\n",
        "    ax = fig.add_subplot(1,3,i)\n",
        "    i+=1\n",
        "    f = all_cervix_images[all_cervix_images['type'] == t]['imagepath'].values[0]\n",
        "    plt.imshow(plt.imread(f))\n",
        "    plt.title('sample for cervix {}'.format(t))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "4db0231d-6d7c-5780-400d-d27cbea25434"
      },
      "source": [
        "## Image dimensions\n",
        "\n",
        "Now, in order to get an idea of how many different shapes of images by class there are, lets have a look at. To reduce runtime, take only a subsample per class."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "24dca5d4-1055-00b3-d5e1-a9eb8de6cb64"
      },
      "outputs": [],
      "source": [
        "from collections import defaultdict\n",
        "\n",
        "images = defaultdict(list)\n",
        "\n",
        "for t in all_cervix_images['type'].unique():\n",
        "    sample_counter = 0\n",
        "    for _, row in all_cervix_images[all_cervix_images['type'] == t].iterrows():\n",
        "        #print('reading image {}'.format(row.imagepath))\n",
        "        try:\n",
        "            img = imread(row.imagepath)\n",
        "            sample_counter +=1\n",
        "            images[t].append(img)\n",
        "        except:\n",
        "            print('image read failed for {}'.format(row.imagepath))\n",
        "        if sample_counter > 35:\n",
        "            break"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "c3aa56f1-e78e-55e4-a961-52a189c18403"
      },
      "outputs": [],
      "source": [
        "dfs = []\n",
        "for t in all_cervix_images['type'].unique():\n",
        "    t_ = pd.DataFrame(\n",
        "        {\n",
        "            'nrows': list(map(lambda i: i.shape[0], images[t])),\n",
        "            'ncols': list(map(lambda i: i.shape[1], images[t])),\n",
        "            'nchans': list(map(lambda i: i.shape[2], images[t])),\n",
        "            'type': t\n",
        "        }\n",
        "    )\n",
        "    dfs.append(t_)\n",
        "\n",
        "shapes_df = pd.concat(dfs, axis=0)\n",
        "shapes_df_grouped = shapes_df.groupby(by=['nchans', 'ncols', 'nrows', 'type']).size().reset_index().sort_values(['type', 0], ascending=False)\n",
        "shapes_df_grouped"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "dabf9549-9b4c-11b8-6182-ec71de2aab5d"
      },
      "source": [
        "All of the images in our sample have three channels, we can ignore this information for now. Now lets build a barplot to get an idea of the distribution of image dimensions by cervix type."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "336a5362-4eee-090d-5ac5-3a84e6970b00"
      },
      "outputs": [],
      "source": [
        "shapes_df_grouped['size_with_type'] = shapes_df_grouped.apply(lambda row: '{}-{}-{}'.format(row.ncols, row.nrows, row.type), axis=1)\n",
        "shapes_df_grouped = shapes_df_grouped.set_index(shapes_df_grouped['size_with_type'].values)\n",
        "shapes_df_grouped['count'] = shapes_df_grouped[[0]]\n",
        "\n",
        "plt.figure(figsize=(10,8))\n",
        "#shapes_df_grouped['count'].plot.barh(figsize=(10,8))\n",
        "sns.barplot(x=\"count\", y=\"size_with_type\", data=shapes_df_grouped)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "1008461b-6a10-64a2-1716-4ba7d2d2dce4"
      },
      "source": [
        "# TSNE embedding\n",
        "\n",
        "We will now take all of the sample images, rescale them & convert them to grayscale. This will result in a matrix, where each row are all flattened pixel for the grayscale images.\n",
        "\n",
        "The original images have, as we have seen earlier, quite a high resolution, so scaling them down to **100 x 100** is resulting in a great loss of information, so the embedding to two dimensions is likely not going to have a good structure where we can separate visually by cervical cancer types. Also, we are giving only very few images per class that TSNE can work with to find a good, distance preserving, embedding.\n",
        "\n",
        "I've added the option to choose to convert them to grayscale before passing them to TSNE. Per default each image will now keep its RGB information and therefore will be transformed to a 100*100*3 = 30.000 dimensional vector."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "f83a4a94-b331-a1a5-def9-68678758c30b"
      },
      "outputs": [],
      "source": [
        "def transform_image(img, rescaled_dim, to_gray=False):\n",
        "    resized = cv2.resize(img, (rescaled_dim, rescaled_dim), cv2.INTER_LINEAR)\n",
        "\n",
        "    if to_gray:\n",
        "        resized = cv2.cvtColor(resized, cv2.COLOR_RGB2GRAY).astype('float')\n",
        "    else:\n",
        "        resized = resized.astype('float')\n",
        "\n",
        "    normalized = cv2.normalize(resized, None, 0.0, 1.0, cv2.NORM_MINMAX)\n",
        "    timg = normalized.reshape(1, np.prod(normalized.shape))\n",
        "\n",
        "    return timg/np.linalg.norm(timg)\n",
        "\n",
        "rescaled_dim = 100\n",
        "\n",
        "all_images = []\n",
        "all_image_types = []\n",
        "\n",
        "for t in all_cervix_images['type'].unique():\n",
        "    all_images = all_images + images[t]\n",
        "    all_image_types = all_image_types + len(images[t])*[t]\n",
        "\n",
        "# - normalize each uint8 image to the value interval [0, 1] as float image\n",
        "# - rgb to gray\n",
        "# - downsample image to rescaled_dim X rescaled_dim\n",
        "# - L2 norm of each sample = 1\n",
        "gray_all_images_as_vecs = [transform_image(img, rescaled_dim) for img in all_images]\n",
        "\n",
        "gray_imgs_mat = np.array(gray_all_images_as_vecs).squeeze()\n",
        "all_image_types = np.array(all_image_types)\n",
        "gray_imgs_mat.shape, all_image_types.shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "ec6a4789-f799-8ce7-7854-0a1fe0a88baf"
      },
      "source": [
        "### 3D t-SNE with cervix indicators\n",
        "\n",
        "Now let's project the 100x100x3 images to three dimensions to check for low dimensional patterns."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "e7fd77ee-8449-2ca7-2691-1906ee10286b"
      },
      "outputs": [],
      "source": [
        "from sklearn.manifold import TSNE\n",
        "tsne = TSNE(\n",
        "    n_components=3,\n",
        "    init='random', # pca\n",
        "    random_state=101,\n",
        "    method='barnes_hut',\n",
        "    n_iter=500,\n",
        "    verbose=2\n",
        ").fit_transform(gray_imgs_mat)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "508824d4-7475-5722-bb85-73c1bd79c1ee"
      },
      "outputs": [],
      "source": [
        "from sklearn import preprocessing\n",
        "\n",
        "trace1 = go.Scatter3d(\n",
        "    x=tsne[:,0],\n",
        "    y=tsne[:,1],\n",
        "    z=tsne[:,2],\n",
        "    mode='markers',\n",
        "    marker=dict(\n",
        "        sizemode='diameter',\n",
        "        color = preprocessing.LabelEncoder().fit_transform(all_image_types),\n",
        "        colorscale = 'Portland',\n",
        "        colorbar = dict(title = 'cervix types'),\n",
        "        line=dict(color='rgb(255, 255, 255)'),\n",
        "        opacity=0.9\n",
        "    )\n",
        ")\n",
        "\n",
        "data=[trace1]\n",
        "layout=dict(height=800, width=800, title='3D embedding of images')\n",
        "fig=dict(data=data, layout=layout)\n",
        "py.iplot(fig, filename='3DBubble')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "5f8e4fcc-7686-ed91-bcfb-78a7384cf911"
      },
      "source": [
        "We can clearly see that there is a big heterogeneous cluster and a few quite distant outliers."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "c9a14afc-2aab-8519-b4d2-53c9eb36337c"
      },
      "outputs": [],
      "source": [
        "for t in all_cervix_images['type'].unique():\n",
        "    tsne_t = tsne[np.where(all_image_types == t), :][0]\n",
        "    plt.scatter(tsne_t[:, 0], tsne_t[:, 1])\n",
        "plt.legend(all_cervix_images['type'].unique())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "dba5ac7f-3868-04c6-79f6-3236bfea95a1"
      },
      "source": [
        "# Image clustering with TSNE embedding\n",
        "\n",
        "Let us now use the compressed images and the distance-preserving transformation of TSNE to get an understanding of what image clusters there are.\n",
        "\n",
        "For that we will use matplotlib to replace the previously red, green and blue points with the actual images that were input to the TSNE transformation."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "72cd02f8-1bb5-77d6-3029-6b78111abe6c"
      },
      "outputs": [],
      "source": [
        "from matplotlib.offsetbox import OffsetImage, AnnotationBbox\n",
        "def imscatter(x, y, images, ax=None, zoom=0.01):\n",
        "    ax = plt.gca()\n",
        "    images = [OffsetImage(image, zoom=zoom) for image in images]\n",
        "    artists = []\n",
        "    for x0, y0, im0 in zip(x, y, images):\n",
        "        ab = AnnotationBbox(im0, (x0, y0), xycoords='data', frameon=False)\n",
        "        artists.append(ax.add_artist(ab))\n",
        "    ax.update_datalim(np.column_stack([x, y]))\n",
        "    ax.autoscale()\n",
        "    #return artists\n",
        "\n",
        "nimgs = 60\n",
        "plt.figure(figsize=(10,8))\n",
        "imscatter(tsne[0:nimgs,0], tsne[0:nimgs,1], all_images[0:nimgs])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "660e028d-fba5-e767-850e-6eaa2c463f84"
      },
      "source": [
        "# Clustering of pairwise image distances\n",
        "\n",
        "To get a different view of how images relate to each other from a purely numerical point of view, lets now look at pairwise distances. For that we'll use scipy's pdist method.\n",
        "\n",
        "The yellow somewhat clustered area tells us there are a few images that have relatively high distance to all other images in the sample of our training images we read.\n",
        "On the left and top of the clustermap we find one of three colors for each row and column, this color indicates the type of cervix.\n",
        "\n",
        "* Type 1: Red\n",
        "* Type 2: Green\n",
        "* Type 3: Blue"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "fe9f7daa-880d-b944-9705-7bec5a550e32"
      },
      "outputs": [],
      "source": [
        "pal = sns.color_palette(\"hls\", 3)\n",
        "sns.palplot(pal)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "f54fc443-44d5-69a9-0d2a-1d506ebd467f"
      },
      "outputs": [],
      "source": [
        "from scipy.spatial.distance import pdist, squareform\n",
        "\n",
        "sq_dists = squareform(pdist(gray_imgs_mat))\n",
        "\n",
        "all_image_types = list(all_image_types)\n",
        "\n",
        "d = {\n",
        "    'Type_1': pal[0],\n",
        "    'Type_2': pal[1],\n",
        "    'Type_3': pal[2]\n",
        "}\n",
        "\n",
        "# translate each sample to its color\n",
        "colors = list(map(lambda t: d[t], all_image_types))\n",
        "\n",
        "sns.clustermap(\n",
        "    sq_dists,\n",
        "    figsize=(12,12),\n",
        "    row_colors=colors, col_colors=colors,\n",
        "    cmap=plt.get_cmap('viridis')\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "3c09b509-3249-d7e0-2c07-f9df3c81cb72"
      },
      "source": [
        "Here is the unclustered distance matrix."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "1becfcaa-0503-2ee6-dc38-8d29ac8e463a"
      },
      "outputs": [],
      "source": [
        "\n",
        "mask = np.zeros_like(sq_dists, dtype=np.bool)\n",
        "mask[np.triu_indices_from(mask)] = True\n",
        "\n",
        "plt.figure(figsize=(12,12))\n",
        "sns.heatmap(sq_dists, cmap=plt.get_cmap('viridis'), square=True, mask=mask)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "763f409d-3e8d-2045-8014-cbd2d4e48001"
      },
      "source": [
        "----------\n",
        "\n",
        "# Image neighbourhood\n",
        "\n",
        "Given the pool of cervix images, and the similarity matrix above, we will now plot the extrem cases of the similarity matrix.\n",
        "\n",
        "To do that we find the image that has:\n",
        "\n",
        "* maximal distance on average compared to the all other images\n",
        "* minimal distance on average compared to the all other images\n",
        "\n",
        "and compare both candidates to the mean image from out set of images."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "32ac6a8f-4636-1f6a-b762-1d4992704a1d"
      },
      "outputs": [],
      "source": [
        "# upper triangle of matrix set to np.nan\n",
        "sq_dists[np.triu_indices_from(mask)] = np.nan\n",
        "sq_dists[0, 0] = np.nan\n",
        "\n",
        "fig = plt.figure(figsize=(12,8))\n",
        "# maximally dissimilar image\n",
        "ax = fig.add_subplot(1,3,1)\n",
        "maximally_dissimilar_image_idx = np.nanargmax(np.nanmean(sq_dists, axis=1))\n",
        "plt.imshow(all_images[maximally_dissimilar_image_idx])\n",
        "plt.title('maximally dissimilar')\n",
        "\n",
        "# maximally similar image\n",
        "ax = fig.add_subplot(1,3,2)\n",
        "maximally_similar_image_idx = np.nanargmin(np.nanmean(sq_dists, axis=1))\n",
        "plt.imshow(all_images[maximally_similar_image_idx])\n",
        "plt.title('maximally similar')\n",
        "\n",
        "# now compute the mean image\n",
        "ax = fig.add_subplot(1,3,3)\n",
        "mean_img = gray_imgs_mat.mean(axis=0).reshape(rescaled_dim, rescaled_dim, 3)\n",
        "plt.imshow(cv2.normalize(mean_img, None, 0.0, 1.0, cv2.NORM_MINMAX))\n",
        "plt.title('mean image')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "940d21df-393e-99e2-8a17-0b314167fc75"
      },
      "source": [
        "The image on the left has quite a lot of blue in it which I would not expect to be present in the majority of samples. I think it is a good representation of maximally dissimilar."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "af9d24c3-b3e0-6fc5-eca6-0eb5f672c793"
      },
      "source": [
        "----------"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "a7b22b2d-86d2-1567-ba25-57d734402371"
      },
      "source": [
        "# Model Selection\n",
        "\n",
        "Now that we've established a basic idea about the data, let's do the most straightforward approach, where we take the resized color images and labels and train a, most likely quite heavily regularized, linear model like logistic regression on it.\n",
        "\n",
        "It is quite important to understand that we only have read a few training instances, 108, and have thousands of dimensions. To be able to cope with that we'll most likely end up using L1 regularization.\n",
        "\n",
        "For the multi-class problem we are faced with here, we'll use standard approach of OVR (one vs rest), meaning we will train three models where each of them is designed to distinguish class 1, 2 and 3 from the others respectively."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "50191441-b485-9dad-d015-9936815e67af"
      },
      "outputs": [],
      "source": [
        "from sklearn.preprocessing import LabelEncoder\n",
        "from sklearn.preprocessing import Normalizer\n",
        "y = LabelEncoder().fit_transform(all_image_types).reshape(-1)\n",
        "X = gray_imgs_mat # no need for normalizing, we already did this earlier Normalizer().fit_transform(gray_imgs_mat)\n",
        "X.shape, y.shape"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "68d8cd18-9a36-c3bf-a6ba-7c5e2a0e51f3"
      },
      "outputs": [],
      "source": [
        "from sklearn.linear_model import LogisticRegression\n",
        "from sklearn.model_selection import GridSearchCV, train_test_split\n",
        "\n",
        "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)\n",
        "\n",
        "X_train.shape, X_test.shape, y_train.shape, y_test.shape"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "bec40df9-ba5a-7ccb-3da5-1d367d5317aa"
      },
      "outputs": [],
      "source": [
        "y_train, y_test"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "c76652c1-6200-a650-06a7-cbcd6c32b212"
      },
      "outputs": [],
      "source": [
        "clf = LogisticRegression()\n",
        "grid = {\n",
        "    'C': [1e-9, 1e-6, 1e-3, 1e0],\n",
        "    'penalty': ['l1', 'l2']\n",
        "}\n",
        "cv = GridSearchCV(clf, grid, scoring='neg_log_loss', n_jobs=-1, verbose=1)\n",
        "cv.fit(X_train, y_train)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "7f97b140-3611-7e2e-f410-7edccb87d377"
      },
      "outputs": [],
      "source": [
        "for i in range(1, len(cv.cv_results_['params'])+1):\n",
        "    rank = cv.cv_results_['rank_test_score'][i-1]\n",
        "    s = cv.cv_results_['mean_test_score'][i-1]\n",
        "    sd = cv.cv_results_['std_test_score'][i-1]\n",
        "    params = cv.cv_results_['params'][i-1]\n",
        "    print(\"{0}. Mean validation neg log loss: {1:.6f} (std: {2:.6f}) - {3}\".format(\n",
        "        rank,\n",
        "        s,\n",
        "        sd,\n",
        "        params\n",
        "    ))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "d10b2a63-f1d8-40d9-2a4e-919056e59adc"
      },
      "outputs": [],
      "source": [
        "y_test_hat_p = cv.predict_proba(X_test)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "43805be4-5ad9-6500-ede7-543485701285"
      },
      "outputs": [],
      "source": [
        "plt.figure(figsize=(12,8))\n",
        "sns.distplot(y_test_hat_p[:,0], color='red')\n",
        "sns.distplot(y_test_hat_p[:,1], color='blue')\n",
        "sns.distplot(y_test_hat_p[:,2], color='green')\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "c5e66954-b3f0-6d4f-1d1e-7abbe3e9a623"
      },
      "outputs": [],
      "source": [
        "dfy = pd.DataFrame({'0': y_test_hat_p[:,0], '1': y_test_hat_p[:,1], '2': y_test_hat_p[:,2]})\n",
        "sns.pairplot(dfy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "38e033ce-6e48-f7e6-ee67-f497ff9fd48c"
      },
      "source": [
        "## Confusion matrix\n",
        "\n",
        "The confusion matrix is a standard analysis tool in binary and multi-class classification where in each entry **C_{i,j}** corresponds to the number of samples that have true class label **i** and are predicted as **j**.\n",
        "\n",
        "We can see that our models have a great problem with confusing class zero with class two."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "747cc004-7745-d1ac-685f-0ada954baba8"
      },
      "outputs": [],
      "source": [
        "from sklearn.metrics import confusion_matrix\n",
        "\n",
        "y_test_hat = cv.predict(X_test)\n",
        "\n",
        "data = [\n",
        "    go.Heatmap(\n",
        "        z=confusion_matrix(y_test, y_test_hat),\n",
        "        x=[0, 1, 2],\n",
        "        y=[0, 1, 2],\n",
        "        colorscale='Viridis',\n",
        "        text = True ,\n",
        "        opacity = 1.0\n",
        "    )\n",
        "]\n",
        "\n",
        "layout = go.Layout(\n",
        "    title='Test Confusion matrix',\n",
        "    xaxis = dict(ticks='', nticks=36),\n",
        "    yaxis = dict(ticks='' ),\n",
        "    width = 900, height = 700,\n",
        "    \n",
        ")\n",
        "\n",
        "\n",
        "fig = go.Figure(data=data, layout=layout)\n",
        "py.iplot(fig, filename='labelled-heatmap')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "_cell_guid": "d63c4c82-3e23-726d-930b-a1838019586c"
      },
      "source": [
        "## Confusion matrix on training set\n",
        "\n",
        "Looking at the train confusion matrix, we can see that we almost only predict class 1."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "594b463c-c745-d249-768f-c1f47e7799c8"
      },
      "outputs": [],
      "source": [
        "from sklearn.metrics import confusion_matrix\n",
        "\n",
        "y_train_hat = cv.predict(X_train)\n",
        "\n",
        "data = [\n",
        "    go.Heatmap(\n",
        "        z=confusion_matrix(y_train, y_train_hat),\n",
        "        x=[0, 1, 2],\n",
        "        y=[0, 1, 2],\n",
        "        colorscale='Viridis',\n",
        "        text = True ,\n",
        "        opacity = 1.0\n",
        "    )\n",
        "]\n",
        "\n",
        "layout = go.Layout(\n",
        "    title='Training Confusion matrix',\n",
        "    xaxis = dict(ticks='', nticks=36),\n",
        "    yaxis = dict(ticks='' ),\n",
        "    width = 900, height = 700,\n",
        "    \n",
        ")\n",
        "\n",
        "\n",
        "fig = go.Figure(data=data, layout=layout)\n",
        "py.iplot(fig, filename='labelled-heatmap')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "_cell_guid": "75c5beb1-3a6f-9bc4-8090-9c9cf9e3a8f1"
      },
      "outputs": [],
      "source": [
        ""
      ]
    }
  ],
  "metadata": {
    "_change_revision": 0,
    "_is_fork": false,
    "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.0"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}